def test_log_handler_mk_filter_params(contract): log_handler = LogHandler(contract.web3) filter_params = log_handler.mk_filter_params(1, 2) assert 'fromBlock' in filter_params assert 'toBlock' in filter_params filter_params_with_address = log_handler.mk_filter_params( 1, 2, contract.address) assert contract.address == filter_params_with_address['address'] topics = [encode_hex(test_event_signature)] filter_params_with_address = log_handler.mk_filter_params( 1, 2, contract.address, topics) assert topics == filter_params_with_address['topics']
def test_log_handler_get_new_logs_without_forks(contract): w3 = contract.web3 log_handler = LogHandler(w3) counter = itertools.count() contract.transact(default_tx_detail).emit_log(next(counter)) mine(w3, 1) logs_block2 = log_handler.get_new_logs(address=contract.address) assert len(logs_block2) == 1 assert int(logs_block2[0]['data'], 16) == 0 assert log_handler.get_new_logs() == tuple() contract.transact(default_tx_detail).emit_log(next(counter)) mine(w3, 1) logs_block3 = log_handler.get_new_logs(address=contract.address) assert len(logs_block3) == 1 assert int(logs_block3[0]['data'], 16) == 1 assert log_handler.get_new_logs() == tuple() contract.transact(default_tx_detail).emit_log(next(counter)) mine(w3, 1) contract.transact(default_tx_detail).emit_log(next(counter)) mine(w3, 1) logs_block4_5 = log_handler.get_new_logs(address=contract.address) assert len(logs_block4_5) == 2 assert int(logs_block4_5[0]['data'], 16) == 2 assert int(logs_block4_5[1]['data'], 16) == 3 assert log_handler.get_new_logs() == tuple()
def test_log_handler_get_new_logs_with_forks(contract): w3 = contract.web3 log_handler = LogHandler(w3) counter = itertools.count() snapshot_id = take_snapshot(w3) current_block_number = w3.eth.blockNumber contract.transact(default_tx_detail).emit_log(next(counter)) mine(w3, 1) revert_to_snapshot(w3, snapshot_id) assert w3.eth.blockNumber == current_block_number contract.transact(default_tx_detail).emit_log(next(counter)) mine(w3, 1) contract.transact(default_tx_detail).emit_log(next(counter)) mine(w3, 1) logs = log_handler.get_new_logs() # assert len(logs) == 2 assert int(logs[0]['data'], 16) == 1 assert int(logs[1]['data'], 16) == 2 assert log_handler.get_new_logs() == tuple()
def test_shard_tracker_fetch_candidate_head(vmc, mock_score, mock_is_new_head, expected_score, expected_is_new_head): shard_id = 0 log_handler = LogHandler(vmc.web3) shard_tracker = ShardTracker(shard_id, log_handler, vmc.address) mock_collation_added_logs = [{ 'header': [None] * 10, 'score': mock_score[i], 'is_new_head': mock_is_new_head[i], } for i in range(len(mock_score))] # mock collation_added_logs shard_tracker.new_logs = mock_collation_added_logs for i in range(len(mock_score)): log = shard_tracker.fetch_candidate_head() assert log['score'] == expected_score[i] assert log['is_new_head'] == expected_is_new_head[i] with pytest.raises(NextLogUnavailable): log = shard_tracker.fetch_candidate_head()
def test_vmc_contract_calls(vmc): # noqa: F811 shard_id = 0 validator_index = 0 primary_key = test_keys[validator_index] primary_addr = primary_key.public_key.to_canonical_address() default_gas = vmc.config['DEFAULT_GAS'] log_handler = LogHandler(vmc.web3) shard_tracker = ShardTracker(shard_id, log_handler, vmc.address) vmc.set_shard_tracker(shard_id, shard_tracker) # test `mk_build_transaction_detail` ###################################### build_transaction_detail = vmc.mk_build_transaction_detail( nonce=0, gas=10000, ) assert 'nonce' in build_transaction_detail assert 'gas' in build_transaction_detail assert 'chainId' in build_transaction_detail with pytest.raises(ValueError): build_transaction_detail = vmc.mk_build_transaction_detail( nonce=None, gas=10000, ) with pytest.raises(ValueError): build_transaction_detail = vmc.mk_build_transaction_detail( nonce=0, gas=None, ) # test `mk_contract_tx_detail` ###################################### tx_detail = vmc.mk_contract_tx_detail( sender_address=ZERO_ADDR, gas=vmc.config['DEFAULT_GAS'], ) assert 'from' in tx_detail assert 'gas' in tx_detail with pytest.raises(ValueError): tx_detail = vmc.mk_contract_tx_detail( sender_address=ZERO_ADDR, gas=None, ) with pytest.raises(ValueError): tx_detail = vmc.mk_contract_tx_detail( sender_address=None, gas=vmc.config['DEFAULT_GAS'], ) # test the deployment of vmc ###################################### # deploy vmc if it is not deployed yet. if not is_vmc_deployed(vmc): logger.debug('is_vmc_deployed(vmc) == True') # import test_key import_key(vmc, primary_key) deploy_initiating_contracts(vmc, primary_key) mine(vmc, 1) assert is_vmc_deployed(vmc) lookahead_blocks = vmc.config['LOOKAHEAD_PERIODS'] * vmc.config[ 'PERIOD_LENGTH'] # test `deposit` and `get_eligible_proposer` ###################################### # now we require 1 validator. # if there is currently no validator, we deposit one. # else, there should only be one validator, for easier testing. num_validators = vmc.call( vmc.mk_contract_tx_detail(sender_address=primary_addr, gas=default_gas)).num_validators() if num_validators == 0: # deposit as the first validator validator_addr = send_deposit_tx(vmc) # TODO: error occurs when we don't mine so many blocks mine(vmc, lookahead_blocks) assert vmc.get_eligible_proposer(shard_id) == validator_addr # assert the current_block_number >= LOOKAHEAD_PERIODS * PERIOD_LENGTH # to ensure that `get_eligible_proposer` works current_block_number = vmc.web3.eth.blockNumber if current_block_number < lookahead_blocks: mine(vmc, lookahead_blocks - current_block_number) assert vmc.web3.eth.blockNumber >= lookahead_blocks num_validators = vmc.call( vmc.mk_contract_tx_detail(sender_address=primary_addr, gas=default_gas)).num_validators() assert num_validators == 1 assert vmc.get_eligible_proposer(shard_id) != ZERO_ADDR logger.debug("vmc_handler.num_validators()=%s", num_validators) # test `add_header` ###################################### # create a testing collation header, whose parent is the genesis header0_1 = mk_testing_colhdr(vmc, shard_id, GENESIS_COLHDR_HASH, 1) # if a header is added before its parent header is added, `add_header` should fail # TransactionFailed raised when assertions fail with pytest.raises(TransactionFailed): header_parent_not_added = mk_testing_colhdr( vmc, shard_id, header0_1.hash, 1, ) add_header_constant_call(vmc, header_parent_not_added) # when a valid header is added, the `add_header` call should succeed vmc.add_header(header0_1) mine(vmc, vmc.config['PERIOD_LENGTH']) # if a header is added before, the second trial should fail with pytest.raises(TransactionFailed): add_header_constant_call(vmc, header0_1) # when a valid header is added, the `add_header` call should succeed header0_2 = mk_testing_colhdr(vmc, shard_id, header0_1.hash, 2) vmc.add_header(header0_2) mine(vmc, vmc.config['PERIOD_LENGTH']) # confirm the score of header1 and header2 are correct or not colhdr0_1_score = vmc.call( vmc.mk_contract_tx_detail(sender_address=primary_addr, gas=default_gas)).get_collation_header_score( shard_id, header0_1.hash) assert colhdr0_1_score == 1 colhdr0_2_score = vmc.call( vmc.mk_contract_tx_detail(sender_address=primary_addr, gas=default_gas)).get_collation_header_score( shard_id, header0_2.hash) assert colhdr0_2_score == 2 # confirm the logs are correct assert vmc.get_next_log(shard_id)['score'] == 2 assert vmc.get_next_log(shard_id)['score'] == 1 with pytest.raises(NextLogUnavailable): vmc.get_next_log(shard_id) # filter logs in multiple shards vmc.set_shard_tracker(1, ShardTracker(1, LogHandler(vmc.web3), vmc.address)) header1_1 = mk_testing_colhdr(vmc, 1, GENESIS_COLHDR_HASH, 1) vmc.add_header(header1_1) mine(vmc, 1) header0_3 = mk_testing_colhdr(vmc, shard_id, header0_2.hash, 3) vmc.add_header(header0_3) mine(vmc, 1) assert vmc.get_next_log(0)['score'] == 3 # ensure that `get_next_log(0)` does not affect `get_next_log(1)` assert vmc.get_next_log(1)['score'] == 1 logs = vmc.web3.eth.getLogs({ "fromBlock": 0, "toBlock": vmc.web3.eth.blockNumber, "topics": [ encode_hex(ShardTracker.COLLATION_ADDED_TOPIC), ] }) assert len(logs) == 4 vmc.tx_to_shard( test_keys[1].public_key.to_canonical_address(), shard_id, 100000, 1, b'', value=1234567, ) mine(vmc, 1) receipt_value = vmc.call( vmc.mk_contract_tx_detail(sender_address=primary_addr, gas=default_gas)).receipts__value(0) # the receipt value should be equaled to the transaction value assert receipt_value == 1234567 # test `withdraw` ###################################### send_withdraw_tx(vmc, validator_index) mine(vmc, 1) # if the only validator withdraws, because there is no validator anymore, the result of # `num_validators` must be 0. num_validators = vmc.call( vmc.mk_contract_tx_detail(sender_address=primary_addr, gas=default_gas)).num_validators() assert num_validators == 0
def test_smc_contract_calls(smc_handler): # noqa: F811 web3 = smc_handler.web3 shard_id = 0 validator_index = 0 primary_key = test_keys[validator_index] primary_addr = primary_key.public_key.to_canonical_address() default_gas = smc_handler.config['DEFAULT_GAS'] shard_0_tracker = ShardTracker(shard_id, LogHandler(web3), smc_handler.address) lookahead_blocks = (smc_handler.config['LOOKAHEAD_PERIODS'] * smc_handler.config['PERIOD_LENGTH']) # test `deposit` and `get_eligible_proposer` ###################################### # now we require 1 validator. # if there is currently no validator, we deposit one. # else, there should only be one validator, for easier testing. num_validators = smc_handler.call( make_call_context(sender_address=primary_addr, gas=default_gas)).num_validators() if num_validators == 0: # deposit as the first validator smc_handler.deposit() # TODO: error occurs when we don't mine so many blocks mine(web3, lookahead_blocks) assert smc_handler.get_eligible_proposer( shard_id) == smc_handler.sender_address # assert the current_block_number >= LOOKAHEAD_PERIODS * PERIOD_LENGTH # to ensure that `get_eligible_proposer` works current_block_number = web3.eth.blockNumber if current_block_number < lookahead_blocks: mine(web3, lookahead_blocks - current_block_number) assert web3.eth.blockNumber >= lookahead_blocks num_validators = smc_handler.call( make_call_context(sender_address=primary_addr, gas=default_gas)).num_validators() assert num_validators == 1 assert smc_handler.get_eligible_proposer(shard_id) != ZERO_ADDR logger.debug("smc_handler.num_validators()=%s", num_validators) # test `add_header` ###################################### # create a testing collation header, whose parent is the genesis header0_1 = make_testing_colhdr(smc_handler, shard_id, GENESIS_COLHDR_HASH, 1) # if a header is added before its parent header is added, `add_header` should fail # TransactionFailed raised when assertions fail with pytest.raises(TransactionFailed): header_parent_not_added = make_testing_colhdr( smc_handler, shard_id, header0_1.hash, 1, ) add_header_constant_call(smc_handler, header_parent_not_added) # when a valid header is added, the `add_header` call should succeed smc_handler.add_header(header0_1) mine(web3, smc_handler.config['PERIOD_LENGTH']) # if a header is added before, the second trial should fail with pytest.raises(TransactionFailed): add_header_constant_call(smc_handler, header0_1) # when a valid header is added, the `add_header` call should succeed header0_2 = make_testing_colhdr(smc_handler, shard_id, header0_1.hash, 2) smc_handler.add_header(header0_2) mine(web3, smc_handler.config['PERIOD_LENGTH']) # confirm the score of header1 and header2 are correct or not colhdr0_1_score = smc_handler.call( make_call_context(sender_address=primary_addr, gas=default_gas)).get_collation_header_score( shard_id, header0_1.hash) assert colhdr0_1_score == 1 colhdr0_2_score = smc_handler.call( make_call_context(sender_address=primary_addr, gas=default_gas)).get_collation_header_score( shard_id, header0_2.hash) assert colhdr0_2_score == 2 # confirm the logs are correct assert shard_0_tracker.get_next_log()['score'] == 2 assert shard_0_tracker.get_next_log()['score'] == 1 with pytest.raises(NextLogUnavailable): shard_0_tracker.get_next_log() # filter logs in multiple shards shard_1_tracker = ShardTracker(1, LogHandler(web3), smc_handler.address) header1_1 = make_testing_colhdr(smc_handler, 1, GENESIS_COLHDR_HASH, 1) smc_handler.add_header(header1_1) mine(web3, 1) header0_3 = make_testing_colhdr(smc_handler, shard_id, header0_2.hash, 3) smc_handler.add_header(header0_3) mine(web3, 1) assert shard_0_tracker.get_next_log()['score'] == 3 assert shard_1_tracker.get_next_log()['score'] == 1 logs = web3.eth.getLogs({ "fromBlock": 0, "toBlock": web3.eth.blockNumber, "topics": [ encode_hex(COLLATION_ADDED_TOPIC), ] }) assert len(logs) == 4 smc_handler.tx_to_shard( test_keys[1].public_key.to_canonical_address(), shard_id, 100000, 1, b'', value=1234567, ) mine(web3, 1) receipt_value = smc_handler.call( make_call_context(sender_address=primary_addr, gas=default_gas)).receipts__value(0) # the receipt value should be equaled to the transaction value assert receipt_value == 1234567 # test `withdraw` ###################################### smc_handler.withdraw(validator_index) mine(web3, 1) # if the only validator withdraws, because there is no validator anymore, the result of # `num_validators` must be 0. num_validators = smc_handler.call( make_call_context(sender_address=primary_addr, gas=default_gas)).num_validators() assert num_validators == 0