|
1 | 1 | import pytest
|
2 | 2 |
|
3 | 3 | from eth_utils import (
|
4 |
| - to_wei |
| 4 | + to_wei, |
| 5 | + ValidationError, |
5 | 6 | )
|
6 | 7 |
|
7 | 8 | from eth.chains.base import (
|
|
12 | 13 | build,
|
13 | 14 | disable_pow_check,
|
14 | 15 | mine_block,
|
| 16 | + mine_blocks, |
15 | 17 | byzantium_at,
|
16 | 18 | frontier_at,
|
17 | 19 | homestead_at,
|
@@ -82,3 +84,215 @@ def test_rewards(vm_fn, miner_1_balance, miner_2_balance):
|
82 | 84 | # But we also ensure the balance matches the numbers that we calculated on paper
|
83 | 85 | assert coinbase_balance == to_wei(miner_1_balance, 'ether')
|
84 | 86 | assert other_miner_balance == to_wei(miner_2_balance, 'ether')
|
| 87 | + |
| 88 | + |
| 89 | +@pytest.mark.parametrize( |
| 90 | + 'vm_fn, fork_at_block_number, miner_1_balance, miner_2_balance', |
| 91 | + ( |
| 92 | + (frontier_at, 3, 50.15625, 1.25), |
| 93 | + (frontier_at, 4, 50.15625, 1.875), |
| 94 | + (frontier_at, 5, 50.15625, 2.5), |
| 95 | + (frontier_at, 6, 50.15625, 3.125), |
| 96 | + (frontier_at, 7, 50.15625, 3.75), |
| 97 | + (frontier_at, 8, 50.15625, 4.375), |
| 98 | +
|
| 99 | + (homestead_at, 3, 50.15625, 1.25), |
| 100 | + (homestead_at, 4, 50.15625, 1.875), |
| 101 | + (homestead_at, 5, 50.15625, 2.5), |
| 102 | + (homestead_at, 6, 50.15625, 3.125), |
| 103 | + (homestead_at, 7, 50.15625, 3.75), |
| 104 | + (homestead_at, 8, 50.15625, 4.375), |
| 105 | +
|
| 106 | + (tangerine_whistle_at, 3, 50.15625, 1.25), |
| 107 | + (tangerine_whistle_at, 4, 50.15625, 1.875), |
| 108 | + (tangerine_whistle_at, 5, 50.15625, 2.5), |
| 109 | + (tangerine_whistle_at, 6, 50.15625, 3.125), |
| 110 | + (tangerine_whistle_at, 7, 50.15625, 3.75), |
| 111 | + (tangerine_whistle_at, 8, 50.15625, 4.375), |
| 112 | +
|
| 113 | + (spurious_dragon_at, 3, 50.15625, 1.25), |
| 114 | + (spurious_dragon_at, 4, 50.15625, 1.875), |
| 115 | + (spurious_dragon_at, 5, 50.15625, 2.5), |
| 116 | + (spurious_dragon_at, 6, 50.15625, 3.125), |
| 117 | + (spurious_dragon_at, 7, 50.15625, 3.75), |
| 118 | + (spurious_dragon_at, 8, 50.15625, 4.375), |
| 119 | +
|
| 120 | + (byzantium_at, 3, 30.09375, 0.75), |
| 121 | + (byzantium_at, 4, 30.09375, 1.125), |
| 122 | + (byzantium_at, 5, 30.09375, 1.5), |
| 123 | + (byzantium_at, 6, 30.09375, 1.875), |
| 124 | + (byzantium_at, 7, 30.09375, 2.25), |
| 125 | + (byzantium_at, 8, 30.09375, 2.625), |
| 126 | +
|
| 127 | + (constantinople_at, 3, 20.0625, 0.5), |
| 128 | + (constantinople_at, 4, 20.0625, 0.75), |
| 129 | + (constantinople_at, 5, 20.0625, 1), |
| 130 | + (constantinople_at, 6, 20.0625, 1.25), |
| 131 | + (constantinople_at, 7, 20.0625, 1.5), |
| 132 | + (constantinople_at, 8, 20.0625, 1.75), |
| 133 | + ) |
| 134 | +) |
| 135 | +def test_rewards_uncle_created_at_different_generations( |
| 136 | + vm_fn, |
| 137 | + fork_at_block_number, |
| 138 | + miner_1_balance, |
| 139 | + miner_2_balance): |
| 140 | + OTHER_MINER_ADDRESS = 20 * b'\x01' |
| 141 | + TOTAL_BLOCKS_CANONICAL_CHAIN = 10 |
| 142 | + |
| 143 | + chain = build( |
| 144 | + MiningChain, |
| 145 | + vm_fn(0), |
| 146 | + disable_pow_check(), |
| 147 | + genesis(), |
| 148 | + mine_blocks(TOTAL_BLOCKS_CANONICAL_CHAIN - 1), |
| 149 | + ) |
| 150 | + |
| 151 | + fork_chain = build( |
| 152 | + chain, |
| 153 | + at_block_number(fork_at_block_number), |
| 154 | + mine_block(extra_data=b'fork-it!', coinbase=OTHER_MINER_ADDRESS), # fork 2 |
| 155 | + ) |
| 156 | + |
| 157 | + # we don't use canonical head here because the fork chain is non-canonical. |
| 158 | + uncle = fork_chain.get_block_header_by_hash(fork_chain.header.parent_hash) |
| 159 | + assert uncle.block_number == fork_at_block_number + 1 |
| 160 | + |
| 161 | + chain = build( |
| 162 | + chain, |
| 163 | + mine_block(uncles=[uncle]), |
| 164 | + ) |
| 165 | + |
| 166 | + header = chain.get_canonical_head() |
| 167 | + block = chain.get_block_by_hash(header.hash) |
| 168 | + |
| 169 | + vm = chain.get_vm() |
| 170 | + coinbase_balance = vm.state.account_db.get_balance(block.header.coinbase) |
| 171 | + other_miner_balance = vm.state.account_db.get_balance(uncle.coinbase) |
| 172 | + |
| 173 | + # We first test if the balance matches what we would determine |
| 174 | + # if we made all the API calls involved ourselves. |
| 175 | + assert coinbase_balance == (vm.get_block_reward() * |
| 176 | + TOTAL_BLOCKS_CANONICAL_CHAIN + |
| 177 | + vm.get_nephew_reward()) |
| 178 | + |
| 179 | + assert other_miner_balance == vm.get_uncle_reward(block.number, uncle) |
| 180 | + |
| 181 | + # But we also ensure the balance matches the numbers that we calculated on paper |
| 182 | + assert coinbase_balance == to_wei(miner_1_balance, 'ether') |
| 183 | + assert other_miner_balance == to_wei(miner_2_balance, 'ether') |
| 184 | + |
| 185 | + |
| 186 | +@pytest.mark.parametrize( |
| 187 | + 'vm_fn', |
| 188 | + ( |
| 189 | + frontier_at, |
| 190 | + homestead_at, |
| 191 | + tangerine_whistle_at, |
| 192 | + spurious_dragon_at, |
| 193 | + byzantium_at, |
| 194 | + constantinople_at, |
| 195 | + ) |
| 196 | +) |
| 197 | +def test_uncle_block_inclusion_validity(vm_fn): |
| 198 | + # This test ensures that a forked block which is behind by |
| 199 | + # more than 6 layers cannot act as an ancestor to the current block |
| 200 | + |
| 201 | + OTHER_MINER_ADDRESS = 20 * b'\x01' |
| 202 | + |
| 203 | + chain = build( |
| 204 | + MiningChain, |
| 205 | + vm_fn(0), |
| 206 | + disable_pow_check(), |
| 207 | + genesis(), |
| 208 | + mine_block(), # 1 |
| 209 | + mine_block(), # 2 |
| 210 | + ) |
| 211 | + |
| 212 | + fork_chain = build( |
| 213 | + chain, |
| 214 | + at_block_number(1), |
| 215 | + mine_block(extra_data=b'fork-it!', coinbase=OTHER_MINER_ADDRESS), # fork 2 |
| 216 | + ) |
| 217 | + |
| 218 | + # we don't use canonical head here because the fork chain is non-canonical. |
| 219 | + uncle = fork_chain.get_block_header_by_hash(fork_chain.header.parent_hash) |
| 220 | + assert uncle.block_number == 2 |
| 221 | + |
| 222 | + with pytest.raises(ValidationError): |
| 223 | + chain = build( |
| 224 | + chain, |
| 225 | + # Mines blocks from 3 to 8 (both numbers inclusive) |
| 226 | + mine_blocks(6), |
| 227 | + # Mine block 9 with uncle |
| 228 | + mine_block(uncles=[uncle]), |
| 229 | + ) |
| 230 | + |
| 231 | + |
| 232 | +@pytest.mark.parametrize( |
| 233 | + 'vm_fn_uncle, vm_fn_nephew, miner_1_balance, miner_2_balance', |
| 234 | + ( |
| 235 | + (frontier_at, homestead_at, 50.15625, 1.25), |
| 236 | + (homestead_at, tangerine_whistle_at, 50.15625, 1.25), |
| 237 | + (tangerine_whistle_at, spurious_dragon_at, 50.15625, 1.25), |
| 238 | + (spurious_dragon_at, byzantium_at, 36.09375, 0.75), |
| 239 | + (byzantium_at, constantinople_at, 23.0625, 0.5), |
| 240 | + ) |
| 241 | +) |
| 242 | +def test_rewards_nephew_uncle_different_vm( |
| 243 | + vm_fn_uncle, |
| 244 | + vm_fn_nephew, |
| 245 | + miner_1_balance, |
| 246 | + miner_2_balance): |
| 247 | + OTHER_MINER_ADDRESS = 20 * b'\x01' |
| 248 | + TOTAL_BLOCKS_CANONICAL_CHAIN = 10 |
| 249 | + VM_CHANGE_BLOCK_NUMBER = 4 |
| 250 | + |
| 251 | + chain = build( |
| 252 | + MiningChain, |
| 253 | + vm_fn_uncle(0), |
| 254 | + vm_fn_nephew(VM_CHANGE_BLOCK_NUMBER), |
| 255 | + disable_pow_check(), |
| 256 | + genesis(), |
| 257 | + mine_blocks(TOTAL_BLOCKS_CANONICAL_CHAIN - 1), |
| 258 | + ) |
| 259 | + |
| 260 | + fork_chain = build( |
| 261 | + chain, |
| 262 | + at_block_number(3), |
| 263 | + mine_block(extra_data=b'fork-it!', coinbase=OTHER_MINER_ADDRESS), # fork 2 |
| 264 | + ) |
| 265 | + |
| 266 | + # we don't use canonical head here because the fork chain is non-canonical. |
| 267 | + uncle = fork_chain.get_block_header_by_hash(fork_chain.header.parent_hash) |
| 268 | + assert uncle.block_number == 4 |
| 269 | + |
| 270 | + chain = build( |
| 271 | + chain, |
| 272 | + mine_block(uncles=[uncle]), |
| 273 | + ) |
| 274 | + |
| 275 | + header = chain.get_canonical_head() |
| 276 | + block = chain.get_block_by_hash(header.hash) |
| 277 | + assert len(block.uncles) == 1 |
| 278 | + assert block.uncles[0] == uncle |
| 279 | + |
| 280 | + vm = chain.get_vm() |
| 281 | + coinbase_balance = vm.state.account_db.get_balance(block.header.coinbase) |
| 282 | + other_miner_balance = vm.state.account_db.get_balance(uncle.coinbase) |
| 283 | + |
| 284 | + uncle_vm = chain.get_vm_class_for_block_number(0) |
| 285 | + nephew_vm = chain.get_vm_class_for_block_number(VM_CHANGE_BLOCK_NUMBER) |
| 286 | + |
| 287 | + # We first test if the balance matches what we would determine |
| 288 | + # if we made all the API calls involved ourselves. |
| 289 | + assert coinbase_balance == ( |
| 290 | + uncle_vm.get_block_reward() * 3 + |
| 291 | + nephew_vm.get_block_reward() * (TOTAL_BLOCKS_CANONICAL_CHAIN - 3) + |
| 292 | + vm.get_nephew_reward() |
| 293 | + ) |
| 294 | + assert other_miner_balance == vm.get_uncle_reward(block.number, uncle) |
| 295 | + |
| 296 | + # But we also ensure the balance matches the numbers that we calculated on paper |
| 297 | + assert coinbase_balance == to_wei(miner_1_balance, 'ether') |
| 298 | + assert other_miner_balance == to_wei(miner_2_balance, 'ether') |
0 commit comments