def test_xshard_tx_sent(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_id=0) acc2 = Address.create_from_identity(id1, full_shard_id=1) acc3 = Address.create_random_account(full_shard_id=0) env = get_test_env(genesis_account=acc1, genesis_minor_quarkash=10000000) state = create_default_shard_state(env=env, shard_id=0) env1 = get_test_env(genesis_account=acc1, genesis_minor_quarkash=10000000) state1 = create_default_shard_state(env=env1, shard_id=1) # Add a root block to update block gas limit so that xshard tx can be included root_block = ( state.root_tip.create_block_to_append().add_minor_block_header( state.header_tip).add_minor_block_header( state1.header_tip).finalize()) state.add_root_block(root_block) tx = create_transfer_transaction( shard_state=state, key=id1.get_key(), from_address=acc1, to_address=acc2, value=888888, gas=opcodes.GTXXSHARDCOST + opcodes.GTXCOST, ) state.add_tx(tx) b1 = state.create_block_to_mine(address=acc3) self.assertEqual(len(b1.tx_list), 1) self.assertEqual(state.evm_state.gas_used, 0) # Should succeed state.finalize_and_add_block(b1) self.assertEqual(len(state.evm_state.xshard_list), 1) self.assertEqual( state.evm_state.xshard_list[0], CrossShardTransactionDeposit( tx_hash=tx.get_hash(), from_address=acc1, to_address=acc2, value=888888, gas_price=1, ), ) self.assertEqual( state.get_balance(id1.recipient), 10000000 - 888888 - opcodes.GTXCOST - opcodes.GTXXSHARDCOST, ) # Make sure the xshard gas is not used by local block self.assertEqual(state.evm_state.gas_used, opcodes.GTXCOST + opcodes.GTXXSHARDCOST) # GTXXSHARDCOST is consumed by remote shard self.assertEqual(state.get_balance(acc3.recipient), opcodes.GTXCOST // 2)
def test_blocks_with_incorrect_height(self): env = get_test_env() r_state, s_states = create_default_state(env) root_block = r_state.create_block_to_mine([]) root_block.header.height += 1 with self.assertRaisesRegexp(ValueError, "incorrect block height"): r_state.add_block(root_block)
def test_shard_state_add_root_block_too_many_minor_blocks(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_id=0) env = get_test_env(genesis_account=acc1, genesis_minor_quarkash=10000000, shard_size=1) state = create_default_shard_state(env=env, shard_id=0) headers = [state.header_tip] for i in range(13): b = state.get_tip().create_block_to_append(address=acc1) state.finalize_and_add_block(b) headers.append(b.header) root_block = (state.root_tip.create_block_to_append(). extend_minor_block_header_list(headers).finalize()) # Too many blocks self.assertRaises(ValueError, state.add_root_block, root_block) self.assertEqual(state.get_unconfirmed_header_list(), headers[:13]) # 10 blocks is okay root_block.minor_block_header_list = headers[:13] root_block.finalize() state.add_root_block(root_block)
def test_shard_state_fork_resolve_with_higher_root_chain(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_id=0) env = get_test_env(genesis_account=acc1, genesis_minor_quarkash=10000000) state = create_default_shard_state(env=env, shard_id=0) b0 = state.get_tip().create_block_to_append() state.finalize_and_add_block(b0) root_block = ( state.root_tip.create_block_to_append().add_minor_block_header( b0.header).finalize()) self.assertEqual(state.header_tip, b0.header) self.assertTrue(state.add_root_block(root_block)) b1 = state.get_tip().create_block_to_append() b2 = state.get_tip().create_block_to_append(nonce=1) b2.header.hash_prev_root_block = root_block.header.get_hash() b3 = state.get_tip().create_block_to_append(nonce=2) b3.header.hash_prev_root_block = root_block.header.get_hash() state.finalize_and_add_block(b1) self.assertEqual(state.header_tip, b1.header) # Fork happens, although they have the same height, b2 survives since it confirms root block state.finalize_and_add_block(b2) self.assertEqual(state.header_tip, b2.header) # b3 confirms the same root block as b2, so it will not override b2 state.finalize_and_add_block(b3) self.assertEqual(state.header_tip, b2.header)
def test_native_token_transfer_0_value_success(self): """to prevent storage spamming, do not delta_token_balance does not take action if value is 0 """ MALICIOUS0 = token_id_encode("MALICIOUS0") id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_key=0) env = get_test_env( genesis_account=acc1, genesis_minor_token_balances={ self.GENESIS_TOKEN: 10000000, "MALICIOUS0": 0, }, ) state = create_default_shard_state(env=env) tx = create_transfer_transaction( shard_state=state, key=id1.get_key(), from_address=acc1, to_address=acc1, value=0, gas=opcodes.GTXCOST, gas_token_id=self.genesis_token, transfer_token_id=MALICIOUS0, ) self.assertFalse(state.add_tx(tx))
def test_root_state_add_block_missing_minor_block_header(self): env = get_test_env() r_state, s_states = create_default_state(env) s_state0 = s_states[2 | 0] s_state1 = s_states[2 | 1] b0 = s_state0.get_tip().create_block_to_append() s_state0.finalize_and_add_block(b0) b1 = s_state1.get_tip().create_block_to_append() s_state1.finalize_and_add_block(b1) r_state.add_validated_minor_block_hash(b0.header.get_hash()) r_state.add_validated_minor_block_hash(b1.header.get_hash()) root_block = ( r_state.tip.create_block_to_append().add_minor_block_header( b1.header).finalize()) with self.assertRaises(ValueError): r_state.add_block(root_block) root_block = ( r_state.tip.create_block_to_append().add_minor_block_header( b0.header).finalize()) with self.assertRaises(ValueError): r_state.add_block(root_block)
def test_root_state_and_shard_state_add_two_blocks(self): env = get_test_env() r_state, s_states = create_default_state(env) s_state0 = s_states[2 | 0] s_state1 = s_states[2 | 1] b0 = s_state0.get_tip().create_block_to_append() add_minor_block_to_cluster(s_states, b0) b1 = s_state1.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()) root_block0 = ( r_state.tip.create_block_to_append().add_minor_block_header( s_state0.db.get_minor_block_by_height(0).header ).add_minor_block_header(b0.header).add_minor_block_header( s_state1.db.get_minor_block_by_height( 0).header).add_minor_block_header(b1.header).finalize()) self.assertTrue(r_state.add_block(root_block0)) b2 = s_state0.get_tip().create_block_to_append() add_minor_block_to_cluster(s_states, b2) b3 = s_state1.get_tip().create_block_to_append() add_minor_block_to_cluster(s_states, b3) r_state.add_validated_minor_block_hash(b2.header.get_hash()) r_state.add_validated_minor_block_hash(b3.header.get_hash()) root_block1 = ( r_state.tip.create_block_to_append().add_minor_block_header( b2.header).add_minor_block_header(b3.header).finalize()) self.assertTrue(r_state.add_block(root_block1))
def test_blocks_with_incorrect_total_difficulty(self): env = get_test_env() r_state, s_states = create_default_state(env) root_block = r_state.create_block_to_mine([]) root_block.header.total_difficulty += 1 with self.assertRaisesRegexp(ValueError, "incorrect total difficulty"): r_state.add_block(root_block)
def test_root_state_add_two_blocks(self): env = get_test_env() r_state, s_states = create_default_state(env) s_state0 = s_states[2 | 0] s_state1 = s_states[2 | 1] b0 = s_state0.create_block_to_mine() add_minor_block_to_cluster(s_states, b0) b1 = s_state1.create_block_to_mine() add_minor_block_to_cluster(s_states, b1) 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) root_block0 = r_state.create_block_to_mine([b0.header, b1.header]) self.assertTrue(r_state.add_block(root_block0)) b2 = s_state0.create_block_to_mine() add_minor_block_to_cluster(s_states, b2) b3 = s_state1.create_block_to_mine() add_minor_block_to_cluster(s_states, b3) r_state.add_validated_minor_block_hash( b2.header.get_hash(), b2.header.coinbase_amount_map.balance_map) r_state.add_validated_minor_block_hash( b3.header.get_hash(), b3.header.coinbase_amount_map.balance_map) root_block1 = r_state.create_block_to_mine([b2.header, b3.header]) self.assertTrue(r_state.add_block(root_block1))
def test_root_coinbase_decay(self): env = get_test_env() r_state, s_states = create_default_state(env) coinbase = r_state._calculate_root_block_coinbase( [], env.quark_chain_config.ROOT.EPOCH_INTERVAL) self.assertEqual( coinbase, { env.quark_chain_config.genesis_token: env.quark_chain_config.ROOT.COINBASE_AMOUNT * env.quark_chain_config.BLOCK_REWARD_DECAY_FACTOR }, ) coinbase = r_state._calculate_root_block_coinbase( [], env.quark_chain_config.ROOT.EPOCH_INTERVAL + 1) self.assertEqual( coinbase, { env.quark_chain_config.genesis_token: env.quark_chain_config.ROOT.COINBASE_AMOUNT * env.quark_chain_config.BLOCK_REWARD_DECAY_FACTOR }, ) coinbase = r_state._calculate_root_block_coinbase( [], env.quark_chain_config.ROOT.EPOCH_INTERVAL * 2) self.assertEqual( coinbase, { env.quark_chain_config.genesis_token: env.quark_chain_config.ROOT.COINBASE_AMOUNT * env.quark_chain_config.BLOCK_REWARD_DECAY_FACTOR**2 }, )
def test_root_state_add_root_block_too_many_minor_blocks(self): env = get_test_env() r_state, s_states = create_default_state(env) s_state0 = s_states[2 | 0] headers = [] max_mblock_in_rblock = ( s_state0.shard_config.max_blocks_per_shard_in_one_root_block) for i in range(max_mblock_in_rblock + 1): b = s_state0.create_block_to_mine() add_minor_block_to_cluster(s_states, b) headers.append(b.header) r_state.add_validated_minor_block_hash( b.header.get_hash(), b.header.coinbase_amount_map.balance_map) root_block = r_state.create_block_to_mine( m_header_list=headers, create_time=headers[-1].create_time + 1) with self.assertRaisesRegexp( ValueError, "too many minor blocks in the root block for shard"): r_state.add_block(root_block) headers = headers[:max_mblock_in_rblock] root_block = r_state.create_block_to_mine( m_header_list=headers, create_time=headers[-1].create_time + 1) r_state.add_block(root_block)
def test_root_state_and_shard_state_add_block(self): env = get_test_env() r_state, s_states = create_default_state(env) s_state0 = s_states[2 | 0] s_state1 = s_states[2 | 1] b0 = s_state0.create_block_to_mine() add_minor_block_to_cluster(s_states, b0) b1 = s_state1.create_block_to_mine() 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()) root_block = r_state.create_block_to_mine([b0.header, b1.header]) self.assertTrue(r_state.add_block(root_block)) self.assertIsNone(r_state.get_root_block_by_height(3)) self.assertEqual(r_state.get_root_block_by_height(2), root_block) self.assertEqual(r_state.get_root_block_by_height(None), root_block) self.assertEqual( r_state.get_root_block_by_height(1), r_state.get_root_block_by_hash(root_block.header.hash_prev_block), ) self.assertTrue(s_state0.add_root_block(root_block)) self.assertEqual(s_state0.root_tip, root_block.header) self.assertTrue(s_state1.add_root_block(root_block)) self.assertEqual(s_state1.root_tip, root_block.header)
def test_root_state_add_block(self): env = get_test_env() r_state, s_states = create_default_state(env) # chain_id is 0 s_state0 = s_states[2 | 0] s_state1 = s_states[2 | 1] b0 = s_state0.get_tip().create_block_to_append() s_state0.finalize_and_add_block(b0) b1 = s_state1.get_tip().create_block_to_append() s_state1.finalize_and_add_block(b1) r_state.add_validated_minor_block_hash(b0.header.get_hash()) r_state.add_validated_minor_block_hash(b1.header.get_hash()) root_block = ( r_state.tip.create_block_to_append().add_minor_block_header( s_state0.db.get_minor_block_by_height(0).header ).add_minor_block_header(b0.header).add_minor_block_header( s_state1.db.get_minor_block_by_height( 0).header).add_minor_block_header(b1.header).finalize()) self.assertTrue(r_state.add_block(root_block)) self.assertIsNone(r_state.get_root_block_by_height(2)) self.assertEqual(r_state.get_root_block_by_height(1), root_block) self.assertEqual(r_state.get_root_block_by_height(None), root_block) self.assertEqual( r_state.get_root_block_by_height(0), r_state.get_root_block_by_hash(root_block.header.hash_prev_block), )
def test_gas_price(self): id_list = [Identity.create_random_identity() for _ in range(5)] acc_list = [ Address.create_from_identity(i, full_shard_id=0) for i in id_list ] env = get_test_env(genesis_account=acc_list[0], genesis_minor_quarkash=10000000) state = create_default_shard_state(env=env) # 5 tx per block, make 3 blocks for _ in range(3): for j in range(5): state.add_tx( create_transfer_transaction( shard_state=state, key=id_list[j].get_key(), from_address=acc_list[j], to_address=random.choice(acc_list), value=0, gas_price=42 if j == 0 else 0, )) b = state.create_block_to_mine(address=acc_list[1]) state.finalize_and_add_block(b) # for testing purposes, update percentile to take max gas price state.gas_price_suggestion_oracle.percentile = 100 gas_price = state.gas_price() self.assertEqual(gas_price, 42) # results should be cached (same header). updating oracle shouldn't take effect state.gas_price_suggestion_oracle.percentile = 50 gas_price = state.gas_price() self.assertEqual(gas_price, 42)
def test_add_non_neighbor_tx_fail(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_id=0) acc2 = Address.create_random_account( full_shard_id=3) # not acc1's neighbor acc3 = Address.create_random_account( full_shard_id=8) # acc1's neighbor env = get_test_env(genesis_account=acc1, genesis_minor_quarkash=10000000, shard_size=64) state = create_default_shard_state(env=env) tx = create_transfer_transaction( shard_state=state, key=id1.get_key(), from_address=acc1, to_address=acc2, value=0, gas=1000000, ) self.assertFalse(state.add_tx(tx)) self.assertEqual(len(state.tx_queue), 0) tx = create_transfer_transaction( shard_state=state, key=id1.get_key(), from_address=acc1, to_address=acc3, value=0, gas=1000000, ) self.assertTrue(state.add_tx(tx)) self.assertEqual(len(state.tx_queue), 1)
def test_root_state_and_shard_state_add_block(self): env = get_test_env() r_state, s_states = create_default_state(env) self.assertEqual(r_state.tip.total_difficulty, 2000000) s_state0 = s_states[2 | 0] s_state1 = s_states[2 | 1] b0 = s_state0.create_block_to_mine() add_minor_block_to_cluster(s_states, b0) b1 = s_state1.create_block_to_mine() add_minor_block_to_cluster(s_states, b1) 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) root_block = r_state.create_block_to_mine([b0.header, b1.header]) self.assertEqual(root_block.header.total_difficulty, 3000976) self.assertTrue(r_state.add_block(root_block)) self.assertIsNone(r_state.db.get_root_block_by_height(3)) self.assertEqual(r_state.db.get_root_block_by_height(2), root_block) tip_height = r_state.tip.height self.assertEqual(r_state.db.get_root_block_by_height(tip_height), root_block) self.assertEqual( r_state.db.get_root_block_by_height(1), r_state.db.get_root_block_by_hash( root_block.header.hash_prev_block), ) self.assertTrue(s_state0.add_root_block(root_block)) self.assertEqual(s_state0.root_tip, root_block.header) self.assertTrue(s_state1.add_root_block(root_block)) self.assertEqual(s_state1.root_tip, root_block.header)
def test_native_token_gas(self): """in-shard transfer QETH using native token as gas TODODLL: support native token as gas """ QETH = token_id_encode("QETH") id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_key=0) acc2 = Address.create_random_account(full_shard_key=0) acc3 = Address.create_random_account(full_shard_key=0) env = get_test_env(genesis_account=acc1, genesis_minor_token_balances={"QETH": 10000000}) state = create_default_shard_state(env=env) tx = create_transfer_transaction( shard_state=state, key=id1.get_key(), from_address=acc1, to_address=acc2, value=12345, gas=21000, gas_token_id=QETH, transfer_token_id=QETH, ) self.assertFalse( state.add_tx(tx) ) # warning log: Failed to add transaction: Gas token must be QKC return self.assertTrue(state.add_tx(tx)) b1 = state.create_block_to_mine(address=acc3) self.assertEqual(len(b1.tx_list), 1) state.finalize_and_add_block(b1) self.assertEqual(state.header_tip, b1.header) self.assertEqual(state.get_token_balance(id1.recipient, QETH), 10000000 - opcodes.GTXCOST) self.assertEqual( state.get_token_balance(acc1.recipient, QETH), 10000000 - opcodes.GTXCOST - 12345, ) self.assertEqual(state.get_token_balance(acc2.recipient, QETH), 12345) # tx fee self.assertEqual( state.get_token_balance(acc3.recipient, QETH), self.getAfterTaxReward(opcodes.GTXCOST), ) # miner coinbase self.assertEqual( state.get_token_balance(acc3.recipient, self.genesis_token), self.getAfterTaxReward(self.shard_coinbase), ) tx_list, _ = state.db.get_transactions_by_address(acc1) self.assertEqual(tx_list[0].value, 12345) self.assertEqual(tx_list[0].gas_token_id, QETH) self.assertEqual(tx_list[0].transfer_token_id, QETH) tx_list, _ = state.db.get_transactions_by_address(acc2) self.assertEqual(tx_list[0].value, 12345) self.assertEqual(tx_list[0].gas_token_id, QETH) self.assertEqual(tx_list[0].transfer_token_id, QETH)
def test_root_state_and_shard_state_fork(self): env = get_test_env() r_state, s_states = create_default_state(env) s_state0 = s_states[2 | 0] s_state1 = s_states[2 | 1] b0 = s_state0.get_tip().create_block_to_append() b2 = s_state0.get_tip().create_block_to_append() add_minor_block_to_cluster(s_states, b0) b1 = s_state1.get_tip().create_block_to_append(nonce=1) b3 = s_state1.get_tip().create_block_to_append(nonce=1) 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()) root_block0 = ( r_state.tip.create_block_to_append().add_minor_block_header( s_state0.db.get_minor_block_by_height(0).header ).add_minor_block_header(b0.header).add_minor_block_header( s_state1.db.get_minor_block_by_height( 0).header).add_minor_block_header(b1.header).finalize()) root_block1 = r_state.tip.create_block_to_append() self.assertTrue(r_state.add_block(root_block0)) self.assertTrue(s_state0.add_root_block(root_block0)) self.assertTrue(s_state1.add_root_block(root_block0)) add_minor_block_to_cluster(s_states, b2) add_minor_block_to_cluster(s_states, b3) r_state.add_validated_minor_block_hash(b2.header.get_hash()) r_state.add_validated_minor_block_hash(b3.header.get_hash()) root_block1 = (root_block1.add_minor_block_header( s_state0.db.get_minor_block_by_height(0).header ).add_minor_block_header(b2.header).add_minor_block_header( s_state1.db.get_minor_block_by_height( 0).header).add_minor_block_header(b3.header).finalize()) self.assertFalse(r_state.add_block(root_block1)) self.assertFalse(s_state0.add_root_block(root_block1)) self.assertFalse(s_state1.add_root_block(root_block1)) b4 = b2.create_block_to_append() b5 = b3.create_block_to_append() add_minor_block_to_cluster(s_states, b4) add_minor_block_to_cluster(s_states, b5) r_state.add_validated_minor_block_hash(b4.header.get_hash()) r_state.add_validated_minor_block_hash(b5.header.get_hash()) root_block2 = ( root_block1.create_block_to_append().add_minor_block_header( b4.header).add_minor_block_header(b5.header).finalize()) self.assertTrue(r_state.add_block(root_block2)) self.assertTrue(s_state0.add_root_block(root_block2)) self.assertTrue(s_state1.add_root_block(root_block2)) self.assertEqual(r_state.tip, root_block2.header) self.assertEqual(s_state0.root_tip, root_block2.header) self.assertEqual(s_state1.root_tip, root_block2.header)
def test_add_minor_block_with_wrong_root_block_hash(self): """ Test for the following case +--+ |r1| /+--+ / | +--+ / +--+ +--+ |r0|<----|m1|<---|m3| +--+ \ +--+ +--+ ^ \ | | \+--+ | | |r2|<----+ | +--+ | | | +--+ +------|m2| +--+ where m3 is invalid because m3 depends on r2, whose minor chain is not the same chain as m3 """ env = get_test_env(shard_size=1) r_state, s_states = create_default_state(env) s_state0 = s_states[1 | 0] root_block0 = r_state.get_tip_block() m1 = s_state0.get_tip().create_block_to_append(nonce=0) m2 = s_state0.get_tip().create_block_to_append(nonce=1) add_minor_block_to_cluster(s_states, m1) add_minor_block_to_cluster(s_states, m2) r_state.add_validated_minor_block_hash(m1.header.get_hash()) r_state.add_validated_minor_block_hash(m2.header.get_hash()) root_block1 = (root_block0.create_block_to_append( nonce=0).add_minor_block_header(m1.header).finalize()) root_block2 = (root_block0.create_block_to_append( nonce=1).add_minor_block_header(m2.header).finalize()) self.assertTrue(r_state.add_block(root_block1)) self.assertFalse(r_state.add_block(root_block2)) self.assertTrue(s_state0.add_root_block(root_block1)) self.assertFalse(s_state0.add_root_block(root_block2)) m3 = m1.create_block_to_append() m3.header.hash_prev_root_block = root_block2.header.get_hash() with self.assertRaises(ValueError): add_minor_block_to_cluster(s_states, m3) m4 = m1.create_block_to_append() m4.header.hash_prev_root_block = root_block1.header.get_hash() add_minor_block_to_cluster(s_states, m4) # Test recovery s_state0_recovered = ShardState(env, full_shard_id=1 | 0, db=s_state0.raw_db) s_state0_recovered.init_from_root_block(root_block1) with self.assertRaises(ValueError): add_minor_block_to_cluster(s_states, m3)
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 test_add_root_block_revert_header_tip(self): """ block's hash_prev_root_block must be on the same chain with root_tip to update tip. +--+ |r1|<-------------+ /+--+ | / | | +--+ / +--+ +--+ +--+ |r0|<----|m1|<---|m2| <---|m3| +--+ \ +--+ +--+ +--+ \ | \ \+--+. +--+ |r2|<-----|r3| (r3 includes m2) +--+ +--+ Initial state: r0 <- m1 <- m2 Adding r1, r2, m3 makes r1 the root_tip, m3 the header_tip Adding r3 should change the root_tip to r3, header_tip to m2 """ id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_id=0) env = get_test_env(genesis_account=acc1, genesis_minor_quarkash=10000000) state = create_default_shard_state(env=env, shard_id=0) m1 = state.get_tip().create_block_to_append(address=acc1) state.finalize_and_add_block(m1) m2 = state.get_tip().create_block_to_append(address=acc1) state.finalize_and_add_block(m2) r1 = state.root_tip.create_block_to_append() r2 = state.root_tip.create_block_to_append() r1.minor_block_header_list.append(m1.header) r1.finalize() state.add_root_block(r1) r2.minor_block_header_list.append(m1.header) r2.header.create_time = r1.header.create_time + 1 # make r2, r1 different r2.finalize() self.assertNotEqual(r1.header.get_hash(), r2.header.get_hash()) state.add_root_block(r2) self.assertEqual(state.root_tip, r1.header) m3 = state.create_block_to_mine(address=acc1) self.assertEqual(m3.header.hash_prev_root_block, r1.header.get_hash()) state.finalize_and_add_block(m3) r3 = r2.create_block_to_append(address=acc1) r3.add_minor_block_header(m2.header) r3.finalize() state.add_root_block(r3) self.assertEqual(state.root_tip, r3.header) self.assertEqual(state.header_tip, m2.header)
def setUp(self): super().setUp() config = get_test_env().quark_chain_config self.root_coinbase = config.ROOT.COINBASE_AMOUNT self.shard_coinbase = next(iter(config.shards.values())).COINBASE_AMOUNT # to make test verification easier, assume following tax rate assert config.REWARD_TAX_RATE == 0.5 self.tax_rate = config.reward_tax_rate # type: Fraction self.GENESIS_TOKEN = config.GENESIS_TOKEN # type: str self.genesis_token = config.genesis_token # type: int
def test_blocks_with_incorrect_version(self): env = get_test_env() r_state, s_states = create_default_state(env) root_block = r_state.create_block_to_mine([]) root_block.header.version = 1 with self.assertRaisesRegexp(ValueError, "incorrect root block version"): r_state.add_block(root_block) root_block.header.version = 0 r_state.add_block(root_block)
def test_add_root_block_with_minor_block_with_wrong_root_block_hash(self): """ Test for the following case +--+ +--+ |r1|<---|r3| /+--+ +--+ / | | +--+ / +--+ +--+ |r0|<----|m1|<---|m2| +--+ \ +--+ +--+ \ | | \+--+ | |r2|<----+ +--+ where r3 is invalid because m2 depends on r2, which is not in the r3 chain. """ env = get_test_env(shard_size=1) r_state, s_states = create_default_state(env) s_state0 = s_states[1 | 0] genesis_header = s_state0.header_tip root_block0 = r_state.get_tip_block() m1 = s_state0.get_tip().create_block_to_append() add_minor_block_to_cluster(s_states, m1) r_state.add_validated_minor_block_hash(m1.header.get_hash()) root_block1 = (root_block0.create_block_to_append( nonce=0).add_minor_block_header( genesis_header).add_minor_block_header(m1.header).finalize()) root_block2 = (root_block0.create_block_to_append( nonce=1).add_minor_block_header( genesis_header).add_minor_block_header(m1.header).finalize()) self.assertTrue(r_state.add_block(root_block1)) self.assertFalse(r_state.add_block(root_block2)) self.assertTrue(s_state0.add_root_block(root_block1)) self.assertFalse(s_state0.add_root_block(root_block2)) m2 = m1.create_block_to_append() m2.header.hash_prev_root_block = root_block2.header.get_hash() add_minor_block_to_cluster(s_states, m2) r_state.add_validated_minor_block_hash(m2.header.get_hash()) root_block3 = ( root_block1.create_block_to_append().add_minor_block_header( m2.header).finalize()) with self.assertRaises(ValueError): r_state.add_block(root_block3) root_block4 = ( root_block2.create_block_to_append().add_minor_block_header( m2.header).finalize()) self.assertTrue(r_state.add_block(root_block4))
def test_root_chain_first_consensus(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_id=0) env0 = get_test_env(genesis_account=acc1, genesis_minor_quarkash=10000000) env1 = get_test_env(genesis_account=acc1, genesis_minor_quarkash=10000000) state0 = create_default_shard_state(env=env0, shard_id=0) state1 = create_default_shard_state(env=env1, shard_id=1) # Add one block and prepare a fork b0 = state0.get_tip().create_block_to_append(address=acc1) b2 = state0.get_tip().create_block_to_append( address=Address.create_empty_account()) state0.finalize_and_add_block(b0) state0.finalize_and_add_block(b2) b1 = state1.get_tip().create_block_to_append() b1.finalize(evm_state=state1.run_block(b1)) # Create a root block containing the block with the x-shard tx state0.add_cross_shard_tx_list_by_minor_block_hash( h=b1.header.get_hash(), tx_list=CrossShardTransactionList(tx_list=[])) root_block = ( state0.root_tip.create_block_to_append().add_minor_block_header( b0.header).add_minor_block_header(b1.header).finalize()) state0.add_root_block(root_block) b00 = b0.create_block_to_append() state0.finalize_and_add_block(b00) self.assertEqual(state0.header_tip, b00.header) # Create another fork that is much longer (however not confirmed by root_block) b3 = b2.create_block_to_append() state0.finalize_and_add_block(b3) b4 = b3.create_block_to_append() state0.finalize_and_add_block(b4) self.assertGreater(b4.header.height, b00.header.height) self.assertEqual(state0.header_tip, b00.header)
def test_not_update_tip_on_root_fork(self): """ block's hash_prev_root_block must be on the same chain with root_tip to update tip. +--+ a. |r1| /+--+ / | +--+ / +--+ +--+ |r0|<----|m1|<---|m2| c. +--+ \ +--+ +--+ \ | | \+--+ | b. |r2|<----+ +--+ Initial state: r0 <- m1 Then adding r1, r2, m2 should not make m2 the tip because r1 is the root tip and r2 and r1 are not on the same root chain. """ id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_id=0) env = get_test_env(genesis_account=acc1, genesis_minor_quarkash=10000000) state = create_default_shard_state(env=env, shard_id=0) m1 = state.get_tip().create_block_to_append(address=acc1) state.finalize_and_add_block(m1) r1 = state.root_tip.create_block_to_append() r2 = state.root_tip.create_block_to_append() r1.minor_block_header_list.append(m1.header) r1.finalize() state.add_root_block(r1) r2.minor_block_header_list.append(m1.header) r2.header.create_time = r1.header.create_time + 1 # make r2, r1 different r2.finalize() self.assertNotEqual(r1.header.get_hash(), r2.header.get_hash()) state.add_root_block(r2) self.assertEqual(state.root_tip, r1.header) m2 = m1.create_block_to_append(address=acc1) m2.header.hash_prev_root_block = r2.header.get_hash() state.finalize_and_add_block(m2) # m2 is added self.assertEqual( state.db.get_minor_block_by_hash(m2.header.get_hash()), m2) # but m1 should still be the tip self.assertEqual(state.header_tip, m1.header)
def test_duplicated_tx(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_id=0) acc2 = Address.create_random_account(full_shard_id=0) acc3 = Address.create_random_account(full_shard_id=0) env = get_test_env(genesis_account=acc1, genesis_minor_quarkash=10000000) state = create_default_shard_state(env=env) tx = create_transfer_transaction( shard_state=state, key=id1.get_key(), from_address=acc1, to_address=acc2, value=12345, ) self.assertTrue(state.add_tx(tx)) self.assertFalse(state.add_tx(tx)) # already in tx_queue self.assertEqual(len(state.tx_queue), 1) self.assertEqual(len(state.tx_dict), 1) block, i = state.get_transaction_by_hash(tx.get_hash()) self.assertEqual(len(block.tx_list), 1) self.assertEqual(block.tx_list[0], tx) self.assertEqual(block.header.create_time, 0) self.assertEqual(i, 0) b1 = state.create_block_to_mine(address=acc3) self.assertEqual(len(b1.tx_list), 1) # Should succeed state.finalize_and_add_block(b1) self.assertEqual(state.header_tip, b1.header) self.assertEqual(state.get_balance(id1.recipient), 10000000 - opcodes.GTXCOST - 12345) self.assertEqual(state.get_balance(acc2.recipient), 12345) self.assertEqual(state.get_balance(acc3.recipient), opcodes.GTXCOST // 2) # Check receipts self.assertEqual(len(state.evm_state.receipts), 1) self.assertEqual(state.evm_state.receipts[0].state_root, b"\x01") self.assertEqual(state.evm_state.receipts[0].gas_used, 21000) block, i = state.get_transaction_by_hash(tx.get_hash()) self.assertEqual(block, b1) self.assertEqual(i, 0) # tx already confirmed self.assertTrue(state.db.contain_transaction_hash(tx.get_hash())) self.assertFalse(state.add_tx(tx))
def test_native_token_transfer(self): """in-shard transfer QETH using genesis_token as gas """ QETH = token_id_encode("QETH") id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_key=0) acc2 = Address.create_random_account(full_shard_key=0) acc3 = Address.create_random_account(full_shard_key=0) env = get_test_env( genesis_account=acc1, genesis_minor_token_balances={ self.GENESIS_TOKEN: 10000000, "QETH": 99999 }, ) state = create_default_shard_state(env=env) tx = create_transfer_transaction( shard_state=state, key=id1.get_key(), from_address=acc1, to_address=acc2, value=12345, gas=21000, gas_token_id=self.genesis_token, transfer_token_id=QETH, ) self.assertTrue(state.add_tx(tx)) b1 = state.create_block_to_mine(address=acc3) self.assertEqual(len(b1.tx_list), 1) state.finalize_and_add_block(b1) self.assertEqual(state.header_tip, b1.header) self.assertEqual( state.get_token_balance(id1.recipient, self.genesis_token), 10000000 - opcodes.GTXCOST, ) self.assertEqual(state.get_token_balance(acc1.recipient, QETH), 99999 - 12345) self.assertEqual(state.get_token_balance(acc2.recipient, QETH), 12345) self.assertEqual( state.get_token_balance(acc3.recipient, self.genesis_token), self.getAfterTaxReward(opcodes.GTXCOST + self.shard_coinbase), ) tx_list, _ = state.db.get_transactions_by_address(acc1) self.assertEqual(tx_list[0].value, 12345) self.assertEqual(tx_list[0].gas_token_id, self.genesis_token) self.assertEqual(tx_list[0].transfer_token_id, QETH) tx_list, _ = state.db.get_transactions_by_address(acc2) self.assertEqual(tx_list[0].value, 12345) self.assertEqual(tx_list[0].gas_token_id, self.genesis_token) self.assertEqual(tx_list[0].transfer_token_id, QETH)
def test_native_token_gas(self): """in-shard transfer QETH using native token as gas """ qeth = token_id_encode("QETH") id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_key=0) acc2 = Address.create_random_account(full_shard_key=0) # Miner acc3 = Address.create_random_account(full_shard_key=0) env = get_test_env( genesis_account=acc1, genesis_minor_token_balances={"QETH": 10000000}, charge_gas_reserve=True, ) state = create_default_shard_state(env=env) tx = create_transfer_transaction( shard_state=state, key=id1.get_key(), from_address=acc1, to_address=acc2, value=12345, gas=21000, gas_token_id=qeth, transfer_token_id=qeth, ) self.assertTrue(state.add_tx(tx)) b1 = state.create_block_to_mine(address=acc3) self.assertEqual(len(b1.tx_list), 1) state.finalize_and_add_block(b1) self.assertEqual(state.header_tip, b1.header) self.assertEqual( state.get_token_balance(acc1.recipient, qeth), 10000000 - opcodes.GTXCOST - 12345, ) self.assertEqual(state.get_token_balance(acc2.recipient, qeth), 12345) # after-tax coinbase + tx fee should only be in QKC self.assertEqual( state.get_token_balance(acc3.recipient, self.genesis_token), self.get_after_tax_reward(self.shard_coinbase + opcodes.GTXCOST), ) tx_list, _ = state.db.get_transactions_by_address(acc1) self.assertEqual(tx_list[0].value, 12345) self.assertEqual(tx_list[0].gas_token_id, qeth) self.assertEqual(tx_list[0].transfer_token_id, qeth) tx_list, _ = state.db.get_transactions_by_address(acc2) self.assertEqual(tx_list[0].value, 12345) self.assertEqual(tx_list[0].gas_token_id, qeth) self.assertEqual(tx_list[0].transfer_token_id, qeth)
def setUp(self): super().setUp() id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_id=0) env = get_test_env(genesis_account=acc1, genesis_minor_quarkash=10000000) state = create_default_shard_state(env=env) tx = create_contract_creation_with_event_transaction( shard_state=state, key=id1.get_key(), from_address=acc1, to_full_shard_id=acc1.full_shard_id, ) self.assertTrue(state.add_tx(tx)) b = state.create_block_to_mine(address=acc1, gas_limit=1000000) hit_block = b # will be used later state.finalize_and_add_block(b) start_height = b.header.height # https://hastebin.com/debezaqocu.cs # 1 log with 2 topics - sha3(b'Hi(address)') and msg.sender log = Log.create_from_eth_log(state.evm_state.receipts[0].logs[0], b, 0, 0) # add other random blocks with normal tx for _ in range(10): tx = create_transfer_transaction( shard_state=state, key=id1.get_key(), from_address=acc1, to_address=Address.create_from_identity( Identity.create_random_identity(), full_shard_id=0), value=1, gas=40000, ) self.assertTrue(state.add_tx(tx)) b = state.create_block_to_mine(address=acc1, gas_limit=100000) state.finalize_and_add_block(b) self.assertEqual(b.header.height, start_height + 10) self.hit_block = hit_block self.log = log self.state = state self.start_height = start_height def filter_gen_with_criteria(criteria, addresses=None): return Filter(state.db, addresses or [], criteria, start_height, start_height + 10) self.filter_gen_with_criteria = filter_gen_with_criteria