Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions quarkchain/cluster/master.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
19 changes: 16 additions & 3 deletions quarkchain/cluster/shard_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
apply_transaction,
validate_transaction,
apply_xshard_deposit,
convert_to_default_chain_token_gasprice,
)
from quarkchain.evm.specials import SystemContract
from quarkchain.evm.state import State as EvmState
Expand Down Expand Up @@ -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.
default_gasprice = convert_to_default_chain_token_gasprice(
evm_state, evm_tx.gas_token_id, evm_tx.gasprice
)
if default_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(
Expand Down Expand Up @@ -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)

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 evm_tx.gasprice < self.env.quark_chain_config.MIN_MINING_GAS_PRICE:
if default_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
Expand Down
89 changes: 89 additions & 0 deletions quarkchain/cluster/tests/test_cluster.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions quarkchain/cluster/tests/test_shard_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
get_gas_utility_info,
pay_native_token_as_gas,
validate_transaction,
convert_to_default_chain_token_gasprice,
)
from quarkchain.evm.specials import SystemContract
from quarkchain.evm.state import State as EvmState
Expand Down Expand Up @@ -3274,6 +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(
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
Expand Down
9 changes: 9 additions & 0 deletions quarkchain/evm/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,15 @@ def mk_receipt(state, success, logs, contract_address, contract_full_shard_key):
return o


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()
_, 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
Expand Down