From b0bcc7dce79b19d7bf86e619e4aa79d6d95d6f7d Mon Sep 17 00:00:00 2001 From: ninjaahhh Date: Fri, 27 Mar 2020 15:33:12 -0700 Subject: [PATCH 1/2] Reduce shard staking requirement for PoSW --- quarkchain/cluster/posw.py | 5 ++- quarkchain/cluster/root_state.py | 4 ++- quarkchain/cluster/shard_state.py | 35 ++++++++++++-------- quarkchain/cluster/tests/test_shard_state.py | 27 +++++++++++---- 4 files changed, 49 insertions(+), 22 deletions(-) diff --git a/quarkchain/cluster/posw.py b/quarkchain/cluster/posw.py index 18f72a02c..9943ad631 100644 --- a/quarkchain/cluster/posw.py +++ b/quarkchain/cluster/posw.py @@ -64,6 +64,7 @@ def get_posw_info( header: Header, stake_func: Callable[[], int], block_cnt: Dict[bytes, int], + stake_per_block: Optional[int] = None, signer: Optional[bytes] = None, ) -> Optional[PoSWInfo]: if ( @@ -75,7 +76,9 @@ def get_posw_info( # evaluate stakes before the to-be-added block stakes = stake_func() coinbase_recipient = header.coinbase_address.recipient - block_threshold = min(config.WINDOW_SIZE, stakes // config.TOTAL_STAKE_PER_BLOCK) + + required_stakes_per_block = stake_per_block or config.TOTAL_STAKE_PER_BLOCK + block_threshold = min(config.WINDOW_SIZE, stakes // required_stakes_per_block) cnt = block_cnt.get(coinbase_recipient, 0) diff = header.difficulty diff --git a/quarkchain/cluster/root_state.py b/quarkchain/cluster/root_state.py index 139f85c89..6b34b3dff 100644 --- a/quarkchain/cluster/root_state.py +++ b/quarkchain/cluster/root_state.py @@ -665,4 +665,6 @@ def get_posw_info( block.header.hash_prev_block, self.db.get_root_block_header_by_hash, ) - return get_posw_info(config, block.header, lambda: stakes, block_cnt, signer) + return get_posw_info( + config, block.header, lambda: stakes, block_cnt, signer=signer + ) diff --git a/quarkchain/cluster/shard_state.py b/quarkchain/cluster/shard_state.py index 7cba5a75b..c3e3d6aea 100644 --- a/quarkchain/cluster/shard_state.py +++ b/quarkchain/cluster/shard_state.py @@ -1024,22 +1024,13 @@ def add_block( return evm_state.xshard_list, coinbase_amount_map def get_coinbase_amount_map(self, height) -> TokenBalanceMap: - epoch = ( - height - // self.env.quark_chain_config.shards[self.full_shard_id].EPOCH_INTERVAL - ) - decay_numerator = ( - self.env.quark_chain_config.block_reward_decay_factor.numerator ** epoch - ) - decay_denominator = ( - self.env.quark_chain_config.block_reward_decay_factor.denominator ** epoch - ) coinbase_amount = ( - self.env.quark_chain_config.shards[self.full_shard_id].COINBASE_AMOUNT + self.__decay_by_epoch( + self.env.quark_chain_config.shards[self.full_shard_id].COINBASE_AMOUNT, + height, + ) * self.local_fee_rate.numerator - * decay_numerator // self.local_fee_rate.denominator - // decay_denominator ) # shard coinbase only in genesis_token return TokenBalanceMap( @@ -1838,6 +1829,10 @@ def _posw_info(self, block: MinorBlock) -> Optional[PoSWInfo]: if header.height == 0: # genesis return None block_cnt = self._get_posw_coinbase_blockcnt(header.hash_prev_minor_block) + # require stakes will decay as our mining rewards + stake_per_block = self.__decay_by_epoch( + self.shard_config.POSW_CONFIG.TOTAL_STAKE_PER_BLOCK, header.height + ) return get_posw_info( self.shard_config.POSW_CONFIG, header, @@ -1848,6 +1843,7 @@ def _posw_info(self, block: MinorBlock) -> Optional[PoSWInfo]: self.env.quark_chain_config.genesis_token, ), block_cnt, + stake_per_block, ) def _get_evm_state_from_height(self, height: Optional[int]) -> Optional[EvmState]: @@ -1970,3 +1966,16 @@ def _qkchashx_enabled(self, header): config.ENABLE_QKCHASHX_HEIGHT is not None and header.height >= config.ENABLE_QKCHASHX_HEIGHT ) + + def __decay_by_epoch(self, value: int, block_height: int): + epoch = ( + block_height + // self.env.quark_chain_config.shards[self.full_shard_id].EPOCH_INTERVAL + ) + decay_numerator = ( + self.env.quark_chain_config.block_reward_decay_factor.numerator ** epoch + ) + decay_denominator = ( + self.env.quark_chain_config.block_reward_decay_factor.denominator ** epoch + ) + return value * decay_numerator // decay_denominator diff --git a/quarkchain/cluster/tests/test_shard_state.py b/quarkchain/cluster/tests/test_shard_state.py index 0d5715d27..673884ce4 100644 --- a/quarkchain/cluster/tests/test_shard_state.py +++ b/quarkchain/cluster/tests/test_shard_state.py @@ -1532,9 +1532,6 @@ def test_xshard_gas_limit_from_multiple_shards(self): env1 = get_test_env( genesis_account=acc1, genesis_minor_quarkash=10000000, shard_size=64 ) - env2 = 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=16) state2 = create_default_shard_state(env=env1, shard_id=8) @@ -1679,10 +1676,9 @@ def test_xshard_gas_limit_from_multiple_shards(self): 10000000 + 1000000 + 12345 + 888888 + 111111, ) - def test_xshard_rootblock_coinbase(self): + def test_xshard_root_block_coinbase(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=16) env0 = get_test_env( genesis_account=acc1, genesis_minor_quarkash=10000000, shard_size=64 @@ -1738,7 +1734,6 @@ def test_xshard_sender_gas_limit(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=16) - acc3 = Address.create_random_account(full_shard_key=0) env0 = get_test_env( genesis_account=acc1, genesis_minor_quarkash=10000000, shard_size=64 @@ -2775,7 +2770,6 @@ def test_enable_tx_timestamp(self): def test_enable_evm_timestamp_with_contract_create(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_key=0) - acc2 = Address.create_random_account(full_shard_key=0) env = get_test_env(genesis_account=acc1, genesis_minor_quarkash=10000000) state = create_default_shard_state(env=env) @@ -3727,3 +3721,22 @@ def tx_gen(to): state_to.get_token_balance(miner.recipient, token_id=self.genesis_token), self.get_after_tax_reward(self.shard_coinbase + (3 * gas_price) * 9000), ) + + def test_posw_stake_by_block_decay_by_epoch(self): + acc = Address(b"\x01" * 20, full_shard_key=0) + env = get_test_env(genesis_account=acc, genesis_minor_quarkash=200) + state = create_default_shard_state(env=env, shard_id=0, posw_override=True) + + state.shard_config.CONSENSUS_TYPE = ConsensusType.POW_DOUBLESHA256 + state.shard_config.POSW_CONFIG.TOTAL_STAKE_PER_BLOCK = 100 + state.shard_config.POSW_CONFIG.WINDOW_SIZE = 256 + + b1 = state.get_tip().create_block_to_append(address=acc) + posw_info = state._posw_info(b1) + # 200 qkc with 100 required per block, should equal 2 mineable blocks + self.assertEqual(posw_info.posw_mineable_blocks, 200 / 100) + + # decay (factor = 0.5) should kick in and double mineable blocks + b1.header.height = state.shard_config.EPOCH_INTERVAL + posw_info = state._posw_info(b1) + self.assertEqual(posw_info.posw_mineable_blocks, 200 / (100 / 2)) From 1474d09000310e6c1ea11283c4f3867dd876a12f Mon Sep 17 00:00:00 2001 From: ninjaahhh Date: Mon, 30 Mar 2020 14:20:24 -0700 Subject: [PATCH 2/2] Address comments --- quarkchain/cluster/shard_state.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quarkchain/cluster/shard_state.py b/quarkchain/cluster/shard_state.py index c3e3d6aea..9404f8217 100644 --- a/quarkchain/cluster/shard_state.py +++ b/quarkchain/cluster/shard_state.py @@ -1843,7 +1843,7 @@ def _posw_info(self, block: MinorBlock) -> Optional[PoSWInfo]: self.env.quark_chain_config.genesis_token, ), block_cnt, - stake_per_block, + stake_per_block=stake_per_block, ) def _get_evm_state_from_height(self, height: Optional[int]) -> Optional[EvmState]: