class Issue2235Test(ConfluxTestFramework): def set_test_params(self): self.num_nodes = 1 def setup_network(self): self.add_nodes(self.num_nodes) self.start_node(0, ["--archive"]) self.rpc = RpcClient(self.nodes[0]) def run_test(self): # --- # .- | A | <--- ... # --- | --- # ... <--- | 0 | <-* # --- | --- # .- | B | <--- ... # --- block_number_0 = int(self.rpc.block_by_epoch("latest_mined")['epochNumber'], 0) block_0 = self.rpc.block_by_epoch("latest_mined")['hash'] block_a = self.rpc.generate_custom_block(parent_hash = block_0, referee = [], txs = []) block_b = self.rpc.generate_custom_block(parent_hash = block_0, referee = [], txs = []) # create pivot chain from 'A', make sure 'A' is executed parent_hash = block_a for _ in range(5): block = self.rpc.generate_custom_block(parent_hash = parent_hash, referee = [], txs = []) parent_hash = block block_after_0 = self.rpc.block_by_block_number(hex(block_number_0 + 1))["hash"] assert_equal(block_a, block_after_0) # switch pivot chain to 'B', make sure 'B' is executed parent_hash = block_b for _ in range(6): block = self.rpc.generate_custom_block(parent_hash = parent_hash, referee = [], txs = []) parent_hash = block block_after_0 = self.rpc.block_by_block_number(hex(block_number_0 + 1))["hash"] assert_equal(block_b, block_after_0) # switch pivot chain back to 'A', make sure 'A' is executed parent_hash = block_a for _ in range(7): block = self.rpc.generate_custom_block(parent_hash = parent_hash, referee = [], txs = []) parent_hash = block block_after_0 = self.rpc.block_by_block_number(hex(block_number_0 + 1))["hash"] assert_equal(block_a, block_after_0) # <<< (#2235) self.log.info("Pass")
class Issue2229(ConfluxTestFramework): def set_test_params(self): self.num_nodes = 1 self.conf_parameters["generate_tx_period_us"] = "100000" self.conf_parameters["adaptive_weight_beta"] = "1" self.conf_parameters["timer_chain_block_difficulty_ratio"] = "3" self.conf_parameters["timer_chain_beta"] = "20" self.conf_parameters["era_epoch_count"] = "100" self.conf_parameters["dev_snapshot_epoch_count"] = "25" def setup_network(self): self.add_nodes(self.num_nodes) self.start_node(0, ["--archive"]) self.rpc = RpcClient(self.nodes[0]) def run_test(self): # check in current era block = self.rpc.block_by_epoch("latest_mined") assert_ne(block["blockNumber"], None) # create a few new eras parent_hash = block["hash"] for _ in range(500): parent_hash = self.rpc.generate_custom_block(parent_hash = parent_hash, referee = [], txs = []) parent_hash # check in old era block = self.rpc.block_by_hash(block["hash"]) assert_ne(block["blockNumber"], None) # << self.log.info("Pass")
class Issue2159Test(ConfluxTestFramework): def set_test_params(self): self.num_nodes = 1 self.conf_parameters = { # make `cfx_getEpochReceipts` available through ws "public_rpc_apis": "\"cfx,debug\"", # limit max response payload size "jsonrpc_ws_max_payload_bytes": 1024, } def setup_network(self): self.add_nodes(self.num_nodes) self.start_node(FULLNODE, ["--archive"]) # set up RPC over HTTP node = self.nodes[FULLNODE] self.rpc = RpcClient(node) # set up RPC over WS url = pubsub_url(node.index, node.rpchost, node.pubsubport) self.ws = WebSocketsClient(block_on(websockets.connect(url))) # wait for phase changes to complete self.nodes[FULLNODE].wait_for_phase(["NormalSyncPhase"]) def run_test(self): # generate block with many transactions parent_hash = self.rpc.block_by_epoch("latest_mined")['hash'] start_nonce = self.rpc.get_nonce(self.rpc.GENESIS_ADDR) txs = [self.rpc.new_tx(nonce=start_nonce + ii) for ii in range(0, 100)] hash = self.rpc.generate_custom_block(parent_hash=parent_hash, referee=[], txs=txs) epoch = self.rpc.block_by_hash(hash)["epochNumber"] # make sure block is executed self.rpc.generate_empty_blocks(5) # getting epoch receipts should result in error try: resp = block_on( self.ws.send(Request("cfx_getEpochReceipts", epoch))) assert False, "cfx_getEpochReceipts request should have failed" except ReceivedErrorResponseError as e: self.log.info(e.response) assert e.response.data.startswith("\"Oversized payload") except Exception as e: assert False, f"unexpected error: {e}" # this should succeed # resp = self.rpc.node.cfx_getEpochReceipts(epoch) self.log.info("Pass")
def run_test(self): num_blocks = 200 checkpoint_epoch = 100 # Generate checkpoint on node[0] client = RpcClient(self.nodes[0]) genesis_nonce = client.get_nonce(client.GENESIS_ADDR) for _ in range(num_blocks): tx = client.new_tx(nonce=genesis_nonce) tx_hash = client.send_tx(tx) assert tx_hash == tx.hash_hex() genesis_nonce += 1 client.generate_block(100) # Start node[1] as full node to sync checkpoint # Change phase from CatchUpSyncBlockHeader to CatchUpCheckpoint # only when there is at least one connected peer. self.start_node(1, ["--full"], phase_to_wait=None) connect_nodes(self.nodes, 1, 0) # FIXME full node issue that hang at phase CatchUpRecoverBlockFromDbPhase self.nodes[1].wait_for_phase(["NormalSyncPhase"], wait_time=30) sync_blocks(self.nodes, sync_count=False) client = RpcClient(self.nodes[1]) # At epoch 1, block header exists while body not synchronized try: print(client.block_by_epoch(client.EPOCH_NUM(1))) except ReceivedErrorResponseError as e: assert 'Internal error' == e.response.message # There is no state from epoch 1 to checkpoint_epoch # Note, state of genesis epoch always exists assert client.epoch_number() >= checkpoint_epoch for i in range(1, checkpoint_epoch): try: client.get_balance(client.GENESIS_ADDR, client.EPOCH_NUM(i)) raise AssertionError( "should be not state for epoch {}".format(i)) except ReceivedErrorResponseError as e: assert "State for epoch" in e.response.message assert "does not exist" in e.response.message # State should exist at checkpoint client.get_balance(client.GENESIS_ADDR, client.EPOCH_NUM(checkpoint_epoch)) # There should be states after checkpoint for i in range(checkpoint_epoch + 1, client.epoch_number() - 3): client.get_balance(client.GENESIS_ADDR, client.EPOCH_NUM(i))
def run_test(self): num_blocks = 200 checkpoint_epoch = 100 # Generate checkpoint on node[0] client = RpcClient(self.nodes[0]) self.genesis_nonce = client.get_nonce(client.GENESIS_ADDR) for _ in range(num_blocks): txs = self._generate_txs(0, random.randint(5, 10)) client.generate_block_with_fake_txs(txs) # Start node[1] as full node to sync checkpoint # Change phase from CatchUpSyncBlockHeader to CatchUpCheckpoint # only when there is at least one connected peer. self.start_node(1, ["--full"], phase_to_wait=None) connect_nodes(self.nodes, 1, 0) # FIXME full node issue that hang at phase CatchUpRecoverBlockFromDbPhase self.nodes[1].wait_for_phase(["NormalSyncPhase"], wait_time=30) sync_blocks(self.nodes, sync_count=False) client = RpcClient(self.nodes[1]) # At epoch 1, block header exists while body not synchronized try: print(client.block_by_epoch(client.EPOCH_NUM(1))) except ReceivedErrorResponseError as e: assert 'Internal error' == e.response.message # There is no state from epoch 1 to checkpoint_epoch # Note, state of genesis epoch always exists assert client.epoch_number() >= checkpoint_epoch # FIXME: we minus REWARD_EPOCH_COUNT here as a workaround. # FIXME: after the state boundary is implemented in consensus, # FIXME: this workaround should be removed. for i in range(1, checkpoint_epoch - 11): try: client.get_balance(client.GENESIS_ADDR, client.EPOCH_NUM(i)) raise AssertionError( "should not have state for epoch {}".format(i)) except ReceivedErrorResponseError as e: assert "State for epoch" in e.response.message assert "does not exist" in e.response.message # State should exist at checkpoint client.get_balance(client.GENESIS_ADDR, client.EPOCH_NUM(checkpoint_epoch)) # There should be states after checkpoint for i in range(checkpoint_epoch, client.epoch_number() - 3): client.get_balance(client.GENESIS_ADDR, client.EPOCH_NUM(i))
def run_test(self): client = RpcClient(self.nodes[0]) self.genesis = client.block_by_epoch("0x0", False)["hash"] self.test_same_ip_replace_always(client) self.test_subnet_quota(client)
class LogFilteringTest(ConfluxTestFramework): def set_test_params(self): self.num_nodes = 1 def setup_network(self): self.setup_nodes() def run_test(self): priv_key = default_config["GENESIS_PRI_KEY"] sender = eth_utils.encode_hex(priv_to_addr(priv_key)) self.rpc = RpcClient(self.nodes[0]) # apply filter, we expect no logs filter = Filter() result = self.rpc.get_logs(filter) assert_equal(result, []) # deploy contract bytecode_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), CONTRACT_PATH) assert(os.path.isfile(bytecode_file)) bytecode = open(bytecode_file).read() _, contractAddr = self.deploy_contract(sender, priv_key, bytecode) # apply filter, we expect a single log with 2 topics filter = Filter(from_epoch="earliest", to_epoch="latest_state") logs0 = self.rpc.get_logs(filter) self.assert_response_format_correct(logs0) assert_equal(len(logs0), 1) assert_equal(len(logs0[0]["topics"]), 2) assert_equal(logs0[0]["topics"][0], CONSTRUCTED_TOPIC) assert_equal(logs0[0]["topics"][1], self.address_to_topic(sender)) assert_equal(logs0[0]["data"], self.address_to_topic(sender)) # call method receipt = self.call_contract(sender, priv_key, contractAddr, encode_hex_0x(keccak(b"foo()")), storage_limit=64) # apply filter, we expect two logs with 2 and 3 topics respectively filter = Filter(from_epoch="earliest", to_epoch="latest_state") logs1 = self.rpc.get_logs(filter) self.assert_response_format_correct(logs1) assert_equal(len(logs1), 2) assert_equal(logs1[0], logs0[0]) assert_equal(len(logs1[1]["topics"]), 3) assert_equal(logs1[1]["topics"][0], FOO_TOPIC) assert_equal(logs1[1]["topics"][1], self.address_to_topic(sender)) assert_equal(logs1[1]["topics"][2], self.number_to_topic(1)) # apply filter for specific block, we expect a single log with 3 topics filter = Filter(block_hashes=[receipt["blockHash"]]) logs = self.rpc.get_logs(filter) self.assert_response_format_correct(logs) assert_equal(len(logs), 1) assert_equal(logs[0], logs1[1]) # call many times for ii in range(2, NUM_CALLS): self.call_contract(sender, priv_key, contractAddr, encode_hex_0x(keccak(b"foo()")), storage_limit=0) # apply filter, we expect NUM_CALLS log entries with increasing uint32 fields filter = Filter(from_epoch="earliest", to_epoch="latest_state") logs = self.rpc.get_logs(filter) self.assert_response_format_correct(logs) assert_equal(len(logs), NUM_CALLS) for ii in range(2, NUM_CALLS): assert_equal(len(logs[ii]["topics"]), 3) assert_equal(logs[ii]["topics"][0], FOO_TOPIC) assert(logs[ii]["topics"][1] == self.address_to_topic(sender)) assert_equal(logs[ii]["topics"][2], self.number_to_topic(ii)) # apply filter for specific topics filter = Filter(topics=[CONSTRUCTED_TOPIC]) logs = self.rpc.get_logs(filter) self.assert_response_format_correct(logs) assert_equal(len(logs), 1) filter = Filter(topics=[FOO_TOPIC]) logs = self.rpc.get_logs(filter) self.assert_response_format_correct(logs) assert_equal(len(logs), NUM_CALLS - 1) filter = Filter(topics=[None, self.address_to_topic(sender)]) logs = self.rpc.get_logs(filter) self.assert_response_format_correct(logs) assert_equal(len(logs), NUM_CALLS) # find logs with `FOO_TOPIC` as 1st topic and `3` or `4` as 3rd topic filter = Filter(topics=[FOO_TOPIC, None, [self.number_to_topic(3), self.number_to_topic(4)]]) logs = self.rpc.get_logs(filter) self.assert_response_format_correct(logs) assert_equal(len(logs), 2) # apply filter with limit filter = Filter(limit=hex(NUM_CALLS // 2)) logs = self.rpc.get_logs(filter) self.assert_response_format_correct(logs) assert_equal(len(logs), NUM_CALLS // 2) # apply filter with offset filter = Filter(offset=hex(NUM_CALLS // 4)) logs = self.rpc.get_logs(filter) self.assert_response_format_correct(logs) assert_equal(len(logs), 3 * NUM_CALLS // 4) # apply filter for specific contract address _, contractAddr2 = self.deploy_contract(sender, priv_key, bytecode) filter = Filter(address=[contractAddr]) logs = self.rpc.get_logs(filter) self.assert_response_format_correct(logs) assert_equal(len(logs), NUM_CALLS) filter = Filter(address=[contractAddr2]) logs = self.rpc.get_logs(filter) self.assert_response_format_correct(logs) assert_equal(len(logs), 1) # apply filter to very first epoch, we expect no logs filter = Filter(from_epoch="earliest", to_epoch="earliest") result = self.rpc.get_logs(filter) assert_equal(result, []) # generate two blocks with `NUM_CALLS` transactions in each; # transactions will generate 2 logs each parent_hash = self.rpc.block_by_epoch("latest_mined")['hash'] start_nonce = self.rpc.get_nonce(sender) txs1 = [self.rpc.new_contract_tx(receiver=contractAddr, data_hex=encode_hex_0x(keccak(b"bar()")), sender=sender, priv_key=priv_key, storage_limit=64, nonce = start_nonce + ii) for ii in range(0, NUM_CALLS)] block_hash_1 = self.rpc.generate_custom_block(parent_hash = parent_hash, referee = [], txs = txs1) epoch_1 = self.rpc.block_by_hash(block_hash_1)["epochNumber"] txs2 = [self.rpc.new_contract_tx(receiver=contractAddr, data_hex=encode_hex_0x(keccak(b"bar()")), sender=sender, priv_key=priv_key, storage_limit=64, nonce = start_nonce + NUM_CALLS + ii) for ii in range(0, NUM_CALLS)] block_hash_2 = self.rpc.generate_custom_block(parent_hash = block_hash_1, referee = [], txs = txs2) epoch_2 = self.rpc.block_by_hash(block_hash_2)["epochNumber"] txs = txs1 txs.extend(txs2) # blocks not executed yet, filtering should fail # filter = Filter(block_hashes=[block_hash_1, block_hash_2], topics=[BAR_TOPIC]) # assert_raises_rpc_error(None, None, self.rpc.get_logs, filter) # generate some more blocks to ensure our two blocks are executed self.rpc.generate_blocks(10) # filtering for these two blocks should return logs in correct order filter = Filter(block_hashes=[block_hash_1, block_hash_2], topics=[BAR_TOPIC]) logs = self.rpc.get_logs(filter) assert_equal(len(logs), 4 * NUM_CALLS) log_index = 0 transaction_index = 0 transaction_log_index = 0 for ii in range(0, 4 * NUM_CALLS): assert_equal(logs[ii]["address"], contractAddr) assert_equal(logs[ii]["blockHash"], block_hash_1 if ii < 2 * NUM_CALLS else block_hash_2) assert_equal(logs[ii]["epochNumber"], epoch_1 if ii < 2 * NUM_CALLS else epoch_2) assert_equal(logs[ii]["transactionHash"], txs[ii // 2].hash_hex()) assert_equal(len(logs[ii]["topics"]), 3) assert_equal(logs[ii]["topics"][0], BAR_TOPIC) assert_equal(logs[ii]["topics"][1], self.address_to_topic(sender)) assert_equal(logs[ii]["topics"][2], self.number_to_topic(ii)) # logIndex: # 0, 1, 2, 3, 4, 6, 7, 8, ..., 2 * NUM_CALLS, 0, 1, 2, ... assert_equal(logs[ii]["logIndex"], hex(log_index % (2 * NUM_CALLS))) log_index += 1 # transactionIndex: # 0, 0, 1, 1, 2, 2, 3, 3, ..., NUM_CALLS, 0, 0, 1, 1, ... assert_equal(logs[ii]["transactionIndex"], hex((transaction_index // 2) % NUM_CALLS)) transaction_index += 1 # transactionLogIndex: # 0, 1, 0, 1, 0, 1, 0, 1, ... assert_equal(logs[ii]["transactionLogIndex"], hex(transaction_log_index % 2)) transaction_log_index += 1 # block hash order should not affect log order filter = Filter(block_hashes=[block_hash_2, block_hash_1], topics=[BAR_TOPIC]) logs2 = self.rpc.get_logs(filter) assert_equal(logs, logs2) # given a limit, we should receive the _last_ few logs filter = Filter(block_hashes=[block_hash_1, block_hash_2], limit = hex(3 * NUM_CALLS + NUM_CALLS // 2), topics=[BAR_TOPIC]) logs = self.rpc.get_logs(filter) assert_equal(len(logs), 3 * NUM_CALLS + NUM_CALLS // 2) for ii in range(0, 3 * NUM_CALLS + NUM_CALLS // 2): assert_equal(len(logs[ii]["topics"]), 3) assert_equal(logs[ii]["topics"][0], BAR_TOPIC) assert_equal(logs[ii]["topics"][1], self.address_to_topic(sender)) assert_equal(logs[ii]["topics"][2], self.number_to_topic(NUM_CALLS // 2 + ii)) # given an offset and a limit, we should receive the corresponding logs filter = Filter(block_hashes=[block_hash_1, block_hash_2], offset = hex(NUM_CALLS // 2), limit = hex(NUM_CALLS // 2), topics=[BAR_TOPIC]) logs = self.rpc.get_logs(filter) assert_equal(len(logs), NUM_CALLS // 2) for ii in range(0, NUM_CALLS // 2): assert_equal(len(logs[ii]["topics"]), 3) assert_equal(logs[ii]["topics"][0], BAR_TOPIC) assert_equal(logs[ii]["topics"][1], self.address_to_topic(sender)) assert_equal(logs[ii]["topics"][2], self.number_to_topic(3 * NUM_CALLS + ii)) filter = Filter(from_epoch = epoch_1, to_epoch = epoch_2, offset = hex(NUM_CALLS // 2), limit = hex(NUM_CALLS // 2), topics=[BAR_TOPIC]) logs2 = self.rpc.get_logs(filter) assert_equal(logs, logs2) # test paging use case BATCH_SIZE = 7 filter = Filter(block_hashes=[block_hash_1, block_hash_2], topics=[BAR_TOPIC]) all_logs = self.rpc.get_logs(filter) collected_logs = [] offset = 0 while True: filter = Filter(block_hashes=[block_hash_1, block_hash_2], offset = hex(offset), limit = hex(BATCH_SIZE), topics=[BAR_TOPIC]) logs = self.rpc.get_logs(filter) if len(logs) == 0: break collected_logs = logs + collected_logs offset += BATCH_SIZE assert_equal(collected_logs, all_logs) # get-logs-filter-max-epoch-range should limit the number of epochs queried. self.stop_node(0) self.start_node(0, ["--get-logs-filter-max-epoch-range", "16"]) filter = Filter(from_epoch="0x0", to_epoch="0x0f", topics=[BAR_TOPIC]) # should not raise error self.rpc.get_logs(filter) filter = Filter(from_epoch="0x0", to_epoch="0x10", topics=[BAR_TOPIC]) assert_raises_rpc_error(None, None, self.rpc.get_logs, filter) self.log.info("Pass") def address_to_topic(self, address): return "0x" + address[2:].zfill(64) def number_to_topic(self, number): return "0x" + ("%x" % number).zfill(64) def deploy_contract(self, sender, priv_key, data_hex): c0 = self.rpc.get_collateral_for_storage(sender) tx = self.rpc.new_contract_tx(receiver="", data_hex=data_hex, sender=sender, priv_key=priv_key, storage_limit=512) assert_equal(self.rpc.send_tx(tx, True), tx.hash_hex()) receipt = self.rpc.get_transaction_receipt(tx.hash_hex()) assert_equal(receipt["outcomeStatus"], "0x0") address = receipt["contractCreated"] c1 = self.rpc.get_collateral_for_storage(sender) assert_equal(c1 - c0, 512 * COLLATERAL_UNIT_IN_DRIP) assert_is_hex_string(address) return receipt, address def call_contract(self, sender, priv_key, contract, data_hex, storage_limit): c0 = self.rpc.get_collateral_for_storage(sender) tx = self.rpc.new_contract_tx(receiver=contract, data_hex=data_hex, sender=sender, priv_key=priv_key, storage_limit=storage_limit) assert_equal(self.rpc.send_tx(tx, True), tx.hash_hex()) receipt = self.rpc.get_transaction_receipt(tx.hash_hex()) assert_equal(receipt["outcomeStatus"], "0x0") c1 = self.rpc.get_collateral_for_storage(sender) assert_equal(c1 - c0, storage_limit * COLLATERAL_UNIT_IN_DRIP) return receipt def assert_response_format_correct(self, response): assert_equal(type(response), list) for log in response: self.assert_log_format_correct(log) def assert_log_format_correct(self, log): assert_is_hex_string(log["address"]) assert_is_hex_string(log["epochNumber"]) assert_is_hex_string(log["logIndex"]) assert_is_hex_string(log["transactionIndex"]) assert_is_hex_string(log["transactionLogIndex"]) assert_is_hash_string(log["blockHash"]) assert_is_hash_string(log["transactionHash"]) assert_equal(type(log["topics"]), list)
class PhantomTransactionTest(ConfluxTestFramework): def set_test_params(self): self.num_nodes = 1 self.conf_parameters["chain_id"] = str(10) self.conf_parameters["evm_chain_id"] = str(11) self.conf_parameters["evm_transaction_block_ratio"] = str(1) def setup_network(self): self.add_nodes(self.num_nodes) self.start_node(0, ["--archive"]) self.rpc = RpcClient(self.nodes[0]) ip = self.nodes[0].ip port = self.nodes[0].ethrpcport self.w3 = Web3(Web3.HTTPProvider(f'http://{ip}:{port}/')) assert_equal(self.w3.isConnected(), True) def run_test(self): # initialize Conflux account self.cfxPrivkey = default_config['GENESIS_PRI_KEY'] self.cfxAccount = self.rpc.GENESIS_ADDR print(f'Using Conflux account {self.cfxAccount}') # initialize EVM account self.evmAccount = self.w3.eth.account.privateKeyToAccount('0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef') print(f'Using EVM account {self.evmAccount.address}') self.cross_space_transfer(self.evmAccount.address, 1 * 10 ** 18) assert_equal(self.nodes[0].eth_getBalance(self.evmAccount.address), hex(1 * 10 ** 18)) # deploy Conflux space contract confluxContractAddr = self.deploy_conflux_space(CONFLUX_CONTRACT_PATH) print(f'Conflux contract: {confluxContractAddr}') # deploy EVM space contract evmContractAddr = self.deploy_evm_space(EVM_CONTRACT_PATH) print(f'EVM contract: {evmContractAddr}') # --- # .-----------------| D |.... # V --- | # --- --- --- --- # ... <-- | A | <- | B | <- | C | <- | E | <- ... # --- --- --- --- # # A --- B --- C --- D --- E # block number 0 | 1 | 2 | 3 | 4 | # epoch number 0 | 1 | 2 | 3 | cfx_next_nonce = self.rpc.get_nonce(self.cfxAccount) cfx_tx_hashes = [] evm_next_nonce = self.w3.eth.getTransactionCount(self.evmAccount.address) evm_tx_hashes = [] def emitConflux(n): nonlocal cfx_next_nonce, cfx_tx_hashes data_hex = (encode_hex_0x(keccak(b"emitConflux(uint256)"))[:10] + encode_u256(n)) tx = self.rpc.new_contract_tx(receiver=confluxContractAddr, data_hex=data_hex, nonce = cfx_next_nonce, sender=self.cfxAccount, priv_key=self.cfxPrivkey) cfx_next_nonce += 1 cfx_tx_hashes.append(tx.hash_hex()) return tx def emitComplex(n): nonlocal cfx_next_nonce, cfx_tx_hashes data_hex = encode_hex_0x(keccak(b"emitComplex(uint256,bytes20)"))[:10] + encode_u256(n) + encode_bytes20(evmContractAddr.replace('0x', '')) tx = self.rpc.new_contract_tx(receiver=confluxContractAddr, data_hex=data_hex, nonce = cfx_next_nonce, sender=self.cfxAccount, priv_key=self.cfxPrivkey) cfx_next_nonce += 1 cfx_tx_hashes.append(tx.hash_hex()) return tx def emitEVM(n): nonlocal evm_next_nonce, evm_tx_hashes data_hex = (encode_hex_0x(keccak(b"emitEVM(uint256)"))[:10] + encode_u256(n)) tx, hash = self.construct_evm_tx(receiver=evmContractAddr, data_hex=data_hex, nonce = evm_next_nonce) evm_next_nonce += 1 evm_tx_hashes.append(hash) return tx # generate ledger block_0 = self.rpc.block_by_epoch("latest_mined")['hash'] block_a = self.rpc.generate_custom_block(parent_hash = block_0, referee = [], txs = [ emitConflux(11), emitEVM(12), emitComplex(13), ]) block_b = self.rpc.generate_custom_block(parent_hash = block_a, referee = [], txs = [ emitConflux(14), emitEVM(15), emitComplex(16), ]) block_c = self.rpc.generate_custom_block(parent_hash = block_b, referee = [], txs = []) block_d = self.rpc.generate_custom_block(parent_hash = block_a, referee = [], txs = [ emitConflux(21), emitEVM(22), emitComplex(23), ]) block_e = self.rpc.generate_custom_block(parent_hash = block_c, referee = [block_d], txs = [ emitConflux(24), emitEVM(25), emitComplex(26), ]) [epoch_a, block_number_a] = [self.rpc.block_by_hash(block_a)[key] for key in ['epochNumber', 'blockNumber']] [epoch_b, block_number_b] = [self.rpc.block_by_hash(block_b)[key] for key in ['epochNumber', 'blockNumber']] [epoch_d, block_number_d] = [self.rpc.block_by_hash(block_d)[key] for key in ['epochNumber', 'blockNumber']] [epoch_e, block_number_e] = [self.rpc.block_by_hash(block_e)[key] for key in ['epochNumber', 'blockNumber']] # make sure transactions have been executed parent_hash = block_e for _ in range(5): block = self.rpc.generate_custom_block(parent_hash = parent_hash, referee = [], txs = []) parent_hash = block for h in cfx_tx_hashes: receipt = self.rpc.get_transaction_receipt(h) assert_equal(receipt["outcomeStatus"], "0x0") for h in evm_tx_hashes: receipt = self.w3.eth.waitForTransactionReceipt(h) assert_equal(receipt["status"], 1) # TODO: add failing tx # --------------------------------------------------------------------- # Conflux perspective: # A: 2 txs (events: [11], [13, X, X, 13, X, X, 13]) X ~ internal contract event # B: 2 txs (events: [14], [16, X, X, 16, X, X, 16]) # C: / # D: 2 txs (events: [21], [23, X, X, 23, X, X, 23]) # E: 2 txs (events: [24], [26, X, X, 26, X, X, 26]) # block #A block = self.nodes[0].cfx_getBlockByHash(block_a, True) assert_equal(len(block["transactions"]), 2) block2 = self.nodes[0].cfx_getBlockByBlockNumber(block_number_a, True) assert_equal(block2, block) tx_hashes = self.nodes[0].cfx_getBlockByHash(block_a, False)["transactions"] assert_equal(len(tx_hashes), 2) for idx, tx in enumerate(block["transactions"]): # check returned hash assert_equal(tx["hash"], tx_hashes[idx]) # check indexing # assert_equal(tx["transactionIndex"], hex(idx)) # check cfx_getTransactionByHash assert_equal(tx, self.nodes[0].cfx_getTransactionByHash(tx["hash"])) receipts = self.nodes[0].cfx_getEpochReceipts(epoch_a) assert_equal(len(receipts), 1) # 1 block assert_equal(len(receipts[0]), 2) # 2 receipts receipts2 = self.nodes[0].cfx_getEpochReceipts(f'hash:{block_a}') assert_equal(receipts2, receipts) assert_equal(len(receipts[0][0]["logs"]), 1) assert_equal(receipts[0][0]["logs"][0]["data"], number_to_topic(11)) assert_equal(len(receipts[0][1]["logs"]), 7) assert_equal(receipts[0][1]["logs"][0]["data"], number_to_topic(13)) # Call, Outcome, ... assert_equal(receipts[0][1]["logs"][3]["data"], number_to_topic(13)) # Call, Outcome, ... assert_equal(receipts[0][1]["logs"][6]["data"], number_to_topic(13)) # TODO.... # --------------------------------------------------------------------- # EVM perspective: # A: 5 txs (events: [12], [], [13], [], [13, 13]) # B: 5 txs (events: [15], [], [16], [], [16, 16]) # C: / # E: 10 txs (events: [22], [], [23], [], [23, 23], [25], [], [26], [], [26, 26]) # block #A block = self.nodes[0].eth_getBlockByNumber(epoch_a, True) assert_equal(len(block["transactions"]), 5) block2 = self.nodes[0].eth_getBlockByHash(block_a, True) assert_equal(block2, block) count = int(self.nodes[0].eth_getBlockTransactionCountByNumber(epoch_a), 16) assert_equal(count, len(block["transactions"])) count = int(self.nodes[0].eth_getBlockTransactionCountByHash(block_a), 16) assert_equal(count, len(block["transactions"])) tx_hashes = self.nodes[0].eth_getBlockByNumber(epoch_a, False)["transactions"] assert_equal(len(tx_hashes), 5) for idx, tx in enumerate(block["transactions"]): # check returned hash assert_equal(tx["hash"], tx_hashes[idx]) # check indexing assert_equal(tx["transactionIndex"], hex(idx)) # check eth_getTransactionByHash assert_equal(tx, self.nodes[0].eth_getTransactionByHash(tx["hash"])) # TODO: check transaction details receipts = self.nodes[0].parity_getBlockReceipts(epoch_a) assert_equal(len(receipts), 5) receipts2 = self.nodes[0].parity_getBlockReceipts({ "blockHash": block_a }) assert_equal(receipts2, receipts) receipts2 = self.nodes[0].parity_getBlockReceipts({ "blockHash": block_a, "requireCanonical": True }) assert_equal(receipts2, receipts) receipts2 = self.nodes[0].parity_getBlockReceipts({ "blockHash": block_a, "requireCanonical": False }) assert_equal(receipts2, receipts) logIndex = 0 filter = { "fromBlock": epoch_a, "toBlock": epoch_a } logsFiltered = self.nodes[0].eth_getLogs(filter) assert_equal(len(logsFiltered), 4) for idx, receipt in enumerate(receipts): assert_equal(receipt["blockHash"], block_a) assert_equal(receipt["blockNumber"], epoch_a) assert_equal(receipt["contractAddress"], None) assert_equal(receipt["status"], "0x1") assert_equal(receipt["transactionHash"], tx_hashes[idx]) assert_equal(receipt["transactionIndex"], hex(idx)) # TODO: check logs bloom, cumulative gas used # check eth_getTransactionReceipt assert_equal(receipt, self.nodes[0].eth_getTransactionReceipt(receipt["transactionHash"])) for idx2, log in enumerate(receipt["logs"]): assert_equal(log["address"], evmContractAddr.lower()) assert_equal(log["blockHash"], block_a) assert_equal(log["blockNumber"], epoch_a) assert_equal(log["transactionHash"], tx_hashes[idx]) assert_equal(log["transactionIndex"], hex(idx)) assert_equal(log["logIndex"], hex(logIndex)) assert_equal(log["transactionLogIndex"], hex(idx2)) assert_equal(log["removed"], False) assert_equal(log, logsFiltered[logIndex]) logIndex += 1 assert_equal(len(receipts[0]["logs"]), 1) assert_equal(receipts[0]["logs"][0]["data"], number_to_topic(12)) assert_equal(len(receipts[1]["logs"]), 0) assert_equal(len(receipts[2]["logs"]), 1) assert_equal(receipts[2]["logs"][0]["data"], number_to_topic(13)) assert_equal(len(receipts[3]["logs"]), 0) assert_equal(len(receipts[4]["logs"]), 2) assert_equal(receipts[4]["logs"][0]["data"], number_to_topic(13)) assert_equal(receipts[4]["logs"][1]["data"], number_to_topic(13)) # block #D block = self.nodes[0].eth_getBlockByHash(block_d, True) assert_equal(block, None) count = self.nodes[0].eth_getBlockTransactionCountByHash(block_d) assert_equal(count, None) # block #E block = self.nodes[0].eth_getBlockByNumber(epoch_e, True) assert_equal(len(block["transactions"]), 10) block2 = self.nodes[0].eth_getBlockByHash(block_e, True) assert_equal(block2, block) count = int(self.nodes[0].eth_getBlockTransactionCountByNumber(epoch_e), 16) assert_equal(count, len(block["transactions"])) count = int(self.nodes[0].eth_getBlockTransactionCountByHash(block_e), 16) assert_equal(count, len(block["transactions"])) tx_hashes = self.nodes[0].eth_getBlockByNumber(epoch_e, False)["transactions"] assert_equal(len(tx_hashes), 10) for idx, tx in enumerate(block["transactions"]): # check returned hash assert_equal(tx["hash"], tx_hashes[idx]) # check indexing assert_equal(tx["transactionIndex"], hex(idx)) # check eth_getTransactionByHash assert_equal(tx, self.nodes[0].eth_getTransactionByHash(tx["hash"])) receipts = self.nodes[0].parity_getBlockReceipts(epoch_e) assert_equal(len(receipts), 10) receipts2 = self.nodes[0].parity_getBlockReceipts({ "blockHash": block_e }) assert_equal(receipts2, receipts) logIndex = 0 filter = { "fromBlock": epoch_e, "toBlock": epoch_e } logsFiltered = self.nodes[0].eth_getLogs(filter) assert_equal(len(logsFiltered), 8) for idx, receipt in enumerate(receipts): assert_equal(receipt["blockHash"], block_e) assert_equal(receipt["blockNumber"], epoch_e) assert_equal(receipt["contractAddress"], None) assert_equal(receipt["status"], "0x1") assert_equal(receipt["transactionHash"], tx_hashes[idx]) assert_equal(receipt["transactionIndex"], hex(idx)) # TODO: check logs bloom, cumulative gas used # check eth_getTransactionReceipt assert_equal(receipt, self.nodes[0].eth_getTransactionReceipt(receipt["transactionHash"])) for idx2, log in enumerate(receipt["logs"]): assert_equal(log["address"], evmContractAddr.lower()) assert_equal(log["blockHash"], block_e) assert_equal(log["blockNumber"], epoch_e) assert_equal(log["transactionHash"], tx_hashes[idx]) assert_equal(log["transactionIndex"], hex(idx)) assert_equal(log["logIndex"], hex(logIndex)) assert_equal(log["transactionLogIndex"], hex(idx2)) assert_equal(log["removed"], False) assert_equal(log, logsFiltered[logIndex]) logIndex += 1 assert_equal(len(receipts[0]["logs"]), 1) assert_equal(receipts[0]["logs"][0]["data"], number_to_topic(22)) assert_equal(len(receipts[1]["logs"]), 0) assert_equal(len(receipts[2]["logs"]), 1) assert_equal(receipts[2]["logs"][0]["data"], number_to_topic(23)) assert_equal(len(receipts[3]["logs"]), 0) assert_equal(len(receipts[4]["logs"]), 2) assert_equal(receipts[4]["logs"][0]["data"], number_to_topic(23)) assert_equal(receipts[4]["logs"][0]["data"], number_to_topic(23)) assert_equal(len(receipts[5]["logs"]), 1) assert_equal(receipts[5]["logs"][0]["data"], number_to_topic(25)) assert_equal(len(receipts[6]["logs"]), 0) assert_equal(len(receipts[7]["logs"]), 1) assert_equal(receipts[7]["logs"][0]["data"], number_to_topic(26)) assert_equal(len(receipts[8]["logs"]), 0) assert_equal(len(receipts[9]["logs"]), 2) assert_equal(receipts[9]["logs"][0]["data"], number_to_topic(26)) assert_equal(receipts[9]["logs"][0]["data"], number_to_topic(26)) # --------------------------------------------------------------------- # make sure pending transactions can be retrieved even before execution evm_next_nonce += 1 signed = self.evmAccount.signTransaction({ "to": evmContractAddr, "value": 0, "gasPrice": 1, "gas": 150000, "nonce": evm_next_nonce, "chainId": 11, "data": "0x", }) tx_hash = self.w3.eth.sendRawTransaction(signed["rawTransaction"]) tx = self.nodes[0].eth_getTransactionByHash(tx_hash.hex()) assert_ne(tx, None) self.log.info("Pass") def cross_space_transfer(self, to, value): to = to.replace('0x', '') tx = self.rpc.new_tx( value=value, receiver="0x0888000000000000000000000000000000000006", data=decode_hex(f"0xda8d5daf{to}000000000000000000000000"), nonce=self.rpc.get_nonce(self.cfxAccount), gas=1000000, ) self.rpc.send_tx(tx, True) def deploy_conflux_space(self, bytecode_path): bytecode_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), bytecode_path) assert(os.path.isfile(bytecode_file)) bytecode = open(bytecode_file).read() tx = self.rpc.new_contract_tx(receiver="", data_hex=bytecode, sender=self.cfxAccount, priv_key=self.cfxPrivkey, storage_limit=20000) assert_equal(self.rpc.send_tx(tx, True), tx.hash_hex()) receipt = self.rpc.get_transaction_receipt(tx.hash_hex()) assert_equal(receipt["outcomeStatus"], "0x0") addr = receipt["contractCreated"] assert_is_hex_string(addr) return addr def deploy_evm_space(self, bytecode_path): bytecode_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), bytecode_path) assert(os.path.isfile(bytecode_file)) bytecode = open(bytecode_file).read() nonce = self.w3.eth.getTransactionCount(self.evmAccount.address) signed = self.evmAccount.signTransaction({ "to": None, "value": 0, "gasPrice": 1, "gas": 500000, "nonce": nonce, "chainId": 11, "data": bytecode, }) tx_hash = signed["hash"] return_tx_hash = self.w3.eth.sendRawTransaction(signed["rawTransaction"]) assert_equal(tx_hash, return_tx_hash) self.rpc.generate_block(1) self.rpc.generate_blocks(20, 1) receipt = self.w3.eth.waitForTransactionReceipt(tx_hash) assert_equal(receipt["status"], 1) addr = receipt["contractAddress"] return addr def construct_evm_tx(self, receiver, data_hex, nonce): signed = self.evmAccount.signTransaction({ "to": receiver, "value": 0, "gasPrice": 1, "gas": 150000, "nonce": nonce, "chainId": 11, "data": data_hex, }) tx = [nonce, 1, 150000, bytes.fromhex(receiver.replace('0x', '')), 0, bytes.fromhex(data_hex.replace('0x', '')), signed["v"], signed["r"], signed["s"]] return tx, signed["hash"]
def run_test(self): num_blocks = 200 snapshot_epoch = 150 # Generate checkpoint on node[0] archive_node_client = RpcClient(self.nodes[0]) self.genesis_nonce = archive_node_client.get_nonce( archive_node_client.GENESIS_ADDR) blocks_in_era = [] for i in range(num_blocks): txs = self._generate_txs(0, random.randint(50, 100)) block_hash = archive_node_client.generate_block_with_fake_txs(txs) if i >= snapshot_epoch: blocks_in_era.append(block_hash) sync_blocks(self.nodes[:-1]) self.log.info("All archive nodes synced") # Start node[full_node_index] as full node to sync checkpoint # Change phase from CatchUpSyncBlockHeader to CatchUpCheckpoint # only when there is at least one connected peer. full_node_index = self.num_nodes - 1 self.start_node(full_node_index, ["--full"], phase_to_wait=None) for i in range(self.num_nodes - 1): connect_nodes(self.nodes, full_node_index, i) self.log.info("Wait for full node to sync, index=%d", full_node_index) self.nodes[full_node_index].wait_for_phase(["NormalSyncPhase"], wait_time=240) sync_blocks(self.nodes, sync_count=False) full_node_client = RpcClient(self.nodes[full_node_index]) # At epoch 1, block header exists while body not synchronized try: print( full_node_client.block_by_epoch(full_node_client.EPOCH_NUM(1))) except ReceivedErrorResponseError as e: assert 'Internal error' == e.response.message # There is no state from epoch 1 to snapshot_epoch # Note, state of genesis epoch always exists assert full_node_client.epoch_number() >= snapshot_epoch wait_until( lambda: full_node_client.epoch_number() == archive_node_client. epoch_number() and full_node_client.epoch_number("latest_state") == archive_node_client.epoch_number("latest_state")) # We have snapshot_epoch for state execution but # don't offer snapshot_epoch for Rpc clients. for i in range(1, snapshot_epoch + 1): try: full_node_client.get_balance(full_node_client.GENESIS_ADDR, full_node_client.EPOCH_NUM(i)) raise AssertionError( "should not have state for epoch {}".format(i)) except ReceivedErrorResponseError as e: assert "State for epoch" in e.response.message assert "does not exist" in e.response.message # Wait for execution to complete. time.sleep(1) # There should be states after checkpoint for i in range(snapshot_epoch + 1, full_node_client.epoch_number() - 3): full_balance = full_node_client.get_balance( full_node_client.GENESIS_ADDR, full_node_client.EPOCH_NUM(i)) archive_balance = archive_node_client.get_balance( archive_node_client.GENESIS_ADDR, archive_node_client.EPOCH_NUM(i)) assert_equal(full_balance, archive_balance) # Blocks within execution defer (5 epochs) and reward_defer (12 epochs) do not have state_valid available_blocks = blocks_in_era[:-17] assert_blocks_valid(self.nodes[:-1], available_blocks) assert_blocks_valid(self.nodes[-1:], available_blocks)
class LogFilteringTest(ConfluxTestFramework): def set_test_params(self): self.num_nodes = 1 self.conf_parameters["dev_snapshot_epoch_count"] = str( SNAPSHOT_EPOCH_COUNT) def setup_network(self): self.add_nodes(self.num_nodes) self.start_node(0, ["--archive"]) self.rpc = RpcClient(self.nodes[0]) def run_test(self): priv_key = default_config["GENESIS_PRI_KEY"] sender = eth_utils.encode_hex(priv_to_addr(priv_key)) # deploy storage test contract bytecode_file = os.path.join( os.path.dirname(os.path.realpath(__file__)), CONTRACT_PATH) assert (os.path.isfile(bytecode_file)) bytecode = open(bytecode_file).read() tx = self.rpc.new_contract_tx(receiver="", data_hex=bytecode, sender=sender, priv_key=priv_key, storage_limit=20000) assert_equal(self.rpc.send_tx(tx, True), tx.hash_hex()) receipt = self.rpc.get_transaction_receipt(tx.hash_hex()) contractAddr = receipt["contractCreated"] assert_is_hex_string(contractAddr) # --- --- # .-----------------| D |.... .----------------| H |..... # V --- | V --- | # --- --- --- --- --- --- --- # ... <-- | A | <- | B | <- | C | <- | E | <- | F | <- | G | <- | I | <- ... # --- --- --- --- --- --- --- # # A --- B --- C --- D --- E --- F --- G --- H --- I # block number 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | # epoch number 0 | 1 | 2 | 3 | 4 | 5 | 6 | start_nonce = self.rpc.get_nonce(self.rpc.GENESIS_ADDR) txs = [ self.rpc.new_contract_tx(receiver=contractAddr, data_hex=encode_hex_0x(keccak(b"foo()")), nonce=start_nonce + 0, sender=sender, priv_key=priv_key, storage_limit=64), self.rpc.new_contract_tx(receiver=contractAddr, data_hex=encode_hex_0x(keccak(b"foo()")), nonce=start_nonce + 1, sender=sender, priv_key=priv_key), self.rpc.new_contract_tx(receiver=contractAddr, data_hex=encode_hex_0x(keccak(b"foo()")), nonce=start_nonce + 2, sender=sender, priv_key=priv_key), self.rpc.new_contract_tx(receiver=contractAddr, data_hex=encode_hex_0x(keccak(b"foo()")), nonce=start_nonce + 3, sender=sender, priv_key=priv_key), self.rpc.new_contract_tx(receiver=contractAddr, data_hex=encode_hex_0x(keccak(b"foo()")), nonce=start_nonce + 4, sender=sender, priv_key=priv_key), self.rpc.new_contract_tx(receiver=contractAddr, data_hex=encode_hex_0x(keccak(b"foo()")), nonce=start_nonce + 5, sender=sender, priv_key=priv_key), self.rpc.new_contract_tx(receiver=contractAddr, data_hex=encode_hex_0x(keccak(b"foo()")), nonce=start_nonce + 6, sender=sender, priv_key=priv_key), self.rpc.new_contract_tx(receiver=contractAddr, data_hex=encode_hex_0x(keccak(b"foo()")), nonce=start_nonce + 7, sender=sender, priv_key=priv_key), self.rpc.new_contract_tx(receiver=contractAddr, data_hex=encode_hex_0x(keccak(b"foo()")), nonce=start_nonce + 8, sender=sender, priv_key=priv_key), ] block_0 = self.rpc.block_by_epoch("latest_mined")['hash'] epoch_0 = int(self.rpc.block_by_hash(block_0)['epochNumber'], 0) block_number_0 = int(self.rpc.block_by_hash(block_0)['blockNumber'], 0) block_a = self.rpc.generate_custom_block(parent_hash=block_0, referee=[], txs=[txs[0]]) block_b = self.rpc.generate_custom_block(parent_hash=block_a, referee=[], txs=[txs[1]]) block_c = self.rpc.generate_custom_block(parent_hash=block_b, referee=[], txs=[txs[2]]) block_d = self.rpc.generate_custom_block(parent_hash=block_a, referee=[], txs=[txs[3]]) block_e = self.rpc.generate_custom_block(parent_hash=block_c, referee=[block_d], txs=[txs[4]]) block_f = self.rpc.generate_custom_block(parent_hash=block_e, referee=[], txs=[txs[5]]) block_g = self.rpc.generate_custom_block(parent_hash=block_f, referee=[], txs=[txs[6]]) block_h = self.rpc.generate_custom_block(parent_hash=block_e, referee=[], txs=[txs[7]]) block_i = self.rpc.generate_custom_block(parent_hash=block_g, referee=[block_h], txs=[txs[8]]) # make sure transactions have been executed parent_hash = block_i for _ in range(5): block = self.rpc.generate_custom_block(parent_hash=parent_hash, referee=[], txs=[]) parent_hash = block # check logs block_number_a = int(self.rpc.block_by_hash(block_a)['blockNumber'], 0) block_number_i = int(self.rpc.block_by_hash(block_i)['blockNumber'], 0) self.check_logs(block_number_a, block_number_i, sender) # check logs in old era for _ in range(10 * SNAPSHOT_EPOCH_COUNT): block = self.rpc.generate_custom_block(parent_hash=parent_hash, referee=[], txs=[]) parent_hash = block self.check_logs(block_number_a, block_number_i, sender) self.log.info("Pass") def check_logs(self, first_block_number, last_block_number, sender): # check the number of logs returned for different ranges for from_block in range(first_block_number, last_block_number + 1): for to_block in range(from_block, last_block_number + 1): filter = Filter(from_block=hex(from_block), to_block=hex(to_block)) logs = self.rpc.get_logs(filter) assert_equal(len(logs), to_block - from_block + 1) # check the event parameters in each block for block_number in range(first_block_number, last_block_number + 1): logs = self.rpc.get_logs( Filter(from_block=hex(block_number), to_block=hex(block_number))) assert_equal(len(logs), 1) assert_equal(logs[0]["topics"][0], FOO_TOPIC) assert_equal(logs[0]["topics"][1], address_to_topic(sender)) assert_equal( logs[0]["topics"][2], number_to_topic(block_number - first_block_number + 1))
class Issue2260(ConfluxTestFramework): def set_test_params(self): self.num_nodes = 1 def setup_network(self): self.add_nodes(self.num_nodes) self.start_node(0, ["--archive"]) self.rpc = RpcClient(self.nodes[0]) def run_test(self): priv_key = default_config["GENESIS_PRI_KEY"] sender = eth_utils.encode_hex(priv_to_addr(priv_key)) # deploy storage test contract bytecode_file = os.path.join( os.path.dirname(os.path.realpath(__file__)), CONTRACT_PATH) assert (os.path.isfile(bytecode_file)) bytecode = open(bytecode_file).read() tx = self.rpc.new_contract_tx(receiver="", data_hex=bytecode, sender=sender, priv_key=priv_key, storage_limit=20000) assert_equal(self.rpc.send_tx(tx, True), tx.hash_hex()) receipt = self.rpc.get_transaction_receipt(tx.hash_hex()) contractAddr = receipt["contractCreated"] assert_is_hex_string(contractAddr) # --- # .-----------------| D |.... # V --- | # --- --- --- --- # ... <-- | A | <- | B | <- | C | <- | E | <- ... # --- --- --- --- # # A --- B --- C --- D --- E # block number 0 | 1 | 2 | 3 | 4 | # epoch number 0 | 1 | 2 | 3 | start_nonce = self.rpc.get_nonce(self.rpc.GENESIS_ADDR) txs = [ self.rpc.new_contract_tx(receiver=contractAddr, data_hex=encode_hex_0x(keccak(b"foo()")), nonce=start_nonce + 0, sender=sender, priv_key=priv_key, storage_limit=64), self.rpc.new_contract_tx(receiver=contractAddr, data_hex=encode_hex_0x(keccak(b"foo()")), nonce=start_nonce + 1, sender=sender, priv_key=priv_key), self.rpc.new_contract_tx(receiver=contractAddr, data_hex=encode_hex_0x(keccak(b"foo()")), nonce=start_nonce + 2, sender=sender, priv_key=priv_key), self.rpc.new_contract_tx(receiver=contractAddr, data_hex=encode_hex_0x(keccak(b"foo()")), nonce=start_nonce + 3, sender=sender, priv_key=priv_key), ] block_0 = self.rpc.block_by_epoch("latest_mined")['hash'] epoch_0 = int(self.rpc.block_by_hash(block_0)['epochNumber'], 0) block_number_0 = int(self.rpc.block_by_hash(block_0)['blockNumber'], 0) block_a = self.rpc.generate_custom_block(parent_hash=block_0, referee=[], txs=[]) block_b = self.rpc.generate_custom_block(parent_hash=block_a, referee=[], txs=[]) block_c = self.rpc.generate_custom_block(parent_hash=block_b, referee=[], txs=[]) block_d = self.rpc.generate_custom_block(parent_hash=block_a, referee=[], txs=txs[0:2]) block_e = self.rpc.generate_custom_block(parent_hash=block_c, referee=[block_d], txs=txs[2:4]) # make sure transactions have been executed parent_hash = block_e for _ in range(5): block = self.rpc.generate_custom_block(parent_hash=parent_hash, referee=[], txs=[]) parent_hash = block # check logs block_number_a = int(self.rpc.block_by_hash(block_a)['blockNumber'], 0) block_number_d = int(self.rpc.block_by_hash(block_d)['blockNumber'], 0) filter = Filter(from_block=hex(block_number_a), to_block=hex(block_number_d), offset=hex(0), limit=hex(1)) logs = self.rpc.get_logs(filter) assert_equal(len(logs), 1) assert_equal(logs[0]["topics"][2], number_to_topic(2)) filter = Filter(from_block=hex(block_number_a), to_block=hex(block_number_d), offset=hex(1), limit=hex(1)) logs = self.rpc.get_logs(filter) assert_equal(len(logs), 1) assert_equal(logs[0]["topics"][2], number_to_topic(1)) filter = Filter(from_block=hex(block_number_a), to_block=hex(block_number_d), offset=hex(0), limit=hex(2)) logs = self.rpc.get_logs(filter) assert_equal(len(logs), 2) assert_equal(logs[0]["topics"][2], number_to_topic(1)) assert_equal(logs[1]["topics"][2], number_to_topic(2)) self.log.info("Pass")
class LogFilteringTest(ConfluxTestFramework): def set_test_params(self): self.num_nodes = 1 def setup_network(self): self.setup_nodes() def run_test(self): priv_key = default_config["GENESIS_PRI_KEY"] sender = eth_utils.encode_hex(priv_to_addr(priv_key)) self.rpc = RpcClient(self.nodes[0]) # apply filter, we expect no logs filter = Filter() result = self.rpc.get_logs(filter) assert_equal(result, []) # deploy contract bytecode_file = os.path.join( os.path.dirname(os.path.realpath(__file__)), CONTRACT_PATH) assert (os.path.isfile(bytecode_file)) bytecode = open(bytecode_file).read() _, contractAddr = self.deploy_contract(sender, priv_key, bytecode) # apply filter, we expect a single log with 2 topics filter = Filter(from_epoch="earliest", to_epoch="latest_state") logs0 = self.rpc.get_logs(filter) self.assert_response_format_correct(logs0) assert_equal(len(logs0), 1) assert_equal(len(logs0[0]["topics"]), 2) assert_equal(logs0[0]["topics"][0], CONSTRUCTED_TOPIC) assert_equal(logs0[0]["topics"][1], self.address_to_topic(sender)) assert_equal(logs0[0]["data"], self.address_to_topic(sender)) # call method receipt = self.call_contract(sender, priv_key, contractAddr, encode_hex_0x(keccak(b"foo()")), storage_limit=64) # apply filter, we expect two logs with 2 and 3 topics respectively filter = Filter(from_epoch="earliest", to_epoch="latest_state") logs1 = self.rpc.get_logs(filter) self.assert_response_format_correct(logs1) assert_equal(len(logs1), 2) assert_equal(logs1[0], logs0[0]) assert_equal(len(logs1[1]["topics"]), 3) assert_equal(logs1[1]["topics"][0], CALLED_TOPIC) assert_equal(logs1[1]["topics"][1], self.address_to_topic(sender)) assert_equal(logs1[1]["topics"][2], self.number_to_topic(1)) # apply filter for specific block, we expect a single log with 3 topics filter = Filter(block_hashes=[receipt["blockHash"]]) logs = self.rpc.get_logs(filter) self.assert_response_format_correct(logs) assert_equal(len(logs), 1) assert_equal(logs[0], logs1[1]) # call many times for ii in range(2, NUM_CALLS): self.call_contract(sender, priv_key, contractAddr, encode_hex_0x(keccak(b"foo()")), storage_limit=0) # apply filter, we expect NUM_CALLS log entries with increasing uint32 fields filter = Filter(from_epoch="earliest", to_epoch="latest_state") logs = self.rpc.get_logs(filter) self.assert_response_format_correct(logs) assert_equal(len(logs), NUM_CALLS) for ii in range(2, NUM_CALLS): assert_equal(len(logs[ii]["topics"]), 3) assert_equal(logs[ii]["topics"][0], CALLED_TOPIC) assert (logs[ii]["topics"][1] == self.address_to_topic(sender)) assert_equal(logs[ii]["topics"][2], self.number_to_topic(ii)) # apply filter for specific topics filter = Filter(topics=[CONSTRUCTED_TOPIC]) logs = self.rpc.get_logs(filter) self.assert_response_format_correct(logs) assert_equal(len(logs), 1) filter = Filter(topics=[CALLED_TOPIC]) logs = self.rpc.get_logs(filter) self.assert_response_format_correct(logs) assert_equal(len(logs), NUM_CALLS - 1) filter = Filter(topics=[None, self.address_to_topic(sender)]) logs = self.rpc.get_logs(filter) self.assert_response_format_correct(logs) assert_equal(len(logs), NUM_CALLS) # find logs with `CALLED_TOPIC` as 1st topic and `3` or `4` as 3rd topic filter = Filter(topics=[ CALLED_TOPIC, None, [self.number_to_topic(3), self.number_to_topic(4)] ]) logs = self.rpc.get_logs(filter) self.assert_response_format_correct(logs) assert_equal(len(logs), 2) # apply filter with limit filter = Filter(limit=hex(NUM_CALLS // 2)) logs = self.rpc.get_logs(filter) self.assert_response_format_correct(logs) assert_equal(len(logs), NUM_CALLS // 2) # apply filter for specific contract address _, contractAddr2 = self.deploy_contract(sender, priv_key, bytecode) filter = Filter(address=[contractAddr]) logs = self.rpc.get_logs(filter) self.assert_response_format_correct(logs) assert_equal(len(logs), NUM_CALLS) filter = Filter(address=[contractAddr2]) logs = self.rpc.get_logs(filter) self.assert_response_format_correct(logs) assert_equal(len(logs), 1) # apply filter to very first epoch, we expect no logs filter = Filter(from_epoch="earliest", to_epoch="earliest") result = self.rpc.get_logs(filter) assert_equal(result, []) # generate two blocks with `NUM_CALLS` valid logs in each parent_hash = self.rpc.block_by_epoch("latest_mined")['hash'] start_nonce = self.rpc.get_nonce(sender) txs = [ self.rpc.new_contract_tx(receiver=contractAddr, data_hex=encode_hex_0x(keccak(b"foo()")), sender=sender, priv_key=priv_key, storage_limit=64, nonce=start_nonce + ii) for ii in range(0, NUM_CALLS) ] block_hash_1 = self.rpc.generate_custom_block(parent_hash=parent_hash, referee=[], txs=txs) txs = [ self.rpc.new_contract_tx(receiver=contractAddr, data_hex=encode_hex_0x(keccak(b"foo()")), sender=sender, priv_key=priv_key, storage_limit=64, nonce=start_nonce + NUM_CALLS + ii) for ii in range(0, NUM_CALLS) ] block_hash_2 = self.rpc.generate_custom_block(parent_hash=block_hash_1, referee=[], txs=txs) # blocks not executed yet, filtering should fail filter = Filter(block_hashes=[block_hash_1, block_hash_2]) assert_raises_rpc_error(None, None, self.rpc.get_logs, filter) # generate some more blocks to ensure our two blocks are executed self.rpc.generate_blocks(10) # filtering for these two blocks should return logs in correct order filter = Filter(block_hashes=[block_hash_1, block_hash_2]) logs = self.rpc.get_logs(filter) assert_equal(len(logs), 2 * NUM_CALLS) for ii in range(0, 2 * NUM_CALLS): assert_equal(len(logs[ii]["topics"]), 3) assert_equal(logs[ii]["topics"][0], CALLED_TOPIC) assert (logs[ii]["topics"][1] == self.address_to_topic(sender)) assert_equal(logs[ii]["topics"][2], self.number_to_topic(ii + NUM_CALLS)) # block hash order should not affect log order filter = Filter(block_hashes=[block_hash_2, block_hash_1]) logs2 = self.rpc.get_logs(filter) assert_equal(logs, logs2) # given a limit, we should receive the _last_ few logs filter = Filter(block_hashes=[block_hash_1, block_hash_2], limit=hex(NUM_CALLS + NUM_CALLS // 2)) logs = self.rpc.get_logs(filter) assert_equal(len(logs), NUM_CALLS + NUM_CALLS // 2) for ii in range(0, NUM_CALLS + NUM_CALLS // 2): assert_equal(len(logs[ii]["topics"]), 3) assert_equal(logs[ii]["topics"][0], CALLED_TOPIC) assert (logs[ii]["topics"][1] == self.address_to_topic(sender)) assert_equal(logs[ii]["topics"][2], self.number_to_topic(ii + NUM_CALLS + NUM_CALLS // 2)) self.log.info("Pass") def address_to_topic(self, address): return "0x" + address[2:].zfill(64) def number_to_topic(self, number): return "0x" + ("%x" % number).zfill(64) def deploy_contract(self, sender, priv_key, data_hex): c0 = self.rpc.get_collateral_for_storage(sender) tx = self.rpc.new_contract_tx(receiver="", data_hex=data_hex, sender=sender, priv_key=priv_key, storage_limit=253) assert_equal(self.rpc.send_tx(tx, True), tx.hash_hex()) receipt = self.rpc.get_transaction_receipt(tx.hash_hex()) assert_equal(receipt["outcomeStatus"], 0) address = receipt["contractCreated"] c1 = self.rpc.get_collateral_for_storage(sender) assert_equal(c1 - c0, 253 * 10**18 // 1024) assert_is_hex_string(address) return receipt, address def call_contract(self, sender, priv_key, contract, data_hex, storage_limit): c0 = self.rpc.get_collateral_for_storage(sender) tx = self.rpc.new_contract_tx(receiver=contract, data_hex=data_hex, sender=sender, priv_key=priv_key, storage_limit=storage_limit) assert_equal(self.rpc.send_tx(tx, True), tx.hash_hex()) receipt = self.rpc.get_transaction_receipt(tx.hash_hex()) assert_equal(receipt["outcomeStatus"], 0) c1 = self.rpc.get_collateral_for_storage(sender) assert_equal(c1 - c0, storage_limit * 10**18 // 1024) return receipt def assert_response_format_correct(self, response): assert_equal(type(response), list) for log in response: self.assert_log_format_correct(log) def assert_log_format_correct(self, log): assert_is_hex_string(log["address"]) assert_is_hex_string(log["epochNumber"]) assert_is_hex_string(log["logIndex"]) assert_is_hex_string(log["transactionIndex"]) assert_is_hex_string(log["transactionLogIndex"]) assert_is_hash_string(log["blockHash"]) assert_is_hash_string(log["transactionHash"]) assert_equal(type(log["topics"]), list)
class CrossSpaceLogFilteringTest(ConfluxTestFramework): def set_test_params(self): self.num_nodes = 1 self.conf_parameters["evm_chain_id"] = str(10) self.conf_parameters["evm_transaction_block_ratio"] = str(1) def setup_network(self): self.add_nodes(self.num_nodes) self.start_node(0, ["--archive"]) self.rpc = RpcClient(self.nodes[0]) ip = self.nodes[0].ip port = self.nodes[0].rpcport self.w3 = Web3(Web3.HTTPProvider(f'http://{ip}:{port}/')) assert_equal(self.w3.isConnected(), True) def run_test(self): # initialize Conflux account self.cfxPrivkey = default_config['GENESIS_PRI_KEY'] self.cfxAccount = self.rpc.GENESIS_ADDR print(f'Using Conflux account {self.cfxAccount}') # initialize EVM account self.evmAccount = self.w3.eth.account.privateKeyToAccount( '0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef' ) print(f'Using EVM account {self.evmAccount.address}') self.cross_space_transfer(self.evmAccount.address, 1 * 10**18) assert_equal(self.nodes[0].eth_getBalance(self.evmAccount.address), hex(1 * 10**18)) # deploy Conflux space contract confluxContractAddr = self.deploy_conflux_space(CONFLUX_CONTRACT_PATH) print(f'Conflux contract: {confluxContractAddr}') # deploy EVM space contract evmContractAddr = self.deploy_evm_space(EVM_CONTRACT_PATH) print(f'EVM contract: {evmContractAddr}') # --- # .-----------------| D |.... # V --- | # --- --- --- --- # ... <-- | A | <- | B | <- | C | <- | E | <- ... # --- --- --- --- # # A --- B --- C --- D --- E # block number 0 | 1 | 2 | 3 | 4 | # epoch number 0 | 1 | 2 | 3 | cfx_next_nonce = self.rpc.get_nonce(self.cfxAccount) cfx_tx_hashes = [] evm_next_nonce = self.w3.eth.getTransactionCount( self.evmAccount.address) evm_tx_hashes = [] def emitConflux(n): nonlocal cfx_next_nonce, cfx_tx_hashes data_hex = (encode_hex_0x(keccak(b"emitConflux(uint256)"))[:10] + encode_u256(n)) tx = self.rpc.new_contract_tx(receiver=confluxContractAddr, data_hex=data_hex, nonce=cfx_next_nonce, sender=self.cfxAccount, priv_key=self.cfxPrivkey) cfx_next_nonce += 1 cfx_tx_hashes.append(tx.hash_hex()) return tx def emitBoth(n): nonlocal cfx_next_nonce, cfx_tx_hashes data_hex = encode_hex_0x( keccak(b"emitBoth(uint256,bytes20)"))[:10] + encode_u256( n) + encode_bytes20(evmContractAddr.replace('0x', '')) tx = self.rpc.new_contract_tx(receiver=confluxContractAddr, data_hex=data_hex, nonce=cfx_next_nonce, sender=self.cfxAccount, priv_key=self.cfxPrivkey) cfx_next_nonce += 1 cfx_tx_hashes.append(tx.hash_hex()) return tx def emitEVM(n): nonlocal evm_next_nonce, evm_tx_hashes data_hex = (encode_hex_0x(keccak(b"emitEVM(uint256)"))[:10] + encode_u256(n)) tx, hash = self.construct_evm_tx(receiver=evmContractAddr, data_hex=data_hex, nonce=evm_next_nonce) evm_next_nonce += 1 evm_tx_hashes.append(hash) return tx # generate ledger block_0 = self.rpc.block_by_epoch("latest_mined")['hash'] block_a = self.rpc.generate_custom_block(parent_hash=block_0, referee=[], txs=[ emitConflux(11), emitBoth(12), emitEVM(13), ]) block_b = self.rpc.generate_custom_block(parent_hash=block_a, referee=[], txs=[ emitConflux(14), emitBoth(15), emitEVM(16), ]) block_c = self.rpc.generate_custom_block(parent_hash=block_b, referee=[], txs=[]) block_d = self.rpc.generate_custom_block(parent_hash=block_a, referee=[], txs=[ emitConflux(21), emitBoth(22), emitEVM(23), ]) block_e = self.rpc.generate_custom_block(parent_hash=block_c, referee=[block_d], txs=[ emitConflux(24), emitBoth(25), emitEVM(26), ]) epoch_a = self.rpc.block_by_hash(block_a)['epochNumber'] epoch_b = self.rpc.block_by_hash(block_b)['epochNumber'] epoch_e = self.rpc.block_by_hash(block_e)['epochNumber'] # make sure transactions have been executed parent_hash = block_e for _ in range(5): block = self.rpc.generate_custom_block(parent_hash=parent_hash, referee=[], txs=[]) parent_hash = block for h in cfx_tx_hashes: receipt = self.rpc.get_transaction_receipt(h) assert_equal(receipt["outcomeStatus"], "0x0") for h in evm_tx_hashes: receipt = self.w3.eth.waitForTransactionReceipt(h) assert_equal(receipt["status"], 1) # check Conflux events filter = Filter(topics=[TEST_EVENT_TOPIC], from_epoch=epoch_a, to_epoch=epoch_e) logs = self.rpc.get_logs(filter) assert_equal(len(logs), 8) # --------------- 1 block per epoch --------------- # check EVM events # we expect 4 events: #12, #13, #15, #16 filter = { "topics": [TEST_EVENT_TOPIC], "fromBlock": epoch_a, "toBlock": epoch_b } logs = self.nodes[0].eth_getLogs(filter) assert_equal(len(logs), 4) # emitBoth: TestEvent(12) assert_equal(logs[0]["data"], number_to_topic(12)) assert_equal(logs[0]["address"], evmContractAddr.lower()) assert_equal(logs[0]["blockHash"], block_a) assert_equal(logs[0]["blockNumber"], epoch_a) assert_equal(logs[0]["transactionHash"], cfx_tx_hashes[1]) # TODO: should use phantom tx here # assert_equal(logs[0]["logIndex"], '0x0') # assert_equal(logs[0]["transactionIndex"], '0x0') # assert_equal(logs[0]["transactionLogIndex"], '0x0') assert_equal(logs[0]["removed"], False) # emitEVM: TestEvent(13) assert_equal(logs[1]["data"], number_to_topic(13)) assert_equal(logs[1]["address"], evmContractAddr.lower()) assert_equal(logs[1]["blockHash"], block_a) assert_equal(logs[1]["blockNumber"], epoch_a) assert_equal(logs[1]["transactionHash"], evm_tx_hashes[0].hex()) # assert_equal(logs[1]["logIndex"], '0x1') # assert_equal(logs[1]["transactionIndex"], '0x1') assert_equal(logs[1]["transactionLogIndex"], '0x0') assert_equal(logs[1]["removed"], False) # emitBoth: TestEvent(15) assert_equal(logs[2]["data"], number_to_topic(15)) assert_equal(logs[2]["address"], evmContractAddr.lower()) assert_equal(logs[2]["blockHash"], block_b) assert_equal(logs[2]["blockNumber"], epoch_b) assert_equal(logs[2]["transactionHash"], cfx_tx_hashes[3]) # TODO: should use phantom tx here # assert_equal(logs[2]["logIndex"], '0x0') # assert_equal(logs[2]["transactionIndex"], '0x0') # assert_equal(logs[2]["transactionLogIndex"], '0x0') assert_equal(logs[2]["removed"], False) # emitEVM: TestEvent(16) assert_equal(logs[3]["data"], number_to_topic(16)) assert_equal(logs[3]["address"], evmContractAddr.lower()) assert_equal(logs[3]["blockHash"], block_b) assert_equal(logs[3]["blockNumber"], epoch_b) assert_equal(logs[3]["transactionHash"], evm_tx_hashes[1].hex()) # assert_equal(logs[3]["logIndex"], '0x1') # assert_equal(logs[3]["transactionIndex"], '0x1') assert_equal(logs[3]["transactionLogIndex"], '0x0') assert_equal(logs[3]["removed"], False) # --------------- 2 blocks per epoch --------------- # check EVM events # we expect 4 events: #22, #23, #25, #26 filter = { "topics": [TEST_EVENT_TOPIC], "fromBlock": epoch_e, "toBlock": epoch_e } logs = self.nodes[0].eth_getLogs(filter) assert_equal(len(logs), 4) # emitBoth: TestEvent(22) assert_equal(logs[0]["data"], number_to_topic(22)) assert_equal(logs[0]["address"], evmContractAddr.lower()) assert_equal(logs[0]["blockHash"], block_e) assert_equal(logs[0]["blockNumber"], epoch_e) assert_equal(logs[0]["transactionHash"], cfx_tx_hashes[5]) # TODO: should use phantom tx here # assert_equal(logs[0]["logIndex"], '0x0') # assert_equal(logs[0]["transactionIndex"], '0x0') # assert_equal(logs[0]["transactionLogIndex"], '0x0') assert_equal(logs[0]["removed"], False) # emitEVM: TestEvent(23) assert_equal(logs[1]["data"], number_to_topic(23)) assert_equal(logs[1]["address"], evmContractAddr.lower()) assert_equal(logs[1]["blockHash"], block_e) assert_equal(logs[1]["blockNumber"], epoch_e) assert_equal(logs[1]["transactionHash"], evm_tx_hashes[2].hex()) # assert_equal(logs[1]["logIndex"], '0x1') # assert_equal(logs[1]["transactionIndex"], '0x1') assert_equal(logs[1]["transactionLogIndex"], '0x0') assert_equal(logs[1]["removed"], False) # emitBoth: TestEvent(25) assert_equal(logs[2]["data"], number_to_topic(25)) assert_equal(logs[2]["address"], evmContractAddr.lower()) assert_equal(logs[2]["blockHash"], block_e) assert_equal(logs[2]["blockNumber"], epoch_e) assert_equal(logs[2]["transactionHash"], cfx_tx_hashes[7]) # TODO: should use phantom tx here # assert_equal(logs[2]["logIndex"], '0x2') # assert_equal(logs[2]["transactionIndex"], '0x2') # assert_equal(logs[2]["transactionLogIndex"], '0x0') assert_equal(logs[2]["removed"], False) # emitEVM: TestEvent(26) assert_equal(logs[3]["data"], number_to_topic(26)) assert_equal(logs[3]["address"], evmContractAddr.lower()) assert_equal(logs[3]["blockHash"], block_e) assert_equal(logs[3]["blockNumber"], epoch_e) assert_equal(logs[3]["transactionHash"], evm_tx_hashes[3].hex()) # assert_equal(logs[3]["logIndex"], '0x3') # assert_equal(logs[3]["transactionIndex"], '0x3') assert_equal(logs[3]["transactionLogIndex"], '0x0') assert_equal(logs[3]["removed"], False) # --------------- other fields --------------- # filter by block hash filter = {"topics": [TEST_EVENT_TOPIC], "blockHash": block_c} logs_2 = self.nodes[0].eth_getLogs(filter) assert_equal(logs_2, []) filter = { "topics": [TEST_EVENT_TOPIC], "blockHash": block_d } # from EVM perspective, D does not exist assert_raises_rpc_error(None, None, self.nodes[0].eth_getLogs, filter) filter = {"topics": [TEST_EVENT_TOPIC], "blockHash": block_e} logs_2 = self.nodes[0].eth_getLogs(filter) assert_equal(logs_2, logs) # filter limit filter = { "topics": [TEST_EVENT_TOPIC], "blockHash": block_e, "limit": 1 } logs_2 = self.nodes[0].eth_getLogs(filter) assert_equal(logs_2, [logs[-1]]) # "earliest", "latest" filter = { "topics": [TEST_EVENT_TOPIC], "fromBlock": "earliest", "toBlock": "latest" } logs_2 = self.nodes[0].eth_getLogs(filter) assert_equal(len(logs_2), 8) filter = { "topics": [TEST_EVENT_TOPIC], "fromBlock": "earliest", "toBlock": "latest", "limit": 4 } logs_2 = self.nodes[0].eth_getLogs(filter) assert_equal(logs_2, logs) # address filter = {"address": confluxContractAddr} logs_2 = self.nodes[0].eth_getLogs(filter) assert_equal(logs_2, []) filter = {"address": evmContractAddr} logs_2 = self.nodes[0].eth_getLogs(filter) assert_equal(len(logs_2), 8) self.log.info("Pass") def cross_space_transfer(self, to, value): to = to.replace('0x', '') tx = self.rpc.new_tx( value=value, receiver="0x0888000000000000000000000000000000000006", data=decode_hex(f"0xda8d5daf{to}000000000000000000000000"), nonce=self.rpc.get_nonce(self.cfxAccount), gas=1000000, ) self.rpc.send_tx(tx, True) def deploy_conflux_space(self, bytecode_path): bytecode_file = os.path.join( os.path.dirname(os.path.realpath(__file__)), bytecode_path) assert (os.path.isfile(bytecode_file)) bytecode = open(bytecode_file).read() tx = self.rpc.new_contract_tx(receiver="", data_hex=bytecode, sender=self.cfxAccount, priv_key=self.cfxPrivkey, storage_limit=20000) assert_equal(self.rpc.send_tx(tx, True), tx.hash_hex()) receipt = self.rpc.get_transaction_receipt(tx.hash_hex()) assert_equal(receipt["outcomeStatus"], "0x0") addr = receipt["contractCreated"] assert_is_hex_string(addr) return addr def deploy_evm_space(self, bytecode_path): bytecode_file = os.path.join( os.path.dirname(os.path.realpath(__file__)), bytecode_path) assert (os.path.isfile(bytecode_file)) bytecode = open(bytecode_file).read() nonce = self.w3.eth.getTransactionCount(self.evmAccount.address) signed = self.evmAccount.signTransaction({ "to": None, "value": 0, "gasPrice": 1, "gas": 210000, "nonce": nonce, "chainId": 10, "data": bytecode, }) tx_hash = signed["hash"] return_tx_hash = self.w3.eth.sendRawTransaction( signed["rawTransaction"]) assert_equal(tx_hash, return_tx_hash) self.rpc.generate_block(1) self.rpc.generate_blocks(20, 1) receipt = self.w3.eth.waitForTransactionReceipt(tx_hash) assert_equal(receipt["status"], 1) addr = receipt["contractAddress"] return addr def construct_evm_tx(self, receiver, data_hex, nonce): signed = self.evmAccount.signTransaction({ "to": receiver, "value": 0, "gasPrice": 1, "gas": 150000, "nonce": nonce, "chainId": 10, "data": data_hex, }) tx = [ nonce, 1, 150000, bytes.fromhex(receiver.replace('0x', '')), 0, bytes.fromhex(data_hex.replace('0x', '')), signed["v"], signed["r"], signed["s"] ] return tx, signed["hash"]
class ContextInternalContractTest(ConfluxTestFramework): def set_test_params(self): self.num_nodes = 1 def setup_network(self): self.add_nodes(self.num_nodes) self.start_node(0, ["--archive"]) self.rpc = RpcClient(self.nodes[0]) def run_test(self): priv_key = default_config["GENESIS_PRI_KEY"] sender = eth_utils.encode_hex(priv_to_addr(priv_key)) # deploy storage test contract bytecode_file = os.path.join( os.path.dirname(os.path.realpath(__file__)), CONTRACT_PATH) assert (os.path.isfile(bytecode_file)) bytecode = open(bytecode_file).read() tx = self.rpc.new_contract_tx(receiver="", data_hex=bytecode, sender=sender, priv_key=priv_key, storage_limit=20000) assert_equal(self.rpc.send_tx(tx, True), tx.hash_hex()) receipt = self.rpc.get_transaction_receipt(tx.hash_hex()) contractAddr = receipt["contractCreated"] assert_is_hex_string(contractAddr) # --- --- --- --- # .- | A | <--- | C | <--- | D | <--- | E | <--- ... # --- | --- --- --- --- # ... <--- | P | <-* . # --- | --- . # .- | B | <.................. # --- # 0 --- A --- C --- B --- D --- # block number: x | x+1 | x+2 | x+3 | x+4 | # epoch number: y | y+1 | y+2 | y + 3 | start_nonce = self.rpc.get_nonce(self.rpc.GENESIS_ADDR) epoch_number_p = int( self.rpc.block_by_epoch("latest_mined")['epochNumber'], 0) block_number_p = int( self.rpc.block_by_epoch("latest_mined")['epochNumber'], 0) assert_equal(epoch_number_p, block_number_p) block_p = self.rpc.block_by_epoch("latest_mined")['hash'] txs = [ self.rpc.new_contract_tx(receiver=contractAddr, data_hex=encode_hex_0x( keccak(b"getBlockNumber()")), nonce=start_nonce + 0, sender=sender, priv_key=priv_key), self.rpc.new_contract_tx(receiver=contractAddr, data_hex=encode_hex_0x( keccak(b"getEpochNumber()")), nonce=start_nonce + 1, sender=sender, priv_key=priv_key), self.rpc.new_contract_tx(receiver=contractAddr, data_hex=encode_hex_0x( keccak(b"getBlockNumber()")), nonce=start_nonce + 2, sender=sender, priv_key=priv_key), self.rpc.new_contract_tx(receiver=contractAddr, data_hex=encode_hex_0x( keccak(b"getEpochNumber()")), nonce=start_nonce + 3, sender=sender, priv_key=priv_key), self.rpc.new_contract_tx(receiver=contractAddr, data_hex=encode_hex_0x( keccak(b"getBlockNumber()")), nonce=start_nonce + 4, sender=sender, priv_key=priv_key), self.rpc.new_contract_tx(receiver=contractAddr, data_hex=encode_hex_0x( keccak(b"getEpochNumber()")), nonce=start_nonce + 5, sender=sender, priv_key=priv_key), self.rpc.new_contract_tx(receiver=contractAddr, data_hex=encode_hex_0x( keccak(b"getBlockNumber()")), nonce=start_nonce + 6, sender=sender, priv_key=priv_key), self.rpc.new_contract_tx(receiver=contractAddr, data_hex=encode_hex_0x( keccak(b"getEpochNumber()")), nonce=start_nonce + 7, sender=sender, priv_key=priv_key), ] block_a = self.rpc.generate_custom_block(parent_hash=block_p, referee=[], txs=txs[0:2]) block_c = self.rpc.generate_custom_block(parent_hash=block_a, referee=[], txs=txs[2:4]) block_b = self.rpc.generate_custom_block(parent_hash=block_p, referee=[], txs=txs[4:6]) block_d = self.rpc.generate_custom_block(parent_hash=block_c, referee=[block_b], txs=txs[6:8]) # make sure transactions have been executed parent_hash = block_d for _ in range(5): block = self.rpc.generate_custom_block(parent_hash=parent_hash, referee=[], txs=[]) parent_hash = block # transactions in block A # note: topic-1 of each log is the emitted block/epoch number block_number_a = int( self.rpc.get_transaction_receipt( txs[0].hash_hex())['logs'][0]['topics'][1], 16) epoch_number_a = int( self.rpc.get_transaction_receipt( txs[1].hash_hex())['logs'][0]['topics'][1], 16) assert_equal(block_number_a, block_number_p + 1) assert_equal(epoch_number_a, epoch_number_p + 1) # transactions in block B block_number_b = int( self.rpc.get_transaction_receipt( txs[4].hash_hex())['logs'][0]['topics'][1], 16) epoch_number_b = int( self.rpc.get_transaction_receipt( txs[5].hash_hex())['logs'][0]['topics'][1], 16) assert_equal(block_number_b, block_number_p + 3) assert_equal(epoch_number_b, epoch_number_p + 3) # transactions in block C block_number_c = int( self.rpc.get_transaction_receipt( txs[2].hash_hex())['logs'][0]['topics'][1], 16) epoch_number_c = int( self.rpc.get_transaction_receipt( txs[3].hash_hex())['logs'][0]['topics'][1], 16) assert_equal(block_number_c, block_number_p + 2) assert_equal(epoch_number_c, epoch_number_p + 2) # transactions in block d block_number_d = int( self.rpc.get_transaction_receipt( txs[6].hash_hex())['logs'][0]['topics'][1], 16) epoch_number_d = int( self.rpc.get_transaction_receipt( txs[7].hash_hex())['logs'][0]['topics'][1], 16) assert_equal(block_number_d, block_number_p + 4) assert_equal(epoch_number_d, epoch_number_p + 3) self.log.info("Pass")