def wait_for_tx(self, all_txs, check_status):
     for tx in all_txs:
         for i in range(3):
             try:
                 retry = True
                 while retry:
                     try:
                         wait_until(
                             lambda: checktx(self.nodes[0], tx.hash_hex()),
                             timeout=20)
                         retry = False
                     except CannotSendRequest:
                         time.sleep(0.01)
                 break
             except AssertionError as _:
                 self.nodes[0].p2p.send_protocol_msg(
                     Transactions(transactions=[tx]))
             if i == 2:
                 raise AssertionError(
                     "Tx {} not confirmed after 30 seconds".format(
                         tx.hash_hex()))
     # After having optimistic execution, get_receipts may get receipts with not deferred block, these extra blocks
     # ensure that later get_balance can get correct executed balance for all transactions
     client = RpcClient(self.nodes[0])
     for _ in range(5):
         client.generate_block()
     receipts = [
         client.get_transaction_receipt(tx.hash_hex()) for tx in all_txs
     ]
     self.log.debug("Receipts received: {}".format(receipts))
     if check_status:
         map(lambda x: assert_equal(x['outcomeStatus'], 0), receipts)
     return receipts
Esempio n. 2
0
    def test(self, num_senders, num_receivers, num_txs):
        self.log.debug("Initializing {} senders".format(num_senders))
        senders = self.init_senders(num_senders)

        self.log.debug("Initializing {} receivers".format(num_receivers))
        receivers = self.init_receivers(num_receivers)

        self.log.info("begin to send {} txs to nodes and generate blocks ...".format(num_txs))
        txs = self.send_txs_async(senders, receivers, num_txs)

        # generate blocks to pack txs
        self.log.info("continue to generate blocks to pack all transactions ...")
        client = RpcClient(self.nodes[0])
        retry = num_txs
        for sender in senders:
            while True:
                receipt = client.get_receipt(sender.last_tx_hash)
                if receipt is not None:
                    break

                assert retry > 0, "some tx not stated yet even after {} retries".format(num_txs)
                retry -= 1

                self.generate_block(num_txs)
                time.sleep(0.5)

        # After having optimistic execution, get_receipts may get receipts with not deferred block, these extra blocks
        # ensure that later get_balance can get correct executed balance for all transactions
        for _ in range(5):
            client.generate_block()

        self.log.info("sync up blocks among nodes ...")
        sync_blocks(self.nodes)

        # check DAG
        self.log.info("begin to validate DAG for all nodes ...")
        self.check_with_rpc(client.epoch_number)
        self.check_with_rpc(client.best_block_hash)
        self.check_with_rpc(client.gas_price)
        self.check_with_rpc(client.chain, True)

        # check receipt
        self.log.info("begin to validate transaction receipts ...")
        for idx in range(self.num_nodes):
            node_client = RpcClient(self.nodes[idx])

            for tx_hash in [sent_tx.hash_hex() for sent_tx in txs]:
                receipt = node_client.get_receipt(tx_hash)
                assert_equal(receipt is None, False)

        # check balance and nonce for all accounts
        self.log.info("begin to validate balance and nonce ...")
        all_accounts = list(senders)
        all_accounts.extend(receivers)
        for idx in range(self.num_nodes):
            self.log.debug("validate for node %d", idx)
            node_client = RpcClient(self.nodes[idx])
            for account in all_accounts:
                assert_equal(node_client.get_balance(account.address), account.balance)
                assert_equal(node_client.get_nonce(account.address), account.nonce)
Esempio n. 3
0
    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))
Esempio n. 4
0
 def run(self):
     try:
         client = RpcClient(self.nodes[self.i])
         # Do not limit num tx in blocks, only limit it with block size
         h = client.generate_block(10000000, self.tx_n * self.tx_data_len)
         self.log.debug("node %d actually generate block %s", self.i, h)
     except Exception as e:
         self.log.error("Node %d fails to generate block", self.i)
         self.log.error(str(e))
Esempio n. 5
0
    def run_test(self):
        time.sleep(3)

        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)
        account = self.w3.eth.account.privateKeyToAccount(
            '0x348ce564d427a3311b6536bbcff9390d69395b06ed6c486954e971d960fe8709'
        )

        sender = account.address

        self.cross_space_transfer(sender, 1 * 10**18)
        assert_equal(1 * 10**18, self.w3.eth.get_balance(sender))

        receiver = Web3.toChecksumAddress(
            "10000000000000000000000000000000000000aa")
        signed = account.signTransaction({
            "to": receiver,
            "value": 5 * 10**17,
            "gasPrice": 1,
            "gas": 21000,
            "nonce": 0,
            "chainId": 10
        })
        tx_hash = signed["hash"]
        return_tx_hash = self.w3.eth.sendRawTransaction(
            signed["rawTransaction"])
        assert_equal(tx_hash, return_tx_hash)

        client = RpcClient(self.nodes[0])
        client.generate_block(1)
        client.generate_blocks(10)
        receipt = self.w3.eth.waitForTransactionReceipt(tx_hash)
        assert_equal(receipt["status"], 1)

        assert_equal(5 * 10**17, self.w3.eth.get_balance(receiver))
        assert_equal(5 * 10**17 - 21000, self.w3.eth.get_balance(sender))

        self.nodes[0].stop()
Esempio n. 6
0
 def run(self):
     try:
         client = RpcClient(self.nodes[self.i])
         # Do not limit num tx in blocks, and block size is already limited by `max_block_size_in_bytes`
         start = time.time()
         h = client.generate_block(10000000, self.max_block_size)
         self.rpc_times.append(round(time.time() - start, 3))
         self.log.debug("node %d actually generate block %s", self.i, h)
     except Exception as e:
         self.log.error("Node %d fails to generate block", self.i)
         self.log.error(str(e))
Esempio n. 7
0
    def run_test(self):
        client0 = RpcClient(self.nodes[0])
        client1 = RpcClient(self.nodes[1])
        genesis = client0.best_block_hash()

        self.log.info("Generating two initial blocks")

        a1 = client0.generate_block()
        a2 = client0.generate_block()
        block_a2 = client0.block_by_hash(a2)
        assert(int(block_a2['height'], 16) == 2)

        self.log.info("Generating two invalid blocks")

        invalid = self.nodes[0].generatefixedblock(genesis, [a2], 0, False, INITIAL_DIFFICULTY)
        invalid2 = self.nodes[0].generatefixedblock(a2, [invalid], 0, False, INITIAL_DIFFICULTY)

        self.log.info("Sync two nodes")
        connect_nodes(self.nodes, 0, 1)
        wait_until(lambda: self.nodes[1].getblockcount() >= 3, timeout = 10)
        self.log.info("Node0 block count " + str(self.nodes[0].getblockcount()))
        self.log.info("Node1 block count " + str(self.nodes[1].getblockcount()))

        self.log.info("Generating a block without referencing partial invalid blocks")

        b1 = client1.generate_block()
        block_b1 = client1.block_by_hash(b1)
        assert(block_b1['parentHash'] == a2)
        assert(len(block_b1['refereeHashes']) == 0)

        self.log.info("Sync two nodes")
        connect_nodes(self.nodes, 1, 0)
        wait_until(lambda: self.nodes[0].getblockcount() >= 6, timeout = 40)
        wait_until(lambda: self.nodes[1].getblockcount() >= 4, timeout = 40)

        timer_cnt = 0
        diff = int(block_b1['difficulty'], 16)
        pow_qual = int(block_b1['powQuality'], 16)
        if diff * TIMER_RATIO <= pow_qual:
            timer_cnt = 1

        self.log.info("Start timer tick " + str(timer_cnt))

        while timer_cnt < TIMER_BETA:
            a = client0.generate_block()
            self.log.info("Generated a block " + a)
            block_a = client0.block_by_hash(a)
            assert(len(block_a['refereeHashes']) == 0)
            diff = int(block_a['difficulty'], 16)
            pow_qual = int(block_a['powQuality'], 16)
            if diff * TIMER_RATIO <= pow_qual:
                timer_cnt += 1
                self.log.info("Timer increased to " + str(timer_cnt))

        self.log.info("Sync two nodes")
        connect_nodes(self.nodes, 0, 1)
        sync_blocks(self.nodes)

        self.log.info("Node1 generating a block to reference two partial invalid blocks")

        b2 = client1.generate_block()
        block_b2 = client1.block_by_hash(b2)
        assert(len(block_b2['refereeHashes']) > 0)
        assert(block_b2['refereeHashes'][0] == invalid2)

        self.log.info("Pass!")
class StorageMaintenanceTest(ConfluxTestFramework):
    def set_test_params(self):
        self.num_nodes = 1
        self.setup_clean_chain = True
        self.mining_author = "0x10000000000000000000000000000000000000aa"
        self.conf_parameters = {"mining_author": "\"10000000000000000000000000000000000000aa\"",
                                "mining_type": "'disable'"
                                }
        self.gasPrice = 1

    def setup_network(self):
        self.log.info("setup nodes ...")
        self.setup_nodes()

    def run_test(self):
        self.rpc = RpcClient(self.nodes[0])
        priv_key = default_config["GENESIS_PRI_KEY"]
        sender = eth_utils.encode_hex(priv_to_addr(priv_key))
        block_reward = 7000000000000000000

        # 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()

        # 1. Produce an empty block
        self.rpc.generate_block()

        # 2. Deploy a contract: this will create 6 blocks and the first block contains 'create contract' transaction,
        # the other 5 blocks are empty.
        receipt, _ = self.deploy_contract(sender, priv_key, bytecode)

        # 3. Produce 10 empty blocks, and the miner's reward for the first block will be updated to world-state
        for _ in range(10): self.rpc.generate_block()
        balance = parse_as_int(self.nodes[0].cfx_getBalance(self.mining_author))
        count = self.nodes[0].getblockcount()
        expected = block_reward
        self.log.info("block count: %d, balance: %d, expected: %d", count, balance, expected)
        assert_equal(balance, expected)

        # 4. Produce 1 empty block, and the miner will receive reward for the second block. This block reward should
        # contains transaction fee.
        self.rpc.generate_blocks(1)
        balance = parse_as_int(self.nodes[0].cfx_getBalance(self.mining_author))
        count = self.nodes[0].getblockcount()
        transaction_fee = parse_as_int(receipt['gasFee'])
        expected += block_reward + transaction_fee
        self.log.info("block count: %d, balance: %d, expected: %d, transaction_fee: %d", count, balance, expected,
                transaction_fee)
        assert_equal(balance, expected)

        # 5. Produce 1 empty block, and the miner will receive reward for the third empty block. This block reward
        # should contains storage maintenance fee.
        self.rpc.generate_blocks(1)
        balance = parse_as_int(self.nodes[0].cfx_getBalance(self.mining_author))
        count = self.nodes[0].getblockcount()
        collateral_for_storage = self.rpc.get_collateral_for_storage(sender)
        storage_fee = collateral_for_storage * 4 // 100 // 63072000
        expected += block_reward + storage_fee
        self.log.info("block count: %d, balance: %d, expected: %d, collateral_for_storage: %d, storage_fee: %d", count,
                balance, expected, collateral_for_storage, storage_fee)
        assert_equal(balance, expected)

        # 6. Produce 1 empty block, and the miner will receive reward for the forth empty block. This block reward
        # should contains storage maintenance fee.
        self.rpc.generate_blocks(1)
        balance = parse_as_int(self.nodes[0].cfx_getBalance(self.mining_author))
        count = self.nodes[0].getblockcount()
        collateral_for_storage = self.rpc.get_collateral_for_storage(sender)
        storage_fee = collateral_for_storage * 4 // 100 // 63072000
        expected += storage_fee + block_reward
        self.log.info("block count: %d, balance: %d, expected: %d, collateral_for_storage: %d, storage_fee: %d", count,
                balance, expected, collateral_for_storage, storage_fee)

        assert_equal(balance, expected)

    def deploy_contract(self, sender, priv_key, data_hex):
        tx = self.rpc.new_contract_tx(receiver="", data_hex=data_hex, sender=sender, priv_key=priv_key, nonce=None,
                gas_price=self.gasPrice,
                storage_limit=20000)
        assert_equal(self.rpc.send_tx(tx, True), tx.hash_hex())
        receipt = self.rpc.get_transaction_receipt(tx.hash_hex())
        address = receipt["contractCreated"]
        assert_is_hex_string(address)
        return receipt, address
Esempio n. 9
0
 def check_packed():
     client = RpcClient(self.nodes[0])
     client.generate_block(1)
     return checktx(self.nodes[0], tx.hash_hex())
Esempio n. 10
0
    def run_test(self):
        sponsor_whitelist_contract_addr = Web3.toChecksumAddress(
            "0888000000000000000000000000000000000001")
        collateral_per_storage_key = COLLATERAL_UNIT_IN_DRIP * 64
        upper_bound = 5 * 10**7

        file_dir = os.path.dirname(os.path.realpath(__file__))

        control_contract_file_path = os.path.join(
            file_dir, "..", "internal_contract", "metadata",
            "SponsorWhitelistControl.json")
        control_contract_dict = json.loads(
            open(control_contract_file_path, "r").read())

        control_contract = get_contract_instance(
            contract_dict=control_contract_dict)

        test_contract = get_contract_instance(
            abi_file=os.path.join(
                file_dir, "contracts/commission_privilege_test_abi.json"),
            bytecode_file=os.path.join(
                file_dir, "contracts/commission_privilege_test_bytecode.dat"),
        )

        start_p2p_connection(self.nodes)

        self.log.info("Initializing contract")
        genesis_key = self.genesis_priv_key
        genesis_addr = encode_hex_0x(self.genesis_addr)
        self.log.info("genesis_addr={}".format(genesis_addr))
        nonce = 0
        gas_price = 1
        gas = CONTRACT_DEFAULT_GAS
        block_gen_thread = BlockGenThread(self.nodes, self.log)
        block_gen_thread.start()
        self.tx_conf = {
            "from": Web3.toChecksumAddress(genesis_addr),
            "nonce": int_to_hex(nonce),
            "gas": int_to_hex(gas),
            "gasPrice": int_to_hex(gas_price),
            "chainId": 0
        }

        # Setup balance for node 0
        node = self.nodes[0]
        client = RpcClient(node)
        (addr1, priv_key1) = client.rand_account()
        self.log.info("addr1={}".format(addr1))
        tx = client.new_tx(sender=genesis_addr,
                           priv_key=genesis_key,
                           value=10**6,
                           nonce=self.get_nonce(self.genesis_addr),
                           receiver=addr1)
        client.send_tx(tx, True)
        assert_equal(client.get_balance(addr1), 10**6)

        # setup contract
        transaction = self.call_contract_function(
            contract=test_contract,
            name="constructor",
            args=[],
            sender_key=self.genesis_priv_key,
            storage_limit=20000)
        contract_addr = self.wait_for_tx([transaction],
                                         True)[0]['contractCreated']
        self.log.info("contract_addr={}".format(contract_addr))
        assert_equal(client.get_balance(contract_addr), 0)

        # sponsor the contract succeed
        b0 = client.get_balance(genesis_addr)
        self.call_contract_function(
            contract=control_contract,
            name="setSponsorForGas",
            args=[Web3.toChecksumAddress(contract_addr), upper_bound],
            value=10**18,
            sender_key=self.genesis_priv_key,
            contract_addr=sponsor_whitelist_contract_addr,
            wait=True)
        assert_equal(client.get_sponsor_balance_for_gas(contract_addr), 10**18)
        assert_equal(client.get_sponsor_for_gas(contract_addr), genesis_addr)
        assert_equal(client.get_sponsor_gas_bound(contract_addr), upper_bound)
        assert_equal(client.get_balance(genesis_addr),
                     b0 - 10**18 - charged_of_huge_gas(gas))

        # set privilege for addr1
        b0 = client.get_balance(genesis_addr)
        c0 = client.get_collateral_for_storage(genesis_addr)
        transaction = self.call_contract_function(
            contract=test_contract,
            name="add",
            args=[Web3.toChecksumAddress(addr1)],
            sender_key=genesis_key,
            contract_addr=contract_addr,
            wait=True,
            check_status=True,
            storage_limit=64)
        assert_equal(
            client.get_balance(genesis_addr),
            b0 - charged_of_huge_gas(gas) - collateral_per_storage_key)
        assert_equal(client.get_collateral_for_storage(genesis_addr),
                     c0 + collateral_per_storage_key)
        assert_equal(
            self.wait_for_tx([transaction], True)[0]['gasCoveredBySponsor'],
            False)

        # addr1 call contract with privilege without enough cfx for gas fee
        sb = client.get_sponsor_balance_for_gas(contract_addr)
        b1 = client.get_balance(addr1)
        transaction = self.call_contract_function(contract=test_contract,
                                                  name="foo",
                                                  args=[],
                                                  sender_key=priv_key1,
                                                  contract_addr=contract_addr,
                                                  wait=True,
                                                  check_status=True)
        assert_equal(client.get_balance(addr1), b1)
        assert_equal(client.get_sponsor_balance_for_gas(contract_addr),
                     sb - charged_of_huge_gas(gas))
        assert_equal(
            self.wait_for_tx([transaction], True)[0]['gasCoveredBySponsor'],
            True)

        # sponsor collateral for the contract succeed
        b0 = client.get_balance(genesis_addr)
        self.call_contract_function(
            contract=control_contract,
            name="setSponsorForCollateral",
            args=[Web3.toChecksumAddress(contract_addr)],
            value=10**18,  # 1 CFX = 1KB
            sender_key=self.genesis_priv_key,
            contract_addr=sponsor_whitelist_contract_addr,
            wait=True)
        assert_equal(client.get_sponsor_balance_for_collateral(contract_addr),
                     10**18)
        assert_equal(client.get_sponsor_for_collateral(contract_addr),
                     genesis_addr)
        assert_equal(client.get_balance(genesis_addr),
                     b0 - 10**18 - charged_of_huge_gas(gas))

        # addr1 call contract with privilege without enough cfx for storage
        sb = client.get_sponsor_balance_for_gas(contract_addr)
        b1 = client.get_balance(addr1)
        transaction = self.call_contract_function(contract=test_contract,
                                                  name="foo",
                                                  args=[],
                                                  sender_key=priv_key1,
                                                  contract_addr=contract_addr,
                                                  wait=True,
                                                  check_status=True,
                                                  storage_limit=1024)
        assert_equal(client.get_balance(addr1), b1)
        assert_equal(client.get_sponsor_balance_for_gas(contract_addr),
                     sb - charged_of_huge_gas(gas))
        assert_equal(
            self.wait_for_tx([transaction],
                             True)[0]['storageCoveredBySponsor'], True)

        # addr1 call with larger storage limit, should not packed
        transaction = self.call_contract_function(contract=test_contract,
                                                  name="foo",
                                                  args=[],
                                                  sender_key=priv_key1,
                                                  contract_addr=contract_addr,
                                                  storage_limit=1025)
        for _ in range(10):
            client.generate_block()

        tx_info = self.nodes[0].txpool_txWithPoolInfo(transaction.hash_hex())
        assert_equal(int(tx_info['local_nonce'], 16), 2)
        assert_equal(tx_info['local_balance_enough'], False)
        assert_equal(tx_info['packed'], False)

        # send 1025 * 10 ** 18 // 1024 CFX to addr1
        tx = client.new_tx(sender=genesis_addr,
                           priv_key=genesis_key,
                           value=1025 * 10**18 // 1024,
                           nonce=self.get_nonce(self.genesis_addr),
                           receiver=addr1)
        client.send_tx(tx, True)
        assert_equal(client.get_balance(addr1), 10**6 + 1025 * 10**18 // 1024)
        for _ in range(10):
            client.generate_block()
        tx_info = self.nodes[0].txpool_txWithPoolInfo(transaction.hash_hex())
        # Now addr1 pays for storage collateral by itself.
        assert_equal(
            self.wait_for_tx([transaction],
                             True)[0]['storageCoveredBySponsor'], False)
        assert_equal(int(tx_info['local_nonce'], 16), 3)
        assert_equal(tx_info['packed'], True)

        self.log.info("Pass")
Esempio n. 11
0
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"]
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"]
Esempio n. 13
0
    def run_test(self):
        time.sleep(3)

        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)
        account = self.w3.eth.account.privateKeyToAccount(
            '0x348ce564d427a3311b6536bbcff9390d69395b06ed6c486954e971d960fe8709'
        )

        sender = account.address

        self.cross_space_transfer(sender, 1 * 10**18)
        assert_equal(1 * 10**18, self.w3.eth.get_balance(sender))

        self.test_deploy_1820()

        # Send eip-155 transaction
        receiver = Web3.toChecksumAddress(
            "10000000000000000000000000000000000000aa")
        signed = account.signTransaction({
            "to": receiver,
            "value": 1 * 10**17,
            "gasPrice": 1,
            "gas": 21000,
            "nonce": 0,
            "chainId": 10
        })
        tx_hash = signed["hash"]
        return_tx_hash = self.w3.eth.sendRawTransaction(
            signed["rawTransaction"])
        assert_equal(tx_hash, return_tx_hash)

        client = RpcClient(self.nodes[0])
        client.generate_block(1)
        client.generate_blocks(10)
        receipt = self.w3.eth.waitForTransactionReceipt(tx_hash)
        assert_equal(receipt["status"], 1)

        # Send pre eip-155 transaction
        signed = account.signTransaction({
            "to": receiver,
            "value": 1 * 10**17,
            "gasPrice": 1,
            "gas": 21000,
            "nonce": 1
        })
        tx_hash = signed["hash"]
        return_tx_hash = self.w3.eth.sendRawTransaction(
            signed["rawTransaction"])
        assert_equal(tx_hash, return_tx_hash)

        client.generate_block(1)
        client.generate_blocks(10)
        receipt = self.w3.eth.waitForTransactionReceipt(tx_hash)
        assert_equal(receipt["status"], 1)

        assert_equal(2 * 10**17, self.w3.eth.get_balance(receiver))
        assert_equal(8 * 10**17 - 42000, self.w3.eth.get_balance(sender))

        # Send to transaction
        mapped_sender = keccak_256(self.genesis_addr).digest()[-20:]
        receiver = Web3.toChecksumAddress(mapped_sender.hex())
        signed = account.signTransaction({
            "to": receiver,
            "value": 2 * 10**17,
            "gasPrice": 1,
            "gas": 21000,
            "nonce": 2
        })
        self.w3.eth.sendRawTransaction(signed["rawTransaction"])

        client = RpcClient(self.nodes[0])
        client.generate_block(1)
        client.generate_blocks(10)
        receipt = self.w3.eth.waitForTransactionReceipt(tx_hash)
        assert_equal(receipt["status"], 1)

        assert_equal(2 * 10**17, self.w3.eth.get_balance(mapped_sender))

        # Withdraw transaction
        self.cross_space_withdraw(1 * 10**17)

        assert_equal(1 * 10**17, self.w3.eth.get_balance(mapped_sender))

        # Send transaction with large chain-id, should not panic.
        signed = account.signTransaction({
            "to": receiver,
            "value": 1 * 10**17,
            "gasPrice": 1,
            "gas": 21000,
            "nonce": 3,
            "chainId": 2**33
        })
        assert_raises(ValueError, self.w3.eth.sendRawTransaction,
                      signed["rawTransaction"])

        self.nodes[0].stop()
Esempio n. 14
0
class CrossSpaceLogFilteringTest(ConfluxTestFramework):
    def set_test_params(self):
        self.num_nodes = 1
        self.conf_parameters["evm_chain_id"] = str(10)

    def setup_network(self):
        self.add_nodes(self.num_nodes)
        self.start_node(0, ["--archive"])
        self.rpc = RpcClient(self.nodes[0])

        self.cfxPrivkey = default_config['GENESIS_PRI_KEY']
        self.cfxAccount = self.rpc.GENESIS_ADDR
        print(f'Using Conflux account {self.cfxAccount}')

        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 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}')

        # #1: call emitConflux(1)
        # this will emit 1 event in the Conflux space
        data_hex = encode_hex_0x(
            keccak(b"emitConflux(uint256)"))[:10] + encode_u256(1)
        receipt = self.call_conflux_space(confluxContractAddr, data_hex)

        assert_equal(len(receipt["logs"]), 1)
        assert_equal(receipt["logs"][0]["data"],
                     number_to_topic(1))  # TestEvent(1)

        # #2: call emitBoth(2)
        # this will emit 2 events in the Conflux space (our contract + internal contract) and 1 event in the EVM space
        data_hex = encode_hex_0x(
            keccak(b"emitBoth(uint256,bytes20)"))[:10] + encode_u256(
                2) + encode_bytes20(evmContractAddr.replace('0x', ''))
        receipt = self.call_conflux_space(confluxContractAddr, data_hex)

        assert_equal(len(receipt["logs"]), 2)
        assert_equal(receipt["logs"][0]["data"],
                     number_to_topic(2))  # TestEvent(2)
        # NOTE: EVM-space events are not returned here

        # #3: call emitEVM(3)
        # this will emit 1 event in the EVM space
        data_hex = encode_hex_0x(
            keccak(b"emitEVM(uint256)"))[:10] + encode_u256(3)
        receipt = self.call_evm_space(evmContractAddr, data_hex)

        assert_equal(len(receipt["logs"]), 1)
        assert_equal(receipt["logs"][0]["data"],
                     number_to_topic(3))  # TestEvent(3)
        # NOTE: EVM-space events are not returned here

        # check Conflux events
        # we expect two events from #1 and #2
        filter = Filter(topics=[TEST_EVENT_TOPIC],
                        from_epoch="earliest",
                        to_epoch="latest_state")
        logs = self.rpc.get_logs(filter)
        assert_equal(len(logs), 2)
        assert_equal(logs[0]["data"], number_to_topic(1))  # TestEvent(1)
        assert_equal(logs[1]["data"], number_to_topic(2))  # TestEvent(2)

        # check EVM events
        # we expect two events from #2 and #3
        filter = {
            "topics": [TEST_EVENT_TOPIC],
            "fromBlock": "earliest",
            "toBlock": "latest"
        }
        logs = self.nodes[0].eth_getLogs(filter)
        assert_equal(len(logs), 2)

        assert_equal(logs[0]["data"], number_to_topic(2))  # TestEvent(2)
        assert_equal(logs[0]["address"], evmContractAddr.lower())
        assert_equal(logs[0]["removed"], False)

        assert_equal(logs[1]["data"], number_to_topic(3))  # TestEvent(3)
        assert_equal(logs[1]["address"], evmContractAddr.lower())
        assert_equal(logs[1]["removed"], False)

        # TODO(thegaram): add more detailed tests once we have more control over block production
        # - events in pivot and non-pivot blocks
        # - log.blockHash and log.blockNumber should correspond to pivot block
        # - logIndex, transactionIndex, transactionLogIndex

        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 call_conflux_space(self, receiver, data_hex):
        tx = self.rpc.new_contract_tx(
            receiver=receiver,
            data_hex=data_hex,
            sender=self.cfxAccount,
            priv_key=self.cfxPrivkey,
        )

        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")
        return receipt

    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 call_evm_space(self, to, data):
        nonce = self.w3.eth.getTransactionCount(self.evmAccount.address)

        signed = self.evmAccount.signTransaction({
            "to": to,
            "value": 0,
            "gasPrice": 1,
            "gas": 150000,
            "nonce": nonce,
            "chainId": 10,
            "data": data,
        })

        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)
        return receipt