def test_interpret_currency(self): amount = 1260 receiver = BOB_VK sender = ALICE_VK contract_tx = self.ordered_tx( ContractTransactionBuilder.create_currency_tx(sender_sk=ALICE_SK, receiver_vk=receiver, amount=amount)) self.interpreter = SenecaInterpreter() currency_contract = get_contract_exports( self.interpreter.ex, self.interpreter.contracts_table, contract_id='currency') sender_initial_balance = currency_contract.get_balance(sender) receiver_initial_balance = currency_contract.get_balance(receiver) self.interpreter.interpret(contract_tx) # Assert the contract ran and updated the expected rows self.assertEqual(currency_contract.get_balance(sender), sender_initial_balance - amount) self.assertEqual(currency_contract.get_balance(receiver), receiver_initial_balance + amount) # Assert the contract was added to the queue self.assertEqual(self.interpreter.queue_size, 1) self.assertEqual(self.interpreter.queue[0], contract_tx.transaction)
def test_flushes_with_update(self): """ Tests that calling .flush on an self.interpreter with update_state=True after interpreting several transactions successfully commits the changes to the database """ sender = ALICE_VK receiver = BOB_VK self.interpreter = SenecaInterpreter() dummy_contract = get_contract_exports(self.interpreter.ex, self.interpreter.contracts_table, contract_id='dummy') sender_initial_balance = dummy_contract.get_balance(sender) contract_tx = self.ordered_tx( ContractTransactionBuilder.create_dummy_tx(sender_sk=ALICE_SK, receiver_vk=receiver, fail=False)) self.interpreter.interpret(contract_tx) self.assertEqual(dummy_contract.get_balance(sender), sender_initial_balance + 500) contract_tx = self.ordered_tx( ContractTransactionBuilder.create_dummy_tx(sender_sk=ALICE_SK, receiver_vk=receiver, fail=False)) self.interpreter.interpret(contract_tx) self.interpreter.flush(update_state=True) self.assertEqual(dummy_contract.get_balance(sender), sender_initial_balance + 1000)
def test_rerun_fail(self): orig = SenecaInterpreter._run_contract def mocked_rerun(*args, **kwargs): if kwargs.get('rerun'): return None return orig(*args, **kwargs) sender = ALICE_VK receiver = BOB_VK self.interpreter = SenecaInterpreter() dummy_contract = get_contract_exports(self.interpreter.ex, self.interpreter.contracts_table, contract_id='dummy') sender_initial_balance = dummy_contract.get_balance(sender) contract_tx = self.ordered_tx( ContractTransactionBuilder.create_dummy_tx(sender_sk=ALICE_SK, receiver_vk=receiver, fail=False)) self.interpreter.interpret(contract_tx) contract_tx = self.ordered_tx( ContractTransactionBuilder.create_dummy_tx(sender_sk=ALICE_SK, receiver_vk=receiver, fail=True)) with mock.patch( 'cilantro.protocol.interpreter.SenecaInterpreter._run_contract', side_effect=mocked_rerun, autospec=True) as mock_some_method: with self.assertRaises(Exception) as context: self.interpreter.interpret(contract_tx)
def test_check_contract_async_shuffled_adhoc_checks(self): def assertCondition(): self.assertEqual(contracts[0].transaction, self.interpreter.queue[0]) self.assertEqual(contracts[2].transaction, self.interpreter.queue[1]) self.assertEqual(contracts[1].transaction, self.interpreter.queue[2]) self.assertEqual(len(self.interpreter.queue), 3) self.interpreter.stop() sender = ALICE_VK receiver = BOB_VK now = int(time.time() * 1000) self.interpreter = SenecaInterpreter() contracts = [ self.ordered_tx(ContractTransactionBuilder.create_dummy_tx(sender_sk=ALICE_SK, receiver_vk=receiver, fail=False)) \ for i in range(5) ] contracts[0]._data.utcTimeMs = now - 2000 contracts[1]._data.utcTimeMs = now contracts[2]._data.utcTimeMs = now - 1000 contracts[3]._data.utcTimeMs = now + 5000 contracts[4]._data.utcTimeMs = now + 6000 for c in contracts: self.interpreter.interpret(c, async=True) t = Timer(2.8, assertCondition) t.start()
def test_run_bad_contract_reverts_to_last_successful_contract_remove_partial( self): """ Tests that running a failing contract reverts any database changes it made before the point of failure """ sender = ALICE_VK receiver = BOB_VK self.interpreter = SenecaInterpreter() dummy_contract = get_contract_exports(self.interpreter.ex, self.interpreter.contracts_table, contract_id='dummy') sender_initial_balance = dummy_contract.get_balance(sender) self.assertEqual(dummy_contract.get_balance(sender), sender_initial_balance) contract_tx = self.ordered_tx( ContractTransactionBuilder.create_dummy_tx(sender_sk=ALICE_SK, receiver_vk=receiver, fail=False)) self.interpreter.interpret(contract_tx) self.assertEqual(dummy_contract.get_balance(sender), sender_initial_balance + 500) # NOTE it attempts to update the balance to 123 and add the same user again # Updating should work and adding already added user contract_tx = self.ordered_tx( ContractTransactionBuilder.create_dummy_tx(sender_sk=ALICE_SK, receiver_vk=receiver, fail=True)) self.interpreter.interpret(contract_tx) self.assertEqual(dummy_contract.get_balance(sender), sender_initial_balance + 500)
def run_contracts_in_interpreter(num_contracts=100): interpreter = SenecaInterpreter() # Generate the contracts log.notice("Generating {} contracts...".format(num_contracts)) contracts = [God.random_contract_tx() for _ in range(num_contracts)] log.notice("Done generating contracts.") # Interpret them count = 0 start = time.time() log.info("Running contracts...") for contract in contracts: interpreter._run_contract(contract) count += 1 if count % checkpoint_freq == 0: log.notice("{} contracts run so far.".format(count)) total_time = time.time() - start cps = num_contracts / total_time log.important("Ran {} contracts in {} seconds".format( num_contracts, round(total_time, 4))) log.important("{} contracts per second.".format(round(cps, 2))) assert interpreter.queue_size == num_contracts, "Interpreter queue size {} does not match num contracts {}..y tho"\ .format(interpreter.queue_size, num_contracts)
def test_restore_to_beginning(self): orig = SenecaInterpreter._run_contract sender = ALICE_VK receiver = BOB_VK self.interpreter = SenecaInterpreter() dummy_contract = get_contract_exports(self.interpreter.ex, self.interpreter.contracts_table, contract_id='dummy') sender_initial_balance = dummy_contract.get_balance(sender) contract_tx = self.ordered_tx( ContractTransactionBuilder.create_dummy_tx(sender_sk=ALICE_SK, receiver_vk=receiver, fail=True)) self.interpreter.interpret(contract_tx)
def test_run_bad_contract_reverts_to_last_successful_contract(self): """ Tests that running a failing contract reverts any database changes it made before the point of failure """ receiver = BOB_VK sender = ALICE_VK self.interpreter = SenecaInterpreter() currency_contract = get_contract_exports( self.interpreter.ex, self.interpreter.contracts_table, contract_id='currency') sender_initial_balance = currency_contract.get_balance(sender) contract_tx = self.ordered_tx( ContractTransactionBuilder.create_currency_tx(sender_sk=ALICE_SK, receiver_vk=receiver, amount=1000)) self.interpreter.interpret(contract_tx) self.assertEqual(currency_contract.get_balance(sender), sender_initial_balance - 1000) contract_tx = self.ordered_tx( ContractTransactionBuilder.create_currency_tx(sender_sk=ALICE_SK, receiver_vk=receiver, amount=200)) self.interpreter.interpret(contract_tx) self.assertEqual(currency_contract.get_balance(sender), sender_initial_balance - 1200) contract_tx = self.ordered_tx( ContractTransactionBuilder.create_currency_tx(sender_sk=ALICE_SK, receiver_vk=receiver, amount=60)) self.interpreter.interpret(contract_tx) self.assertEqual(currency_contract.get_balance(sender), sender_initial_balance - 1260) contract_tx = self.ordered_tx( ContractTransactionBuilder.create_currency_tx(sender_sk=ALICE_SK, receiver_vk=receiver, amount=3696947)) self.interpreter.interpret(contract_tx) self.assertEqual(currency_contract.get_balance(sender), sender_initial_balance - 1260)
def __init__(self, signing_key, witness_list, url, sbb_index): self.log = get_logger("SubBlockBuilder_{}".format(sb_index)) # Comment out below for more granularity in debugging # self.log.setLevel(logging.INFO) #self.log.important("SubBlockBuilder started with url {}".format(url)) # Register signal handler to teardown signal.signal(signal.SIGTERM, self._signal_teardown) # need to revisit this when threading strategy is clear self.loop = asyncio.new_event_loop() asyncio.set_event_loop(self.loop) self.signing_key = signing_key # witness_list should be comma separated list of ip:vk self.witness_table = self._parse_witness_list(witness_list) self.url = url self.sbb_index = sbb_index self.block_num = (int) sbb_index / 16 # hard code this for now self.sub_block_num = (int) sb_index % 16 self.num_txs = 0 self.num_sub_blocks = 0 self.tasks = [] #SenecaInterpreter connect with BlockManager (parent process that spawned this one) self.context = zmq.asyncio.Context() self.socket = self.context.socket(zmq.PAIR) # For communication with main process self.socket.connect(self.url) # do we need this still? or do we move it to a util methods self.verifying_key = wallet.get_vk(self.signing_key) skg = SigningKey(seed=bytes.fromhex(sk)) self.vk = skg.verify_key.encode().hex() self.public_key = self.vk2pk(self.vk) self.private_key = crypto_sign_ed25519_sk_to_curve25519(skg._signing_key).hex() priv = PrivateKey(bytes.fromhex(self.private_key)) publ = priv.public_key self.public_key = public_key = encode(publ._public_key) self.secret = secret_key = encode(priv._private_key) self.pending_txs = LinkedHashTable() self.interpreter = SenecaInterpreter() self._recently_seen = CappedSet(max_size=DUPE_TABLE_SIZE) try: self._subscribe_to_witnesses() # start event loop and start listening witness sockets as well as mgr self.run_loop_second_time() except Exception as e: err_msg = '\n' + '!' * 64 + '\nSBB terminating with exception:\n' + str(traceback.format_exc()) err_msg += '\n' + '!' * 64 + '\n' self.log.error(err_msg) finally: self._teardown()
def test_check_contract_correct_order(self): sender = ALICE_VK receiver = BOB_VK now = int(time.time() * 1000) self.interpreter = SenecaInterpreter() contracts = [ self.ordered_tx(ContractTransactionBuilder.create_dummy_tx(sender_sk=ALICE_SK, receiver_vk=receiver, fail=False)) \ for i in range(5) ] contracts[0]._data.utcTimeMs = now - 2000 contracts[1]._data.utcTimeMs = now - 1000 contracts[2]._data.utcTimeMs = now contracts[3]._data.utcTimeMs = now + 1000 contracts[4]._data.utcTimeMs = now + 2000 for c in contracts: self.interpreter.interpret(c, async=True) self.assertEqual(contracts, [heappop(self.interpreter.heap)[1] for i in range(5)])
def test_queue_binary(self): """ Tests that queue_binary returns a list of serialized ContractTransactions """ sender = ALICE_VK receiver = BOB_VK self.interpreter = SenecaInterpreter() dummy_contract = get_contract_exports(self.interpreter.ex, self.interpreter.contracts_table, contract_id='dummy') contracts = [] for i in range(5): contract_tx = self.ordered_tx( ContractTransactionBuilder.create_dummy_tx( sender_sk=ALICE_SK, receiver_vk=receiver, fail=False)) self.interpreter.interpret(contract_tx) contracts.append(contract_tx.transaction) for actual, expected in zip([c.serialize() for c in contracts], self.interpreter.queue_binary): self.assertEqual(actual, expected)
def test_interpret_invalid_type(self): self.interpreter = SenecaInterpreter() not_a_contract = 'sup bro im a string' self.assertRaises(AssertionError, self.interpreter.interpret, not_a_contract)
def test_init(self): self.interpreter = SenecaInterpreter() # this should not blow up self.assertTrue(self.interpreter.ex is not None) self.assertTrue(self.interpreter.contracts_table is not None)
class TestSenecaInterpreter(TestCase): def setUp(self): reset_db() def tearDown(self): self.interpreter.ex.cur.close() self.interpreter.ex.conn.close() def ordered_tx(self, contract): return OrderingContainer.create(contract, MN_VK) def test_init(self): self.interpreter = SenecaInterpreter() # this should not blow up self.assertTrue(self.interpreter.ex is not None) self.assertTrue(self.interpreter.contracts_table is not None) def test_interpret_invalid_type(self): self.interpreter = SenecaInterpreter() not_a_contract = 'sup bro im a string' self.assertRaises(AssertionError, self.interpreter.interpret, not_a_contract) def test_interpret_currency(self): amount = 1260 receiver = BOB_VK sender = ALICE_VK contract_tx = self.ordered_tx( ContractTransactionBuilder.create_currency_tx(sender_sk=ALICE_SK, receiver_vk=receiver, amount=amount)) self.interpreter = SenecaInterpreter() currency_contract = get_contract_exports( self.interpreter.ex, self.interpreter.contracts_table, contract_id='currency') sender_initial_balance = currency_contract.get_balance(sender) receiver_initial_balance = currency_contract.get_balance(receiver) self.interpreter.interpret(contract_tx) # Assert the contract ran and updated the expected rows self.assertEqual(currency_contract.get_balance(sender), sender_initial_balance - amount) self.assertEqual(currency_contract.get_balance(receiver), receiver_initial_balance + amount) # Assert the contract was added to the queue self.assertEqual(self.interpreter.queue_size, 1) self.assertEqual(self.interpreter.queue[0], contract_tx.transaction) def test_run_bad_contract_reverts_to_last_successful_contract(self): """ Tests that running a failing contract reverts any database changes it made before the point of failure """ receiver = BOB_VK sender = ALICE_VK self.interpreter = SenecaInterpreter() currency_contract = get_contract_exports( self.interpreter.ex, self.interpreter.contracts_table, contract_id='currency') sender_initial_balance = currency_contract.get_balance(sender) contract_tx = self.ordered_tx( ContractTransactionBuilder.create_currency_tx(sender_sk=ALICE_SK, receiver_vk=receiver, amount=1000)) self.interpreter.interpret(contract_tx) self.assertEqual(currency_contract.get_balance(sender), sender_initial_balance - 1000) contract_tx = self.ordered_tx( ContractTransactionBuilder.create_currency_tx(sender_sk=ALICE_SK, receiver_vk=receiver, amount=200)) self.interpreter.interpret(contract_tx) self.assertEqual(currency_contract.get_balance(sender), sender_initial_balance - 1200) contract_tx = self.ordered_tx( ContractTransactionBuilder.create_currency_tx(sender_sk=ALICE_SK, receiver_vk=receiver, amount=60)) self.interpreter.interpret(contract_tx) self.assertEqual(currency_contract.get_balance(sender), sender_initial_balance - 1260) contract_tx = self.ordered_tx( ContractTransactionBuilder.create_currency_tx(sender_sk=ALICE_SK, receiver_vk=receiver, amount=3696947)) self.interpreter.interpret(contract_tx) self.assertEqual(currency_contract.get_balance(sender), sender_initial_balance - 1260) def test_run_bad_contract_reverts_to_last_successful_contract_remove_partial( self): """ Tests that running a failing contract reverts any database changes it made before the point of failure """ sender = ALICE_VK receiver = BOB_VK self.interpreter = SenecaInterpreter() dummy_contract = get_contract_exports(self.interpreter.ex, self.interpreter.contracts_table, contract_id='dummy') sender_initial_balance = dummy_contract.get_balance(sender) self.assertEqual(dummy_contract.get_balance(sender), sender_initial_balance) contract_tx = self.ordered_tx( ContractTransactionBuilder.create_dummy_tx(sender_sk=ALICE_SK, receiver_vk=receiver, fail=False)) self.interpreter.interpret(contract_tx) self.assertEqual(dummy_contract.get_balance(sender), sender_initial_balance + 500) # NOTE it attempts to update the balance to 123 and add the same user again # Updating should work and adding already added user contract_tx = self.ordered_tx( ContractTransactionBuilder.create_dummy_tx(sender_sk=ALICE_SK, receiver_vk=receiver, fail=True)) self.interpreter.interpret(contract_tx) self.assertEqual(dummy_contract.get_balance(sender), sender_initial_balance + 500) def test_flushes_with_update(self): """ Tests that calling .flush on an self.interpreter with update_state=True after interpreting several transactions successfully commits the changes to the database """ sender = ALICE_VK receiver = BOB_VK self.interpreter = SenecaInterpreter() dummy_contract = get_contract_exports(self.interpreter.ex, self.interpreter.contracts_table, contract_id='dummy') sender_initial_balance = dummy_contract.get_balance(sender) contract_tx = self.ordered_tx( ContractTransactionBuilder.create_dummy_tx(sender_sk=ALICE_SK, receiver_vk=receiver, fail=False)) self.interpreter.interpret(contract_tx) self.assertEqual(dummy_contract.get_balance(sender), sender_initial_balance + 500) contract_tx = self.ordered_tx( ContractTransactionBuilder.create_dummy_tx(sender_sk=ALICE_SK, receiver_vk=receiver, fail=False)) self.interpreter.interpret(contract_tx) self.interpreter.flush(update_state=True) self.assertEqual(dummy_contract.get_balance(sender), sender_initial_balance + 1000) def test_flushes_without_update(self): """ Tests that calling .flush on an self.interpreter with update_state=False after interpreting several transactions successfully rolls back """ sender = ALICE_VK receiver = BOB_VK self.interpreter = SenecaInterpreter() dummy_contract = get_contract_exports(self.interpreter.ex, self.interpreter.contracts_table, contract_id='dummy') sender_initial_balance = dummy_contract.get_balance(sender) contract_tx = self.ordered_tx( ContractTransactionBuilder.create_dummy_tx(sender_sk=ALICE_SK, receiver_vk=receiver, fail=False)) self.interpreter.interpret(contract_tx) contract_tx = self.ordered_tx( ContractTransactionBuilder.create_dummy_tx(sender_sk=ALICE_SK, receiver_vk=receiver, fail=False)) self.interpreter.interpret(contract_tx) self.interpreter.flush(update_state=True) self.assertEqual(dummy_contract.get_balance(sender), sender_initial_balance + 1000) contract_tx = self.ordered_tx( ContractTransactionBuilder.create_dummy_tx(sender_sk=ALICE_SK, receiver_vk=receiver, fail=False)) self.interpreter.interpret(contract_tx) self.assertEqual(dummy_contract.get_balance(sender), sender_initial_balance + 1500) self.interpreter.flush(update_state=False) self.assertEqual(dummy_contract.get_balance(sender), sender_initial_balance + 1000) def test_queue_binary(self): """ Tests that queue_binary returns a list of serialized ContractTransactions """ sender = ALICE_VK receiver = BOB_VK self.interpreter = SenecaInterpreter() dummy_contract = get_contract_exports(self.interpreter.ex, self.interpreter.contracts_table, contract_id='dummy') contracts = [] for i in range(5): contract_tx = self.ordered_tx( ContractTransactionBuilder.create_dummy_tx( sender_sk=ALICE_SK, receiver_vk=receiver, fail=False)) self.interpreter.interpret(contract_tx) contracts.append(contract_tx.transaction) for actual, expected in zip([c.serialize() for c in contracts], self.interpreter.queue_binary): self.assertEqual(actual, expected) def test_check_contract_correct_order(self): sender = ALICE_VK receiver = BOB_VK now = int(time.time() * 1000) self.interpreter = SenecaInterpreter() contracts = [ self.ordered_tx(ContractTransactionBuilder.create_dummy_tx(sender_sk=ALICE_SK, receiver_vk=receiver, fail=False)) \ for i in range(5) ] contracts[0]._data.utcTimeMs = now - 2000 contracts[1]._data.utcTimeMs = now - 1000 contracts[2]._data.utcTimeMs = now contracts[3]._data.utcTimeMs = now + 1000 contracts[4]._data.utcTimeMs = now + 2000 for c in contracts: self.interpreter.interpret(c, async=True) self.assertEqual(contracts, [heappop(self.interpreter.heap)[1] for i in range(5)]) def test_check_contract_correct_order_shuffled(self): sender = ALICE_VK receiver = BOB_VK now = int(time.time() * 1000) self.interpreter = SenecaInterpreter() contracts = [ self.ordered_tx(ContractTransactionBuilder.create_dummy_tx(sender_sk=ALICE_SK, receiver_vk=receiver, fail=False)) \ for i in range(5) ] contracts[0]._data.utcTimeMs = now - 2000 contracts[1]._data.utcTimeMs = now - 1000 contracts[2]._data.utcTimeMs = now + 1000 contracts[3]._data.utcTimeMs = now contracts[4]._data.utcTimeMs = now - 3000 for c in contracts: self.interpreter.interpret(c, async=True) self.assertEqual(contracts[4], heappop(self.interpreter.heap)[1]) self.assertEqual(contracts[0], heappop(self.interpreter.heap)[1]) self.assertEqual(contracts[1], heappop(self.interpreter.heap)[1]) self.assertEqual(contracts[3], heappop(self.interpreter.heap)[1]) self.assertEqual(contracts[2], heappop(self.interpreter.heap)[1]) @async_run_for(3) def test_check_contract_async_shuffled_adhoc_checks(self): def assertCondition(): self.assertEqual(contracts[0].transaction, self.interpreter.queue[0]) self.assertEqual(contracts[2].transaction, self.interpreter.queue[1]) self.assertEqual(contracts[1].transaction, self.interpreter.queue[2]) self.assertEqual(len(self.interpreter.queue), 3) self.interpreter.stop() sender = ALICE_VK receiver = BOB_VK now = int(time.time() * 1000) self.interpreter = SenecaInterpreter() contracts = [ self.ordered_tx(ContractTransactionBuilder.create_dummy_tx(sender_sk=ALICE_SK, receiver_vk=receiver, fail=False)) \ for i in range(5) ] contracts[0]._data.utcTimeMs = now - 2000 contracts[1]._data.utcTimeMs = now contracts[2]._data.utcTimeMs = now - 1000 contracts[3]._data.utcTimeMs = now + 5000 contracts[4]._data.utcTimeMs = now + 6000 for c in contracts: self.interpreter.interpret(c, async=True) t = Timer(2.8, assertCondition) t.start() def test_rerun_fail(self): orig = SenecaInterpreter._run_contract def mocked_rerun(*args, **kwargs): if kwargs.get('rerun'): return None return orig(*args, **kwargs) sender = ALICE_VK receiver = BOB_VK self.interpreter = SenecaInterpreter() dummy_contract = get_contract_exports(self.interpreter.ex, self.interpreter.contracts_table, contract_id='dummy') sender_initial_balance = dummy_contract.get_balance(sender) contract_tx = self.ordered_tx( ContractTransactionBuilder.create_dummy_tx(sender_sk=ALICE_SK, receiver_vk=receiver, fail=False)) self.interpreter.interpret(contract_tx) contract_tx = self.ordered_tx( ContractTransactionBuilder.create_dummy_tx(sender_sk=ALICE_SK, receiver_vk=receiver, fail=True)) with mock.patch( 'cilantro.protocol.interpreter.SenecaInterpreter._run_contract', side_effect=mocked_rerun, autospec=True) as mock_some_method: with self.assertRaises(Exception) as context: self.interpreter.interpret(contract_tx) def test_restore_to_beginning(self): orig = SenecaInterpreter._run_contract sender = ALICE_VK receiver = BOB_VK self.interpreter = SenecaInterpreter() dummy_contract = get_contract_exports(self.interpreter.ex, self.interpreter.contracts_table, contract_id='dummy') sender_initial_balance = dummy_contract.get_balance(sender) contract_tx = self.ordered_tx( ContractTransactionBuilder.create_dummy_tx(sender_sk=ALICE_SK, receiver_vk=receiver, fail=True)) self.interpreter.interpret(contract_tx)
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Properties shared among all states (ie via self.parent.some_prop) self.pending_sigs, self.pending_txs = deque(), deque() self.interpreter = SenecaInterpreter() self.current_hash = BlockStorageDriver.get_latest_block_hash()