diff --git a/.circleci/config.yml b/.circleci/config.yml index f9db91d227..92a2b1d9e5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -14,6 +14,8 @@ jobs: working_directory: ~/app steps: + - run: sudo apt-get update; sudo apt-get -y install autoconf build-essential libgmp3-dev libtool + - checkout - run: mix local.hex --force @@ -70,6 +72,11 @@ jobs: - run: mix compile + # Ensure NIF is compiled for libsecp256k1 + - run: + command: make + working_directory: "deps/libsecp256k1" + # `deps` needs to be cached with `_build` because `_build` will symlink into `deps` - save_cache: diff --git a/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex index e5f193652a..3008b37cf8 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex @@ -45,13 +45,13 @@
<%= output["value"] %> - + <%= gettext("WEI")%> <%= gettext("ETH")%>
<% else %> - <%= output["value"] %> + <%= values(output["value"], output["type"]) %> <% end %> <% end %> <% end %> diff --git a/apps/block_scout_web/lib/block_scout_web/views/smart_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/smart_contract_view.ex index 0d499104be..c96c5efe91 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/smart_contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/smart_contract_view.ex @@ -10,6 +10,12 @@ defmodule BlockScoutWeb.SmartContractView do def named_argument?(%{"name" => _}), do: true def named_argument?(_), do: false + def values(addresses, type) when type == "address[]" do + addresses + |> Enum.map(&values(&1, "address")) + |> Enum.join(", ") + end + def values(value, type) when type in ["address", "address payable"] do {:ok, address} = Explorer.Chain.Hash.Address.cast(value) to_string(address) diff --git a/apps/block_scout_web/test/block_scout_web/views/tokens/smart_contract_view_test.exs b/apps/block_scout_web/test/block_scout_web/views/tokens/smart_contract_view_test.exs index 503a206c25..5ab8133bf5 100644 --- a/apps/block_scout_web/test/block_scout_web/views/tokens/smart_contract_view_test.exs +++ b/apps/block_scout_web/test/block_scout_web/views/tokens/smart_contract_view_test.exs @@ -80,6 +80,16 @@ defmodule BlockScoutWeb.SmartContractViewTest do assert SmartContractView.values(value, "address payable") == "0x5f26097334b6a32b7951df61fd0c5803ec5d8354" end + test "convert each value to string and join them when receiving 'address[]' as the type" do + value = [ + <<95, 38, 9, 115, 52, 182, 163, 43, 121, 81, 223, 97, 253, 12, 88, 3, 236, 93, 131, 84>>, + <<207, 38, 14, 163, 23, 85, 86, 55, 197, 95, 112, 229, 93, 186, 141, 90, 216, 65, 76, 176>> + ] + + assert SmartContractView.values(value, "address[]") == + "0x5f26097334b6a32b7951df61fd0c5803ec5d8354, 0xcf260ea317555637c55f70e55dba8d5ad8414cb0" + end + test "returns the value when the type is neither 'address' nor 'address payable'" do value = "POA" diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex index ebade1d4da..b202d8a64d 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex @@ -11,16 +11,23 @@ defmodule EthereumJSONRPC.Block do @type elixir :: %{String.t() => non_neg_integer | DateTime.t() | String.t() | nil} @type params :: %{ difficulty: pos_integer(), + extra_data: EthereumJSONRPC.hash(), gas_limit: non_neg_integer(), gas_used: non_neg_integer(), hash: EthereumJSONRPC.hash(), + logs_bloom: EthereumJSONRPC.hash(), miner_hash: EthereumJSONRPC.hash(), + mix_hash: EthereumJSONRPC.hash(), nonce: EthereumJSONRPC.hash(), number: non_neg_integer(), parent_hash: EthereumJSONRPC.hash(), + receipts_root: EthereumJSONRPC.hash(), + sha3_uncles: EthereumJSONRPC.hash(), size: non_neg_integer(), + state_root: EthereumJSONRPC.hash(), timestamp: DateTime.t(), total_difficulty: non_neg_integer(), + transactions_root: EthereumJSONRPC.hash(), uncles: [EthereumJSONRPC.hash()] } @@ -95,16 +102,23 @@ defmodule EthereumJSONRPC.Block do ...> ) %{ difficulty: 340282366920938463463374607431465537093, + extra_data: "0xd5830108048650617269747986312e32322e31826c69", gas_limit: 6706541, gas_used: 0, hash: "0x52c867bc0a91e573dc39300143c3bead7408d09d45bdb686749f02684ece72f3", + logs_bloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", miner_hash: "0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca", + mix_hash: "0x0", nonce: 0, number: 1, parent_hash: "0x5b28c1bfd3a15230c9a46b399cd0f9a6920d432e85381cc6a140b06e8410112f", + receipts_root: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + sha3_uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", size: 576, + state_root: "0xc196ad59d867542ef20b29df5f418d07dc7234f4bc3d25260526620b7958a8fb", timestamp: Timex.parse!("2017-12-15T21:03:30Z", "{ISO:Extended:Z}"), total_difficulty: 340282366920938463463374607431465668165, + transactions_root: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", uncles: [] } @@ -136,16 +150,23 @@ defmodule EthereumJSONRPC.Block do ...> ) %{ difficulty: 17561410778, + extra_data: "0x476574682f4c5649562f76312e302e302f6c696e75782f676f312e342e32", gas_limit: 5000, gas_used: 0, hash: "0x4d9423080290a650eaf6db19c87c76dff83d1b4ab64aefe6e5c5aa2d1f4b6623", + logs_bloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + mix_hash: "0xbbb93d610b2b0296a59f18474ac3d6086a9902aa7ca4b9a306692f7c3d496fdf", miner_hash: "0xbb7b8287f3f0a933474a79eae42cbca977791171", nonce: 5539500215739777653, number: 59, parent_hash: "0xcd5b5c4cecd7f18a13fe974255badffd58e737dc67596d56bc01f063dd282e9e", + receipts_root: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + sha3_uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", size: 542, + state_root: "0x6fd0a5d82ca77d9f38c3ebbde11b11d304a5fcf3854f291df64395ab38ed43ba", timestamp: Timex.parse!("2015-07-30T15:32:07Z", "{ISO:Extended:Z}"), total_difficulty: 1039309006117, + transactions_root: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", uncles: [] } @@ -154,30 +175,43 @@ defmodule EthereumJSONRPC.Block do def elixir_to_params( %{ "difficulty" => difficulty, + "extraData" => extra_data, "gasLimit" => gas_limit, "gasUsed" => gas_used, "hash" => hash, + "logsBloom" => logs_bloom, "miner" => miner_hash, "number" => number, "parentHash" => parent_hash, + "receiptsRoot" => receipts_root, + "sha3Uncles" => sha3_uncles, "size" => size, + "stateRoot" => state_root, "timestamp" => timestamp, "totalDifficulty" => total_difficulty, + "transactionsRoot" => transactions_root, "uncles" => uncles } = elixir ) do %{ difficulty: difficulty, + extra_data: extra_data, gas_limit: gas_limit, gas_used: gas_used, hash: hash, + logs_bloom: logs_bloom, miner_hash: miner_hash, + mix_hash: Map.get(elixir, "mixHash", "0x0"), nonce: Map.get(elixir, "nonce", 0), number: number, parent_hash: parent_hash, + receipts_root: receipts_root, + sha3_uncles: sha3_uncles, size: size, + state_root: state_root, timestamp: timestamp, total_difficulty: total_difficulty, + transactions_root: transactions_root, uncles: uncles } end diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/blocks.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/blocks.ex index a6964e17db..1eef103513 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/blocks.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/blocks.ex @@ -45,16 +45,23 @@ defmodule EthereumJSONRPC.Blocks do [ %{ difficulty: 131072, + extra_data: "0x", gas_limit: 6700000, gas_used: 0, hash: "0x5b28c1bfd3a15230c9a46b399cd0f9a6920d432e85381cc6a140b06e8410112f", + logs_bloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", miner_hash: "0x0000000000000000000000000000000000000000", + mix_hash: "0x0", nonce: 0, number: 0, parent_hash: "0x0000000000000000000000000000000000000000000000000000000000000000", + receipts_root: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + sha3_uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", size: 533, + state_root: "0xfad4af258fd11939fae0c6c6eec9d340b1caac0b0196fd9a1bc3f489c5bf00b3", timestamp: Timex.parse!("1970-01-01T00:00:00Z", "{ISO:Extended:Z}"), total_difficulty: 131072, + transactions_root: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", uncles: ["0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273311"] } ] diff --git a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/request_coordinator_test.exs b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/request_coordinator_test.exs index 4eedbe93dd..d299491eca 100644 --- a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/request_coordinator_test.exs +++ b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/request_coordinator_test.exs @@ -10,15 +10,6 @@ defmodule EthereumJSONRPC.RequestCoordinatorTest do setup :set_mox_global setup :verify_on_exit! - defp sleep_time(timeouts) do - wait_per_timeout = - :ethereum_jsonrpc - |> Application.get_env(RequestCoordinator) - |> Keyword.fetch!(:wait_per_timeout) - - timeouts * wait_per_timeout - end - setup do table = Application.get_env(:ethereum_jsonrpc, EthereumJSONRPC.RequestCoordinator)[:rolling_window_opts][:table] diff --git a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/web_socket/web_socket_client_test.exs b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/web_socket/web_socket_client_test.exs index 11629769cb..986eeb362a 100644 --- a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/web_socket/web_socket_client_test.exs +++ b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/web_socket/web_socket_client_test.exs @@ -95,18 +95,6 @@ defmodule EthereumJSONRPC.WebSocket.WebSocketClientTest do end end - defp cowboy(0) do - dispatch = :cowboy_router.compile([{:_, [{"/websocket", EthereumJSONRPC.WebSocket.Cowboy.WebSocketHandler, []}]}]) - {:ok, _} = :cowboy.start_http(EthereumJSONRPC.WebSocket.Cowboy, 100, [], env: [dispatch: dispatch]) - :ranch.get_port(EthereumJSONRPC.WebSocket.Cowboy) - end - - defp cowboy(port) do - dispatch = :cowboy_router.compile([{:_, [{"/websocket", EthereumJSONRPC.WebSocket.Cowboy.WebSocketHandler, []}]}]) - {:ok, _} = :cowboy.start_http(EthereumJSONRPC.WebSocket.Cowboy, 100, [port: port], env: [dispatch: dispatch]) - port - end - defp example_state(_) do %{state: %WebSocketClient{url: "ws://example.com"}} end diff --git a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc_test.exs b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc_test.exs index fd3dc3766a..1c19a1fa97 100644 --- a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc_test.exs +++ b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc_test.exs @@ -207,10 +207,15 @@ defmodule EthereumJSONRPCTest do "gasLimit" => "0x0", "gasUsed" => "0x0", "hash" => block_hash, + "extraData" => "0x0", + "logsBloom" => "0x0", "miner" => "0x0", "number" => block_number, "parentHash" => "0x0", + "receiptsRoot" => "0x0", "size" => "0x0", + "sha3Uncles" => "0x0", + "stateRoot" => "0x0", "timestamp" => "0x0", "totalDifficulty" => "0x0", "transactions" => [ @@ -231,6 +236,7 @@ defmodule EthereumJSONRPCTest do "value" => "0x0" } ], + "transactionsRoot" => "0x0", "uncles" => [] } } @@ -364,16 +370,22 @@ defmodule EthereumJSONRPCTest do id: 0, result: %{ "difficulty" => "0x0", + "extraData" => "0x0", "gasLimit" => "0x0", "gasUsed" => "0x0", "hash" => "0x0", + "logsBloom" => "0x0", "miner" => "0x0", "number" => "0x0", "parentHash" => "0x0", + "receiptsRoot" => "0x0", + "sha3Uncles" => "0x0", "size" => "0x0", + "stateRoot" => "0x0", "timestamp" => "0x0", "totalDifficulty" => "0x0", "transactions" => [], + "transactionsRoot" => "0x0", "uncles" => [] }, jsonrpc: "2.0" diff --git a/apps/indexer/config/config.exs b/apps/indexer/config/config.exs index ac8db25c30..43ff7c3d18 100644 --- a/apps/indexer/config/config.exs +++ b/apps/indexer/config/config.exs @@ -5,9 +5,10 @@ use Mix.Config import Bitwise config :indexer, + block_transformer: Indexer.Block.Transform.Base, + ecto_repos: [Explorer.Repo], # bytes - memory_limit: 1 <<< 30, - ecto_repos: [Explorer.Repo] + memory_limit: 1 <<< 30 config :logger, :indexer, # keep synced with `config/config.exs` diff --git a/apps/indexer/lib/indexer/block/fetcher.ex b/apps/indexer/lib/indexer/block/fetcher.ex index 07f35ee5c0..41a705a869 100644 --- a/apps/indexer/lib/indexer/block/fetcher.ex +++ b/apps/indexer/lib/indexer/block/fetcher.ex @@ -9,6 +9,7 @@ defmodule Indexer.Block.Fetcher do alias Indexer.{AddressExtraction, CoinBalance, MintTransfer, Token, TokenTransfers} alias Indexer.Address.{CoinBalances, TokenBalances} alias Indexer.Block.Fetcher.Receipts + alias Indexer.Block.Transform @type address_hash_to_fetched_balance_block_number :: %{String.t() => Block.block_number()} @type transaction_hash_to_block_number :: %{String.t() => Block.block_number()} @@ -96,6 +97,7 @@ defmodule Indexer.Block.Fetcher do transactions: transactions_without_receipts, block_second_degree_relations: block_second_degree_relations } = result, + blocks = Transform.transform_blocks(blocks), {:receipts, {:ok, receipt_params}} <- {:receipts, Receipts.fetch(state, transactions_without_receipts)}, %{logs: logs, receipts: receipts} = receipt_params, transactions_with_receipts = Receipts.put(transactions_without_receipts, receipts), diff --git a/apps/indexer/lib/indexer/block/transform.ex b/apps/indexer/lib/indexer/block/transform.ex new file mode 100644 index 0000000000..72f7f46f58 --- /dev/null +++ b/apps/indexer/lib/indexer/block/transform.ex @@ -0,0 +1,31 @@ +defmodule Indexer.Block.Transform do + @moduledoc """ + Protocol for transforming blocks. + """ + + @type block :: map() + + @doc """ + Transforms a block. + """ + @callback transform(block :: block()) :: block() + + @doc """ + Runs a list of blocks through the configured block transformer. + """ + def transform_blocks(blocks) when is_list(blocks) do + transformer = Application.get_env(:indexer, :block_transformer) + + unless transformer do + raise ArgumentError, + """ + No block transformer defined. Set a blocker transformer." + + config :indexer, + block_transformer: Indexer.Block.Transform.Base + """ + end + + Enum.map(blocks, &transformer.transform/1) + end +end diff --git a/apps/indexer/lib/indexer/block/transform/base.ex b/apps/indexer/lib/indexer/block/transform/base.ex new file mode 100644 index 0000000000..c094f9b1bc --- /dev/null +++ b/apps/indexer/lib/indexer/block/transform/base.ex @@ -0,0 +1,14 @@ +defmodule Indexer.Block.Transform.Base do + @moduledoc """ + Default block transformer to be used. + """ + + alias Indexer.Block.Transform + + @behaviour Transform + + @impl Transform + def transform(block) when is_map(block) do + block + end +end diff --git a/apps/indexer/lib/indexer/block/transform/clique.ex b/apps/indexer/lib/indexer/block/transform/clique.ex new file mode 100644 index 0000000000..bafab1e509 --- /dev/null +++ b/apps/indexer/lib/indexer/block/transform/clique.ex @@ -0,0 +1,16 @@ +defmodule Indexer.Block.Transform.Clique do + @moduledoc """ + Handles block transforms for Clique chain. + """ + + alias Indexer.Block.{Transform, Util} + + @behaviour Transform + + @impl Transform + def transform(block) when is_map(block) do + miner_address = Util.signer(block) + + %{block | miner_hash: miner_address} + end +end diff --git a/apps/indexer/lib/indexer/block/util.ex b/apps/indexer/lib/indexer/block/util.ex new file mode 100644 index 0000000000..fbf0d77997 --- /dev/null +++ b/apps/indexer/lib/indexer/block/util.ex @@ -0,0 +1,75 @@ +defmodule Indexer.Block.Util do + @moduledoc """ + Helper functions for parsing block information. + """ + + @doc """ + Calculates the signer's address by recovering the ECDSA public key. + + https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm + """ + def signer(block) when is_map(block) do + # Last 65 bytes is the signature. Multiply by two since we haven't transformed to raw bytes + {extra_data, signature} = String.split_at(trim_prefix(block.extra_data), -130) + + block = %{block | extra_data: extra_data} + + signature_hash = signature_hash(block) + + recover_pub_key(signature_hash, decode(signature)) + end + + # Signature hash calculated from the block header. + # Needed for PoA-based chains + defp signature_hash(block) do + header_data = [ + decode(block.parent_hash), + decode(block.sha3_uncles), + decode(block.miner_hash), + decode(block.state_root), + decode(block.transactions_root), + decode(block.receipts_root), + decode(block.logs_bloom), + block.difficulty, + block.number, + block.gas_limit, + block.gas_used, + DateTime.to_unix(block.timestamp), + decode(block.extra_data), + decode(block.mix_hash), + decode(block.nonce) + ] + + :keccakf1600.hash(:sha3_256, ExRLP.encode(header_data)) + end + + defp trim_prefix("0x" <> rest), do: rest + + defp decode("0x" <> rest) do + decode(rest) + end + + defp decode(data) do + Base.decode16!(data, case: :mixed) + end + + # Recovers the key from the signature hash and signature + defp recover_pub_key(signature_hash, signature) do + << + r::bytes-size(32), + s::bytes-size(32), + v::integer-size(8) + >> = signature + + # First byte represents compression which can be ignored + # Private key is the last 64 bytes + {:ok, <<_compression::bytes-size(1), private_key::binary>>} = + :libsecp256k1.ecdsa_recover_compact(signature_hash, r <> s, :uncompressed, v) + + # Public key comes from the last 20 bytes + <<_::bytes-size(12), public_key::binary>> = :keccakf1600.hash(:sha3_256, private_key) + + miner_address = Base.encode16(public_key, case: :lower) + "0x" <> miner_address + end +end diff --git a/apps/indexer/mix.exs b/apps/indexer/mix.exs index f8b26a7f47..44b5d3dea5 100644 --- a/apps/indexer/mix.exs +++ b/apps/indexer/mix.exs @@ -46,10 +46,14 @@ defmodule Indexer.MixProject do [ # JSONRPC access to Parity for `Explorer.Indexer` {:ethereum_jsonrpc, in_umbrella: true}, + # RLP encoding + {:ex_rlp, "~> 0.3"}, # Code coverage {:excoveralls, "~> 0.10.0", only: [:test], github: "KronicDeth/excoveralls", branch: "circle-workflows"}, # Importing to database {:explorer, in_umbrella: true}, + # libsecp2561k1 crypto functions + {:libsecp256k1, "~> 0.1.10"}, # Log errors and application output to separate files {:logger_file_backend, "~> 0.0.10"}, # Mocking `EthereumJSONRPC.Transport`, so we avoid hitting real chains for local testing diff --git a/apps/indexer/test/indexer/block/catchup/bound_interval_supervisor_test.exs b/apps/indexer/test/indexer/block/catchup/bound_interval_supervisor_test.exs index 1e1bf69492..07d97fc87b 100644 --- a/apps/indexer/test/indexer/block/catchup/bound_interval_supervisor_test.exs +++ b/apps/indexer/test/indexer/block/catchup/bound_interval_supervisor_test.exs @@ -448,20 +448,26 @@ defmodule Indexer.Block.Catchup.BoundIntervalSupervisorTest do jsonrpc: "2.0", result: %{ "difficulty" => "0x0", + "extraData" => "0x0", "gasLimit" => "0x0", "gasUsed" => "0x0", "hash" => Explorer.Factory.block_hash() |> to_string(), + "logsBloom" => "0x0", "miner" => "0xb2930b35844a230f00e51431acae96fe543a0347", "number" => "0x0", "parentHash" => Explorer.Factory.block_hash() |> to_string(), + "receiptsRoot" => "0x0", + "sha3Uncles" => "0x0", "size" => "0x0", + "stateRoot" => "0x0", "timestamp" => "0x0", "totalDifficulty" => "0x0", "transactions" => [], + "transactionsRoot" => "0x0", "uncles" => [] } } diff --git a/apps/indexer/test/indexer/block/transform/base_test.exs b/apps/indexer/test/indexer/block/transform/base_test.exs new file mode 100644 index 0000000000..b55adebc57 --- /dev/null +++ b/apps/indexer/test/indexer/block/transform/base_test.exs @@ -0,0 +1,42 @@ +defmodule Indexer.Block.Transform.BaseTest do + use ExUnit.Case + + alias Indexer.Block.Transform.Base + + @block %{ + difficulty: 1, + extra_data: + "0xd68301080d846765746886676f312e3130856c696e7578000000000000000000773ab2ca8f47904a14739ad80a75b71d9d29b9fff8b7ecdcb73efffa6f74122f17d304b5dc8e6e5f256c9474dd115c8d4dae31b7a3d409e5c3270f8fde41cd8c00", + gas_limit: 7_753_377, + gas_used: 1_810_195, + hash: "0x7004c895e812c55b0c2be8a46d72ca300a683dc27d1d7917ee7742d4d0359c1f", + logs_bloom: + "0x00000000000000020000000000002000000400000000000000000000000000000000000000000000040000080004000020000010000000000000000000000000000000000000000008000008000000000000000000200000000000000000000000000000020000000000000000000800000000000000804000000010080000000800000000000000000000000000000000000000000000800000000000080000000008000400000000404000000000000000000000000200000000000000000000000002000000000000001002000000000000002000000008000000000020000000000000000000000000000000000000000000000000400000800000000000", + miner: "0x0000000000000000000000000000000000000000", + mix_hash: "0x0000000000000000000000000000000000000000000000000000000000000000", + nonce: "0x0000000000000000", + number: 2_848_394, + parent_hash: "0x20350fc367e19d3865be1ea7da72ab81f8f9941c43ac6bb24a34a0a7caa2f3df", + receipts_root: "0x6ade4ac1079ea50cfadcce2b75ffbe4f9b14bf69b4607bbf1739463076ca6246", + sha3_uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + size: 6437, + state_root: "0x23f63347851bcd109059d007d71e19c4f5e73b7f0862bebcd04458333a004d92", + timestamp: DateTime.from_unix!(1_534_796_040), + total_difficulty: 5_353_647, + transactions: [ + "0x7e3bb851fc74a436826d2af6b96e4db9484431811ef0d9c9e78370488d33d4e5", + "0x3976fd1e3d2a715c3cfcfde9bd3210798c26c017b8edb841d319227ecb3322fb", + "0xd8db124005bb8b6fda7b71fd56ac782552a66af58fe843ba3c4930423b87d1d2", + "0x10c1a1ca4d9f4b2bd5b89f7bbcbbc2d69e166fe23662b8db4f6beae0f50ac9fd", + "0xaa58a6545677c796a56b8bc874174c8cfd31a6c6e6ca3a87e086d4f66d52858a" + ], + transactions_root: "0xde8d25c0b9b54310128a21601331094b43f910f9f96102869c2e2dca94884bf4", + uncles: [] + } + + describe "transform/1" do + test "passes the block through unchanged" do + assert Base.transform(@block) == @block + end + end +end diff --git a/apps/indexer/test/indexer/block/transform/clique_test.exs b/apps/indexer/test/indexer/block/transform/clique_test.exs new file mode 100644 index 0000000000..4af5d05895 --- /dev/null +++ b/apps/indexer/test/indexer/block/transform/clique_test.exs @@ -0,0 +1,43 @@ +defmodule Indexer.Block.Transform.CliqueTest do + use ExUnit.Case + + alias Indexer.Block.Transform.Clique + + @block %{ + difficulty: 1, + extra_data: + "0xd68301080d846765746886676f312e3130856c696e7578000000000000000000773ab2ca8f47904a14739ad80a75b71d9d29b9fff8b7ecdcb73efffa6f74122f17d304b5dc8e6e5f256c9474dd115c8d4dae31b7a3d409e5c3270f8fde41cd8c00", + gas_limit: 7_753_377, + gas_used: 1_810_195, + hash: "0x7004c895e812c55b0c2be8a46d72ca300a683dc27d1d7917ee7742d4d0359c1f", + logs_bloom: + "0x00000000000000020000000000002000000400000000000000000000000000000000000000000000040000080004000020000010000000000000000000000000000000000000000008000008000000000000000000200000000000000000000000000000020000000000000000000800000000000000804000000010080000000800000000000000000000000000000000000000000000800000000000080000000008000400000000404000000000000000000000000200000000000000000000000002000000000000001002000000000000002000000008000000000020000000000000000000000000000000000000000000000000400000800000000000", + miner_hash: "0x0000000000000000000000000000000000000000", + mix_hash: "0x0000000000000000000000000000000000000000000000000000000000000000", + nonce: "0x0000000000000000", + number: 2_848_394, + parent_hash: "0x20350fc367e19d3865be1ea7da72ab81f8f9941c43ac6bb24a34a0a7caa2f3df", + receipts_root: "0x6ade4ac1079ea50cfadcce2b75ffbe4f9b14bf69b4607bbf1739463076ca6246", + sha3_uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + size: 6437, + state_root: "0x23f63347851bcd109059d007d71e19c4f5e73b7f0862bebcd04458333a004d92", + timestamp: DateTime.from_unix!(1_534_796_040), + total_difficulty: 5_353_647, + transactions: [ + "0x7e3bb851fc74a436826d2af6b96e4db9484431811ef0d9c9e78370488d33d4e5", + "0x3976fd1e3d2a715c3cfcfde9bd3210798c26c017b8edb841d319227ecb3322fb", + "0xd8db124005bb8b6fda7b71fd56ac782552a66af58fe843ba3c4930423b87d1d2", + "0x10c1a1ca4d9f4b2bd5b89f7bbcbbc2d69e166fe23662b8db4f6beae0f50ac9fd", + "0xaa58a6545677c796a56b8bc874174c8cfd31a6c6e6ca3a87e086d4f66d52858a" + ], + transactions_root: "0xde8d25c0b9b54310128a21601331094b43f910f9f96102869c2e2dca94884bf4", + uncles: [] + } + + describe "transform/1" do + test "updates the miner hash with signer address" do + expected = %{@block | miner_hash: "0xfc18cbc391de84dbd87db83b20935d3e89f5dd91"} + assert Clique.transform(@block) == expected + end + end +end diff --git a/apps/indexer/test/indexer/block/transform_test.exs b/apps/indexer/test/indexer/block/transform_test.exs new file mode 100644 index 0000000000..dd6bb0b84e --- /dev/null +++ b/apps/indexer/test/indexer/block/transform_test.exs @@ -0,0 +1,56 @@ +defmodule Indexer.Block.TransformTest do + use ExUnit.Case + + alias Indexer.Block.Transform + + @block %{ + difficulty: 1, + extra_data: + "0xd68301080d846765746886676f312e3130856c696e7578000000000000000000773ab2ca8f47904a14739ad80a75b71d9d29b9fff8b7ecdcb73efffa6f74122f17d304b5dc8e6e5f256c9474dd115c8d4dae31b7a3d409e5c3270f8fde41cd8c00", + gas_limit: 7_753_377, + gas_used: 1_810_195, + hash: "0x7004c895e812c55b0c2be8a46d72ca300a683dc27d1d7917ee7742d4d0359c1f", + logs_bloom: + "0x00000000000000020000000000002000000400000000000000000000000000000000000000000000040000080004000020000010000000000000000000000000000000000000000008000008000000000000000000200000000000000000000000000000020000000000000000000800000000000000804000000010080000000800000000000000000000000000000000000000000000800000000000080000000008000400000000404000000000000000000000000200000000000000000000000002000000000000001002000000000000002000000008000000000020000000000000000000000000000000000000000000000000400000800000000000", + miner_hash: "0x0000000000000000000000000000000000000000", + mix_hash: "0x0000000000000000000000000000000000000000000000000000000000000000", + nonce: "0x0000000000000000", + number: 2_848_394, + parent_hash: "0x20350fc367e19d3865be1ea7da72ab81f8f9941c43ac6bb24a34a0a7caa2f3df", + receipts_root: "0x6ade4ac1079ea50cfadcce2b75ffbe4f9b14bf69b4607bbf1739463076ca6246", + sha3_uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + size: 6437, + state_root: "0x23f63347851bcd109059d007d71e19c4f5e73b7f0862bebcd04458333a004d92", + timestamp: DateTime.from_unix!(1_534_796_040), + total_difficulty: 5_353_647, + transactions: [ + "0x7e3bb851fc74a436826d2af6b96e4db9484431811ef0d9c9e78370488d33d4e5", + "0x3976fd1e3d2a715c3cfcfde9bd3210798c26c017b8edb841d319227ecb3322fb", + "0xd8db124005bb8b6fda7b71fd56ac782552a66af58fe843ba3c4930423b87d1d2", + "0x10c1a1ca4d9f4b2bd5b89f7bbcbbc2d69e166fe23662b8db4f6beae0f50ac9fd", + "0xaa58a6545677c796a56b8bc874174c8cfd31a6c6e6ca3a87e086d4f66d52858a" + ], + transactions_root: "0xde8d25c0b9b54310128a21601331094b43f910f9f96102869c2e2dca94884bf4", + uncles: [] + } + + @blocks [@block, @block] + + describe "transform_blocks/1" do + setup do + original = Application.get_env(:indexer, :block_transformer) + + on_exit(fn -> Application.put_env(:indexer, :block_transformer, original) end) + end + + test "transforms a list of blocks" do + assert Transform.transform_blocks(@blocks) + end + + test "raises when no transformer is configured" do + Application.put_env(:indexer, :block_transformer, nil) + + assert_raise ArgumentError, fn -> Transform.transform_blocks(@blocks) end + end + end +end diff --git a/apps/indexer/test/indexer/block/uncle/fetcher_test.exs b/apps/indexer/test/indexer/block/uncle/fetcher_test.exs index 970f1700a0..5c70683aa7 100644 --- a/apps/indexer/test/indexer/block/uncle/fetcher_test.exs +++ b/apps/indexer/test/indexer/block/uncle/fetcher_test.exs @@ -69,6 +69,9 @@ defmodule Indexer.Block.Uncle.FetcherTest do "number" => number_quantity, "parentHash" => "0x006edcaa1e6fde822908783bc4ef1ad3675532d542fce53537557391cfe34c3c", "size" => "0x243", + "receiptsRoot" => "0x0", + "sha3Uncles" => "0x0", + "stateRoot" => "0x0", "timestamp" => "0x5b437f41", "totalDifficulty" => "0x342337ffffffffffffffffffffffffed8d29bb", "transactions" => [ @@ -93,6 +96,7 @@ defmodule Indexer.Block.Uncle.FetcherTest do "value" => "0x0" } ], + "transactionsRoot" => "0x0", "uncles" => [uncle_uncle_hash_data] } } diff --git a/apps/indexer/test/indexer/block/util_test.exs b/apps/indexer/test/indexer/block/util_test.exs new file mode 100644 index 0000000000..f0228cb1be --- /dev/null +++ b/apps/indexer/test/indexer/block/util_test.exs @@ -0,0 +1,40 @@ +defmodule Indexer.Block.UtilTest do + use ExUnit.Case + + alias Indexer.Block.Util + + test "signer/1" do + data = %{ + difficulty: 1, + extra_data: + "0xd68301080d846765746886676f312e3130856c696e7578000000000000000000773ab2ca8f47904a14739ad80a75b71d9d29b9fff8b7ecdcb73efffa6f74122f17d304b5dc8e6e5f256c9474dd115c8d4dae31b7a3d409e5c3270f8fde41cd8c00", + gas_limit: 7_753_377, + gas_used: 1_810_195, + hash: "0x7004c895e812c55b0c2be8a46d72ca300a683dc27d1d7917ee7742d4d0359c1f", + logs_bloom: + "0x00000000000000020000000000002000000400000000000000000000000000000000000000000000040000080004000020000010000000000000000000000000000000000000000008000008000000000000000000200000000000000000000000000000020000000000000000000800000000000000804000000010080000000800000000000000000000000000000000000000000000800000000000080000000008000400000000404000000000000000000000000200000000000000000000000002000000000000001002000000000000002000000008000000000020000000000000000000000000000000000000000000000000400000800000000000", + miner_hash: "0x0000000000000000000000000000000000000000", + mix_hash: "0x0000000000000000000000000000000000000000000000000000000000000000", + nonce: "0x0000000000000000", + number: 2_848_394, + parent_hash: "0x20350fc367e19d3865be1ea7da72ab81f8f9941c43ac6bb24a34a0a7caa2f3df", + receipts_root: "0x6ade4ac1079ea50cfadcce2b75ffbe4f9b14bf69b4607bbf1739463076ca6246", + sha3_uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + size: 6437, + state_root: "0x23f63347851bcd109059d007d71e19c4f5e73b7f0862bebcd04458333a004d92", + timestamp: DateTime.from_unix!(1_534_796_040), + total_difficulty: 5_353_647, + transactions: [ + "0x7e3bb851fc74a436826d2af6b96e4db9484431811ef0d9c9e78370488d33d4e5", + "0x3976fd1e3d2a715c3cfcfde9bd3210798c26c017b8edb841d319227ecb3322fb", + "0xd8db124005bb8b6fda7b71fd56ac782552a66af58fe843ba3c4930423b87d1d2", + "0x10c1a1ca4d9f4b2bd5b89f7bbcbbc2d69e166fe23662b8db4f6beae0f50ac9fd", + "0xaa58a6545677c796a56b8bc874174c8cfd31a6c6e6ca3a87e086d4f66d52858a" + ], + transactions_root: "0xde8d25c0b9b54310128a21601331094b43f910f9f96102869c2e2dca94884bf4", + uncles: [] + } + + assert Util.signer(data) == "0xfc18cbc391de84dbd87db83b20935d3e89f5dd91" + end +end diff --git a/mix.lock b/mix.lock index 8c75e5a4c7..4a62ae5e91 100644 --- a/mix.lock +++ b/mix.lock @@ -32,6 +32,7 @@ "ex_cldr_units": {:hex, :ex_cldr_units, "1.1.1", "b3c7256709bdeb3740a5f64ce2bce659eb9cf4cc1afb4cf94aba033b4a18bc5f", [:mix], [{:ex_cldr, "~> 1.0", [hex: :ex_cldr, optional: false]}, {:ex_cldr_numbers, "~> 1.0", [hex: :ex_cldr_numbers, optional: false]}]}, "ex_doc": {:hex, :ex_doc, "0.19.1", "519bb9c19526ca51d326c060cb1778d4a9056b190086a8c6c115828eaccea6cf", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.7", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"}, "ex_machina": {:hex, :ex_machina, "2.2.1", "df84d0b23487aaa8570c35e586d7f9f197a7787e1121344a41d8832a7ea41edf", [:mix], [{:ecto, "~> 2.1", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"}, + "ex_rlp": {:hex, :ex_rlp, "0.3.1", "190554f7b26f79734fc5a772241eec14a71b2e83576e43f451479feb017013e9", [:mix], [], "hexpm"}, "exactor": {:hex, :exactor, "2.2.4", "5efb4ddeb2c48d9a1d7c9b465a6fffdd82300eb9618ece5d34c3334d5d7245b1", [:mix], []}, "excoveralls": {:git, "https://github.com/KronicDeth/excoveralls.git", "0a859b68851eeba9b43eba59fbc8f9098299cfe1", [branch: "circle-workflows"]}, "exjsx": {:hex, :exjsx, "4.0.0", "60548841e0212df401e38e63c0078ec57b33e7ea49b032c796ccad8cde794b5c", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, optional: false]}]}, @@ -50,7 +51,7 @@ "jsx": {:hex, :jsx, "2.8.3", "a05252d381885240744d955fbe3cf810504eb2567164824e19303ea59eef62cf", [:mix, :rebar3], []}, "junit_formatter": {:hex, :junit_formatter, "2.2.0", "da6093f0740c58a824f9585ebb7cb1b960efaecf48d1fa969e95d9c47c6b19dd", [:mix], [], "hexpm"}, "keccakf1600": {:hex, :keccakf1600_orig, "2.0.0", "0a7217ddb3ee8220d449bbf7575ec39d4e967099f220a91e3dfca4dbaef91963", [:rebar3], []}, - "libsecp256k1": {:hex, :libsecp256k1, "0.1.4", "42b7f76d8e32f85f578ccda0abfdb1afa0c5c231d1fd8aeab9cda352731a2d83", [:rebar3], []}, + "libsecp256k1": {:hex, :libsecp256k1, "0.1.10", "d27495e2b9851c7765129b76c53b60f5e275bd6ff68292c50536bf6b8d091a4d", [:make, :mix], [{:mix_erlang_tasks, "0.1.0", [hex: :mix_erlang_tasks, repo: "hexpm", optional: false]}], "hexpm"}, "logger_file_backend": {:hex, :logger_file_backend, "0.0.10", "876f9f84ae110781207c54321ffbb62bebe02946fe3c13f0d7c5f5d8ad4fa910", [:mix], [], "hexpm"}, "makeup": {:hex, :makeup, "0.5.5", "9e08dfc45280c5684d771ad58159f718a7b5788596099bdfb0284597d368a882", [:mix], [{:nimble_parsec, "~> 0.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"}, "makeup_elixir": {:hex, :makeup_elixir, "0.10.0", "0f09c2ddf352887a956d84f8f7e702111122ca32fbbc84c2f0569b8b65cbf7fa", [:mix], [{:makeup, "~> 0.5.5", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"}, @@ -59,6 +60,7 @@ "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], []}, "mime": {:hex, :mime, "1.3.0", "5e8d45a39e95c650900d03f897fbf99ae04f60ab1daa4a34c7a20a5151b7a5fe", [:mix], [], "hexpm"}, "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], []}, + "mix_erlang_tasks": {:hex, :mix_erlang_tasks, "0.1.0", "36819fec60b80689eb1380938675af215565a89320a9e29c72c70d97512e4649", [:mix], [], "hexpm"}, "mochiweb": {:hex, :mochiweb, "2.18.0", "eb55f1db3e6e960fac4e6db4e2db9ec3602cc9f30b86cd1481d56545c3145d2e", [:rebar3], [], "hexpm"}, "mock": {:hex, :mock, "0.3.2", "e98e998fd76c191c7e1a9557c8617912c53df3d4a6132f561eb762b699ef59fa", [:mix], [{:meck, "~> 0.8.8", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm"}, "mox": {:hex, :mox, "0.4.0", "7f120840f7d626184a3d65de36189ca6f37d432e5d63acd80045198e4c5f7e6e", [:mix], [], "hexpm"},