예제 #1
0
    def __init__(self, env, diff_calc=None):
        self.env = env
        self.root_config = env.quark_chain_config.ROOT
        if not diff_calc:
            cutoff = self.root_config.DIFFICULTY_ADJUSTMENT_CUTOFF_TIME
            diff_factor = self.root_config.DIFFICULTY_ADJUSTMENT_FACTOR
            min_diff = self.root_config.GENESIS.DIFFICULTY
            check(cutoff > 0 and diff_factor > 0 and min_diff > 0)
            diff_calc = EthDifficultyCalculator(
                cutoff=cutoff, diff_factor=diff_factor, minimum_diff=min_diff
            )
        self.diff_calc = diff_calc
        self.raw_db = env.db
        self.db = RootDb(
            self.raw_db,
            env.quark_chain_config,
            count_minor_blocks=env.cluster_config.ENABLE_TRANSACTION_HISTORY,
        )
        # header hash -> [coinbase address] during previous blocks (ascending)
        self.coinbase_addr_cache = LRUCache(maxsize=128)

        self.tip = self.db.get_tip_header()  # type: RootBlockHeader
        if self.tip:
            Logger.info(
                "Recovered root state with tip height {}".format(self.tip.height)
            )
        else:
            self.tip = self.__create_genesis_block()
            Logger.info("Created genesis root block")
예제 #2
0
    def __init__(self, env, diff_calc=None):
        self.env = env
        self.root_config = env.quark_chain_config.ROOT
        if not diff_calc:
            cutoff = self.root_config.DIFFICULTY_ADJUSTMENT_CUTOFF_TIME
            diff_factor = self.root_config.DIFFICULTY_ADJUSTMENT_FACTOR
            min_diff = self.root_config.GENESIS.DIFFICULTY
            check(cutoff > 0 and diff_factor > 0 and min_diff > 0)
            diff_calc = EthDifficultyCalculator(cutoff=cutoff,
                                                diff_factor=diff_factor,
                                                minimum_diff=min_diff)
        self.diff_calc = diff_calc
        self.raw_db = env.db
        self.db = RootDb(
            self.raw_db,
            env.quark_chain_config,
            count_minor_blocks=env.cluster_config.ENABLE_TRANSACTION_HISTORY,
        )

        persisted_tip = self.db.get_tip_header()
        if persisted_tip:
            self.tip = persisted_tip
            Logger.info("Recovered root state with tip height {}".format(
                self.tip.height))
        else:
            self.__create_genesis_block()
            Logger.info("Created genesis root block")
예제 #3
0
    def test_root_state_difficulty(self):
        env = get_test_env()
        env.quark_chain_config.SKIP_ROOT_DIFFICULTY_CHECK = False
        env.quark_chain_config.ROOT.GENESIS.DIFFICULTY = 1000
        diff_calc = EthDifficultyCalculator(cutoff=9,
                                            diff_factor=2048,
                                            minimum_diff=1)
        env.quark_chain_config.NETWORK_ID = (
            1)  # other network ids will skip difficulty check

        r_state, s_states = create_default_state(env, diff_calc=diff_calc)
        g0 = s_states[0].header_tip
        b0 = s_states[0].get_tip().create_block_to_append()
        add_minor_block_to_cluster(s_states, b0)
        g1 = s_states[1].header_tip
        b1 = s_states[1].get_tip().create_block_to_append()
        add_minor_block_to_cluster(s_states, b1)

        r_state.add_validated_minor_block_hash(b0.header.get_hash())
        r_state.add_validated_minor_block_hash(b1.header.get_hash())

        # Check new difficulty
        root_block0 = r_state.create_block_to_mine(
            m_header_list=[b0.header, b1.header],
            address=Address.create_empty_account(),
            create_time=r_state.tip.create_time + 9,
        )
        self.assertEqual(r_state.tip.difficulty, root_block0.header.difficulty)
        root_block0 = r_state.create_block_to_mine(
            m_header_list=[b0.header, b1.header],
            address=Address.create_empty_account(),
            create_time=r_state.tip.create_time + 3,
        )
        self.assertEqual(
            r_state.tip.difficulty + r_state.tip.difficulty // 2048,
            root_block0.header.difficulty,
        )

        root_block0 = r_state.create_block_to_mine(
            m_header_list=[g0, b0.header, g1, b1.header],
            address=Address.create_empty_account(),
            create_time=r_state.tip.create_time + 26,
        ).finalize()
        self.assertEqual(
            r_state.tip.difficulty - r_state.tip.difficulty // 2048,
            root_block0.header.difficulty,
        )

        for i in range(0, 2**32):
            root_block0.header.nonce = i
            if (int.from_bytes(root_block0.header.get_hash(), byteorder="big")
                    * env.quark_chain_config.ROOT.GENESIS.DIFFICULTY < 2**256):
                self.assertTrue(r_state.add_block(root_block0))
                break
            else:
                with self.assertRaises(ValueError):
                    r_state.add_block(root_block0)
예제 #4
0
    def __init__(self, env, diff_calc=None):
        self.env = env
        self.diff_calc = (diff_calc if diff_calc else EthDifficultyCalculator(
            cutoff=45, diff_factor=2048, minimum_diff=1000000))
        self.raw_db = env.db
        self.db = RootDb(self.raw_db,
                         env.quark_chain_config.ROOT.max_root_blocks_in_memory)

        persisted_tip = self.db.get_tip_header()
        if persisted_tip:
            self.tip = persisted_tip
            Logger.info("Recovered root state with tip height {}".format(
                self.tip.height))
        else:
            self.__create_genesis_block()
            Logger.info("Created genesis root block")
예제 #5
0
    def test_shard_state_difficulty(self):
        env = get_test_env()
        for shard in env.quark_chain_config.SHARD_LIST:
            shard.GENESIS.DIFFICULTY = 10000

        env.quark_chain_config.SKIP_MINOR_DIFFICULTY_CHECK = False
        diff_calc = EthDifficultyCalculator(cutoff=9,
                                            diff_factor=2048,
                                            minimum_diff=1)
        env.quark_chain_config.NETWORK_ID = (
            1)  # other network ids will skip difficulty check
        state = create_default_shard_state(env=env,
                                           shard_id=0,
                                           diff_calc=diff_calc)

        # Check new difficulty
        b0 = state.create_block_to_mine(state.header_tip.create_time + 8)
        self.assertEqual(
            b0.header.difficulty,
            state.header_tip.difficulty // 2048 + state.header_tip.difficulty,
        )
        b0 = state.create_block_to_mine(state.header_tip.create_time + 9)
        self.assertEqual(b0.header.difficulty, state.header_tip.difficulty)
        b0 = state.create_block_to_mine(state.header_tip.create_time + 17)
        self.assertEqual(b0.header.difficulty, state.header_tip.difficulty)
        b0 = state.create_block_to_mine(state.header_tip.create_time + 24)
        self.assertEqual(
            b0.header.difficulty,
            state.header_tip.difficulty - state.header_tip.difficulty // 2048,
        )
        b0 = state.create_block_to_mine(state.header_tip.create_time + 35)
        self.assertEqual(
            b0.header.difficulty,
            state.header_tip.difficulty -
            state.header_tip.difficulty // 2048 * 2,
        )

        for i in range(0, 2**32):
            b0.header.nonce = i
            if (int.from_bytes(b0.header.get_hash(), byteorder="big") *
                    env.quark_chain_config.SHARD_LIST[0].GENESIS.DIFFICULTY < 2
                    **256):
                self.assertEqual(state.add_block(b0), [])
                break
            else:
                with self.assertRaises(ValueError):
                    state.add_block(b0)
예제 #6
0
    def test_shard_state_difficulty(self):
        env = get_test_env()
        for shard in env.quark_chain_config.SHARD_LIST:
            shard.GENESIS.DIFFICULTY = 10000

        env.quark_chain_config.SKIP_MINOR_DIFFICULTY_CHECK = False
        diff_calc = EthDifficultyCalculator(cutoff=9,
                                            diff_factor=2048,
                                            minimum_diff=1)
        env.quark_chain_config.NETWORK_ID = (
            1)  # other network ids will skip difficulty check
        state = create_default_shard_state(env=env,
                                           shard_id=0,
                                           diff_calc=diff_calc)

        # Check new difficulty
        b0 = state.create_block_to_mine(state.header_tip.create_time + 8)
        self.assertEqual(
            b0.header.difficulty,
            state.header_tip.difficulty // 2048 + state.header_tip.difficulty,
        )
        b0 = state.create_block_to_mine(state.header_tip.create_time + 9)
        self.assertEqual(b0.header.difficulty, state.header_tip.difficulty)
        b0 = state.create_block_to_mine(state.header_tip.create_time + 17)
        self.assertEqual(b0.header.difficulty, state.header_tip.difficulty)
        b0 = state.create_block_to_mine(state.header_tip.create_time + 24)
        self.assertEqual(
            b0.header.difficulty,
            state.header_tip.difficulty - state.header_tip.difficulty // 2048,
        )
        b0 = state.create_block_to_mine(state.header_tip.create_time + 35)
        self.assertEqual(
            b0.header.difficulty,
            state.header_tip.difficulty -
            state.header_tip.difficulty // 2048 * 2,
        )
예제 #7
0
def create_test_clusters(
    num_cluster,
    genesis_account,
    chain_size,
    shard_size,
    num_slaves,
    genesis_root_heights,
    genesis_minor_quarkash,
    remote_mining=False,
    small_coinbase=False,
    loadtest_accounts=None,
    connect=True,  # connect the bootstrap node by default
    should_set_gas_price_limit=False,
    mblock_coinbase_amount=None,
):
    # so we can have lower minimum diff
    easy_diff_calc = EthDifficultyCalculator(
        cutoff=45, diff_factor=2048, minimum_diff=10
    )

    bootstrap_port = get_next_port()  # first cluster will listen on this port
    cluster_list = []
    loop = asyncio.get_event_loop()

    for i in range(num_cluster):
        env = get_test_env(
            genesis_account,
            genesis_minor_quarkash=genesis_minor_quarkash,
            chain_size=chain_size,
            shard_size=shard_size,
            genesis_root_heights=genesis_root_heights,
            remote_mining=remote_mining,
        )
        env.cluster_config.P2P_PORT = bootstrap_port if i == 0 else get_next_port()
        env.cluster_config.JSON_RPC_PORT = get_next_port()
        env.cluster_config.PRIVATE_JSON_RPC_PORT = get_next_port()
        env.cluster_config.SIMPLE_NETWORK = SimpleNetworkConfig()
        env.cluster_config.SIMPLE_NETWORK.BOOTSTRAP_PORT = bootstrap_port
        env.quark_chain_config.loadtest_accounts = loadtest_accounts or []
        if should_set_gas_price_limit:
            env.quark_chain_config.MIN_TX_POOL_GAS_PRICE = 10
            env.quark_chain_config.MIN_MINING_GAS_PRICE = 10

        if small_coinbase:
            # prevent breaking previous tests after tweaking default rewards
            env.quark_chain_config.ROOT.COINBASE_AMOUNT = 5
            for c in env.quark_chain_config.shards.values():
                c.COINBASE_AMOUNT = 5
        if mblock_coinbase_amount is not None:
            for c in env.quark_chain_config.shards.values():
                c.COINBASE_AMOUNT = mblock_coinbase_amount

        env.cluster_config.SLAVE_LIST = []
        check(is_p2(num_slaves))

        for j in range(num_slaves):
            slave_config = SlaveConfig()
            slave_config.ID = "S{}".format(j)
            slave_config.PORT = get_next_port()
            slave_config.FULL_SHARD_ID_LIST = []
            env.cluster_config.SLAVE_LIST.append(slave_config)

        full_shard_ids = [
            (i << 16) + shard_size + j
            for i in range(chain_size)
            for j in range(shard_size)
        ]
        for i, full_shard_id in enumerate(full_shard_ids):
            slave = env.cluster_config.SLAVE_LIST[i % num_slaves]
            slave.FULL_SHARD_ID_LIST.append(full_shard_id)

        slave_server_list = []
        for j in range(num_slaves):
            slave_env = env.copy()
            slave_env.db = InMemoryDb()
            slave_env.slave_config = env.cluster_config.get_slave_config(
                "S{}".format(j)
            )
            slave_server = SlaveServer(slave_env, name="cluster{}_slave{}".format(i, j))
            slave_server.start()
            slave_server_list.append(slave_server)

        root_state = RootState(env, diff_calc=easy_diff_calc)
        master_server = MasterServer(env, root_state, name="cluster{}_master".format(i))
        master_server.start()

        # Wait until the cluster is ready
        loop.run_until_complete(master_server.cluster_active_future)

        # Substitute diff calculate with an easier one
        for slave in slave_server_list:
            for shard in slave.shards.values():
                shard.state.diff_calc = easy_diff_calc

        # Start simple network and connect to seed host
        network = SimpleNetwork(env, master_server, loop)
        network.start_server()
        if connect and i != 0:
            peer = call_async(network.connect("127.0.0.1", bootstrap_port))
        else:
            peer = None

        cluster_list.append(Cluster(master_server, slave_server_list, network, peer))

    return cluster_list
예제 #8
0
    def test_root_state_difficulty_and_coinbase(self):
        env = get_test_env()
        env.quark_chain_config.SKIP_ROOT_DIFFICULTY_CHECK = False
        env.quark_chain_config.ROOT.GENESIS.DIFFICULTY = 1000
        diff_calc = EthDifficultyCalculator(cutoff=9,
                                            diff_factor=2048,
                                            minimum_diff=1)
        env.quark_chain_config.NETWORK_ID = (
            1)  # other network ids will skip difficulty check
        env.quark_chain_config.REWARD_TAX_RATE = 0.8
        env.quark_chain_config.ROOT.COINBASE_AMOUNT = 5
        for c in env.quark_chain_config.shards.values():
            c.COINBASE_AMOUNT = 5

        r_state, s_states = create_default_state(env, diff_calc=diff_calc)
        s_state0 = s_states[2 | 0]
        s_state1 = s_states[2 | 1]
        g0 = s_state0.header_tip
        b0 = s_state0.get_tip().create_block_to_append()
        add_minor_block_to_cluster(s_states, b0)
        g1 = s_state1.header_tip
        b1 = s_state1.get_tip().create_block_to_append()
        add_minor_block_to_cluster(s_states, b1)
        self.assertEqual(
            b0.header.coinbase_amount_map.balance_map,
            {env.quark_chain_config.genesis_token: 1},
        )
        self.assertEqual(
            b1.header.coinbase_amount_map.balance_map,
            {env.quark_chain_config.genesis_token: 1},
        )

        r_state.add_validated_minor_block_hash(
            b0.header.get_hash(), b0.header.coinbase_amount_map.balance_map)
        r_state.add_validated_minor_block_hash(
            b1.header.get_hash(), b1.header.coinbase_amount_map.balance_map)

        # Test coinbase
        original_reward_tax_rate = env.quark_chain_config.REWARD_TAX_RATE
        for tax_rate in [0.8, 0.6, 0.9]:
            env.quark_chain_config.REWARD_TAX_RATE = tax_rate
            root_block_tmp = r_state.create_block_to_mine(
                m_header_list=[b0.header, b1.header],
                address=Address.create_empty_account(),
                create_time=r_state.tip.create_time + 9,
            )
            self.assertEqual(root_block_tmp.header.signature,
                             bytes(65))  # empty sig
            # still use minor block's coinbase amount, 1
            self.assertEqual(
                root_block_tmp.header.coinbase_amount_map.balance_map[
                    env.quark_chain_config.genesis_token],
                round((1 + 1) / (1 - tax_rate) * tax_rate + 5),
            )
        env.quark_chain_config.REWARD_TAX_RATE = original_reward_tax_rate

        # Check new difficulty
        root_block0 = r_state.create_block_to_mine(
            m_header_list=[b0.header, b1.header],
            address=Address.create_empty_account(),
            create_time=r_state.tip.create_time + 9,
        )
        self.assertEqual(r_state.tip.difficulty, root_block0.header.difficulty)
        root_block0 = r_state.create_block_to_mine(
            m_header_list=[b0.header, b1.header],
            address=Address.create_empty_account(),
            create_time=r_state.tip.create_time + 3,
        )
        self.assertEqual(
            r_state.tip.difficulty + r_state.tip.difficulty // 2048,
            root_block0.header.difficulty,
        )

        root_block0 = r_state.create_block_to_mine(
            m_header_list=[g0, b0.header, g1, b1.header],
            address=Address.create_empty_account(),
            create_time=r_state.tip.create_time + 26,
        )
        self.assertEqual(
            r_state.tip.difficulty - r_state.tip.difficulty // 2048,
            root_block0.header.difficulty,
        )
예제 #9
0
def create_test_clusters(
    num_cluster,
    genesis_account,
    shard_size,
    num_slaves,
    genesis_root_heights,
    remote_mining=False,
):
    # so we can have lower minimum diff
    easy_diff_calc = EthDifficultyCalculator(cutoff=45,
                                             diff_factor=2048,
                                             minimum_diff=10)

    bootstrap_port = get_next_port()  # first cluster will listen on this port
    cluster_list = []
    loop = asyncio.get_event_loop()

    for i in range(num_cluster):
        env = get_test_env(
            genesis_account,
            genesis_minor_quarkash=1000000,
            shard_size=shard_size,
            genesis_root_heights=genesis_root_heights,
            remote_mining=remote_mining,
        )
        env.cluster_config.P2P_PORT = bootstrap_port if i == 0 else get_next_port(
        )
        env.cluster_config.JSON_RPC_PORT = get_next_port()
        env.cluster_config.PRIVATE_JSON_RPC_PORT = get_next_port()
        env.cluster_config.SIMPLE_NETWORK = SimpleNetworkConfig()
        env.cluster_config.SIMPLE_NETWORK.BOOTSTRAP_PORT = bootstrap_port

        env.cluster_config.SLAVE_LIST = []
        for j in range(num_slaves):
            slave_config = SlaveConfig()
            slave_config.ID = "S{}".format(j)
            slave_config.PORT = get_next_port()
            slave_config.SHARD_MASK_LIST = [ShardMask(num_slaves | j)]
            slave_config.DB_PATH_ROOT = None  # TODO: fix the db in config
            env.cluster_config.SLAVE_LIST.append(slave_config)

        slave_server_list = []
        for j in range(num_slaves):
            slave_env = env.copy()
            slave_env.db = InMemoryDb()
            slave_env.slave_config = env.cluster_config.get_slave_config(
                "S{}".format(j))
            slave_server = SlaveServer(slave_env,
                                       name="cluster{}_slave{}".format(i, j))
            slave_server.start()
            slave_server_list.append(slave_server)

        root_state = RootState(env, diff_calc=easy_diff_calc)
        master_server = MasterServer(env,
                                     root_state,
                                     name="cluster{}_master".format(i))
        master_server.start()

        # Wait until the cluster is ready
        loop.run_until_complete(master_server.cluster_active_future)

        # Substitute diff calculate with an easier one
        for slave in slave_server_list:
            for shard in slave.shards.values():
                shard.state.diff_calc = easy_diff_calc

        # Start simple network and connect to seed host
        network = SimpleNetwork(env, master_server)
        network.start_server()
        if i != 0:
            peer = call_async(network.connect("127.0.0.1", bootstrap_port))
        else:
            peer = None

        cluster_list.append(
            Cluster(master_server, slave_server_list, network, peer))

    return cluster_list