From 128598371fa89e61de1af9f2f375840c2b508b16 Mon Sep 17 00:00:00 2001 From: Qi Zhou Date: Mon, 23 Mar 2020 13:42:20 -0700 Subject: [PATCH 1/2] adding tx to queue needs to check gasprice in QKC --- quarkchain/cluster/master.py | 2 - quarkchain/cluster/shard_state.py | 19 ++++- quarkchain/cluster/tests/test_cluster.py | 89 ++++++++++++++++++++ quarkchain/cluster/tests/test_shard_state.py | 2 + quarkchain/evm/messages.py | 9 ++ 5 files changed, 116 insertions(+), 5 deletions(-) diff --git a/quarkchain/cluster/master.py b/quarkchain/cluster/master.py index 18116ba77..5b50a1a9f 100644 --- a/quarkchain/cluster/master.py +++ b/quarkchain/cluster/master.py @@ -1205,8 +1205,6 @@ async def get_primary_account_data( async def add_transaction(self, tx: TypedTransaction, from_peer=None): """ Add transaction to the cluster and broadcast to peers """ evm_tx = tx.tx.to_evm_tx() # type: EvmTransaction - if evm_tx.gasprice < self.env.quark_chain_config.MIN_TX_POOL_GAS_PRICE: - return False evm_tx.set_quark_chain_config(self.env.quark_chain_config) branch = Branch(evm_tx.from_full_shard_id) if branch.value not in self.branch_to_slaves: diff --git a/quarkchain/cluster/shard_state.py b/quarkchain/cluster/shard_state.py index 1b364fc88..f29e443bb 100644 --- a/quarkchain/cluster/shard_state.py +++ b/quarkchain/cluster/shard_state.py @@ -39,6 +39,7 @@ apply_transaction, validate_transaction, apply_xshard_deposit, + get_genesis_gasprice, ) from quarkchain.evm.specials import SystemContract from quarkchain.evm.state import State as EvmState @@ -538,6 +539,16 @@ def add_tx(self, tx: TypedTransaction, xshard_gas_limit=None): evm_tx = self.__validate_tx( tx, evm_state, xshard_gas_limit=xshard_gas_limit ) + + # Don't add the tx if the gasprice in QKC is too low. + # Note that this is not enforced by consensus, + # but miners will likely discard the tx if the gasprice is too low. + genesis_gasprice = get_genesis_gasprice( + evm_state, evm_tx.gas_token_id, evm_tx.gasprice + ) + if genesis_gasprice < self.env.quark_chain_config.MIN_TX_POOL_GAS_PRICE: + return False + self.tx_queue.add_transaction(tx) asyncio.ensure_future( self.subscription_manager.notify_new_pending_tx( @@ -1189,13 +1200,15 @@ def __add_transactions_to_block(self, block: MinorBlock, evm_state: EvmState): break evm_tx = tx.tx.to_evm_tx() + evm_tx.set_quark_chain_config(self.env.quark_chain_config) + genesis_gasprice = get_genesis_gasprice( + evm_state, evm_tx.gas_token_id, evm_tx.gasprice + ) # simply ignore tx with lower gas price than specified - if evm_tx.gasprice < self.env.quark_chain_config.MIN_MINING_GAS_PRICE: + if genesis_gasprice < self.env.quark_chain_config.MIN_MINING_GAS_PRICE: continue - evm_tx.set_quark_chain_config(self.env.quark_chain_config) - # check if TX is disabled if ( self.env.quark_chain_config.ENABLE_TX_TIMESTAMP is not None diff --git a/quarkchain/cluster/tests/test_cluster.py b/quarkchain/cluster/tests/test_cluster.py index a3e2794ad..8adebe968 100644 --- a/quarkchain/cluster/tests/test_cluster.py +++ b/quarkchain/cluster/tests/test_cluster.py @@ -11,6 +11,7 @@ from quarkchain.cluster.tests.test_utils import ( create_transfer_transaction, create_contract_with_storage2_transaction, + mock_pay_native_token_as_gas, ClusterContext, ) from quarkchain.config import ConsensusType @@ -259,6 +260,94 @@ def test_add_transaction(self): ).tx.to_evm_tx() self.assertEqual(actual_evm_tx, expect_evm_tx2) + def test_add_transaction_with_invalid_mnt(self): + id1 = Identity.create_random_identity() + acc1 = Address.create_from_identity(id1, full_shard_key=0) + acc2 = Address.create_from_identity(id1, full_shard_key=1) + + with ClusterContext(2, acc1, should_set_gas_price_limit=True) as clusters: + master = clusters[0].master + + root = call_async(master.get_next_block_to_mine(acc1, branch_value=None)) + call_async(master.add_root_block(root)) + + tx1 = create_transfer_transaction( + shard_state=clusters[0].get_shard_state(0b10), + key=id1.get_key(), + from_address=acc1, + to_address=acc1, + value=12345, + gas_price=10, + gas_token_id=1, + ) + self.assertFalse(call_async(master.add_transaction(tx1))) + + tx2 = create_transfer_transaction( + shard_state=clusters[0].get_shard_state(0b11), + key=id1.get_key(), + from_address=acc2, + to_address=acc1, + value=12345, + gas=30000, + gas_price=10, + gas_token_id=1, + ) + self.assertFalse(call_async(master.add_transaction(tx2))) + + @mock_pay_native_token_as_gas(lambda *x: (50, x[-1] // 5)) + def test_add_transaction_with_valid_mnt(self): + id1 = Identity.create_random_identity() + acc1 = Address.create_from_identity(id1, full_shard_key=0) + + with ClusterContext(2, acc1, should_set_gas_price_limit=True) as clusters: + master = clusters[0].master + + root = call_async(master.get_next_block_to_mine(acc1, branch_value=None)) + call_async(master.add_root_block(root)) + + # gasprice will be 9, which is smaller than 10 as required. + tx0 = create_transfer_transaction( + shard_state=clusters[0].get_shard_state(0b10), + key=id1.get_key(), + from_address=acc1, + to_address=acc1, + value=12345, + gas_price=49, + gas_token_id=1, + ) + self.assertFalse(call_async(master.add_transaction(tx0))) + + # gasprice will be 10, but the balance will be insufficient. + tx1 = create_transfer_transaction( + shard_state=clusters[0].get_shard_state(0b10), + key=id1.get_key(), + from_address=acc1, + to_address=acc1, + value=12345, + gas_price=50, + gas_token_id=1, + ) + self.assertFalse(call_async(master.add_transaction(tx1))) + + tx2 = create_transfer_transaction( + shard_state=clusters[0].get_shard_state(0b10), + key=id1.get_key(), + from_address=acc1, + to_address=acc1, + value=12345, + gas_price=50, + gas_token_id=1, + nonce=5, + ) + self.assertTrue(call_async(master.add_transaction(tx2))) + + # check the tx is received by the other cluster + state1 = clusters[1].get_shard_state(0b10) + tx_queue, expect_evm_tx2 = state1.tx_queue, tx2.tx.to_evm_tx() + assert_true_with_timeout(lambda: len(tx_queue) == 1) + actual_evm_tx = tx_queue.peek()[0].tx.tx.to_evm_tx() + self.assertEqual(actual_evm_tx, expect_evm_tx2) + def test_add_minor_block_request_list(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_key=0) diff --git a/quarkchain/cluster/tests/test_shard_state.py b/quarkchain/cluster/tests/test_shard_state.py index 4fdfce30a..780b05449 100644 --- a/quarkchain/cluster/tests/test_shard_state.py +++ b/quarkchain/cluster/tests/test_shard_state.py @@ -28,6 +28,7 @@ get_gas_utility_info, pay_native_token_as_gas, validate_transaction, + get_genesis_gasprice, ) from quarkchain.evm.specials import SystemContract from quarkchain.evm.state import State as EvmState @@ -3274,6 +3275,7 @@ def tx_gen(data: str, value=None, transfer_token_id=None): # get the gas utility information by calling the get_gas_utility_info function refund_percentage, gas_price = get_gas_utility_info(evm_state, token_id, 60000) self.assertEqual((refund_percentage, gas_price), (60, 2)) + self.assertEqual(get_genesis_gasprice(evm_state, token_id, 60000), 2) # exchange the Qkc with the native token refund_percentage, gas_price = pay_native_token_as_gas( evm_state, token_id, 1, 60000 diff --git a/quarkchain/evm/messages.py b/quarkchain/evm/messages.py index 7a4938c4a..f3b460f5e 100644 --- a/quarkchain/evm/messages.py +++ b/quarkchain/evm/messages.py @@ -114,6 +114,15 @@ def mk_receipt(state, success, logs, contract_address, contract_full_shard_key): return o +def get_genesis_gasprice(state, token_id, gas_price): + if token_id == state.shard_config.default_chain_token: + return gas_price + snapshot = state.snapshot() + _, genesis_token_gas_price = get_gas_utility_info(state, token_id, gas_price) + state.revert(snapshot) + return genesis_token_gas_price + + def validate_transaction(state, tx): # (1) The transaction signature is valid; if not tx.sender: # sender is set and validated on Transaction initialization From c3a6205c04786e1351f8591cdbf159c2867475bd Mon Sep 17 00:00:00 2001 From: Qi Zhou Date: Tue, 24 Mar 2020 17:08:50 -0700 Subject: [PATCH 2/2] use the name convert_to_default_chain_token_gasprice --- quarkchain/cluster/shard_state.py | 10 +++++----- quarkchain/cluster/tests/test_shard_state.py | 6 ++++-- quarkchain/evm/messages.py | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/quarkchain/cluster/shard_state.py b/quarkchain/cluster/shard_state.py index f29e443bb..7cba5a75b 100644 --- a/quarkchain/cluster/shard_state.py +++ b/quarkchain/cluster/shard_state.py @@ -39,7 +39,7 @@ apply_transaction, validate_transaction, apply_xshard_deposit, - get_genesis_gasprice, + convert_to_default_chain_token_gasprice, ) from quarkchain.evm.specials import SystemContract from quarkchain.evm.state import State as EvmState @@ -543,10 +543,10 @@ def add_tx(self, tx: TypedTransaction, xshard_gas_limit=None): # Don't add the tx if the gasprice in QKC is too low. # Note that this is not enforced by consensus, # but miners will likely discard the tx if the gasprice is too low. - genesis_gasprice = get_genesis_gasprice( + default_gasprice = convert_to_default_chain_token_gasprice( evm_state, evm_tx.gas_token_id, evm_tx.gasprice ) - if genesis_gasprice < self.env.quark_chain_config.MIN_TX_POOL_GAS_PRICE: + if default_gasprice < self.env.quark_chain_config.MIN_TX_POOL_GAS_PRICE: return False self.tx_queue.add_transaction(tx) @@ -1202,11 +1202,11 @@ def __add_transactions_to_block(self, block: MinorBlock, evm_state: EvmState): evm_tx = tx.tx.to_evm_tx() evm_tx.set_quark_chain_config(self.env.quark_chain_config) - genesis_gasprice = get_genesis_gasprice( + default_gasprice = convert_to_default_chain_token_gasprice( evm_state, evm_tx.gas_token_id, evm_tx.gasprice ) # simply ignore tx with lower gas price than specified - if genesis_gasprice < self.env.quark_chain_config.MIN_MINING_GAS_PRICE: + if default_gasprice < self.env.quark_chain_config.MIN_MINING_GAS_PRICE: continue # check if TX is disabled diff --git a/quarkchain/cluster/tests/test_shard_state.py b/quarkchain/cluster/tests/test_shard_state.py index 780b05449..0d5715d27 100644 --- a/quarkchain/cluster/tests/test_shard_state.py +++ b/quarkchain/cluster/tests/test_shard_state.py @@ -28,7 +28,7 @@ get_gas_utility_info, pay_native_token_as_gas, validate_transaction, - get_genesis_gasprice, + convert_to_default_chain_token_gasprice, ) from quarkchain.evm.specials import SystemContract from quarkchain.evm.state import State as EvmState @@ -3275,7 +3275,9 @@ def tx_gen(data: str, value=None, transfer_token_id=None): # get the gas utility information by calling the get_gas_utility_info function refund_percentage, gas_price = get_gas_utility_info(evm_state, token_id, 60000) self.assertEqual((refund_percentage, gas_price), (60, 2)) - self.assertEqual(get_genesis_gasprice(evm_state, token_id, 60000), 2) + self.assertEqual( + convert_to_default_chain_token_gasprice(evm_state, token_id, 60000), 2 + ) # exchange the Qkc with the native token refund_percentage, gas_price = pay_native_token_as_gas( evm_state, token_id, 1, 60000 diff --git a/quarkchain/evm/messages.py b/quarkchain/evm/messages.py index f3b460f5e..d7267ccfc 100644 --- a/quarkchain/evm/messages.py +++ b/quarkchain/evm/messages.py @@ -114,7 +114,7 @@ def mk_receipt(state, success, logs, contract_address, contract_full_shard_key): return o -def get_genesis_gasprice(state, token_id, gas_price): +def convert_to_default_chain_token_gasprice(state, token_id, gas_price): if token_id == state.shard_config.default_chain_token: return gas_price snapshot = state.snapshot()