def put_confirmed_cross_shard_transaction_deposit_list( self, minor_block_hash, cross_shard_transaction_deposit_list): """Stores a mapping from minor block to the list of CrossShardTransactionDeposit confirmed""" if not self.env.cluster_config.ENABLE_TRANSACTION_HISTORY: return l = CrossShardTransactionList(cross_shard_transaction_deposit_list) self.db.put(b"xr_" + minor_block_hash, l.serialize())
def __get_branch_to_add_xshard_tx_list_request(self, block_hash, xshard_tx_list, prev_root_height): xshard_map = dict( ) # type: Dict[Branch, List[CrossShardTransactionDeposit]] # only broadcast to the shards that have been initialized initialized_shard_ids = self.env.quark_chain_config.get_initialized_shard_ids_before_root_height( prev_root_height) for shard_id in initialized_shard_ids: branch = Branch.create(self.__get_shard_size(), shard_id) xshard_map[branch] = [] for xshard_tx in xshard_tx_list: shard_id = xshard_tx.to_address.get_shard_id( self.__get_shard_size()) branch = Branch.create(self.__get_shard_size(), shard_id) check(branch in xshard_map) xshard_map[branch].append(xshard_tx) branch_to_add_xshard_tx_list_request = ( dict()) # type: Dict[Branch, AddXshardTxListRequest] for branch, tx_list in xshard_map.items(): cross_shard_tx_list = CrossShardTransactionList(tx_list) request = AddXshardTxListRequest(branch, block_hash, cross_shard_tx_list) branch_to_add_xshard_tx_list_request[branch] = request return branch_to_add_xshard_tx_list_request
def add_minor_block_to_cluster(s_states, block): full_shard_id = block.header.branch.get_full_shard_id() s_states[full_shard_id].finalize_and_add_block(block) block_hash = block.header.get_hash() for dst_full_shard_id, state in s_states.items(): if dst_full_shard_id == full_shard_id: continue state.add_cross_shard_tx_list_by_minor_block_hash( block_hash, CrossShardTransactionList(tx_list=[]))
def add_minor_block_to_cluster(s_states, block): shard_id = block.header.branch.get_shard_id() s_states[shard_id].finalize_and_add_block(block) block_hash = block.header.get_hash() for i in range(block.header.branch.get_shard_size()): if i == shard_id: continue s_states[i].add_cross_shard_tx_list_by_minor_block_hash( block_hash, CrossShardTransactionList(tx_list=[]))
def add_minor_block_to_cluster(s_states, block): """Add block to corresponding shard state and broadcast xshard list to other shards""" full_shard_id = block.header.branch.get_full_shard_id() s_states[full_shard_id].finalize_and_add_block(block) block_hash = block.header.get_hash() for dst_full_shard_id, state in s_states.items(): if dst_full_shard_id == full_shard_id: continue state.add_cross_shard_tx_list_by_minor_block_hash( block_hash, CrossShardTransactionList(tx_list=[]))
def test_serialization_and_deserialization(self): cstor_params = { "tx_hash": bytes(32), "from_address": Address.create_random_account(1), "to_address": Address.create_random_account(1), "value": 123, "gas_price": 456, "gas_token_id": 789, "transfer_token_id": 101, } deposit_deprecated = CrossShardTransactionDepositDeprecated( **cstor_params) deposit_v0 = CrossShardTransactionDepositV0(**cstor_params) deposit_v1 = CrossShardTransactionDeposit(**cstor_params) testcases = [ (CrossShardTransactionDeprecatedList([deposit_deprecated]), None), (CrossShardTransactionListV0([deposit_v0]), None), (CrossShardTransactionList([deposit_v1]), 55), ] for ls, refund_rate_update in testcases: if refund_rate_update: ls.tx_list[0].refund_rate = refund_rate_update else: refund_rate_update = 100 # default refund rate, for comparison deserialized = CrossShardTransactionList.from_data(ls.serialize()) self.assertIsInstance(deserialized, CrossShardTransactionList) self.assertEqual(len(deserialized.tx_list), 1) deposit = deserialized.tx_list[0] self.assertIsInstance(deposit, CrossShardTransactionDeposit) self.assertEqual(deposit.refund_rate, refund_rate_update) # serialize and deserialize again deposit_deserialized_again = CrossShardTransactionDeposit.deserialize( deposit.serialize()) self.assertIsInstance(deposit_deserialized_again, CrossShardTransactionDeposit) self.assertEqual(deposit_deserialized_again.refund_rate, refund_rate_update)
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 create_default_state(env, diff_calc=None): r_state = RootState(env=env, diff_calc=diff_calc) s_state_list = dict() for full_shard_id in env.quark_chain_config.get_full_shard_ids(): shard_state = ShardState(env=env, full_shard_id=full_shard_id, db=quarkchain.db.InMemoryDb()) shard_state.init_genesis_state(r_state.get_tip_block()) s_state_list[full_shard_id] = shard_state for state in s_state_list.values(): block_hash = state.header_tip.get_hash() for dst_state in s_state_list.values(): if state == dst_state: continue dst_state.add_cross_shard_tx_list_by_minor_block_hash( block_hash, CrossShardTransactionList(tx_list=[])) r_state.add_validated_minor_block_hash(block_hash) return (r_state, s_state_list)
def create_default_state(env, diff_calc=None): r_state = RootState(env=env, diff_calc=diff_calc) s_state_list = [] for shard_id in range(env.quark_chain_config.SHARD_SIZE): shard_state = ShardState(env=env, shard_id=shard_id, db=quarkchain.db.InMemoryDb()) shard_state.init_genesis_state(r_state.get_tip_block()) s_state_list.append(shard_state) for state in s_state_list: block_hash = state.header_tip.get_hash() for dst_state in s_state_list: if state == dst_state: continue dst_state.add_cross_shard_tx_list_by_minor_block_hash( block_hash, CrossShardTransactionList(tx_list=[])) r_state.add_validated_minor_block_hash(block_hash) return (r_state, s_state_list)
def test_xshard_native_token_gas_received(self): qeth = token_id_encode("QETHXX") id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_key=0) acc2 = Address.create_from_identity(id1, full_shard_key=16) acc3 = Address.create_random_account(full_shard_key=0) env0 = get_test_env( genesis_account=acc1, genesis_minor_token_balances={"QETHXX": 9999999}, shard_size=64, ) env1 = get_test_env( genesis_account=acc1, genesis_minor_token_balances={"QETHXX": 9999999}, shard_size=64, ) state0 = create_default_shard_state(env=env0, shard_id=0) state1 = create_default_shard_state(env=env1, shard_id=16) # Add a root block to allow later minor blocks referencing this root block to # be broadcasted root_block = ( state0.root_tip.create_block_to_append() .add_minor_block_header(state0.header_tip) .add_minor_block_header(state1.header_tip) .finalize() ) state0.add_root_block(root_block) state1.add_root_block(root_block) # Add one block in shard 0 b0 = state0.create_block_to_mine() state0.finalize_and_add_block(b0) b1 = state1.get_tip().create_block_to_append() b1.header.hash_prev_root_block = root_block.header.get_hash() tx = create_transfer_transaction( shard_state=state1, key=id1.get_key(), from_address=acc2, to_address=acc1, value=8888888, gas=opcodes.GTXXSHARDCOST + opcodes.GTXCOST, gas_price=2, gas_token_id=qeth, transfer_token_id=qeth, ) b1.add_tx(tx) # Add a x-shard tx from remote peer state0.add_cross_shard_tx_list_by_minor_block_hash( h=b1.header.get_hash(), tx_list=CrossShardTransactionList( tx_list=[ CrossShardTransactionDeposit( tx_hash=tx.get_hash(), from_address=acc2, to_address=acc1, value=8888888, gas_price=2, gas_token_id=self.genesis_token, transfer_token_id=qeth, ) ] ), ) # Create a root block containing the block with the x-shard tx 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) # Add b0 and make sure all x-shard tx's are added b2 = state0.create_block_to_mine(address=acc3) state0.finalize_and_add_block(b2) self.assertEqual( state0.get_token_balance(acc1.recipient, qeth), 9999999 + 8888888 ) # Half coinbase collected by root + tx fee self.assertEqual( state0.get_token_balance(acc3.recipient, self.genesis_token), self.get_after_tax_reward(self.shard_coinbase + opcodes.GTXXSHARDCOST * 2), ) # X-shard gas used self.assertEqual( state0.evm_state.xshard_receive_gas_used, opcodes.GTXXSHARDCOST )
def __get_confirmed_cross_shard_transaction_deposit_list( self, minor_block_hash): data = self.db.get(b"xr_" + minor_block_hash, None) if not data: return [] return CrossShardTransactionList.from_data(data).tx_list
def get_minor_block_xshard_tx_list(self, h) -> CrossShardTransactionList: key = b"xShard_" + h if key not in self.db: return None return CrossShardTransactionList.from_data(self.db.get(key))
def put_minor_block_xshard_tx_list(self, h, tx_list: CrossShardTransactionList): self.db.put(b"xShard_" + h, tx_list.serialize())
def test_xshard_for_two_root_blocks(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) 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 a root block to allow later minor blocks referencing this root block to # be broadcasted root_block = ( state0.root_tip.create_block_to_append().add_minor_block_header( state0.header_tip).add_minor_block_header( state1.header_tip).finalize()) state0.add_root_block(root_block) state1.add_root_block(root_block) # Add one block in shard 0 b0 = state0.create_block_to_mine() state0.finalize_and_add_block(b0) b1 = state1.get_tip().create_block_to_append() b1.header.hash_prev_root_block = root_block.header.get_hash() tx = create_transfer_transaction( shard_state=state1, key=id1.get_key(), from_address=acc2, to_address=acc1, value=888888, gas=opcodes.GTXXSHARDCOST + opcodes.GTXCOST, ) b1.add_tx(tx) # Add a x-shard tx from state1 state0.add_cross_shard_tx_list_by_minor_block_hash( h=b1.header.get_hash(), tx_list=CrossShardTransactionList(tx_list=[ CrossShardTransactionDeposit( tx_hash=tx.get_hash(), from_address=acc2, to_address=acc1, value=888888, gas_price=2, ) ]), ) # Create a root block containing the block with the x-shard tx root_block0 = ( 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_block0) b2 = state0.get_tip().create_block_to_append() state0.finalize_and_add_block(b2) b3 = b1.create_block_to_append() b3.header.hash_prev_root_block = root_block.header.get_hash() # Add a x-shard tx from state1 state0.add_cross_shard_tx_list_by_minor_block_hash( h=b3.header.get_hash(), tx_list=CrossShardTransactionList(tx_list=[ CrossShardTransactionDeposit( tx_hash=bytes(32), from_address=acc2, to_address=acc1, value=385723, gas_price=3, ) ]), ) root_block1 = ( state0.root_tip.create_block_to_append().add_minor_block_header( b2.header).add_minor_block_header(b3.header).finalize()) state0.add_root_block(root_block1) # Test x-shard gas limit when create_block_to_mine b5 = state0.create_block_to_mine(address=acc3, gas_limit=0) # Current algorithm allows at least one root block to be included self.assertEqual(b5.header.hash_prev_root_block, root_block0.header.get_hash()) b6 = state0.create_block_to_mine(address=acc3, gas_limit=opcodes.GTXXSHARDCOST) self.assertEqual(b6.header.hash_prev_root_block, root_block0.header.get_hash()) # There are two x-shard txs: one is root block coinbase with zero gas, and anonther is from shard 1 b7 = state0.create_block_to_mine(address=acc3, gas_limit=2 * opcodes.GTXXSHARDCOST) self.assertEqual(b7.header.hash_prev_root_block, root_block1.header.get_hash()) b8 = state0.create_block_to_mine(address=acc3, gas_limit=3 * opcodes.GTXXSHARDCOST) self.assertEqual(b8.header.hash_prev_root_block, root_block1.header.get_hash()) # Add b0 and make sure all x-shard tx's are added b4 = state0.create_block_to_mine(address=acc3) self.assertEqual(b4.header.hash_prev_root_block, root_block1.header.get_hash()) state0.finalize_and_add_block(b4) self.assertEqual(state0.get_balance(acc1.recipient), 10000000 + 888888 + 385723) # Half collected by root self.assertEqual(state0.get_balance(acc3.recipient), opcodes.GTXXSHARDCOST * (2 + 3) // 2) # Check gas used for receiving x-shard tx self.assertEqual(state0.evm_state.gas_used, 18000) self.assertEqual(state0.evm_state.xshard_receive_gas_used, 18000)
def test_xshard_tx_received_exclude_non_neighbor(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=3) acc3 = Address.create_random_account(full_shard_id=0) env0 = get_test_env(genesis_account=acc1, genesis_minor_quarkash=10000000, shard_size=64) env1 = get_test_env(genesis_account=acc1, genesis_minor_quarkash=10000000, shard_size=64) state0 = create_default_shard_state(env=env0, shard_id=0) state1 = create_default_shard_state(env=env1, shard_id=3) # Add one block in shard 0 b0 = state0.create_block_to_mine() state0.finalize_and_add_block(b0) b1 = state1.get_tip().create_block_to_append() tx = create_transfer_transaction( shard_state=state1, key=id1.get_key(), from_address=acc2, to_address=acc1, value=888888, gas=opcodes.GTXXSHARDCOST + opcodes.GTXCOST, gas_price=2, ) b1.add_tx(tx) # Add a x-shard tx from remote peer state0.add_cross_shard_tx_list_by_minor_block_hash( h=b1.header.get_hash(), tx_list=CrossShardTransactionList(tx_list=[ CrossShardTransactionDeposit( tx_hash=tx.get_hash(), from_address=acc2, to_address=acc1, value=888888, gas_price=2, ) ]), ) # Create a root block containing the block with the x-shard tx 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) # Add b0 and make sure all x-shard tx's are added b2 = state0.create_block_to_mine(address=acc3) state0.finalize_and_add_block(b2) self.assertEqual(state0.get_balance(acc1.recipient), 10000000) # Half collected by root self.assertEqual(state0.get_balance(acc3.recipient), 0) # X-shard gas used evmState0 = state0.evm_state self.assertEqual(evmState0.xshard_receive_gas_used, 0)