Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement EIP-1234 #447

Merged
merged 1 commit into from
Sep 25, 2018
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,10 @@ Ethereum common tests are created for all clients to test against. We plan to pr
- [GeneralStateTests](https://github.com/ethereum/tests/tree/develop/GeneralStateTests) 1088/1113 = 97.8% passing
- [x] EIP158
- [BlockchainTests](https://github.com/ethereum/tests/tree/develop/BlockchainTests) (Includes GeneralStateTests) 1232/1233 = 99.9% passing
- [GeneralStateTests](https://github.com/ethereum/tests/tree/develop/GeneralStateTests) 1174/1180 = 99.5% passing
- [GeneralStateTests](https://github.com/ethereum/tests/tree/develop/GeneralStateTests) 1175/1181 = 99.5% passing
- [x] Byzantium
- [BlockchainTests](https://github.com/ethereum/tests/tree/develop/BlockchainTests) (Includes GeneralStateTests) 4915/4959 = 99.1% passing
- [GeneralStateTests](https://github.com/ethereum/tests/tree/develop/GeneralStateTests) 4741/4784 = 99.1% passing
- [GeneralStateTests](https://github.com/ethereum/tests/tree/develop/GeneralStateTests) 4756/4784 = 99.4% passing
- [ ] Constantinople: View the community [Constantinople Project Tracker](https://github.com/ethereum/pm/issues/53).

## Updating the Common test
Expand Down
34 changes: 11 additions & 23 deletions apps/blockchain/lib/blockchain/block.ex
Original file line number Diff line number Diff line change
Expand Up @@ -517,20 +517,20 @@ defmodule Blockchain.Block do
end

defp get_difficulty(block, parent_block, chain) do
homestead_block = chain.engine["Ethash"][:homestead_transition]
byzantium_block = chain.engine["Ethash"][:eip100b_transition]

cond do
Header.is_before_homestead?(block.header, homestead_block) ->
Header.get_frontier_difficulty(
Chain.after_bomb_delays?(chain, block.header.number) ->
delay_factor = Chain.bomb_delay_factor_for_block(chain, block.header.number)

Header.get_byzantium_difficulty(
block.header,
if(parent_block, do: parent_block.header, else: nil),
delay_factor,
chain.genesis[:difficulty],
chain.engine["Ethash"][:minimum_difficulty],
chain.engine["Ethash"][:difficulty_bound_divisor]
)

Header.is_before_byzantium?(block.header, byzantium_block) ->
Chain.after_homestead?(chain, block.header.number) ->
Header.get_homestead_difficulty(
block.header,
if(parent_block, do: parent_block.header, else: nil),
Expand All @@ -540,7 +540,7 @@ defmodule Blockchain.Block do
)

true ->
Header.get_byzantium_difficulty(
Header.get_frontier_difficulty(
block.header,
if(parent_block, do: parent_block.header, else: nil),
chain.genesis[:difficulty],
Expand Down Expand Up @@ -734,13 +734,11 @@ defmodule Blockchain.Block do
end

defp create_receipt(block_header, new_state, total_gas_used, logs, tx_status, chain) do
eip658_transition = chain.params[:eip658_transition]

state =
if Header.is_before_byzantium?(block_header, eip658_transition) do
new_state.root_hash
else
if Chain.after_byzantium?(chain, block_header.number) do
tx_status
else
new_state.root_hash
end

%Receipt{
Expand Down Expand Up @@ -860,7 +858,7 @@ defmodule Blockchain.Block do
do: block

def add_rewards(block, db, chain) do
base_reward = calculate_base_reward(block, chain)
base_reward = Chain.block_reward_for_block(chain, block.header.number)

state =
block
Expand All @@ -871,16 +869,6 @@ defmodule Blockchain.Block do
set_state(block, state)
end

defp calculate_base_reward(block, chain) do
byzantium_transition = chain.engine["Ethash"][:eip649_transition]

if Header.is_before_byzantium?(block.header, byzantium_transition) do
chain.engine["Ethash"][:block_reward]
else
chain.engine["Ethash"][:eip649_reward]
end
end

defp add_miner_reward(state, block, base_reward) do
ommer_reward = round(base_reward * length(block.ommers) / @block_reward_ommer_divisor)
reward = ommer_reward + base_reward
Expand Down
100 changes: 96 additions & 4 deletions apps/blockchain/lib/blockchain/chain.ex
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,12 @@ defmodule Blockchain.Chain do
minimum_difficulty: integer(),
difficulty_bound_divisor: integer(),
duration_limit: integer(),
block_reward: integer(),
block_rewards: [{integer(), integer()}],
homestead_transition: integer(),
eip649_reward: integer(),
eip100b_transition: integer(),
eip649_transition: integer()
eip649_transition: integer(),
difficulty_bomb_delays: [{integer(), integer()}]
}

@type params :: %{
Expand Down Expand Up @@ -185,22 +186,113 @@ defmodule Blockchain.Chain do
end
end

@doc """
Convenience function to determine whether a block number is after the
bomb delays introduced in Byzantium and Constantinople
"""
@spec after_bomb_delays?(t(), integer()) :: boolean()
def after_bomb_delays?(chain = %__MODULE__{}, block_number) do
bomb_delays = chain.engine["Ethash"][:difficulty_bomb_delays]

Enum.any?(bomb_delays, fn {hard_fork_number, _delay} ->
block_number >= hard_fork_number
end)
end

@doc """
Function to determine what the bomb delay is for a block number.

Note: This function should not be called on a block number that happens before
bomb delays. Before bomb delays were introduced, the difficulty calculation
was different and thus we do not expect a bomb delay at all.
"""
@spec bomb_delay_factor_for_block(t, integer()) :: integer()
def bomb_delay_factor_for_block(chain = %__MODULE__{}, block_number) do
bomb_delays = chain.engine["Ethash"][:difficulty_bomb_delays]

{_, delay} =
bomb_delays
|> Enum.sort(fn {k1, _}, {k2, _} -> k1 < k2 end)
|> Enum.take_while(fn {k, _} -> k <= block_number end)
|> List.last()

delay
end

@doc """
Determines the base reward for a block number. The reward changed was lowered
in Byzantium and again in Constantinople
"""
@spec block_reward_for_block(t, integer()) :: integer()
def block_reward_for_block(chain = %__MODULE__{}, block_number) do
{_k, reward} =
chain.engine["Ethash"][:block_rewards]
|> Enum.sort(fn {k, _}, {k2, _} -> k < k2 end)
|> Enum.take_while(fn {k, _} -> k <= block_number end)
|> List.last()

reward
end

@doc """
Helper function to determine if block number is after the homestead transition
based on the chain configuration.
"""
@spec after_homestead?(t, integer()) :: boolean()
def after_homestead?(chain, block_number) do
homestead_block = chain.engine["Ethash"][:homestead_transition]

block_number >= homestead_block
end

@doc """
Helper function to determine if block number is after the byzantium transition
based on the chain configuration.
"""
@spec after_byzantium?(t, integer()) :: boolean()
def after_byzantium?(chain, block_number) do
eip658_transition = chain.params[:eip658_transition]

block_number >= eip658_transition
end

@spec get_engine({String.t(), map}) :: {String.t(), engine()}
defp get_engine({engine, %{"params" => params}}) do
config = %{
minimum_difficulty: params["minimumDifficulty"] |> load_hex(),
difficulty_bound_divisor: params["difficultyBoundDivisor"] |> load_hex(),
duration_limit: params["durationLimit"] |> load_hex(),
block_reward: params["blockReward"] |> load_hex(),
block_rewards: params["blockReward"] |> parse_reward(),
homestead_transition: params["homesteadTransition"] |> load_hex(),
eip649_reward: params["eip649Reward"] |> load_hex(),
eip100b_transition: params["eip100bTransition"] |> load_hex(),
eip649_transition: params["eip649Transition"] |> load_hex()
eip649_transition: params["eip649Transition"] |> load_hex(),
difficulty_bomb_delays: params["difficultyBombDelays"] |> parse_bomb_delays()
}

{engine, config}
end

defp parse_reward(block_reward) when is_binary(block_reward) do
[{load_hex("0x00"), load_hex(block_reward)}]
end

defp parse_reward(block_rewards) do
Enum.map(block_rewards, fn {k, v} ->
{block_number, _} = Integer.parse(k)
{block_number, load_hex(v)}
end)
end

defp parse_bomb_delays(nil), do: []

defp parse_bomb_delays(bomb_delays) do
Enum.map(bomb_delays, fn {k, v} ->
{block_number, _} = Integer.parse(k)
{block_number, v}
end)
end

@spec get_params(map) :: params()
defp get_params(map) do
%{
Expand Down
5 changes: 5 additions & 0 deletions apps/blockchain/scripts/generate_blockchain_tests.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ defmodule GenerateBlockchainTests do
@base_path System.cwd() <> "/../../ethereum_common_tests/BlockchainTests/"
@allowed_forks ["Constantinople", "Byzantium", "Frontier", "Homestead", "EIP150", "EIP158"]
@byzantium_failing_tests_path System.cwd() <> "/test/support/byzantium_failing_tests.txt"
@constantinople_failing_tests_path System.cwd() <>
"/test/support/constantinople_failing_tests.txt"
@initial_pass_fail {[], []}
@number_of_test_groups 20
@ten_minutes 1000 * 60 * 10
Expand Down Expand Up @@ -100,6 +102,9 @@ defmodule GenerateBlockchainTests do
"Byzantium" ->
File.open!(@byzantium_failing_tests_path, [:write])

"Constantinople" ->
File.open!(@constantinople_failing_tests_path, [:write])

_ ->
:stdio
end
Expand Down
5 changes: 4 additions & 1 deletion apps/blockchain/test/blockchain/block_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,10 @@ defmodule Blockchain.BlockTest do
ommer = <<0x06::160>>
state = MerklePatriciaTree.Trie.new(db)
chain = Chain.load_chain(:ropsten)
byzantium_block_number = chain.engine["Ethash"][:eip649_transition]

{byzantium_block_number, _} =
chain.engine["Ethash"][:block_rewards]
|> Enum.at(1)

block = %Blockchain.Block{
header: %Header{
Expand Down
64 changes: 64 additions & 0 deletions apps/blockchain/test/blockchain/chain_test.exs
Original file line number Diff line number Diff line change
@@ -1,4 +1,68 @@
defmodule Blockchain.ChainTest do
use ExUnit.Case, async: true
doctest Blockchain.Chain

alias Blockchain.Chain

describe "after_bomb_delays?/2" do
test "checks if the block number is after any of the bomb delays were introduced" do
byzantium_transition = 4_370_000

chain = Chain.load_chain(:foundation)

assert Chain.after_bomb_delays?(chain, byzantium_transition)
assert Chain.after_bomb_delays?(chain, byzantium_transition + 1)
refute Chain.after_bomb_delays?(chain, byzantium_transition - 1)
end
end

describe "bomb_delay_factor_for_block/2" do
test "returns the bomb delay for the block number" do
byzantium_transition = 4_370_000

chain = Chain.load_chain(:foundation)

assert 3_000_000 == Chain.bomb_delay_factor_for_block(chain, byzantium_transition)
assert 3_000_000 == Chain.bomb_delay_factor_for_block(chain, byzantium_transition + 1)
end
end

describe "block_reward_for_block/2" do
test "returns the block reward based on the block number" do
byzantium_transition = 4_370_000
three_eth = 3_000_000_000_000_000_000
five_eth = 5_000_000_000_000_000_000

chain = Chain.load_chain(:foundation)

assert three_eth == Chain.block_reward_for_block(chain, byzantium_transition)
assert three_eth == Chain.block_reward_for_block(chain, byzantium_transition + 1)
assert five_eth == Chain.block_reward_for_block(chain, 0)
assert five_eth == Chain.block_reward_for_block(chain, 1)
end
end

describe "after_homestead?/2" do
test "checks whether or not a block number is after the homestead transition" do
homestead_transition = 1_150_000

chain = Chain.load_chain(:foundation)

assert Chain.after_homestead?(chain, homestead_transition)
assert Chain.after_homestead?(chain, homestead_transition + 1)
refute Chain.after_homestead?(chain, homestead_transition - 1)
end
end

describe "after_byzantium?/2" do
test "checks whether or not a block number is after the byzatium transition" do
byzantium_transition = 4_370_000

chain = Chain.load_chain(:foundation)

assert Chain.after_byzantium?(chain, byzantium_transition)
assert Chain.after_byzantium?(chain, byzantium_transition + 1)
refute Chain.after_byzantium?(chain, byzantium_transition - 1)
end
end
end
Loading