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")
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")
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)
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")
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)
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, )
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
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, )
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