From 88c61f2f84081c2fe25d31c8c1bd9673cc4c7ee4 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Wed, 13 May 2020 14:41:02 +0300 Subject: [PATCH 01/51] Verify contract using creation tx input --- CHANGELOG.md | 1 + .../api/rpc/contract_controller_test.exs | 5 +- ...erified_smart_contract_controller_test.exs | 3 + .../smart_contract/contract_with_lib.json | 1 + apps/explorer/lib/explorer/chain.ex | 37 ++ .../chain/cache/pending_transactions.ex | 42 -- .../smart_contract/solidity/code_compiler.ex | 9 +- .../lib/explorer/smart_contract/verifier.ex | 93 ++-- .../verifier/constructor_arguments.ex | 97 ++++- apps/explorer/package-lock.json | 52 +-- apps/explorer/package.json | 3 +- apps/explorer/priv/compile_solc.js | 3 +- .../smart_contract/publisher_test.exs | 11 +- .../solidity/code_compiler_test.exs | 4 +- .../explorer/smart_contract/verifier_test.exs | 130 +++++- apps/explorer/test/support/factory.ex | 2 + .../smart_contract/compiler_tests.json | 2 + .../smart_contract/contract_from_factory.sol | 18 + .../smart_contract/contract_with_lib.json | 4 +- .../smart_contract/contract_with_lib.sol | 25 ++ .../solidity_5.11_new_whisper_metadata.json | 1 + .../solidity_5.11_new_whisper_metadata.sol | 412 ++++++++++++++++++ 22 files changed, 801 insertions(+), 154 deletions(-) delete mode 100644 apps/explorer/lib/explorer/chain/cache/pending_transactions.ex create mode 100644 apps/explorer/test/support/fixture/smart_contract/contract_from_factory.sol create mode 100644 apps/explorer/test/support/fixture/smart_contract/contract_with_lib.sol create mode 100644 apps/explorer/test/support/fixture/smart_contract/solidity_5.11_new_whisper_metadata.sol diff --git a/CHANGELOG.md b/CHANGELOG.md index 287e54992b..475e409e26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Features ### Fixes +- [#3106](https://github.com/poanetwork/blockscout/pull/3106) - Verify contract using creation tx input ### Chore diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs index b00d457426..98901ee5cd 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs @@ -609,6 +609,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do contract_code_info = Factory.contract_code_info() contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) + insert(:transaction, created_contract_address_hash: contract_address.hash, input: contract_code_info.tx_input) params = %{ "module" => "contract", @@ -658,10 +659,12 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do "name" => name, "optimize" => optimize, "contract" => contract_source_code, - "expected_bytecode" => expected_bytecode + "expected_bytecode" => expected_bytecode, + "tx_input" => tx_input } = contract_data contract_address = insert(:contract_address, contract_code: "0x" <> expected_bytecode) + insert(:transaction, created_contract_address_hash: contract_address.hash, input: "0x" <> tx_input) params = %{ "module" => "contract", diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v1/verified_smart_contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v1/verified_smart_contract_controller_test.exs index d799304324..dd22a12559 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v1/verified_smart_contract_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v1/verified_smart_contract_controller_test.exs @@ -12,6 +12,7 @@ defmodule BlockScoutWeb.API.V1.VerifiedControllerTest do contract_code_info = Factory.contract_code_info() contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) + insert(:transaction, created_contract_address_hash: contract_address.hash, input: contract_code_info.tx_input) params = %{ "address_hash" => to_string(contract_address.hash), @@ -40,10 +41,12 @@ defmodule BlockScoutWeb.API.V1.VerifiedControllerTest do "name" => name, "optimize" => optimize, "contract" => contract_source_code, + "tx_input" => tx_input, "expected_bytecode" => expected_bytecode } = contract_data contract_address = insert(:contract_address, contract_code: "0x" <> expected_bytecode) + insert(:transaction, created_contract_address_hash: contract_address.hash, input: "0x" <> tx_input) params = %{ "address_hash" => to_string(contract_address.hash), diff --git a/apps/block_scout_web/test/support/fixture/smart_contract/contract_with_lib.json b/apps/block_scout_web/test/support/fixture/smart_contract/contract_with_lib.json index aba02b0ec4..59beb157b6 100644 --- a/apps/block_scout_web/test/support/fixture/smart_contract/contract_with_lib.json +++ b/apps/block_scout_web/test/support/fixture/smart_contract/contract_with_lib.json @@ -3,6 +3,7 @@ "compiler_version": "v0.5.11+commit.c082d0b4", "contract": "pragma solidity 0.5.11;library BadSafeMath { function add(uint256 a, uint256 b) public pure returns (uint256) { uint256 c = a + 2 * b; require(c >= a, \"SafeMath: addition overflow\"); return c; }}contract SimpleStorage { uint256 storedData = 10; using BadSafeMath for uint256; function increment(uint256 x) public { storedData = storedData.add(x); } function set(uint256 x) public { storedData = x; } function get() public view returns (uint256) { return storedData; }}", "expected_bytecode": "608060405234801561001057600080fd5b50600436106100415760003560e01c806360fe47b1146100465780636d4ce63c146100655780637cf5dab01461007f575b600080fd5b6100636004803603602081101561005c57600080fd5b503561009c565b005b61006d6100a1565b60408051918252519081900360200190f35b6100636004803603602081101561009557600080fd5b50356100a7565b600055565b60005490565b600054733662e222908fa35f013bee37695d0510098b6d7363771602f79091836040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b15801561010257600080fd5b505af4158015610116573d6000803e3d6000fd5b505050506040513d602081101561012c57600080fd5b50516000555056fea265627a7a723158203e59bfb9a5a2e55d38231922c86d8b2ec9b66cb2f6595613674bc4e15290b60764736f6c634300050b0032", + "tx_input": "6080604052600a60005534801561001557600080fd5b50610169806100256000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806360fe47b1146100465780636d4ce63c146100655780637cf5dab01461007f575b600080fd5b6100636004803603602081101561005c57600080fd5b503561009c565b005b61006d6100a1565b60408051918252519081900360200190f35b6100636004803603602081101561009557600080fd5b50356100a7565b600055565b60005490565b600054733662e222908fa35f013bee37695d0510098b6d7363771602f79091836040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b15801561010257600080fd5b505af4158015610116573d6000803e3d6000fd5b505050506040513d602081101561012c57600080fd5b50516000555056fea265627a7a723158207809bc828bbcd3de3e6b6483facb0c5902901fc8827283c749c8ea0702eb871f64736f6c634300050b0032", "external_libraries": { "BadSafeMath": "0x3662e222908fa35f013bee37695d0510098b6d73" }, diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 371fdd2beb..bf81d2edee 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -2716,6 +2716,40 @@ defmodule Explorer.Chain do |> Data.to_string() end + def smart_contract_creation_tx_bytecode(address_hash) do + creation_tx_query = + from( + tx in Transaction, + where: tx.created_contract_address_hash == ^address_hash, + select: tx.input + ) + + tx_input = + creation_tx_query + |> Repo.one() + + if tx_input do + Data.to_string(tx_input) + else + creation_int_tx_query = + from( + itx in InternalTransaction, + where: itx.created_contract_address_hash == ^address_hash, + select: itx.init + ) + + itx_init_code = + creation_int_tx_query + |> Repo.one() + + if itx_init_code do + Data.to_string(itx_init_code) + else + nil + end + end + end + @doc """ Checks if an address is a contract """ @@ -2777,6 +2811,9 @@ defmodule Explorer.Chain do transaction.contracts_creation_internal_transaction && transaction.contracts_creation_internal_transaction.input -> Data.to_string(transaction.contracts_creation_internal_transaction.input) + transaction.contracts_creation_internal_transaction && transaction.contracts_creation_internal_transaction.init -> + Data.to_string(transaction.contracts_creation_internal_transaction.init) + transaction.contracts_creation_transaction && transaction.contracts_creation_transaction.input -> Data.to_string(transaction.contracts_creation_transaction.input) diff --git a/apps/explorer/lib/explorer/chain/cache/pending_transactions.ex b/apps/explorer/lib/explorer/chain/cache/pending_transactions.ex deleted file mode 100644 index 686b5e7372..0000000000 --- a/apps/explorer/lib/explorer/chain/cache/pending_transactions.ex +++ /dev/null @@ -1,42 +0,0 @@ -defmodule Explorer.Chain.Cache.PendingTransactions do - @moduledoc """ - Caches the latest pending transactions - """ - - alias Explorer.Chain.Transaction - - use Explorer.Chain.OrderedCache, - name: :pending_transactions, - max_size: 51, - preloads: [ - :block, - created_contract_address: :names, - from_address: :names, - to_address: :names, - token_transfers: :token, - token_transfers: :from_address, - token_transfers: :to_address - ], - ttl_check_interval: Application.get_env(:explorer, __MODULE__)[:ttl_check_interval], - global_ttl: Application.get_env(:explorer, __MODULE__)[:global_ttl] - - @type element :: Transaction.t() - - @type id :: {non_neg_integer(), non_neg_integer()} - - def element_to_id(%Transaction{inserted_at: inserted_at, hash: hash}) do - {inserted_at, hash} - end - - def update_pending(transactions) when is_nil(transactions), do: :ok - - def update_pending(transactions) do - transactions - |> Enum.filter(&pending?(&1)) - |> update() - end - - defp pending?(transaction) do - is_nil(transaction.block_hash) and (is_nil(transaction.error) or transaction.error != "dropped/replaced") - end -end diff --git a/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex b/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex index c5c1b01930..3b83cafed6 100644 --- a/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex +++ b/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex @@ -59,7 +59,7 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do "type" => "function" } ], - "bytecode" => "6080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a72305820834bdab406d80509618957aa1a5ad1a4b77f4f1149078675940494ebe5b4147b0029", + "bytecode" => "608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a72305820834bdab406d80509618957aa1a5ad1a4b77f4f1149078675940494ebe5b4147b0029", "name" => "SimpleStorage" } } @@ -92,19 +92,18 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do [ Application.app_dir(:explorer, "priv/compile_solc.js"), create_source_file(code), - compiler_version, + path, optimize_value(optimize), optimization_runs, @new_contract_name, external_libs_string, - checked_evm_version, - path + checked_evm_version ] ) with {:ok, decoded} <- Jason.decode(response), {:ok, contracts} <- get_contracts(decoded), - %{"abi" => abi, "evm" => %{"deployedBytecode" => %{"object" => bytecode}}} <- + %{"abi" => abi, "evm" => %{"bytecode" => %{"object" => bytecode}}} <- get_contract_info(contracts, name) do {:ok, %{"abi" => abi, "bytecode" => bytecode, "name" => name}} else diff --git a/apps/explorer/lib/explorer/smart_contract/verifier.ex b/apps/explorer/lib/explorer/smart_contract/verifier.ex index 884870a4a1..979fb1128d 100644 --- a/apps/explorer/lib/explorer/smart_contract/verifier.ex +++ b/apps/explorer/lib/explorer/smart_contract/verifier.ex @@ -11,6 +11,13 @@ defmodule Explorer.SmartContract.Verifier do alias Explorer.SmartContract.Solidity.CodeCompiler alias Explorer.SmartContract.Verifier.ConstructorArguments + @metadata_hash_prefix_0_4_23 "a165627a7a72305820" + @metadata_hash_prefix_0_5_10 "a265627a7a72305820" + @metadata_hash_prefix_0_5_11 "a265627a7a72315820" + @metadata_hash_prefix_0_6_0 "a264697066735822" + + @metadata_hash_common_suffix "64736f6c6343" + def evaluate_authenticity(_, %{"name" => ""}), do: {:error, :name} def evaluate_authenticity(_, %{"contract_source_code" => ""}), @@ -76,13 +83,16 @@ defmodule Explorer.SmartContract.Verifier do contract_source_code, contract_name ) do - generated_bytecode = extract_bytecode(bytecode) + %{"metadata_hash" => _generated_metadata_hash, "bytecode" => generated_bytecode} = + extract_bytecode_and_metadata_hash(bytecode) - "0x" <> blockchain_bytecode = + "0x" <> blockchain_created_tx_input = address_hash - |> Chain.smart_contract_bytecode() + |> Chain.smart_contract_creation_tx_bytecode() + + %{"metadata_hash" => _metadata_hash, "bytecode" => blockchain_bytecode_without_whisper} = + extract_bytecode_and_metadata_hash(blockchain_created_tx_input) - blockchain_bytecode_without_whisper = extract_bytecode(blockchain_bytecode) empty_constructor_arguments = arguments_data == "" or arguments_data == nil cond do @@ -136,40 +146,55 @@ defmodule Explorer.SmartContract.Verifier do For more information on the swarm hash, check out: https://solidity.readthedocs.io/en/v0.5.3/metadata.html#encoding-of-the-metadata-hash-in-the-bytecode """ - def extract_bytecode("0x" <> code) do - "0x" <> extract_bytecode(code) + def extract_bytecode_and_metadata_hash("0x" <> code) do + %{"metadata_hash" => metadata_hash, "bytecode" => bytecode} = extract_bytecode_and_metadata_hash(code) + %{"metadata_hash" => metadata_hash, "bytecode" => "0x" <> bytecode} end - def extract_bytecode(code) do - do_extract_bytecode([], String.downcase(code)) + def extract_bytecode_and_metadata_hash(code) do + do_extract_bytecode_and_metadata_hash([], String.downcase(code), "") end - defp do_extract_bytecode(extracted, remaining) do + defp do_extract_bytecode_and_metadata_hash(extracted, remaining, metadata_hash) do case remaining do <<>> -> - extracted - |> Enum.reverse() - |> :binary.list_to_bin() + bytecode = + extracted + |> Enum.reverse() + |> :binary.list_to_bin() + + %{"metadata_hash" => metadata_hash, "bytecode" => bytecode} - "a165627a7a72305820" <> <<_::binary-size(64)>> <> "0029" <> _constructor_arguments -> - extracted - |> Enum.reverse() - |> :binary.list_to_bin() + @metadata_hash_prefix_0_4_23 <> <> <> "0029" <> _constructor_arguments -> + bytecode = + extracted + |> Enum.reverse() + |> :binary.list_to_bin() + + %{"metadata_hash" => metadata_hash, "bytecode" => bytecode} # Solidity >= 0.5.9; https://github.com/ethereum/solidity/blob/aa4ee3a1559ebc0354926af962efb3fcc7dc15bd/docs/metadata.rst - "a265627a7a72305820" <> - <<_::binary-size(64)>> <> "64736f6c6343" <> <<_::binary-size(6)>> <> "0032" <> _constructor_arguments -> - extracted - |> Enum.reverse() - |> :binary.list_to_bin() + @metadata_hash_prefix_0_5_10 <> + <> <> + @metadata_hash_common_suffix <> <<_::binary-size(6)>> <> "0032" <> _constructor_arguments -> + bytecode = + extracted + |> Enum.reverse() + |> :binary.list_to_bin() + + %{"metadata_hash" => metadata_hash, "bytecode" => bytecode} # Solidity >= 0.5.11 https://github.com/ethereum/solidity/blob/develop/Changelog.md#0511-2019-08-12 # Metadata: Update the swarm hash to the current specification, changes bzzr0 to bzzr1 and urls to use bzz-raw:// - "a265627a7a72315820" <> - <<_::binary-size(64)>> <> "64736f6c6343" <> <<_::binary-size(6)>> <> "0032" <> _constructor_arguments -> - extracted - |> Enum.reverse() - |> :binary.list_to_bin() + @metadata_hash_prefix_0_5_11 <> + <> <> + @metadata_hash_common_suffix <> <<_::binary-size(6)>> <> "0032" <> _constructor_arguments -> + bytecode = + extracted + |> Enum.reverse() + |> :binary.list_to_bin() + + %{"metadata_hash" => metadata_hash, "bytecode" => bytecode} # Solidity >= 0.6.0 https://github.com/ethereum/solidity/blob/develop/Changelog.md#060-2019-12-17 # https://github.com/ethereum/solidity/blob/26b700771e9cc9c956f0503a05de69a1be427963/docs/metadata.rst#encoding-of-the-metadata-hash-in-the-bytecode @@ -181,14 +206,18 @@ defmodule Explorer.SmartContract.Verifier do # 0x00 0x32 # Note: there is a bug in the docs. Instead of 0x32, 0x33 should be used. # Fixing PR has been created https://github.com/ethereum/solidity/pull/8174 - "a264697066735822" <> - <<_::binary-size(68)>> <> "64736f6c6343" <> <<_::binary-size(6)>> <> "0033" <> _constructor_arguments -> - extracted - |> Enum.reverse() - |> :binary.list_to_bin() + @metadata_hash_prefix_0_6_0 <> + <> <> + @metadata_hash_common_suffix <> <<_::binary-size(6)>> <> "0033" <> _constructor_arguments -> + bytecode = + extracted + |> Enum.reverse() + |> :binary.list_to_bin() + + %{"metadata_hash" => metadata_hash, "bytecode" => bytecode} <> <> rest -> - do_extract_bytecode([next | extracted], rest) + do_extract_bytecode_and_metadata_hash([next | extracted], rest, metadata_hash) end end diff --git a/apps/explorer/lib/explorer/smart_contract/verifier/constructor_arguments.ex b/apps/explorer/lib/explorer/smart_contract/verifier/constructor_arguments.ex index f5676784ae..a647034094 100644 --- a/apps/explorer/lib/explorer/smart_contract/verifier/constructor_arguments.ex +++ b/apps/explorer/lib/explorer/smart_contract/verifier/constructor_arguments.ex @@ -5,6 +5,13 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do alias ABI.{FunctionSelector, TypeDecoder} alias Explorer.Chain + @metadata_hash_prefix_0_4_23 "a165627a7a72305820" + @metadata_hash_prefix_0_5_10 "a265627a7a72305820" + @metadata_hash_prefix_0_5_11 "a265627a7a72315820" + @metadata_hash_prefix_0_6_0 "a264697066735822" + + @metadata_hash_common_suffix "64736f6c6343" + def verify(address_hash, contract_code, arguments_data, contract_source_code, contract_name) do arguments_data = arguments_data |> String.trim_trailing() |> String.trim_leading("0x") @@ -34,19 +41,39 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do defp extract_constructor_arguments(code, check_func, contract_source_code, contract_name) do case code do # Solidity ~ 4.23 # https://solidity.readthedocs.io/en/v0.4.23/metadata.html - "a165627a7a72305820" <> <<_::binary-size(64)>> <> "0029" <> constructor_arguments -> - extract_constructor_arguments_check_func(constructor_arguments, check_func, contract_source_code, contract_name) + @metadata_hash_prefix_0_4_23 <> <<_::binary-size(64)>> <> "0029" <> constructor_arguments -> + split_constructor_arguments_and_extract_check_func( + constructor_arguments, + check_func, + contract_source_code, + contract_name, + @metadata_hash_prefix_0_4_23 + ) # Solidity >= 0.5.10 https://solidity.readthedocs.io/en/v0.5.10/metadata.html - "a265627a7a72305820" <> - <<_::binary-size(64)>> <> "64736f6c6343" <> <<_::binary-size(6)>> <> "0032" <> constructor_arguments -> - extract_constructor_arguments_check_func(constructor_arguments, check_func, contract_source_code, contract_name) + @metadata_hash_prefix_0_5_10 <> + <<_::binary-size(64)>> <> + @metadata_hash_common_suffix <> <<_::binary-size(6)>> <> "0032" <> constructor_arguments -> + split_constructor_arguments_and_extract_check_func( + constructor_arguments, + check_func, + contract_source_code, + contract_name, + @metadata_hash_prefix_0_5_10 + ) # Solidity >= 0.5.11 https://github.com/ethereum/solidity/blob/develop/Changelog.md#0511-2019-08-12 # Metadata: Update the swarm hash to the current specification, changes bzzr0 to bzzr1 and urls to use bzz-raw:// - "a265627a7a72315820" <> - <<_::binary-size(64)>> <> "64736f6c6343" <> <<_::binary-size(6)>> <> "0032" <> constructor_arguments -> - extract_constructor_arguments_check_func(constructor_arguments, check_func, contract_source_code, contract_name) + @metadata_hash_prefix_0_5_11 <> + <<_::binary-size(64)>> <> + @metadata_hash_common_suffix <> <<_::binary-size(6)>> <> "0032" <> constructor_arguments -> + split_constructor_arguments_and_extract_check_func( + constructor_arguments, + check_func, + contract_source_code, + contract_name, + @metadata_hash_prefix_0_5_11 + ) # Solidity >= 0.6.0 https://github.com/ethereum/solidity/blob/develop/Changelog.md#060-2019-12-17 # https://github.com/ethereum/solidity/blob/26b700771e9cc9c956f0503a05de69a1be427963/docs/metadata.rst#encoding-of-the-metadata-hash-in-the-bytecode @@ -58,9 +85,16 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do # 0x00 0x32 # Note: there is a bug in the docs. Instead of 0x32, 0x33 should be used. # Fixing PR has been created https://github.com/ethereum/solidity/pull/8174 - "a264697066735822" <> - <<_::binary-size(68)>> <> "64736f6c6343" <> <<_::binary-size(6)>> <> "0033" <> constructor_arguments -> - extract_constructor_arguments_check_func(constructor_arguments, check_func, contract_source_code, contract_name) + @metadata_hash_prefix_0_6_0 <> + <<_::binary-size(68)>> <> + @metadata_hash_common_suffix <> <<_::binary-size(6)>> <> "0033" <> constructor_arguments -> + split_constructor_arguments_and_extract_check_func( + constructor_arguments, + check_func, + contract_source_code, + contract_name, + @metadata_hash_prefix_0_6_0 + ) <<>> -> check_func.("") @@ -70,6 +104,35 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do end end + defp is_constructor_arguments_still_has_metadata_prefix(constructor_arguments, prefix) do + constructor_arguments_parts = + constructor_arguments + |> String.split(prefix) + |> Enum.count() + + constructor_arguments_parts > 1 + end + + defp split_constructor_arguments_and_extract_check_func( + constructor_arguments, + check_func, + contract_source_code, + contract_name, + metadata_hash_prefix + ) do + if is_constructor_arguments_still_has_metadata_prefix(constructor_arguments, metadata_hash_prefix) do + <<_::binary-size(2)>> <> rest = constructor_arguments + extract_constructor_arguments(rest, check_func, contract_source_code, contract_name) + else + extract_constructor_arguments_check_func( + constructor_arguments, + check_func, + contract_source_code, + contract_name + ) + end + end + defp extract_constructor_arguments_check_func(constructor_arguments, check_func, contract_source_code, contract_name) do constructor_arguments = remove_require_messages_from_constructor_arguments(contract_source_code, constructor_arguments, contract_name) @@ -195,11 +258,17 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do def find_all_requires(contract_source_code) do if contract_source_code do - [_ | requires] = String.split(contract_source_code, "require") + trimmed_source_code = + contract_source_code + |> String.replace(~r/ +/, " ") + |> String.replace("require(", "require (") + + [_ | requires] = + trimmed_source_code + |> String.split("require (") Enum.reduce(requires, [], fn right_from_require, requires_list -> - [_ | [right_from_require_inside]] = String.split(right_from_require, "(", parts: 2) - [require_content | _] = String.split(right_from_require_inside, ");", parts: 2) + [require_content | _] = String.split(right_from_require, ");", parts: 2) [require_content | requires_list] end) else diff --git a/apps/explorer/package-lock.json b/apps/explorer/package-lock.json index 2c3c20aed5..b1ab348051 100644 --- a/apps/explorer/package-lock.json +++ b/apps/explorer/package-lock.json @@ -8,14 +8,6 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, - "bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "requires": { - "file-uri-to-path": "1.0.0" - } - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -26,9 +18,9 @@ } }, "command-exists": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.8.tgz", - "integrity": "sha512-PM54PkseWbiiD/mMsbvW351/u+dafwTJ0ye2qB60G1aGQP9j3xK2gmMDc+R34L3nDtx4qMCitXT75mkbkGJDLw==" + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", + "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==" }, "commander": { "version": "3.0.2", @@ -40,11 +32,6 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" - }, "fs-extra": { "version": "0.30.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", @@ -76,9 +63,9 @@ } }, "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" }, "inflight": { "version": "1.0.6", @@ -107,17 +94,6 @@ "graceful-fs": "^4.1.6" } }, - "keccak": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/keccak/-/keccak-2.0.0.tgz", - "integrity": "sha512-rKe/lRr0KGhjoz97cwg+oeT1Rj/Y4cjae6glArioUC8JBF9ROGZctwIaaruM7d7naovME4Q8WcQSO908A8qcyQ==", - "requires": { - "bindings": "^1.2.1", - "inherits": "^2.0.3", - "nan": "^2.2.1", - "safe-buffer": "^5.1.0" - } - }, "klaw": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", @@ -139,11 +115,6 @@ "brace-expansion": "^1.1.7" } }, - "nan": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", - "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" - }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -175,20 +146,15 @@ "glob": "^7.1.3" } }, - "safe-buffer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" - }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" }, "solc": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/solc/-/solc-0.6.1.tgz", - "integrity": "sha512-iKqNYps2p++x8L9sBg7JeAJb7EmW8VJ/2asAzwlLYcUhj86AzuWLe94UTSQHv1SSCCj/x6lya8twvXkZtlTbIQ==", + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.6.7.tgz", + "integrity": "sha512-a3iocjS1yGzw3Wy7jkqSLX3Vg1lMDCyoKZoVfpOagRGWkh37f11BrcUDO8f73rjdpw2WUBSLJtTQ26i52/0JOg==", "requires": { "command-exists": "^1.2.8", "commander": "3.0.2", diff --git a/apps/explorer/package.json b/apps/explorer/package.json index 8fdead7f1d..995b1fb7d2 100644 --- a/apps/explorer/package.json +++ b/apps/explorer/package.json @@ -13,7 +13,6 @@ }, "scripts": {}, "dependencies": { - "keccak": "^2.0.0", - "solc": "^0.6.1" + "solc": "^0.6.7" } } diff --git a/apps/explorer/priv/compile_solc.js b/apps/explorer/priv/compile_solc.js index e6ba69c300..98f1b8bcfc 100755 --- a/apps/explorer/priv/compile_solc.js +++ b/apps/explorer/priv/compile_solc.js @@ -1,13 +1,12 @@ #!/usr/bin/env node var sourceCodePath = process.argv[2]; -var version = process.argv[3]; +var compilerVersionPath = process.argv[3]; var optimize = process.argv[4]; var optimizationRuns = parseInt(process.argv[5], 10); var newContractName = process.argv[6]; var externalLibraries = JSON.parse(process.argv[7]) var evmVersion = process.argv[8]; -var compilerVersionPath = process.argv[9]; var solc = require('solc') var compilerSnapshot = require(compilerVersionPath); diff --git a/apps/explorer/test/explorer/smart_contract/publisher_test.exs b/apps/explorer/test/explorer/smart_contract/publisher_test.exs index 79fbe55f63..4933a7a4e5 100644 --- a/apps/explorer/test/explorer/smart_contract/publisher_test.exs +++ b/apps/explorer/test/explorer/smart_contract/publisher_test.exs @@ -14,6 +14,7 @@ defmodule Explorer.SmartContract.PublisherTest do contract_code_info = Factory.contract_code_info() contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) + insert(:transaction, created_contract_address_hash: contract_address.hash, input: contract_code_info.tx_input) valid_attrs = %{ "contract_source_code" => contract_code_info.source_code, @@ -44,12 +45,15 @@ defmodule Explorer.SmartContract.PublisherTest do bytecode = "0x608060405234801561001057600080fd5b50600436106100a95760003560e01c80633177029f116100715780633177029f1461025f57806354fd4d50146102c557806370a082311461034857806395d89b41146103a0578063a9059cbb14610423578063dd62ed3e14610489576100a9565b806306fdde03146100ae578063095ea7b31461013157806318160ddd1461019757806323b872dd146101b5578063313ce5671461023b575b600080fd5b6100b6610501565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f65780820151818401526020810190506100db565b50505050905090810190601f1680156101235780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61017d6004803603604081101561014757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061059f565b604051808215151515815260200191505060405180910390f35b61019f610691565b6040518082815260200191505060405180910390f35b610221600480360360608110156101cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610696565b604051808215151515815260200191505060405180910390f35b61024361090f565b604051808260ff1660ff16815260200191505060405180910390f35b6102ab6004803603604081101561027557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610922565b604051808215151515815260200191505060405180910390f35b6102cd610a14565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561030d5780820151818401526020810190506102f2565b50505050905090810190601f16801561033a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61038a6004803603602081101561035e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ab2565b6040518082815260200191505060405180910390f35b6103a8610afa565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103e85780820151818401526020810190506103cd565b50505050905090810190601f1680156104155780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61046f6004803603604081101561043957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610b98565b604051808215151515815260200191505060405180910390f35b6104eb6004803603604081101561049f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cfe565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105975780601f1061056c57610100808354040283529160200191610597565b820191906000526020600020905b81548152906001019060200180831161057a57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b600090565b6000816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610762575081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410155b801561076e5750600082115b1561090357816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610908565b600090505b9392505050565b600460009054906101000a900460ff1681565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60068054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610aaa5780601f10610a7f57610100808354040283529160200191610aaa565b820191906000526020600020905b815481529060010190602001808311610a8d57829003601f168201915b505050505081565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b905780601f10610b6557610100808354040283529160200191610b90565b820191906000526020600020905b815481529060010190602001808311610b7357829003601f168201915b505050505081565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610be85750600082115b15610cf357816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610cf8565b600090505b92915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490509291505056fea265627a7a72305820fe0ba5210ac95870683c2cb054304b04565703bd16c7d7e956df694c9643c6d264736f6c63430005090032" + input = + "0x60806040526040518060400160405280600381526020017f302e3100000000000000000000000000000000000000000000000000000000008152506006908051906020019062000051929190620001e2565b503480156200005f57600080fd5b506040516200105b3803806200105b833981810160405260808110156200008557600080fd5b81019080805190602001909291908051640100000000811115620000a857600080fd5b82810190506020810184811115620000bf57600080fd5b8151856001820283011164010000000082111715620000dd57600080fd5b50509291906020018051906020019092919080516401000000008111156200010457600080fd5b828101905060208101848111156200011b57600080fd5b81518560018202830111640100000000821117156200013957600080fd5b5050929190505050836000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550836002819055508260039080519060200190620001a3929190620001e2565b5081600460006101000a81548160ff021916908360ff1602179055508060059080519060200190620001d7929190620001e2565b505050505062000291565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200022557805160ff191683800117855562000256565b8280016001018555821562000256579182015b828111156200025557825182559160200191906001019062000238565b5b50905062000265919062000269565b5090565b6200028e91905b808211156200028a57600081600090555060010162000270565b5090565b90565b610dba80620002a16000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c80633177029f116100715780633177029f1461025f57806354fd4d50146102c557806370a082311461034857806395d89b41146103a0578063a9059cbb14610423578063dd62ed3e14610489576100a9565b806306fdde03146100ae578063095ea7b31461013157806318160ddd1461019757806323b872dd146101b5578063313ce5671461023b575b600080fd5b6100b6610501565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f65780820151818401526020810190506100db565b50505050905090810190601f1680156101235780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61017d6004803603604081101561014757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061059f565b604051808215151515815260200191505060405180910390f35b61019f610691565b6040518082815260200191505060405180910390f35b610221600480360360608110156101cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610696565b604051808215151515815260200191505060405180910390f35b61024361090f565b604051808260ff1660ff16815260200191505060405180910390f35b6102ab6004803603604081101561027557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610922565b604051808215151515815260200191505060405180910390f35b6102cd610a14565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561030d5780820151818401526020810190506102f2565b50505050905090810190601f16801561033a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61038a6004803603602081101561035e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ab2565b6040518082815260200191505060405180910390f35b6103a8610afa565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103e85780820151818401526020810190506103cd565b50505050905090810190601f1680156104155780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61046f6004803603604081101561043957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610b98565b604051808215151515815260200191505060405180910390f35b6104eb6004803603604081101561049f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cfe565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105975780601f1061056c57610100808354040283529160200191610597565b820191906000526020600020905b81548152906001019060200180831161057a57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b600090565b6000816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610762575081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410155b801561076e5750600082115b1561090357816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610908565b600090505b9392505050565b600460009054906101000a900460ff1681565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60068054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610aaa5780601f10610a7f57610100808354040283529160200191610aaa565b820191906000526020600020905b815481529060010190602001808311610a8d57829003601f168201915b505050505081565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b905780601f10610b6557610100808354040283529160200191610b90565b820191906000526020600020905b815481529060010190602001808311610b7357829003601f168201915b505050505081565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610be85750600082115b15610cf357816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610cf8565b600090505b92915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490509291505056fea265627a7a72305820c63b805fe76ce64d7ba5aedfa99bbfa5b13cc8c2fabad31b2364f8ae421cd40c64736f6c63430005090032" + contract_address = insert(:contract_address, contract_code: bytecode) :transaction |> insert( created_contract_address_hash: contract_address.hash, - input: bytecode <> expected_constructor_arguments + input: input <> expected_constructor_arguments ) |> with_block() @@ -70,6 +74,7 @@ defmodule Explorer.SmartContract.PublisherTest do contract_code_info = Factory.contract_code_info() contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) + insert(:transaction, created_contract_address_hash: contract_address.hash, input: contract_code_info.tx_input) valid_attrs = %{ "contract_source_code" => contract_code_info.source_code, @@ -106,7 +111,7 @@ defmodule Explorer.SmartContract.PublisherTest do :transaction |> insert( created_contract_address_hash: contract_address.hash, - input: contract_code_info.bytecode <> constructor_arguments + input: contract_code_info.tx_input <> constructor_arguments ) |> with_block() @@ -142,8 +147,10 @@ defmodule Explorer.SmartContract.PublisherTest do optimize = contract_data["optimize"] contract = contract_data["contract"] expected_bytecode = contract_data["expected_bytecode"] + tx_input = contract_data["tx_input"] contract_address = insert(:contract_address, contract_code: "0x" <> expected_bytecode) + insert(:transaction, created_contract_address_hash: contract_address.hash, input: "0x" <> tx_input) params = %{ "contract_source_code" => contract, diff --git a/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs b/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs index 29674274ca..270684c8d1 100644 --- a/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs +++ b/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs @@ -92,9 +92,9 @@ defmodule Explorer.SmartContract.Solidity.CodeCompilerTest do ) clean_result = remove_init_data_and_whisper_data(result["bytecode"]) - expected_result = remove_init_data_and_whisper_data(compiler_test["expected_bytecode"]) + expected_result = remove_init_data_and_whisper_data(compiler_test["tx_input"]) - assert clean_result == expected_result + assert expected_result == clean_result end) end diff --git a/apps/explorer/test/explorer/smart_contract/verifier_test.exs b/apps/explorer/test/explorer/smart_contract/verifier_test.exs index 1fdbc2331b..0500fc4a3d 100644 --- a/apps/explorer/test/explorer/smart_contract/verifier_test.exs +++ b/apps/explorer/test/explorer/smart_contract/verifier_test.exs @@ -16,6 +16,7 @@ defmodule Explorer.SmartContract.VerifierTest do contract_code_info: contract_code_info } do contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) + insert(:transaction, created_contract_address_hash: contract_address.hash, input: contract_code_info.tx_input) params = %{ "contract_source_code" => contract_code_info.source_code, @@ -41,8 +42,10 @@ defmodule Explorer.SmartContract.VerifierTest do optimize = contract_data["optimize"] contract = contract_data["contract"] expected_bytecode = contract_data["expected_bytecode"] + tx_input = contract_data["tx_input"] contract_address = insert(:contract_address, contract_code: "0x" <> expected_bytecode) + insert(:transaction, created_contract_address_hash: contract_address.hash, input: "0x" <> tx_input) params = %{ "contract_source_code" => contract, @@ -68,9 +71,16 @@ defmodule Explorer.SmartContract.VerifierTest do contract = contract_data["contract"] expected_bytecode = contract_data["bytecode"] evm_version = contract_data["evm_version"] + input = contract_data["input"] contract_address = insert(:contract_address, contract_code: "0x" <> expected_bytecode) + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: "0x" <> input + ) + params = %{ "contract_source_code" => contract, "compiler_version" => compiler_version, @@ -101,7 +111,7 @@ defmodule Explorer.SmartContract.VerifierTest do :transaction |> insert( created_contract_address_hash: contract_address.hash, - input: contract_code_info.bytecode <> constructor_arguments + input: contract_code_info.tx_input <> constructor_arguments ) |> with_block() @@ -111,7 +121,7 @@ defmodule Explorer.SmartContract.VerifierTest do test "tries to compile with the latest evm version if wrong evm version was provided" do bytecode = - "0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063256fec88146100545780633fa4f245146100a9578063812600df146100d2575b600080fd5b341561005f57600080fd5b6100676100f5565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156100b457600080fd5b6100bc61011b565b6040518082815260200191505060405180910390f35b34156100dd57600080fd5b6100f36004808035906020019091905050610121565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b505600a165627a7a72305820b81379d1ae9d8e0fde05ee02b8bd170f43f8bd3d54da8b7ec203434a23a298980029" + "0x6060604052341561000f57600080fd5b604051602080610223833981016040528080519060200190919050505b8060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b505b61019d806100866000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063256fec88146100545780633fa4f245146100a9578063812600df146100d2575b600080fd5b341561005f57600080fd5b6100676100f5565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156100b457600080fd5b6100bc61011b565b6040518082815260200191505060405180910390f35b34156100dd57600080fd5b6100f36004808035906020019091905050610121565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b505600a165627a7a723058201de7017582ff17d45730bc9aedeac5b399399b71b188f42a164609c1b6f7f4760029" constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a" @@ -156,9 +166,10 @@ defmodule Explorer.SmartContract.VerifierTest do test "verifies a library" do bytecode = - "0x7349f540c22cba15c47a08c235e20081474201a742301460806040526004361060335760003560e01c8063c2985578146038575b600080fd5b603e60b0565b6040805160208082528351818301528351919283929083019185019080838360005b8381101560765781810151838201526020016060565b50505050905090810190601f16801560a25780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b604080518082019091526003815262666f6f60e81b60208201529056fea265627a7a72315820174b282a3ef3b9778d79fbc2e4c36bc939c54dfaaaa51d3122ee6e648093844c64736f6c634300050b0032" + "0x610102610026600b82828239805160001a60731461001957fe5b30600052607381538281f3fe730000000000000000000000000000000000000000301460806040526004361060335760003560e01c8063c2985578146038575b600080fd5b603e60b0565b6040805160208082528351818301528351919283929083019185019080838360005b8381101560765781810151838201526020016060565b50505050905090810190601f16801560a25780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b604080518082019091526003815262666f6f60e81b60208201529056fea265627a7a7231582079c18e1f9cf2812147d15e5d44f16ff748f8b7349d32dc9db50300a3ffbd3a9664736f6c634300050b0032" contract_address = insert(:contract_address, contract_code: bytecode) + insert(:transaction, created_contract_address_hash: contract_address.hash, input: bytecode) code = """ pragma solidity 0.5.11; @@ -192,12 +203,15 @@ defmodule Explorer.SmartContract.VerifierTest do bytecode = "0x608060405234801561001057600080fd5b50600436106100a95760003560e01c80633177029f116100715780633177029f1461025f57806354fd4d50146102c557806370a082311461034857806395d89b41146103a0578063a9059cbb14610423578063dd62ed3e14610489576100a9565b806306fdde03146100ae578063095ea7b31461013157806318160ddd1461019757806323b872dd146101b5578063313ce5671461023b575b600080fd5b6100b6610501565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f65780820151818401526020810190506100db565b50505050905090810190601f1680156101235780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61017d6004803603604081101561014757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061059f565b604051808215151515815260200191505060405180910390f35b61019f610691565b6040518082815260200191505060405180910390f35b610221600480360360608110156101cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610696565b604051808215151515815260200191505060405180910390f35b61024361090f565b604051808260ff1660ff16815260200191505060405180910390f35b6102ab6004803603604081101561027557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610922565b604051808215151515815260200191505060405180910390f35b6102cd610a14565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561030d5780820151818401526020810190506102f2565b50505050905090810190601f16801561033a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61038a6004803603602081101561035e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ab2565b6040518082815260200191505060405180910390f35b6103a8610afa565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103e85780820151818401526020810190506103cd565b50505050905090810190601f1680156104155780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61046f6004803603604081101561043957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610b98565b604051808215151515815260200191505060405180910390f35b6104eb6004803603604081101561049f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cfe565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105975780601f1061056c57610100808354040283529160200191610597565b820191906000526020600020905b81548152906001019060200180831161057a57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b600090565b6000816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610762575081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410155b801561076e5750600082115b1561090357816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610908565b600090505b9392505050565b600460009054906101000a900460ff1681565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60068054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610aaa5780601f10610a7f57610100808354040283529160200191610aaa565b820191906000526020600020905b815481529060010190602001808311610a8d57829003601f168201915b505050505081565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b905780601f10610b6557610100808354040283529160200191610b90565b820191906000526020600020905b815481529060010190602001808311610b7357829003601f168201915b505050505081565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610be85750600082115b15610cf357816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610cf8565b600090505b92915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490509291505056fea265627a7a72305820fe0ba5210ac95870683c2cb054304b04565703bd16c7d7e956df694c9643c6d264736f6c63430005090032" + input = + "0x60806040526040518060400160405280600381526020017f302e3100000000000000000000000000000000000000000000000000000000008152506006908051906020019062000051929190620001e2565b503480156200005f57600080fd5b506040516200105b3803806200105b833981810160405260808110156200008557600080fd5b81019080805190602001909291908051640100000000811115620000a857600080fd5b82810190506020810184811115620000bf57600080fd5b8151856001820283011164010000000082111715620000dd57600080fd5b50509291906020018051906020019092919080516401000000008111156200010457600080fd5b828101905060208101848111156200011b57600080fd5b81518560018202830111640100000000821117156200013957600080fd5b5050929190505050836000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550836002819055508260039080519060200190620001a3929190620001e2565b5081600460006101000a81548160ff021916908360ff1602179055508060059080519060200190620001d7929190620001e2565b505050505062000291565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200022557805160ff191683800117855562000256565b8280016001018555821562000256579182015b828111156200025557825182559160200191906001019062000238565b5b50905062000265919062000269565b5090565b6200028e91905b808211156200028a57600081600090555060010162000270565b5090565b90565b610dba80620002a16000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c80633177029f116100715780633177029f1461025f57806354fd4d50146102c557806370a082311461034857806395d89b41146103a0578063a9059cbb14610423578063dd62ed3e14610489576100a9565b806306fdde03146100ae578063095ea7b31461013157806318160ddd1461019757806323b872dd146101b5578063313ce5671461023b575b600080fd5b6100b6610501565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f65780820151818401526020810190506100db565b50505050905090810190601f1680156101235780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61017d6004803603604081101561014757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061059f565b604051808215151515815260200191505060405180910390f35b61019f610691565b6040518082815260200191505060405180910390f35b610221600480360360608110156101cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610696565b604051808215151515815260200191505060405180910390f35b61024361090f565b604051808260ff1660ff16815260200191505060405180910390f35b6102ab6004803603604081101561027557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610922565b604051808215151515815260200191505060405180910390f35b6102cd610a14565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561030d5780820151818401526020810190506102f2565b50505050905090810190601f16801561033a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61038a6004803603602081101561035e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ab2565b6040518082815260200191505060405180910390f35b6103a8610afa565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103e85780820151818401526020810190506103cd565b50505050905090810190601f1680156104155780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61046f6004803603604081101561043957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610b98565b604051808215151515815260200191505060405180910390f35b6104eb6004803603604081101561049f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cfe565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105975780601f1061056c57610100808354040283529160200191610597565b820191906000526020600020905b81548152906001019060200180831161057a57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b600090565b6000816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610762575081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410155b801561076e5750600082115b1561090357816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610908565b600090505b9392505050565b600460009054906101000a900460ff1681565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60068054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610aaa5780601f10610a7f57610100808354040283529160200191610aaa565b820191906000526020600020905b815481529060010190602001808311610a8d57829003601f168201915b505050505081565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b905780601f10610b6557610100808354040283529160200191610b90565b820191906000526020600020905b815481529060010190602001808311610b7357829003601f168201915b505050505081565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610be85750600082115b15610cf357816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610cf8565b600090505b92915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490509291505056fea265627a7a72305820c63b805fe76ce64d7ba5aedfa99bbfa5b13cc8c2fabad31b2364f8ae421cd40c64736f6c63430005090032" + contract_address = insert(:contract_address, contract_code: bytecode) :transaction |> insert( created_contract_address_hash: contract_address.hash, - input: bytecode <> constructor_arguments + input: input <> constructor_arguments ) |> with_block() @@ -216,6 +230,7 @@ defmodule Explorer.SmartContract.VerifierTest do test "returns error when bytecode doesn't match", %{contract_code_info: contract_code_info} do contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) + insert(:transaction, created_contract_address_hash: contract_address.hash) different_code = "pragma solidity ^0.4.24; contract SimpleStorage {}" @@ -276,7 +291,7 @@ defmodule Explorer.SmartContract.VerifierTest do end end - describe "extract_bytecode/1" do + describe "extract_bytecode_and_metadata_hash/1" do test "extracts the bytecode from the hash" do code = "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a723058203c381c1b48b38d050c54d7ef296ecd411040e19420dfec94772b9c49ae106a0b0029" @@ -286,7 +301,10 @@ defmodule Explorer.SmartContract.VerifierTest do bytecode = "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600" - assert bytecode == Verifier.extract_bytecode(code) + %{"metadata_hash" => _metadata_hash, "bytecode" => bytecode_from_code} = + Verifier.extract_bytecode_and_metadata_hash(code) + + assert bytecode == bytecode_from_code assert bytecode != code assert String.contains?(code, bytecode) == true assert String.contains?(bytecode, "0029") == false @@ -302,11 +320,109 @@ defmodule Explorer.SmartContract.VerifierTest do bytecode = "0x608060405234801561001057600080fd5b5060df80610010029f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600" - assert bytecode == Verifier.extract_bytecode(code) + %{"metadata_hash" => _metadata_hash, "bytecode" => bytecode_from_code} = + Verifier.extract_bytecode_and_metadata_hash(code) + + assert bytecode == bytecode_from_code assert bytecode != code assert String.contains?(code, bytecode) == true assert String.contains?(bytecode, "0029") == true assert String.contains?(bytecode, swarm_source) == false end + + # https://solidity.readthedocs.io/en/v0.6.6/contracts.html?highlight=immutable#constant-and-immutable-state-variables + test "verifies smart-contract with `immutable` assignment" do + bytecode = + "0x6080604052348015600f57600080fd5b506004361060285760003560e01c8063fb49908514602d575b600080fd5b605060048036036020811015604157600080fd5b50356001600160a01b03166064565b604080519115158252519081900360200190f35b7f0000000000000000000000000000000000000000000000056b3977a93ae7c2006001600160a01b038216311191905056fea2646970667358221220b4fbf35809f2d1b85699a897ebb75d00c8c26b29b72decc53db18ddbd853352164736f6c63430006070033" + + tx_input = + "0x60e06040523360601b60c05234801561001757600080fd5b5060405161013f38038061013f8339818101604052604081101561003a57600080fd5b50805160209091015160808290526001600160a01b03163160a081905260c05160601c60cc6100736000395080606652505060cc6000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063fb49908514602d575b600080fd5b605060048036036020811015604157600080fd5b50356001600160a01b03166064565b604080519115158252519081900360200190f35b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b038216311191905056fea2646970667358221220b4fbf35809f2d1b85699a897ebb75d00c8c26b29b72decc53db18ddbd853352164736f6c63430006070033000000000000000000000000000000000000000000000000000000000000000100000000000000000000000023602745048d3b8d0a7f953ad444da4cd237ac83" + + contract_address = insert(:contract_address, contract_code: bytecode) + insert(:transaction, created_contract_address_hash: contract_address.hash, input: tx_input) + + code = """ + pragma solidity >0.6.4 <0.7.0; + + contract C { + uint constant X = 32**22 + 8; + string constant TEXT = "abc"; + bytes32 constant MY_HASH = keccak256("abc"); + uint immutable decimals; + uint immutable maxBalance; + address immutable owner = msg.sender; + + constructor(uint _decimals, address _reference) public { + decimals = _decimals; + // Assignments to immutables can even access the environment. + maxBalance = _reference.balance; + } + + function isBalanceTooHigh(address _other) public view returns (bool) { + return _other.balance > maxBalance; + } + } + """ + + constructor_arguments = + "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000023602745048d3b8d0a7f953ad444da4cd237ac83" + + params = %{ + "contract_source_code" => code, + "compiler_version" => "v0.6.7+commit.b8d736ae", + "evm_version" => "default", + "name" => "C", + "optimization" => true, + "constructor_arguments" => constructor_arguments + } + + assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) + assert abi != nil + end + + test "verifies smart-contract created from another contract" do + path = File.cwd!() <> "/test/support/fixture/smart_contract/contract_from_factory.sol" + contract = File.read!(path) + + constructor_arguments = "4e1477bdc40fc2458bf646f96f269502658277779fdf0f4080fe798a2d45bc37" + + bytecode = + "0x608060405260043610603e5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416638052474d81146043575b600080fd5b348015604e57600080fd5b5060556067565b60408051918252519081900360200190f35b600054815600a165627a7a72305820a1a0ec90e133c3064fab0ae82aa02a020224ea39d2b5421b6788f800bdde02f60029" + + init = + "0x608060405234801561001057600080fd5b506040516020806100cc83398101604052516000556099806100336000396000f300608060405260043610603e5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416638052474d81146043575b600080fd5b348015604e57600080fd5b5060556067565b60408051918252519081900360200190f35b600054815600a165627a7a72305820a1a0ec90e133c3064fab0ae82aa02a020224ea39d2b5421b6788f800bdde02f600294e1477bdc40fc2458bf646f96f269502658277779fdf0f4080fe798a2d45bc37" + + contract_address = insert(:contract_address, contract_code: bytecode) + + transaction = + :transaction + |> insert() + |> with_block() + + :internal_transaction + |> insert( + created_contract_address_hash: contract_address.hash, + init: init, + type: "create", + created_contract_code: bytecode, + input: nil, + transaction_hash: transaction.hash, + index: 0, + block_hash: transaction.block_hash, + block_index: 0 + ) + + params = %{ + "contract_source_code" => contract, + "compiler_version" => "v0.4.26+commit.4563c3fc", + "evm_version" => "default", + "name" => "ContractFromFactory", + "optimization" => true, + "constructor_arguments" => constructor_arguments + } + + assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) + assert abi != nil + end end end diff --git a/apps/explorer/test/support/factory.ex b/apps/explorer/test/support/factory.ex index 668d5fa6f6..09420fc1c4 100644 --- a/apps/explorer/test/support/factory.ex +++ b/apps/explorer/test/support/factory.ex @@ -82,6 +82,8 @@ defmodule Explorer.Factory do %{ bytecode: "0x6080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a72305820f65a3adc1cfb055013d1dc37d0fe98676e2a5963677fa7541a10386d163446680029", + tx_input: + "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a72305820853a985d0a4b20246785fc2f0357c202faa3db289980a48737180f358f9ddc3c0029", name: "SimpleStorage", source_code: """ pragma solidity ^0.4.24; diff --git a/apps/explorer/test/support/fixture/smart_contract/compiler_tests.json b/apps/explorer/test/support/fixture/smart_contract/compiler_tests.json index ee098b3d7f..2958c780be 100644 --- a/apps/explorer/test/support/fixture/smart_contract/compiler_tests.json +++ b/apps/explorer/test/support/fixture/smart_contract/compiler_tests.json @@ -3,6 +3,7 @@ "compiler_version": "v0.4.11+commit.68ef5810", "contract": "pragma solidity ^0.4.11;\n/**\n * @title ERC20Basic\n * @dev Simpler version of ERC20 interface\n * @dev see https://github.com/ethereum/EIPs/issues/179\n */\ncontract ERC20Basic {\n uint256 public totalSupply;\n function balanceOf(address who) public constant returns (uint256);\n function transfer(address to, uint256 value) public returns (bool);\n event Transfer(address indexed from, address indexed to, uint256 value);\n}\n/**\n * @title Ownable\n * @dev The Ownable contract has an owner address, and provides basic authorization control\n * functions, this simplifies the implementation of \"user permissions\".\n */\ncontract Ownable {\n address public owner;\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n /**\n * @dev The Ownable constructor sets the original `owner` of the contract to the sender\n * account.\n */\n function Ownable() {\n owner = msg.sender;\n }\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(msg.sender == owner);\n _;\n }\n /**\n * @dev Allows the current owner to transfer control of the contract to a newOwner.\n * @param newOwner The address to transfer ownership to.\n */\n function transferOwnership(address newOwner) onlyOwner public {\n require(newOwner != address(0));\n OwnershipTransferred(owner, newOwner);\n owner = newOwner;\n }\n}\n// Temporarily have SafeMath here until all contracts have been migrated to SafeMathLib version from OpenZeppelin\n/**\n * Math operations with safety checks\n */\ncontract SafeMath {\n function safeMul(uint a, uint b) internal returns (uint) {\n uint c = a * b;\n assert(a == 0 || c / a == b);\n return c;\n }\n function safeDiv(uint a, uint b) internal returns (uint) {\n assert(b > 0);\n uint c = a / b;\n assert(a == b * c + a % b);\n return c;\n }\n function safeSub(uint a, uint b) internal returns (uint) {\n assert(b <= a);\n return a - b;\n }\n function safeAdd(uint a, uint b) internal returns (uint) {\n uint c = a + b;\n assert(c>=a && c>=b);\n return c;\n }\n function max64(uint64 a, uint64 b) internal constant returns (uint64) {\n return a >= b ? a : b;\n }\n function min64(uint64 a, uint64 b) internal constant returns (uint64) {\n return a < b ? a : b;\n }\n function max256(uint256 a, uint256 b) internal constant returns (uint256) {\n return a >= b ? a : b;\n }\n function min256(uint256 a, uint256 b) internal constant returns (uint256) {\n return a < b ? a : b;\n }\n}\n/**\n * This smart contract code is Copyright 2017 TokenMarket Ltd. For more information see https://tokenmarket.net\n *\n * Licensed under the Apache License, version 2.0: https://github.com/TokenMarketNet/ico/blob/master/LICENSE.txt\n */\n/**\n * This smart contract code is Copyright 2017 TokenMarket Ltd. For more information see https://tokenmarket.net\n *\n * Licensed under the Apache License, version 2.0: https://github.com/TokenMarketNet/ico/blob/master/LICENSE.txt\n */\n/**\n * @title ERC20 interface\n * @dev see https://github.com/ethereum/EIPs/issues/20\n */\ncontract ERC20 is ERC20Basic {\n function allowance(address owner, address spender) public constant returns (uint256);\n function transferFrom(address from, address to, uint256 value) public returns (bool);\n function approve(address spender, uint256 value) public returns (bool);\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n/**\n * Standard ERC20 token with Short Hand Attack and approve() race condition mitigation.\n *\n * Based on code by FirstBlood:\n * https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol\n */\ncontract StandardToken is ERC20, SafeMath {\n /* Token supply got increased and a new owner received these tokens */\n event Minted(address receiver, uint amount);\n /* Actual balances of token holders */\n mapping(address => uint) balances;\n /* approve() allowances */\n mapping (address => mapping (address => uint)) allowed;\n /* Interface declaration */\n function isToken() public constant returns (bool weAre) {\n return true;\n }\n function transfer(address _to, uint _value) returns (bool success) {\n balances[msg.sender] = safeSub(balances[msg.sender], _value);\n balances[_to] = safeAdd(balances[_to], _value);\n Transfer(msg.sender, _to, _value);\n return true;\n }\n function transferFrom(address _from, address _to, uint _value) returns (bool success) {\n uint _allowance = allowed[_from][msg.sender];\n balances[_to] = safeAdd(balances[_to], _value);\n balances[_from] = safeSub(balances[_from], _value);\n allowed[_from][msg.sender] = safeSub(_allowance, _value);\n Transfer(_from, _to, _value);\n return true;\n }\n function balanceOf(address _owner) constant returns (uint balance) {\n return balances[_owner];\n }\n function approve(address _spender, uint _value) returns (bool success) {\n // To change the approve amount you first have to reduce the addresses`\n // allowance to zero by calling `approve(_spender, 0)` if it is not\n // already 0 to mitigate the race condition described here:\n // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n if ((_value != 0) && (allowed[msg.sender][_spender] != 0)) throw;\n allowed[msg.sender][_spender] = _value;\n Approval(msg.sender, _spender, _value);\n return true;\n }\n function allowance(address _owner, address _spender) constant returns (uint remaining) {\n return allowed[_owner][_spender];\n }\n}\n/**\n * This smart contract code is Copyright 2017 TokenMarket Ltd. For more information see https://tokenmarket.net\n *\n * Licensed under the Apache License, version 2.0: https://github.com/TokenMarketNet/ico/blob/master/LICENSE.txt\n */\n/**\n * This smart contract code is Copyright 2017 TokenMarket Ltd. For more information see https://tokenmarket.net\n *\n * Licensed under the Apache License, version 2.0: https://github.com/TokenMarketNet/ico/blob/master/LICENSE.txt\n */\n/**\n * Upgrade agent interface inspired by Lunyr.\n *\n * Upgrade agent transfers tokens to a new contract.\n * Upgrade agent itself can be the token contract, or just a middle man contract doing the heavy lifting.\n */\ncontract UpgradeAgent {\n uint public originalSupply;\n /** Interface marker */\n function isUpgradeAgent() public constant returns (bool) {\n return true;\n }\n function upgradeFrom(address _from, uint256 _value) public;\n}\n/**\n * A token upgrade mechanism where users can opt-in amount of tokens to the next smart contract revision.\n *\n * First envisioned by Golem and Lunyr projects.\n */\ncontract UpgradeableToken is StandardToken {\n /** Contract / person who can set the upgrade path. This can be the same as team multisig wallet, as what it is with its default value. */\n address public upgradeMaster;\n /** The next contract where the tokens will be migrated. */\n UpgradeAgent public upgradeAgent;\n /** How many tokens we have upgraded by now. */\n uint256 public totalUpgraded;\n /**\n * Upgrade states.\n *\n * - NotAllowed: The child contract has not reached a condition where the upgrade can bgun\n * - WaitingForAgent: Token allows upgrade, but we don't have a new agent yet\n * - ReadyToUpgrade: The agent is set, but not a single token has been upgraded yet\n * - Upgrading: Upgrade agent is set and the balance holders can upgrade their tokens\n *\n */\n enum UpgradeState {Unknown, NotAllowed, WaitingForAgent, ReadyToUpgrade, Upgrading}\n /**\n * Somebody has upgraded some of his tokens.\n */\n event Upgrade(address indexed _from, address indexed _to, uint256 _value);\n /**\n * New upgrade agent available.\n */\n event UpgradeAgentSet(address agent);\n /**\n * Do not allow construction without upgrade master set.\n */\n function UpgradeableToken(address _upgradeMaster) {\n upgradeMaster = _upgradeMaster;\n }\n /**\n * Allow the token holder to upgrade some of their tokens to a new contract.\n */\n function upgrade(uint256 value) public {\n UpgradeState state = getUpgradeState();\n if(!(state == UpgradeState.ReadyToUpgrade || state == UpgradeState.Upgrading)) {\n // Called in a bad state\n throw;\n }\n // Validate input value.\n if (value == 0) throw;\n balances[msg.sender] = safeSub(balances[msg.sender], value);\n // Take tokens out from circulation\n totalSupply = safeSub(totalSupply, value);\n totalUpgraded = safeAdd(totalUpgraded, value);\n // Upgrade agent reissues the tokens\n upgradeAgent.upgradeFrom(msg.sender, value);\n Upgrade(msg.sender, upgradeAgent, value);\n }\n /**\n * Set an upgrade agent that handles\n */\n function setUpgradeAgent(address agent) external {\n if(!canUpgrade()) {\n // The token is not yet in a state that we could think upgrading\n throw;\n }\n if (agent == 0x0) throw;\n // Only a master can designate the next agent\n if (msg.sender != upgradeMaster) throw;\n // Upgrade has already begun for an agent\n if (getUpgradeState() == UpgradeState.Upgrading) throw;\n upgradeAgent = UpgradeAgent(agent);\n // Bad interface\n if(!upgradeAgent.isUpgradeAgent()) throw;\n // Make sure that token supplies match in source and target\n if (upgradeAgent.originalSupply() != totalSupply) throw;\n UpgradeAgentSet(upgradeAgent);\n }\n /**\n * Get the state of the token upgrade.\n */\n function getUpgradeState() public constant returns(UpgradeState) {\n if(!canUpgrade()) return UpgradeState.NotAllowed;\n else if(address(upgradeAgent) == 0x00) return UpgradeState.WaitingForAgent;\n else if(totalUpgraded == 0) return UpgradeState.ReadyToUpgrade;\n else return UpgradeState.Upgrading;\n }\n /**\n * Change the upgrade master.\n *\n * This allows us to set a new owner for the upgrade mechanism.\n */\n function setUpgradeMaster(address master) public {\n if (master == 0x0) throw;\n if (msg.sender != upgradeMaster) throw;\n upgradeMaster = master;\n }\n /**\n * Child contract can enable to provide the condition when the upgrade can begun.\n */\n function canUpgrade() public constant returns(bool) {\n return true;\n }\n}\n/**\n * This smart contract code is Copyright 2017 TokenMarket Ltd. For more information see https://tokenmarket.net\n *\n * Licensed under the Apache License, version 2.0: https://github.com/TokenMarketNet/ico/blob/master/LICENSE.txt\n */\n/**\n * Define interface for releasing the token transfer after a successful crowdsale.\n */\ncontract ReleasableToken is ERC20, Ownable {\n /* The finalizer contract that allows unlift the transfer limits on this token */\n address public releaseAgent;\n /** A crowdsale contract can release us to the wild if ICO success. If false we are are in transfer lock up period.*/\n bool public released = false;\n /** Map of agents that are allowed to transfer tokens regardless of the lock down period. These are crowdsale contracts and possible the team multisig itself. */\n mapping (address => bool) public transferAgents;\n /**\n * Limit token transfer until the crowdsale is over.\n *\n */\n modifier canTransfer(address _sender) {\n if(!released) {\n if(!transferAgents[_sender]) {\n throw;\n }\n }\n _;\n }\n /**\n * Set the contract that can call release and make the token transferable.\n *\n * Design choice. Allow reset the release agent to fix fat finger mistakes.\n */\n function setReleaseAgent(address addr) onlyOwner inReleaseState(false) public {\n // We don't do interface check here as we might want to a normal wallet address to act as a release agent\n releaseAgent = addr;\n }\n /**\n * Owner can allow a particular address (a crowdsale contract) to transfer tokens despite the lock up period.\n */\n function setTransferAgent(address addr, bool state) onlyOwner inReleaseState(false) public {\n transferAgents[addr] = state;\n }\n /**\n * One way function to release the tokens to the wild.\n *\n * Can be called only from the release agent that is the final ICO contract. It is only called if the crowdsale has been success (first milestone reached).\n */\n function releaseTokenTransfer() public onlyReleaseAgent {\n released = true;\n }\n /** The function can be called only before or after the tokens have been releasesd */\n modifier inReleaseState(bool releaseState) {\n if(releaseState != released) {\n throw;\n }\n _;\n }\n /** The function can be called only by a whitelisted release agent. */\n modifier onlyReleaseAgent() {\n if(msg.sender != releaseAgent) {\n throw;\n }\n _;\n }\n function transfer(address _to, uint _value) canTransfer(msg.sender) returns (bool success) {\n // Call StandardToken.transfer()\n return super.transfer(_to, _value);\n }\n function transferFrom(address _from, address _to, uint _value) canTransfer(_from) returns (bool success) {\n // Call StandardToken.transferForm()\n return super.transferFrom(_from, _to, _value);\n }\n}\n/**\n * This smart contract code is Copyright 2017 TokenMarket Ltd. For more information see https://tokenmarket.net\n *\n * Licensed under the Apache License, version 2.0: https://github.com/TokenMarketNet/ico/blob/master/LICENSE.txt\n */\n/**\n * This smart contract code is Copyright 2017 TokenMarket Ltd. For more information see https://tokenmarket.net\n *\n * Licensed under the Apache License, version 2.0: https://github.com/TokenMarketNet/ico/blob/master/LICENSE.txt\n */\n/**\n * Safe unsigned safe math.\n *\n * https://blog.aragon.one/library-driven-development-in-solidity-2bebcaf88736#.750gwtwli\n *\n * Originally from https://raw.githubusercontent.com/AragonOne/zeppelin-solidity/master/contracts/SafeMathLib.sol\n *\n * Maintained here until merged to mainline zeppelin-solidity.\n *\n */\nlibrary SafeMathLibExt {\n function times(uint a, uint b) returns (uint) {\n uint c = a * b;\n assert(a == 0 || c / a == b);\n return c;\n }\n function divides(uint a, uint b) returns (uint) {\n assert(b > 0);\n uint c = a / b;\n assert(a == b * c + a % b);\n return c;\n }\n function minus(uint a, uint b) returns (uint) {\n assert(b <= a);\n return a - b;\n }\n function plus(uint a, uint b) returns (uint) {\n uint c = a + b;\n assert(c>=a);\n return c;\n }\n}\n/**\n * A token that can increase its supply by another contract.\n *\n * This allows uncapped crowdsale by dynamically increasing the supply when money pours in.\n * Only mint agents, contracts whitelisted by owner, can mint new tokens.\n *\n */\ncontract MintableTokenExt is StandardToken, Ownable {\n using SafeMathLibExt for uint;\n bool public mintingFinished = false;\n /** List of agents that are allowed to create new tokens */\n mapping (address => bool) public mintAgents;\n event MintingAgentChanged(address addr, bool state );\n struct ReservedTokensData {\n uint inTokens;\n uint inPercentage;\n }\n mapping (address => ReservedTokensData) public reservedTokensList;\n address[] public reservedTokensDestinations;\n uint public reservedTokensDestinationsLen = 0;\n function setReservedTokensList(address addr, uint inTokens, uint inPercentage) onlyOwner {\n reservedTokensDestinations.push(addr);\n reservedTokensDestinationsLen++;\n reservedTokensList[addr] = ReservedTokensData({inTokens:inTokens, inPercentage:inPercentage});\n }\n function getReservedTokensListValInTokens(address addr) constant returns (uint inTokens) {\n return reservedTokensList[addr].inTokens;\n }\n function getReservedTokensListValInPercentage(address addr) constant returns (uint inPercentage) {\n return reservedTokensList[addr].inPercentage;\n }\n function setReservedTokensListMultiple(address[] addrs, uint[] inTokens, uint[] inPercentage) onlyOwner {\n for (uint iterator = 0; iterator < addrs.length; iterator++) {\n setReservedTokensList(addrs[iterator], inTokens[iterator], inPercentage[iterator]);\n }\n }\n /**\n * Create new tokens and allocate them to an address..\n *\n * Only callably by a crowdsale contract (mint agent).\n */\n function mint(address receiver, uint amount) onlyMintAgent canMint public {\n totalSupply = totalSupply.plus(amount);\n balances[receiver] = balances[receiver].plus(amount);\n // This will make the mint transaction apper in EtherScan.io\n // We can remove this after there is a standardized minting event\n Transfer(0, receiver, amount);\n }\n /**\n * Owner can allow a crowdsale contract to mint new tokens.\n */\n function setMintAgent(address addr, bool state) onlyOwner canMint public {\n mintAgents[addr] = state;\n MintingAgentChanged(addr, state);\n }\n modifier onlyMintAgent() {\n // Only crowdsale contracts are allowed to mint new tokens\n if(!mintAgents[msg.sender]) {\n throw;\n }\n _;\n }\n /** Make sure we are not done yet. */\n modifier canMint() {\n if(mintingFinished) throw;\n _;\n }\n}\n/**\n * A crowdsaled token.\n *\n * An ERC-20 token designed specifically for crowdsales with investor protection and further development path.\n *\n * - The token transfer() is disabled until the crowdsale is over\n * - The token contract gives an opt-in upgrade path to a new contract\n * - The same token can be part of several crowdsales through approve() mechanism\n * - The token can be capped (supply set in the constructor) or uncapped (crowdsale contract can mint new tokens)\n *\n */\ncontract CrowdsaleTokenExt is ReleasableToken, MintableTokenExt, UpgradeableToken {\n /** Name and symbol were updated. */\n event UpdatedTokenInformation(string newName, string newSymbol);\n string public name;\n string public symbol;\n uint public decimals;\n /* Minimum ammount of tokens every buyer can buy. */\n uint public minCap;\n /**\n * Construct the token.\n *\n * This token must be created through a team multisig wallet, so that it is owned by that wallet.\n *\n * @param _name Token name\n * @param _symbol Token symbol - should be all caps\n * @param _initialSupply How many tokens we start with\n * @param _decimals Number of decimal places\n * @param _mintable Are new tokens created over the crowdsale or do we distribute only the initial supply? Note that when the token becomes transferable the minting always ends.\n */\n function CrowdsaleTokenExt(string _name, string _symbol, uint _initialSupply, uint _decimals, bool _mintable, uint _globalMinCap)\n UpgradeableToken(msg.sender) {\n // Create any address, can be transferred\n // to team multisig via changeOwner(),\n // also remember to call setUpgradeMaster()\n owner = msg.sender;\n name = _name;\n symbol = _symbol;\n totalSupply = _initialSupply;\n decimals = _decimals;\n minCap = _globalMinCap;\n // Create initially all balance on the team multisig\n balances[owner] = totalSupply;\n if(totalSupply > 0) {\n Minted(owner, totalSupply);\n }\n // No more new supply allowed after the token creation\n if(!_mintable) {\n mintingFinished = true;\n if(totalSupply == 0) {\n throw; // Cannot create a token without supply and no minting\n }\n }\n }\n /**\n * When token is released to be transferable, enforce no new tokens can be created.\n */\n function releaseTokenTransfer() public onlyReleaseAgent {\n mintingFinished = true;\n super.releaseTokenTransfer();\n }\n /**\n * Allow upgrade agent functionality kick in only if the crowdsale was success.\n */\n function canUpgrade() public constant returns(bool) {\n return released && super.canUpgrade();\n }\n /**\n * Owner can update token information here.\n *\n * It is often useful to conceal the actual token association, until\n * the token operations, like central issuance or reissuance have been completed.\n *\n * This function allows the token owner to rename the token after the operations\n * have been completed and then point the audience to use the token contract.\n */\n function setTokenInformation(string _name, string _symbol) onlyOwner {\n name = _name;\n symbol = _symbol;\n UpdatedTokenInformation(name, symbol);\n }\n}", "expected_bytecode": "606060405236156101e05763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166302f652a381146101e257806305d2035b1461020557806306fdde0314610229578063095ea7b3146102b957806318160ddd146102ec57806323b872dd1461030e57806329ff4f5314610347578063313ce56714610365578063338f43a0146103875780633fa615b0146103b557806340c10f19146103d757806342c1867b146103f8578063432146751461042857806345977d031461044b5780634eee966f1461046057806351ed17a4146104f55780635de4ccb01461052a5780635f412d4f14610556578063600440cb1461056857806370a08231146105945780637386f0a7146105c257806380c190cf146105f15780638444b3911461061f578063867c2857146106535780638da5cb5b1461068357806395d89b41146106af578063961325211461073f5780639738968c14610763578063a9059cbb14610787578063b4ffaece146107ba578063c33105171461087f578063c752ff62146108a1578063d1f276d3146108c3578063d7e7088a146108ef578063dd62ed3e1461090d578063eefa597b14610941578063f2fde38b14610965578063f30f850814610983578063ffeb7d75146109a7575bfe5b34156101ea57fe5b610203600160a060020a036004351660243515156109c5565b005b341561020d57fe5b610215610a28565b604080519115158252519081900360200190f35b341561023157fe5b610239610a31565b60408051602080825283518183015283519192839290830191850190808383821561027f575b80518252602083111561027f57601f19909201916020918201910161025f565b505050905090810190601f1680156102ab5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34156102c157fe5b610215600160a060020a0360043516602435610abf565b604080519115158252519081900360200190f35b34156102f457fe5b6102fc610b66565b60408051918252519081900360200190f35b341561031657fe5b610215600160a060020a0360043581169060243516604435610b6c565b604080519115158252519081900360200190f35b341561034f57fe5b610203600160a060020a0360043516610bc4565b005b341561036d57fe5b6102fc610c2a565b60408051918252519081900360200190f35b341561038f57fe5b6102fc600160a060020a0360043516610c30565b60408051918252519081900360200190f35b34156103bd57fe5b6102fc610c4f565b60408051918252519081900360200190f35b34156103df57fe5b610203600160a060020a0360043516602435610c55565b005b341561040057fe5b610215600160a060020a0360043516610e1b565b604080519115158252519081900360200190f35b341561043057fe5b610203600160a060020a03600435166024351515610e30565b005b341561045357fe5b610203600435610ec4565b005b341561046857fe5b610203600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284375050604080516020601f89358b0180359182018390048302840183019094528083529799988101979196509182019450925082915084018382808284375094965061103495505050505050565b005b34156104fd57fe5b610511600160a060020a03600435166111a8565b6040805192835260208301919091528051918290030190f35b341561053257fe5b61053a6111c1565b60408051600160a060020a039092168252519081900360200190f35b341561055e57fe5b6102036111d0565b005b341561057057fe5b61053a611205565b60408051600160a060020a039092168252519081900360200190f35b341561059c57fe5b6102fc600160a060020a0360043516611214565b60408051918252519081900360200190f35b34156105ca57fe5b61053a600435611233565b60408051600160a060020a039092168252519081900360200190f35b34156105f957fe5b6102fc600160a060020a0360043516611265565b60408051918252519081900360200190f35b341561062757fe5b61062f611287565b6040518082600481111561063f57fe5b60ff16815260200191505060405180910390f35b341561065b57fe5b610215600160a060020a03600435166112d4565b604080519115158252519081900360200190f35b341561068b57fe5b61053a6112e9565b60408051600160a060020a039092168252519081900360200190f35b34156106b757fe5b6102396112f8565b60408051602080825283518183015283519192839290830191850190808383821561027f575b80518252602083111561027f57601f19909201916020918201910161025f565b505050905090810190601f1680156102ab5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561074757fe5b610215611386565b604080519115158252519081900360200190f35b341561076b57fe5b610215611396565b604080519115158252519081900360200190f35b341561078f57fe5b610215600160a060020a03600435166024356113bc565b604080519115158252519081900360200190f35b34156107c257fe5b610203600480803590602001908201803590602001908080602002602001604051908101604052809392919081815260200183836020028082843750506040805187358901803560208181028481018201909552818452989a998901989297509082019550935083925085019084908082843750506040805187358901803560208181028481018201909552818452989a99890198929750908201955093508392508501908490808284375094965061141295505050505050565b005b341561088757fe5b6102fc61149f565b60408051918252519081900360200190f35b34156108a957fe5b6102fc6114a5565b60408051918252519081900360200190f35b34156108cb57fe5b61053a6114ab565b60408051600160a060020a039092168252519081900360200190f35b34156108f757fe5b610203600160a060020a03600435166114ba565b005b341561091557fe5b6102fc600160a060020a0360043581169060243516611697565b60408051918252519081900360200190f35b341561094957fe5b6102156116c4565b604080519115158252519081900360200190f35b341561096d57fe5b610203600160a060020a03600435166116ca565b005b341561098b57fe5b610203600160a060020a0360043516602435604435611763565b005b34156109af57fe5b610203600160a060020a03600435166117fe565b005b60035433600160a060020a039081169116146109e15760006000fd5b60045460009060a060020a900460ff16156109fc5760006000fd5b600160a060020a0383166000908152600560205260409020805460ff19168315151790555b5b505b5050565b60065460ff1681565b600e805460408051602060026001851615610100026000190190941693909304601f81018490048402820184019092528181529291830182828015610ab75780601f10610a8c57610100808354040283529160200191610ab7565b820191906000526020600020905b815481529060010190602001808311610a9a57829003601f168201915b505050505081565b60008115801590610af45750600160a060020a0333811660009081526002602090815260408083209387168352929052205415155b15610aff5760006000fd5b600160a060020a03338116600081815260026020908152604080832094881680845294825291829020869055815186815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a35060015b92915050565b60005481565b600454600090849060a060020a900460ff161515610bac57600160a060020a03811660009081526005602052604090205460ff161515610bac5760006000fd5b5b610bb885858561185c565b91505b5b509392505050565b60035433600160a060020a03908116911614610be05760006000fd5b60045460009060a060020a900460ff1615610bfb5760006000fd5b6004805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0384161790555b5b505b50565b60105481565b600160a060020a0381166000908152600860205260409020545b919050565b60115481565b600160a060020a03331660009081526007602052604090205460ff161515610c7d5760006000fd5b60065460ff1615610c8e5760006000fd5b6000547354ca5a7c536dbed5897b78d30a93dcd0e46fbdac6366098d4f9091836000604051602001526040518363ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808381526020018281526020019250505060206040518083038186803b1515610d0a57fe5b6102c65a03f41515610d1857fe5b50506040805180516000908155600160a060020a038616815260016020908152838220549281019190915282517f66098d4f00000000000000000000000000000000000000000000000000000000815260048101929092526024820185905291517354ca5a7c536dbed5897b78d30a93dcd0e46fbdac93506366098d4f92604480840193919291829003018186803b1515610daf57fe5b6102c65a03f41515610dbd57fe5b5050604080518051600160a060020a0386166000818152600160209081528582209390935586845293519094507fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35b5b5b5050565b60076020526000908152604090205460ff1681565b60035433600160a060020a03908116911614610e4c5760006000fd5b60065460ff1615610e5d5760006000fd5b600160a060020a038216600081815260076020908152604091829020805460ff191685151590811790915582519384529083015280517f4b0adf6c802794c7dde28a08a4e07131abcff3bf9603cd71f14f90bec7865efa9281900390910190a15b5b5b5050565b6000610ece611287565b905060035b816004811115610edf57fe5b1480610ef7575060045b816004811115610ef557fe5b145b1515610f035760006000fd5b811515610f105760006000fd5b600160a060020a033316600090815260016020526040902054610f33908361195f565b600160a060020a03331660009081526001602052604081209190915554610f5a908361195f565b600055600d54610f6a9083611976565b600d55600c54604080517f753e88e5000000000000000000000000000000000000000000000000000000008152600160a060020a033381166004830152602482018690529151919092169163753e88e591604480830192600092919082900301818387803b1515610fd757fe5b6102c65a03f11515610fe557fe5b5050600c54604080518581529051600160a060020a03928316935033909216917f7e5c344a8141a805725cb476f76c6953b842222b967edd1f78ddb6e8b3f397ac9181900360200190a35b5050565b60035433600160a060020a039081169116146110505760006000fd5b815161106390600e906020850190611a9c565b50805161107790600f906020840190611a9c565b5060408051818152600e8054600260001961010060018416150201909116049282018390527fd131ab1e6f279deea74e13a18477e13e2107deb6dc8ae955648948be5841fb46929091600f918190602082019060608301908690801561111e5780601f106110f35761010080835404028352916020019161111e565b820191906000526020600020905b81548152906001019060200180831161110157829003601f168201915b50508381038252845460026000196101006001841615020190911604808252602090910190859080156111925780601f1061116757610100808354040283529160200191611192565b820191906000526020600020905b81548152906001019060200180831161117557829003601f168201915b505094505050505060405180910390a15b5b5050565b6008602052600090815260409020805460019091015482565b600c54600160a060020a031681565b60045433600160a060020a039081169116146111ec5760006000fd5b6006805460ff1916600117905561120161199e565b5b5b565b600b54600160a060020a031681565b600160a060020a0381166000908152600160205260409020545b919050565b600980548290811061124157fe5b906000526020600020900160005b915054906101000a9004600160a060020a031681565b600160a060020a0381166000908152600860205260409020600101545b919050565b6000611291611396565b151561129f575060016112ce565b600c54600160a060020a031615156112b9575060026112ce565b600d5415156112ca575060036112ce565b5060045b5b5b5b90565b60056020526000908152604090205460ff1681565b600354600160a060020a031681565b600f805460408051602060026001851615610100026000190190941693909304601f81018490048402820184019092528181529291830182828015610ab75780601f10610a8c57610100808354040283529160200191610ab7565b820191906000526020600020905b815481529060010190602001808311610a9a57829003601f168201915b505050505081565b60045460a060020a900460ff1681565b60045460009060a060020a900460ff1680156113b557506113b56116c4565b5b90505b90565b600454600090339060a060020a900460ff1615156113fc57600160a060020a03811660009081526005602052604090205460ff1615156113fc5760006000fd5b5b61140784846119e8565b91505b5b5092915050565b60035460009033600160a060020a039081169116146114315760006000fd5b5060005b83518110156114975761148e848281518110151561144f57fe5b90602001906020020151848381518110151561146757fe5b90602001906020020151848481518110151561147f57fe5b90602001906020020151611763565b5b600101611435565b5b5b50505050565b600a5481565b600d5481565b600454600160a060020a031681565b6114c2611396565b15156114ce5760006000fd5b600160a060020a03811615156114e45760006000fd5b600b5433600160a060020a039081169116146115005760006000fd5b60045b61150b611287565b600481111561151657fe5b14156115225760006000fd5b600c805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a038381169190911791829055604080516000602091820181905282517f61d3d7a6000000000000000000000000000000000000000000000000000000008152925194909316936361d3d7a6936004808501948390030190829087803b15156115a857fe5b6102c65a03f115156115b657fe5b505060405151151590506115ca5760006000fd5b60008054600c5460408051602090810185905281517f4b2ba0dd00000000000000000000000000000000000000000000000000000000815291519394600160a060020a0390931693634b2ba0dd936004808501948390030190829087803b151561163057fe5b6102c65a03f1151561163e57fe5b5050604051519190911490506116545760006000fd5b600c5460408051600160a060020a039092168252517f7845d5aa74cc410e35571258d954f23b82276e160fe8c188fa80566580f279cc9181900360200190a15b50565b600160a060020a038083166000908152600260209081526040808320938516835292905220545b92915050565b60015b90565b60035433600160a060020a039081169116146116e65760006000fd5b600160a060020a03811615156116fc5760006000fd5b600354604051600160a060020a038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a36003805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0383161790555b5b50565b60035433600160a060020a0390811691161461177f5760006000fd5b60098054600181016117918382611b1b565b916000526020600020900160005b8154600160a060020a038088166101009390930a83810291021990911617909155600a80546001908101909155604080518082018252868152602080820187815260009586526008909152919093209251835551910155505b5b505050565b600160a060020a03811615156118145760006000fd5b600b5433600160a060020a039081169116146118305760006000fd5b600b805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0383161790555b50565b600160a060020a03808416600090815260026020908152604080832033851684528252808320549386168352600190915281205490919061189d9084611976565b600160a060020a0380861660009081526001602052604080822093909355908716815220546118cc908461195f565b600160a060020a0386166000908152600160205260409020556118ef818461195f565b600160a060020a038087166000818152600260209081526040808320338616845282529182902094909455805187815290519288169391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a3600191505b509392505050565b60008282111561196b57fe5b508082035b92915050565b600082820183811080159061198b5750828110155b151561199357fe5b8091505b5092915050565b60045433600160a060020a039081169116146119ba5760006000fd5b6004805474ff0000000000000000000000000000000000000000191660a060020a1790555b5b565b60015b90565b600160a060020a033316600090815260016020526040812054611a0b908361195f565b600160a060020a033381166000908152600160205260408082209390935590851681522054611a3a9083611976565b600160a060020a038085166000818152600160209081526040918290209490945580518681529051919333909316927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a35060015b92915050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10611add57805160ff1916838001178555611b0a565b82800160010185558215611b0a579182015b82811115611b0a578251825591602001919060010190611aef565b5b50611b17929150611b45565b5090565b815481835581811511610a2157600083815260209020610a21918101908301611b45565b5b505050565b6112ce91905b80821115611b175760008155600101611b4b565b5090565b905600a165627a7a72305820c38ce552da1339b41e837cc46418755767ab24eb6b08726516f1e03f7b96fca9002900000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000085570666972696e6700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035546520000000000000000000000000000000000000000000000000000000000", + "tx_input": "60606040526004805460a060020a60ff02191690556006805460ff191690556000600a5534156200002c57fe5b60405162001deb38038062001deb83398101604090815281516020830151918301516060840151608085015160a086015193860195949094019391929091905b335b5b60038054600160a060020a03191633600160a060020a03161790555b600b8054600160a060020a031916600160a060020a0383161790555b5060038054600160a060020a03191633600160a060020a03161790558551620000d890600e9060208901906200019f565b508451620000ee90600f9060208801906200019f565b50600084815560108490556011829055600354600160a060020a031681526001602052604081208590558411156200016c5760035460005460408051600160a060020a039093168352602083019190915280517f30385c845b448a36257a6a1716e6ad2e1bc2cbe333cde1e69fe849ad6511adfe9281900390910190a15b81151562000191576006805460ff191660011790556000541515620001915760006000fd5b5b5b50505050505062000249565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10620001e257805160ff191683800117855562000212565b8280016001018555821562000212579182015b8281111562000212578251825591602001919060010190620001f5565b5b506200022192915062000225565b5090565b6200024691905b808211156200022157600081556001016200022c565b5090565b90565b611b9280620002596000396000f300606060405236156101e05763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166302f652a381146101e257806305d2035b1461020557806306fdde0314610229578063095ea7b3146102b957806318160ddd146102ec57806323b872dd1461030e57806329ff4f5314610347578063313ce56714610365578063338f43a0146103875780633fa615b0146103b557806340c10f19146103d757806342c1867b146103f8578063432146751461042857806345977d031461044b5780634eee966f1461046057806351ed17a4146104f55780635de4ccb01461052a5780635f412d4f14610556578063600440cb1461056857806370a08231146105945780637386f0a7146105c257806380c190cf146105f15780638444b3911461061f578063867c2857146106535780638da5cb5b1461068357806395d89b41146106af578063961325211461073f5780639738968c14610763578063a9059cbb14610787578063b4ffaece146107ba578063c33105171461087f578063c752ff62146108a1578063d1f276d3146108c3578063d7e7088a146108ef578063dd62ed3e1461090d578063eefa597b14610941578063f2fde38b14610965578063f30f850814610983578063ffeb7d75146109a7575bfe5b34156101ea57fe5b610203600160a060020a036004351660243515156109c5565b005b341561020d57fe5b610215610a28565b604080519115158252519081900360200190f35b341561023157fe5b610239610a31565b60408051602080825283518183015283519192839290830191850190808383821561027f575b80518252602083111561027f57601f19909201916020918201910161025f565b505050905090810190601f1680156102ab5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34156102c157fe5b610215600160a060020a0360043516602435610abf565b604080519115158252519081900360200190f35b34156102f457fe5b6102fc610b66565b60408051918252519081900360200190f35b341561031657fe5b610215600160a060020a0360043581169060243516604435610b6c565b604080519115158252519081900360200190f35b341561034f57fe5b610203600160a060020a0360043516610bc4565b005b341561036d57fe5b6102fc610c2a565b60408051918252519081900360200190f35b341561038f57fe5b6102fc600160a060020a0360043516610c30565b60408051918252519081900360200190f35b34156103bd57fe5b6102fc610c4f565b60408051918252519081900360200190f35b34156103df57fe5b610203600160a060020a0360043516602435610c55565b005b341561040057fe5b610215600160a060020a0360043516610e1b565b604080519115158252519081900360200190f35b341561043057fe5b610203600160a060020a03600435166024351515610e30565b005b341561045357fe5b610203600435610ec4565b005b341561046857fe5b610203600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284375050604080516020601f89358b0180359182018390048302840183019094528083529799988101979196509182019450925082915084018382808284375094965061103495505050505050565b005b34156104fd57fe5b610511600160a060020a03600435166111a8565b6040805192835260208301919091528051918290030190f35b341561053257fe5b61053a6111c1565b60408051600160a060020a039092168252519081900360200190f35b341561055e57fe5b6102036111d0565b005b341561057057fe5b61053a611205565b60408051600160a060020a039092168252519081900360200190f35b341561059c57fe5b6102fc600160a060020a0360043516611214565b60408051918252519081900360200190f35b34156105ca57fe5b61053a600435611233565b60408051600160a060020a039092168252519081900360200190f35b34156105f957fe5b6102fc600160a060020a0360043516611265565b60408051918252519081900360200190f35b341561062757fe5b61062f611287565b6040518082600481111561063f57fe5b60ff16815260200191505060405180910390f35b341561065b57fe5b610215600160a060020a03600435166112d4565b604080519115158252519081900360200190f35b341561068b57fe5b61053a6112e9565b60408051600160a060020a039092168252519081900360200190f35b34156106b757fe5b6102396112f8565b60408051602080825283518183015283519192839290830191850190808383821561027f575b80518252602083111561027f57601f19909201916020918201910161025f565b505050905090810190601f1680156102ab5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561074757fe5b610215611386565b604080519115158252519081900360200190f35b341561076b57fe5b610215611396565b604080519115158252519081900360200190f35b341561078f57fe5b610215600160a060020a03600435166024356113bc565b604080519115158252519081900360200190f35b34156107c257fe5b610203600480803590602001908201803590602001908080602002602001604051908101604052809392919081815260200183836020028082843750506040805187358901803560208181028481018201909552818452989a998901989297509082019550935083925085019084908082843750506040805187358901803560208181028481018201909552818452989a99890198929750908201955093508392508501908490808284375094965061141295505050505050565b005b341561088757fe5b6102fc61149f565b60408051918252519081900360200190f35b34156108a957fe5b6102fc6114a5565b60408051918252519081900360200190f35b34156108cb57fe5b61053a6114ab565b60408051600160a060020a039092168252519081900360200190f35b34156108f757fe5b610203600160a060020a03600435166114ba565b005b341561091557fe5b6102fc600160a060020a0360043581169060243516611697565b60408051918252519081900360200190f35b341561094957fe5b6102156116c4565b604080519115158252519081900360200190f35b341561096d57fe5b610203600160a060020a03600435166116ca565b005b341561098b57fe5b610203600160a060020a0360043516602435604435611763565b005b34156109af57fe5b610203600160a060020a03600435166117fe565b005b60035433600160a060020a039081169116146109e15760006000fd5b60045460009060a060020a900460ff16156109fc5760006000fd5b600160a060020a0383166000908152600560205260409020805460ff19168315151790555b5b505b5050565b60065460ff1681565b600e805460408051602060026001851615610100026000190190941693909304601f81018490048402820184019092528181529291830182828015610ab75780601f10610a8c57610100808354040283529160200191610ab7565b820191906000526020600020905b815481529060010190602001808311610a9a57829003601f168201915b505050505081565b60008115801590610af45750600160a060020a0333811660009081526002602090815260408083209387168352929052205415155b15610aff5760006000fd5b600160a060020a03338116600081815260026020908152604080832094881680845294825291829020869055815186815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a35060015b92915050565b60005481565b600454600090849060a060020a900460ff161515610bac57600160a060020a03811660009081526005602052604090205460ff161515610bac5760006000fd5b5b610bb885858561185c565b91505b5b509392505050565b60035433600160a060020a03908116911614610be05760006000fd5b60045460009060a060020a900460ff1615610bfb5760006000fd5b6004805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0384161790555b5b505b50565b60105481565b600160a060020a0381166000908152600860205260409020545b919050565b60115481565b600160a060020a03331660009081526007602052604090205460ff161515610c7d5760006000fd5b60065460ff1615610c8e5760006000fd5b6000547354ca5a7c536dbed5897b78d30a93dcd0e46fbdac6366098d4f9091836000604051602001526040518363ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808381526020018281526020019250505060206040518083038186803b1515610d0a57fe5b6102c65a03f41515610d1857fe5b50506040805180516000908155600160a060020a038616815260016020908152838220549281019190915282517f66098d4f00000000000000000000000000000000000000000000000000000000815260048101929092526024820185905291517354ca5a7c536dbed5897b78d30a93dcd0e46fbdac93506366098d4f92604480840193919291829003018186803b1515610daf57fe5b6102c65a03f41515610dbd57fe5b5050604080518051600160a060020a0386166000818152600160209081528582209390935586845293519094507fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35b5b5b5050565b60076020526000908152604090205460ff1681565b60035433600160a060020a03908116911614610e4c5760006000fd5b60065460ff1615610e5d5760006000fd5b600160a060020a038216600081815260076020908152604091829020805460ff191685151590811790915582519384529083015280517f4b0adf6c802794c7dde28a08a4e07131abcff3bf9603cd71f14f90bec7865efa9281900390910190a15b5b5b5050565b6000610ece611287565b905060035b816004811115610edf57fe5b1480610ef7575060045b816004811115610ef557fe5b145b1515610f035760006000fd5b811515610f105760006000fd5b600160a060020a033316600090815260016020526040902054610f33908361195f565b600160a060020a03331660009081526001602052604081209190915554610f5a908361195f565b600055600d54610f6a9083611976565b600d55600c54604080517f753e88e5000000000000000000000000000000000000000000000000000000008152600160a060020a033381166004830152602482018690529151919092169163753e88e591604480830192600092919082900301818387803b1515610fd757fe5b6102c65a03f11515610fe557fe5b5050600c54604080518581529051600160a060020a03928316935033909216917f7e5c344a8141a805725cb476f76c6953b842222b967edd1f78ddb6e8b3f397ac9181900360200190a35b5050565b60035433600160a060020a039081169116146110505760006000fd5b815161106390600e906020850190611a9c565b50805161107790600f906020840190611a9c565b5060408051818152600e8054600260001961010060018416150201909116049282018390527fd131ab1e6f279deea74e13a18477e13e2107deb6dc8ae955648948be5841fb46929091600f918190602082019060608301908690801561111e5780601f106110f35761010080835404028352916020019161111e565b820191906000526020600020905b81548152906001019060200180831161110157829003601f168201915b50508381038252845460026000196101006001841615020190911604808252602090910190859080156111925780601f1061116757610100808354040283529160200191611192565b820191906000526020600020905b81548152906001019060200180831161117557829003601f168201915b505094505050505060405180910390a15b5b5050565b6008602052600090815260409020805460019091015482565b600c54600160a060020a031681565b60045433600160a060020a039081169116146111ec5760006000fd5b6006805460ff1916600117905561120161199e565b5b5b565b600b54600160a060020a031681565b600160a060020a0381166000908152600160205260409020545b919050565b600980548290811061124157fe5b906000526020600020900160005b915054906101000a9004600160a060020a031681565b600160a060020a0381166000908152600860205260409020600101545b919050565b6000611291611396565b151561129f575060016112ce565b600c54600160a060020a031615156112b9575060026112ce565b600d5415156112ca575060036112ce565b5060045b5b5b5b90565b60056020526000908152604090205460ff1681565b600354600160a060020a031681565b600f805460408051602060026001851615610100026000190190941693909304601f81018490048402820184019092528181529291830182828015610ab75780601f10610a8c57610100808354040283529160200191610ab7565b820191906000526020600020905b815481529060010190602001808311610a9a57829003601f168201915b505050505081565b60045460a060020a900460ff1681565b60045460009060a060020a900460ff1680156113b557506113b56116c4565b5b90505b90565b600454600090339060a060020a900460ff1615156113fc57600160a060020a03811660009081526005602052604090205460ff1615156113fc5760006000fd5b5b61140784846119e8565b91505b5b5092915050565b60035460009033600160a060020a039081169116146114315760006000fd5b5060005b83518110156114975761148e848281518110151561144f57fe5b90602001906020020151848381518110151561146757fe5b90602001906020020151848481518110151561147f57fe5b90602001906020020151611763565b5b600101611435565b5b5b50505050565b600a5481565b600d5481565b600454600160a060020a031681565b6114c2611396565b15156114ce5760006000fd5b600160a060020a03811615156114e45760006000fd5b600b5433600160a060020a039081169116146115005760006000fd5b60045b61150b611287565b600481111561151657fe5b14156115225760006000fd5b600c805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a038381169190911791829055604080516000602091820181905282517f61d3d7a6000000000000000000000000000000000000000000000000000000008152925194909316936361d3d7a6936004808501948390030190829087803b15156115a857fe5b6102c65a03f115156115b657fe5b505060405151151590506115ca5760006000fd5b60008054600c5460408051602090810185905281517f4b2ba0dd00000000000000000000000000000000000000000000000000000000815291519394600160a060020a0390931693634b2ba0dd936004808501948390030190829087803b151561163057fe5b6102c65a03f1151561163e57fe5b5050604051519190911490506116545760006000fd5b600c5460408051600160a060020a039092168252517f7845d5aa74cc410e35571258d954f23b82276e160fe8c188fa80566580f279cc9181900360200190a15b50565b600160a060020a038083166000908152600260209081526040808320938516835292905220545b92915050565b60015b90565b60035433600160a060020a039081169116146116e65760006000fd5b600160a060020a03811615156116fc5760006000fd5b600354604051600160a060020a038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a36003805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0383161790555b5b50565b60035433600160a060020a0390811691161461177f5760006000fd5b60098054600181016117918382611b1b565b916000526020600020900160005b8154600160a060020a038088166101009390930a83810291021990911617909155600a80546001908101909155604080518082018252868152602080820187815260009586526008909152919093209251835551910155505b5b505050565b600160a060020a03811615156118145760006000fd5b600b5433600160a060020a039081169116146118305760006000fd5b600b805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0383161790555b50565b600160a060020a03808416600090815260026020908152604080832033851684528252808320549386168352600190915281205490919061189d9084611976565b600160a060020a0380861660009081526001602052604080822093909355908716815220546118cc908461195f565b600160a060020a0386166000908152600160205260409020556118ef818461195f565b600160a060020a038087166000818152600260209081526040808320338616845282529182902094909455805187815290519288169391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a3600191505b509392505050565b60008282111561196b57fe5b508082035b92915050565b600082820183811080159061198b5750828110155b151561199357fe5b8091505b5092915050565b60045433600160a060020a039081169116146119ba5760006000fd5b6004805474ff0000000000000000000000000000000000000000191660a060020a1790555b5b565b60015b90565b600160a060020a033316600090815260016020526040812054611a0b908361195f565b600160a060020a033381166000908152600160205260408082209390935590851681522054611a3a9083611976565b600160a060020a038085166000818152600160209081526040918290209490945580518681529051919333909316927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a35060015b92915050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10611add57805160ff1916838001178555611b0a565b82800160010185558215611b0a579182015b82811115611b0a578251825591602001919060010190611aef565b5b50611b17929150611b45565b5090565b815481835581811511610a2157600083815260209020610a21918101908301611b45565b5b505050565b6112ce91905b80821115611b175760008155600101611b4b565b5090565b905600a165627a7a723058203028bb284b175e68028f82d996d747776a424d31a87d8b1c56c23a83afe1bc8e002900000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001310000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013100000000000000000000000000000000000000000000000000000000000000", "external_libraries": { "SafeMathLibExt": "0x54ca5a7c536dbed5897b78d30a93dcd0e46fbdac" }, @@ -13,6 +14,7 @@ "compiler_version": "v0.4.11+commit.68ef5810", "contract": "// Temporarily have SafeMath here until all contracts have been migrated to SafeMathLib version from OpenZeppelin\npragma solidity ^0.4.8;\n/**\n * Math operations with safety checks\n */\ncontract SafeMath {\n function safeMul(uint a, uint b) internal returns (uint) {\n uint c = a * b;\n assert(a == 0 || c / a == b);\n return c;\n }\n function safeDiv(uint a, uint b) internal returns (uint) {\n assert(b > 0);\n uint c = a / b;\n assert(a == b * c + a % b);\n return c;\n }\n function safeSub(uint a, uint b) internal returns (uint) {\n assert(b <= a);\n return a - b;\n }\n function safeAdd(uint a, uint b) internal returns (uint) {\n uint c = a + b;\n assert(c>=a && c>=b);\n return c;\n }\n function max64(uint64 a, uint64 b) internal constant returns (uint64) {\n return a >= b ? a : b;\n }\n function min64(uint64 a, uint64 b) internal constant returns (uint64) {\n return a < b ? a : b;\n }\n function max256(uint256 a, uint256 b) internal constant returns (uint256) {\n return a >= b ? a : b;\n }\n function min256(uint256 a, uint256 b) internal constant returns (uint256) {\n return a < b ? a : b;\n }\n}\n/**\n * @title ERC20Basic\n * @dev Simpler version of ERC20 interface\n * @dev see https://github.com/ethereum/EIPs/issues/179\n */\ncontract ERC20Basic {\n uint256 public totalSupply;\n function balanceOf(address who) public constant returns (uint256);\n function transfer(address to, uint256 value) public returns (bool);\n event Transfer(address indexed from, address indexed to, uint256 value);\n}\n/**\n * @title Ownable\n * @dev The Ownable contract has an owner address, and provides basic authorization control\n * functions, this simplifies the implementation of \"user permissions\".\n */\ncontract Ownable {\n address public owner;\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n /**\n * @dev The Ownable constructor sets the original `owner` of the contract to the sender\n * account.\n */\n function Ownable() {\n owner = msg.sender;\n }\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(msg.sender == owner);\n _;\n }\n /**\n * @dev Allows the current owner to transfer control of the contract to a newOwner.\n * @param newOwner The address to transfer ownership to.\n */\n function transferOwnership(address newOwner) onlyOwner public {\n require(newOwner != address(0));\n OwnershipTransferred(owner, newOwner);\n owner = newOwner;\n }\n}\n/**\n * This smart contract code is Copyright 2017 TokenMarket Ltd. For more information see https://tokenmarket.net\n *\n * Licensed under the Apache License, version 2.0: https://github.com/TokenMarketNet/ico/blob/master/LICENSE.txt\n */\n/**\n * This smart contract code is Copyright 2017 TokenMarket Ltd. For more information see https://tokenmarket.net\n *\n * Licensed under the Apache License, version 2.0: https://github.com/TokenMarketNet/ico/blob/master/LICENSE.txt\n */\n/**\n * This smart contract code is Copyright 2017 TokenMarket Ltd. For more information see https://tokenmarket.net\n *\n * Licensed under the Apache License, version 2.0: https://github.com/TokenMarketNet/ico/blob/master/LICENSE.txt\n */\n/**\n * Safe unsigned safe math.\n *\n * https://blog.aragon.one/library-driven-development-in-solidity-2bebcaf88736#.750gwtwli\n *\n * Originally from https://raw.githubusercontent.com/AragonOne/zeppelin-solidity/master/contracts/SafeMathLib.sol\n *\n * Maintained here until merged to mainline zeppelin-solidity.\n *\n */\nlibrary SafeMathLibExt {\n function times(uint a, uint b) returns (uint) {\n uint c = a * b;\n assert(a == 0 || c / a == b);\n return c;\n }\n function divides(uint a, uint b) returns (uint) {\n assert(b > 0);\n uint c = a / b;\n assert(a == b * c + a % b);\n return c;\n }\n function minus(uint a, uint b) returns (uint) {\n assert(b <= a);\n return a - b;\n }\n function plus(uint a, uint b) returns (uint) {\n uint c = a + b;\n assert(c>=a);\n return c;\n }\n}\n/**\n * This smart contract code is Copyright 2017 TokenMarket Ltd. For more information see https://tokenmarket.net\n *\n * Licensed under the Apache License, version 2.0: https://github.com/TokenMarketNet/ico/blob/master/LICENSE.txt\n */\n/*\n * Haltable\n *\n * Abstract contract that allows children to implement an\n * emergency stop mechanism. Differs from Pausable by causing a throw when in halt mode.\n *\n *\n * Originally envisioned in FirstBlood ICO contract.\n */\ncontract Haltable is Ownable {\n bool public halted;\n modifier stopInEmergency {\n if (halted) throw;\n _;\n }\n modifier stopNonOwnersInEmergency {\n if (halted && msg.sender != owner) throw;\n _;\n }\n modifier onlyInEmergency {\n if (!halted) throw;\n _;\n }\n // called by the owner on emergency, triggers stopped state\n function halt() external onlyOwner {\n halted = true;\n }\n // called by the owner on end of emergency, returns to normal state\n function unhalt() external onlyOwner onlyInEmergency {\n halted = false;\n }\n}\n/**\n * This smart contract code is Copyright 2017 TokenMarket Ltd. For more information see https://tokenmarket.net\n *\n * Licensed under the Apache License, version 2.0: https://github.com/TokenMarketNet/ico/blob/master/LICENSE.txt\n */\n/**\n * Interface for defining crowdsale pricing.\n */\ncontract PricingStrategy {\n /** Interface declaration. */\n function isPricingStrategy() public constant returns (bool) {\n return true;\n }\n /** Self check if all references are correctly set.\n *\n * Checks that pricing strategy matches crowdsale parameters.\n */\n function isSane(address crowdsale) public constant returns (bool) {\n return true;\n }\n /**\n * @dev Pricing tells if this is a presale purchase or not.\n @param purchaser Address of the purchaser\n @return False by default, true if a presale purchaser\n */\n function isPresalePurchase(address purchaser) public constant returns (bool) {\n return false;\n }\n /**\n * When somebody tries to buy tokens for X eth, calculate how many tokens they get.\n *\n *\n * @param value - What is the value of the transaction send in as wei\n * @param tokensSold - how much tokens have been sold this far\n * @param weiRaised - how much money has been raised this far in the main token sale - this number excludes presale\n * @param msgSender - who is the investor of this transaction\n * @param decimals - how many decimal units the token has\n * @return Amount of tokens the investor receives\n */\n function calculatePrice(uint value, uint weiRaised, uint tokensSold, address msgSender, uint decimals) public constant returns (uint tokenAmount);\n}\n/**\n * This smart contract code is Copyright 2017 TokenMarket Ltd. For more information see https://tokenmarket.net\n *\n * Licensed under the Apache License, version 2.0: https://github.com/TokenMarketNet/ico/blob/master/LICENSE.txt\n */\n/**\n * Finalize agent defines what happens at the end of succeseful crowdsale.\n *\n * - Allocate tokens for founders, bounties and community\n * - Make tokens transferable\n * - etc.\n */\ncontract FinalizeAgent {\n function isFinalizeAgent() public constant returns(bool) {\n return true;\n }\n /** Return true if we can run finalizeCrowdsale() properly.\n *\n * This is a safety check function that doesn't allow crowdsale to begin\n * unless the finalizer has been set up properly.\n */\n function isSane() public constant returns (bool);\n /** Called once by crowdsale finalize() if the sale was success. */\n function finalizeCrowdsale();\n}\n/**\n * This smart contract code is Copyright 2017 TokenMarket Ltd. For more information see https://tokenmarket.net\n *\n * Licensed under the Apache License, version 2.0: https://github.com/TokenMarketNet/ico/blob/master/LICENSE.txt\n */\n/**\n * @title ERC20 interface\n * @dev see https://github.com/ethereum/EIPs/issues/20\n */\ncontract ERC20 is ERC20Basic {\n function allowance(address owner, address spender) public constant returns (uint256);\n function transferFrom(address from, address to, uint256 value) public returns (bool);\n function approve(address spender, uint256 value) public returns (bool);\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n/**\n * A token that defines fractional units as decimals.\n */\ncontract FractionalERC20Ext is ERC20 {\n uint public decimals;\n uint public minCap;\n}\n/**\n * Abstract base contract for token sales.\n *\n * Handle\n * - start and end dates\n * - accepting investments\n * - minimum funding goal and refund\n * - various statistics during the crowdfund\n * - different pricing strategies\n * - different investment policies (require server side customer id, allow only whitelisted addresses)\n *\n */\ncontract CrowdsaleExt is Haltable {\n /* Max investment count when we are still allowed to change the multisig address */\n uint public MAX_INVESTMENTS_BEFORE_MULTISIG_CHANGE = 5;\n using SafeMathLibExt for uint;\n /* The token we are selling */\n FractionalERC20Ext public token;\n /* How we are going to price our offering */\n PricingStrategy public pricingStrategy;\n /* Post-success callback */\n FinalizeAgent public finalizeAgent;\n /* tokens will be transfered from this address */\n address public multisigWallet;\n /* if the funding goal is not reached, investors may withdraw their funds */\n uint public minimumFundingGoal;\n /* the UNIX timestamp start date of the crowdsale */\n uint public startsAt;\n /* the UNIX timestamp end date of the crowdsale */\n uint public endsAt;\n /* the number of tokens already sold through this contract*/\n uint public tokensSold = 0;\n /* How many wei of funding we have raised */\n uint public weiRaised = 0;\n /* Calculate incoming funds from presale contracts and addresses */\n uint public presaleWeiRaised = 0;\n /* How many distinct addresses have invested */\n uint public investorCount = 0;\n /* How much wei we have returned back to the contract after a failed crowdfund. */\n uint public loadedRefund = 0;\n /* How much wei we have given back to investors.*/\n uint public weiRefunded = 0;\n /* Has this crowdsale been finalized */\n bool public finalized;\n /* Do we need to have unique contributor id for each customer */\n bool public requireCustomerId;\n bool public isWhiteListed;\n address[] public joinedCrowdsales;\n uint public joinedCrowdsalesLen = 0;\n address public lastCrowdsale;\n /**\n * Do we verify that contributor has been cleared on the server side (accredited investors only).\n * This method was first used in FirstBlood crowdsale to ensure all contributors have accepted terms on sale (on the web).\n */\n bool public requiredSignedAddress;\n /* Server side address that signed allowed contributors (Ethereum addresses) that can participate the crowdsale */\n address public signerAddress;\n /** How much ETH each address has invested to this crowdsale */\n mapping (address => uint256) public investedAmountOf;\n /** How much tokens this crowdsale has credited for each investor address */\n mapping (address => uint256) public tokenAmountOf;\n struct WhiteListData {\n bool status;\n uint minCap;\n uint maxCap;\n }\n //is crowdsale updatable\n bool public isUpdatable;\n /** Addresses that are allowed to invest even before ICO offical opens. For testing, for ICO partners, etc. */\n mapping (address => WhiteListData) public earlyParticipantWhitelist;\n /** This is for manul testing for the interaction from owner wallet. You can set it to any value and inspect this in blockchain explorer to see that crowdsale interaction works. */\n uint public ownerTestValue;\n /** State machine\n *\n * - Preparing: All contract initialization calls and variables have not been set yet\n * - Prefunding: We have not passed start time yet\n * - Funding: Active crowdsale\n * - Success: Minimum funding goal reached\n * - Failure: Minimum funding goal not reached before ending time\n * - Finalized: The finalized has been called and succesfully executed\n * - Refunding: Refunds are loaded on the contract for reclaim.\n */\n enum State{Unknown, Preparing, PreFunding, Funding, Success, Failure, Finalized, Refunding}\n // A new investment was made\n event Invested(address investor, uint weiAmount, uint tokenAmount, uint128 customerId);\n // Refund was processed for a contributor\n event Refund(address investor, uint weiAmount);\n // The rules were changed what kind of investments we accept\n event InvestmentPolicyChanged(bool newRequireCustomerId, bool newRequiredSignedAddress, address newSignerAddress);\n // Address early participation whitelist status changed\n event Whitelisted(address addr, bool status);\n // Crowdsale start time has been changed\n event StartsAtChanged(uint newStartsAt);\n // Crowdsale end time has been changed\n event EndsAtChanged(uint newEndsAt);\n function CrowdsaleExt(address _token, PricingStrategy _pricingStrategy, address _multisigWallet, uint _start, uint _end, uint _minimumFundingGoal, bool _isUpdatable, bool _isWhiteListed) {\n owner = msg.sender;\n token = FractionalERC20Ext(_token);\n setPricingStrategy(_pricingStrategy);\n multisigWallet = _multisigWallet;\n if(multisigWallet == 0) {\n throw;\n }\n if(_start == 0) {\n throw;\n }\n startsAt = _start;\n if(_end == 0) {\n throw;\n }\n endsAt = _end;\n // Don't mess the dates\n if(startsAt >= endsAt) {\n throw;\n }\n // Minimum funding goal can be zero\n minimumFundingGoal = _minimumFundingGoal;\n isUpdatable = _isUpdatable;\n isWhiteListed = _isWhiteListed;\n }\n /**\n * Don't expect to just send in money and get tokens.\n */\n function() payable {\n throw;\n }\n /**\n * Make an investment.\n *\n * Crowdsale must be running for one to invest.\n * We must have not pressed the emergency brake.\n *\n * @param receiver The Ethereum address who receives the tokens\n * @param customerId (optional) UUID v4 to track the successful payments on the server side\n *\n */\n function investInternal(address receiver, uint128 customerId) stopInEmergency private {\n // Determine if it's a good time to accept investment from this participant\n if(getState() == State.PreFunding) {\n // Are we whitelisted for early deposit\n throw;\n } else if(getState() == State.Funding) {\n // Retail participants can only come in when the crowdsale is running\n // pass\n if(isWhiteListed) {\n if(!earlyParticipantWhitelist[receiver].status) {\n throw;\n }\n }\n } else {\n // Unwanted state\n throw;\n }\n uint weiAmount = msg.value;\n // Account presale sales separately, so that they do not count against pricing tranches\n uint tokenAmount = pricingStrategy.calculatePrice(weiAmount, weiRaised - presaleWeiRaised, tokensSold, msg.sender, token.decimals());\n if(tokenAmount == 0) {\n // Dust transaction\n throw;\n }\n if(isWhiteListed) {\n if(tokenAmount < earlyParticipantWhitelist[receiver].minCap && tokenAmountOf[receiver] == 0) {\n // tokenAmount < minCap for investor\n throw;\n }\n if(tokenAmount > earlyParticipantWhitelist[receiver].maxCap) {\n // tokenAmount > maxCap for investor\n throw;\n }\n // Check that we did not bust the investor's cap\n if (isBreakingInvestorCap(receiver, tokenAmount)) {\n throw;\n }\n } else {\n if(tokenAmount < token.minCap() && tokenAmountOf[receiver] == 0) {\n throw;\n }\n }\n // Check that we did not bust the cap\n if(isBreakingCap(weiAmount, tokenAmount, weiRaised, tokensSold)) {\n throw;\n }\n // Update investor\n investedAmountOf[receiver] = investedAmountOf[receiver].plus(weiAmount);\n tokenAmountOf[receiver] = tokenAmountOf[receiver].plus(tokenAmount);\n // Update totals\n weiRaised = weiRaised.plus(weiAmount);\n tokensSold = tokensSold.plus(tokenAmount);\n if(pricingStrategy.isPresalePurchase(receiver)) {\n presaleWeiRaised = presaleWeiRaised.plus(weiAmount);\n }\n if(investedAmountOf[receiver] == 0) {\n // A new investor\n investorCount++;\n }\n assignTokens(receiver, tokenAmount);\n // Pocket the money\n if(!multisigWallet.send(weiAmount)) throw;\n if (isWhiteListed) {\n uint num = 0;\n for (var i = 0; i < joinedCrowdsalesLen; i++) {\n if (this == joinedCrowdsales[i])\n num = i;\n }\n if (num + 1 < joinedCrowdsalesLen) {\n for (var j = num + 1; j < joinedCrowdsalesLen; j++) {\n CrowdsaleExt crowdsale = CrowdsaleExt(joinedCrowdsales[j]);\n crowdsale.updateEarlyParicipantWhitelist(msg.sender, this, tokenAmount);\n }\n }\n }\n // Tell us invest was success\n Invested(receiver, weiAmount, tokenAmount, customerId);\n }\n /**\n * Preallocate tokens for the early investors.\n *\n * Preallocated tokens have been sold before the actual crowdsale opens.\n * This function mints the tokens and moves the crowdsale needle.\n *\n * Investor count is not handled; it is assumed this goes for multiple investors\n * and the token distribution happens outside the smart contract flow.\n *\n * No money is exchanged, as the crowdsale team already have received the payment.\n *\n * @param fullTokens tokens as full tokens - decimal places added internally\n * @param weiPrice Price of a single full token in wei\n *\n */\n function preallocate(address receiver, uint fullTokens, uint weiPrice) public onlyOwner {\n uint tokenAmount = fullTokens * 10**token.decimals();\n uint weiAmount = weiPrice * fullTokens; // This can be also 0, we give out tokens for free\n weiRaised = weiRaised.plus(weiAmount);\n tokensSold = tokensSold.plus(tokenAmount);\n investedAmountOf[receiver] = investedAmountOf[receiver].plus(weiAmount);\n tokenAmountOf[receiver] = tokenAmountOf[receiver].plus(tokenAmount);\n assignTokens(receiver, tokenAmount);\n // Tell us invest was success\n Invested(receiver, weiAmount, tokenAmount, 0);\n }\n /**\n * Allow anonymous contributions to this crowdsale.\n */\n function investWithSignedAddress(address addr, uint128 customerId, uint8 v, bytes32 r, bytes32 s) public payable {\n bytes32 hash = sha256(addr);\n if (ecrecover(hash, v, r, s) != signerAddress) throw;\n if(customerId == 0) throw; // UUIDv4 sanity check\n investInternal(addr, customerId);\n }\n /**\n * Track who is the customer making the payment so we can send thank you email.\n */\n function investWithCustomerId(address addr, uint128 customerId) public payable {\n if(requiredSignedAddress) throw; // Crowdsale allows only server-side signed participants\n if(customerId == 0) throw; // UUIDv4 sanity check\n investInternal(addr, customerId);\n }\n /**\n * Allow anonymous contributions to this crowdsale.\n */\n function invest(address addr) public payable {\n if(requireCustomerId) throw; // Crowdsale needs to track participants for thank you email\n if(requiredSignedAddress) throw; // Crowdsale allows only server-side signed participants\n investInternal(addr, 0);\n }\n /**\n * Invest to tokens, recognize the payer and clear his address.\n *\n */\n function buyWithSignedAddress(uint128 customerId, uint8 v, bytes32 r, bytes32 s) public payable {\n investWithSignedAddress(msg.sender, customerId, v, r, s);\n }\n /**\n * Invest to tokens, recognize the payer.\n *\n */\n function buyWithCustomerId(uint128 customerId) public payable {\n investWithCustomerId(msg.sender, customerId);\n }\n /**\n * The basic entry point to participate the crowdsale process.\n *\n * Pay for funding, get invested tokens back in the sender address.\n */\n function buy() public payable {\n invest(msg.sender);\n }\n /**\n * Finalize a succcesful crowdsale.\n *\n * The owner can triggre a call the contract that provides post-crowdsale actions, like releasing the tokens.\n */\n function finalize() public inState(State.Success) onlyOwner stopInEmergency {\n // Already finalized\n if(finalized) {\n throw;\n }\n // Finalizing is optional. We only call it if we are given a finalizing agent.\n if(address(finalizeAgent) != 0) {\n finalizeAgent.finalizeCrowdsale();\n }\n finalized = true;\n }\n /**\n * Allow to (re)set finalize agent.\n *\n * Design choice: no state restrictions on setting this, so that we can fix fat finger mistakes.\n */\n function setFinalizeAgent(FinalizeAgent addr) onlyOwner {\n finalizeAgent = addr;\n // Don't allow setting bad agent\n if(!finalizeAgent.isFinalizeAgent()) {\n throw;\n }\n }\n /**\n * Set policy do we need to have server-side customer ids for the investments.\n *\n */\n function setRequireCustomerId(bool value) onlyOwner {\n requireCustomerId = value;\n InvestmentPolicyChanged(requireCustomerId, requiredSignedAddress, signerAddress);\n }\n /**\n * Set policy if all investors must be cleared on the server side first.\n *\n * This is e.g. for the accredited investor clearing.\n *\n */\n function setRequireSignedAddress(bool value, address _signerAddress) onlyOwner {\n requiredSignedAddress = value;\n signerAddress = _signerAddress;\n InvestmentPolicyChanged(requireCustomerId, requiredSignedAddress, signerAddress);\n }\n /**\n * Allow addresses to do early participation.\n *\n * TODO: Fix spelling error in the name\n */\n function setEarlyParicipantWhitelist(address addr, bool status, uint minCap, uint maxCap) onlyOwner {\n if (!isWhiteListed) throw;\n earlyParticipantWhitelist[addr] = WhiteListData({status:status, minCap:minCap, maxCap:maxCap});\n Whitelisted(addr, status);\n }\n function setEarlyParicipantsWhitelist(address[] addrs, bool[] statuses, uint[] minCaps, uint[] maxCaps) onlyOwner {\n if (!isWhiteListed) throw;\n for (uint iterator = 0; iterator < addrs.length; iterator++) {\n setEarlyParicipantWhitelist(addrs[iterator], statuses[iterator], minCaps[iterator], maxCaps[iterator]);\n }\n }\n function updateEarlyParicipantWhitelist(address addr, address contractAddr, uint tokensBought) {\n if (tokensBought < earlyParticipantWhitelist[addr].minCap) throw;\n if (!isWhiteListed) throw;\n if (addr != msg.sender && contractAddr != msg.sender) throw;\n uint newMaxCap = earlyParticipantWhitelist[addr].maxCap;\n newMaxCap = newMaxCap.minus(tokensBought);\n earlyParticipantWhitelist[addr] = WhiteListData({status:earlyParticipantWhitelist[addr].status, minCap:0, maxCap:newMaxCap});\n }\n function updateJoinedCrowdsales(address addr) onlyOwner {\n joinedCrowdsales[joinedCrowdsalesLen++] = addr;\n }\n function setLastCrowdsale(address addr) onlyOwner {\n lastCrowdsale = addr;\n }\n function clearJoinedCrowdsales() onlyOwner {\n joinedCrowdsalesLen = 0;\n }\n function updateJoinedCrowdsalesMultiple(address[] addrs) onlyOwner {\n clearJoinedCrowdsales();\n for (uint iter = 0; iter < addrs.length; iter++) {\n if(joinedCrowdsalesLen == joinedCrowdsales.length) {\n joinedCrowdsales.length += 1;\n }\n joinedCrowdsales[joinedCrowdsalesLen++] = addrs[iter];\n if (iter == addrs.length - 1)\n setLastCrowdsale(addrs[iter]);\n }\n }\n function setStartsAt(uint time) onlyOwner {\n if (finalized) throw;\n if (!isUpdatable) throw;\n if(now > time) {\n throw; // Don't change past\n }\n if(time > endsAt) {\n throw;\n }\n CrowdsaleExt lastCrowdsaleCntrct = CrowdsaleExt(lastCrowdsale);\n if (lastCrowdsaleCntrct.finalized()) throw;\n startsAt = time;\n StartsAtChanged(startsAt);\n }\n /**\n * Allow crowdsale owner to close early or extend the crowdsale.\n *\n * This is useful e.g. for a manual soft cap implementation:\n * - after X amount is reached determine manual closing\n *\n * This may put the crowdsale to an invalid state,\n * but we trust owners know what they are doing.\n *\n */\n function setEndsAt(uint time) onlyOwner {\n if (finalized) throw;\n if (!isUpdatable) throw;\n if(now > time) {\n throw; // Don't change past\n }\n if(startsAt > time) {\n throw;\n }\n CrowdsaleExt lastCrowdsaleCntrct = CrowdsaleExt(lastCrowdsale);\n if (lastCrowdsaleCntrct.finalized()) throw;\n uint num = 0;\n for (var i = 0; i < joinedCrowdsalesLen; i++) {\n if (this == joinedCrowdsales[i])\n num = i;\n }\n if (num + 1 < joinedCrowdsalesLen) {\n for (var j = num + 1; j < joinedCrowdsalesLen; j++) {\n CrowdsaleExt crowdsale = CrowdsaleExt(joinedCrowdsales[j]);\n if (time > crowdsale.startsAt()) throw;\n }\n }\n endsAt = time;\n EndsAtChanged(endsAt);\n }\n /**\n * Allow to (re)set pricing strategy.\n *\n * Design choice: no state restrictions on the set, so that we can fix fat finger mistakes.\n */\n function setPricingStrategy(PricingStrategy _pricingStrategy) onlyOwner {\n pricingStrategy = _pricingStrategy;\n // Don't allow setting bad agent\n if(!pricingStrategy.isPricingStrategy()) {\n throw;\n }\n }\n /**\n * Allow to change the team multisig address in the case of emergency.\n *\n * This allows to save a deployed crowdsale wallet in the case the crowdsale has not yet begun\n * (we have done only few test transactions). After the crowdsale is going\n * then multisig address stays locked for the safety reasons.\n */\n function setMultisig(address addr) public onlyOwner {\n // Change\n if(investorCount > MAX_INVESTMENTS_BEFORE_MULTISIG_CHANGE) {\n throw;\n }\n multisigWallet = addr;\n }\n /**\n * Allow load refunds back on the contract for the refunding.\n *\n * The team can transfer the funds back on the smart contract in the case the minimum goal was not reached..\n */\n function loadRefund() public payable inState(State.Failure) {\n if(msg.value == 0) throw;\n loadedRefund = loadedRefund.plus(msg.value);\n }\n /**\n * Investors can claim refund.\n *\n * Note that any refunds from proxy buyers should be handled separately,\n * and not through this contract.\n */\n function refund() public inState(State.Refunding) {\n uint256 weiValue = investedAmountOf[msg.sender];\n if (weiValue == 0) throw;\n investedAmountOf[msg.sender] = 0;\n weiRefunded = weiRefunded.plus(weiValue);\n Refund(msg.sender, weiValue);\n if (!msg.sender.send(weiValue)) throw;\n }\n /**\n * @return true if the crowdsale has raised enough money to be a successful.\n */\n function isMinimumGoalReached() public constant returns (bool reached) {\n return weiRaised >= minimumFundingGoal;\n }\n /**\n * Check if the contract relationship looks good.\n */\n function isFinalizerSane() public constant returns (bool sane) {\n return finalizeAgent.isSane();\n }\n /**\n * Check if the contract relationship looks good.\n */\n function isPricingSane() public constant returns (bool sane) {\n return pricingStrategy.isSane(address(this));\n }\n /**\n * Crowdfund state machine management.\n *\n * We make it a function and do not assign the result to a variable, so there is no chance of the variable being stale.\n */\n function getState() public constant returns (State) {\n if(finalized) return State.Finalized;\n else if (address(finalizeAgent) == 0) return State.Preparing;\n else if (!finalizeAgent.isSane()) return State.Preparing;\n else if (!pricingStrategy.isSane(address(this))) return State.Preparing;\n else if (block.timestamp < startsAt) return State.PreFunding;\n else if (block.timestamp <= endsAt && !isCrowdsaleFull()) return State.Funding;\n else if (isMinimumGoalReached()) return State.Success;\n else if (!isMinimumGoalReached() && weiRaised > 0 && loadedRefund >= weiRaised) return State.Refunding;\n else return State.Failure;\n }\n /** This is for manual testing of multisig wallet interaction */\n function setOwnerTestValue(uint val) onlyOwner {\n ownerTestValue = val;\n }\n /** Interface marker. */\n function isCrowdsale() public constant returns (bool) {\n return true;\n }\n //\n // Modifiers\n //\n /** Modified allowing execution only if the crowdsale is currently running. */\n modifier inState(State state) {\n if(getState() != state) throw;\n _;\n }\n //\n // Abstract functions\n //\n /**\n * Check if the current invested breaks our cap rules.\n *\n *\n * The child contract must define their own cap setting rules.\n * We allow a lot of flexibility through different capping strategies (ETH, token count)\n * Called from invest().\n *\n * @param weiAmount The amount of wei the investor tries to invest in the current transaction\n * @param tokenAmount The amount of tokens we try to give to the investor in the current transaction\n * @param weiRaisedTotal What would be our total raised balance after this transaction\n * @param tokensSoldTotal What would be our total sold tokens count after this transaction\n *\n * @return true if taking this investment would break our cap rules\n */\n function isBreakingCap(uint weiAmount, uint tokenAmount, uint weiRaisedTotal, uint tokensSoldTotal) constant returns (bool limitBroken);\n function isBreakingInvestorCap(address receiver, uint tokenAmount) constant returns (bool limitBroken);\n /**\n * Check if the current crowdsale is full and we can no longer sell any tokens.\n */\n function isCrowdsaleFull() public constant returns (bool);\n /**\n * Create new tokens or transfer issued tokens to the investor depending on the cap model.\n */\n function assignTokens(address receiver, uint tokenAmount) private;\n}\n/**\n * This smart contract code is Copyright 2017 TokenMarket Ltd. For more information see https://tokenmarket.net\n *\n * Licensed under the Apache License, version 2.0: https://github.com/TokenMarketNet/ico/blob/master/LICENSE.txt\n */\n/**\n * This smart contract code is Copyright 2017 TokenMarket Ltd. For more information see https://tokenmarket.net\n *\n * Licensed under the Apache License, version 2.0: https://github.com/TokenMarketNet/ico/blob/master/LICENSE.txt\n */\n/**\n * Standard ERC20 token with Short Hand Attack and approve() race condition mitigation.\n *\n * Based on code by FirstBlood:\n * https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol\n */\ncontract StandardToken is ERC20, SafeMath {\n /* Token supply got increased and a new owner received these tokens */\n event Minted(address receiver, uint amount);\n /* Actual balances of token holders */\n mapping(address => uint) balances;\n /* approve() allowances */\n mapping (address => mapping (address => uint)) allowed;\n /* Interface declaration */\n function isToken() public constant returns (bool weAre) {\n return true;\n }\n function transfer(address _to, uint _value) returns (bool success) {\n balances[msg.sender] = safeSub(balances[msg.sender], _value);\n balances[_to] = safeAdd(balances[_to], _value);\n Transfer(msg.sender, _to, _value);\n return true;\n }\n function transferFrom(address _from, address _to, uint _value) returns (bool success) {\n uint _allowance = allowed[_from][msg.sender];\n balances[_to] = safeAdd(balances[_to], _value);\n balances[_from] = safeSub(balances[_from], _value);\n allowed[_from][msg.sender] = safeSub(_allowance, _value);\n Transfer(_from, _to, _value);\n return true;\n }\n function balanceOf(address _owner) constant returns (uint balance) {\n return balances[_owner];\n }\n function approve(address _spender, uint _value) returns (bool success) {\n // To change the approve amount you first have to reduce the addresses`\n // allowance to zero by calling `approve(_spender, 0)` if it is not\n // already 0 to mitigate the race condition described here:\n // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n if ((_value != 0) && (allowed[msg.sender][_spender] != 0)) throw;\n allowed[msg.sender][_spender] = _value;\n Approval(msg.sender, _spender, _value);\n return true;\n }\n function allowance(address _owner, address _spender) constant returns (uint remaining) {\n return allowed[_owner][_spender];\n }\n}\n/**\n * A token that can increase its supply by another contract.\n *\n * This allows uncapped crowdsale by dynamically increasing the supply when money pours in.\n * Only mint agents, contracts whitelisted by owner, can mint new tokens.\n *\n */\ncontract MintableTokenExt is StandardToken, Ownable {\n using SafeMathLibExt for uint;\n bool public mintingFinished = false;\n /** List of agents that are allowed to create new tokens */\n mapping (address => bool) public mintAgents;\n event MintingAgentChanged(address addr, bool state );\n struct ReservedTokensData {\n uint inTokens;\n uint inPercentage;\n }\n mapping (address => ReservedTokensData) public reservedTokensList;\n address[] public reservedTokensDestinations;\n uint public reservedTokensDestinationsLen = 0;\n function setReservedTokensList(address addr, uint inTokens, uint inPercentage) onlyOwner {\n reservedTokensDestinations.push(addr);\n reservedTokensDestinationsLen++;\n reservedTokensList[addr] = ReservedTokensData({inTokens:inTokens, inPercentage:inPercentage});\n }\n function getReservedTokensListValInTokens(address addr) constant returns (uint inTokens) {\n return reservedTokensList[addr].inTokens;\n }\n function getReservedTokensListValInPercentage(address addr) constant returns (uint inPercentage) {\n return reservedTokensList[addr].inPercentage;\n }\n function setReservedTokensListMultiple(address[] addrs, uint[] inTokens, uint[] inPercentage) onlyOwner {\n for (uint iterator = 0; iterator < addrs.length; iterator++) {\n setReservedTokensList(addrs[iterator], inTokens[iterator], inPercentage[iterator]);\n }\n }\n /**\n * Create new tokens and allocate them to an address..\n *\n * Only callably by a crowdsale contract (mint agent).\n */\n function mint(address receiver, uint amount) onlyMintAgent canMint public {\n totalSupply = totalSupply.plus(amount);\n balances[receiver] = balances[receiver].plus(amount);\n // This will make the mint transaction apper in EtherScan.io\n // We can remove this after there is a standardized minting event\n Transfer(0, receiver, amount);\n }\n /**\n * Owner can allow a crowdsale contract to mint new tokens.\n */\n function setMintAgent(address addr, bool state) onlyOwner canMint public {\n mintAgents[addr] = state;\n MintingAgentChanged(addr, state);\n }\n modifier onlyMintAgent() {\n // Only crowdsale contracts are allowed to mint new tokens\n if(!mintAgents[msg.sender]) {\n throw;\n }\n _;\n }\n /** Make sure we are not done yet. */\n modifier canMint() {\n if(mintingFinished) throw;\n _;\n }\n}\n/**\n * ICO crowdsale contract that is capped by amout of tokens.\n *\n * - Tokens are dynamically created during the crowdsale\n *\n *\n */\ncontract MintedTokenCappedCrowdsaleExt is CrowdsaleExt {\n /* Maximum amount of tokens this crowdsale can sell. */\n uint public maximumSellableTokens;\n function MintedTokenCappedCrowdsaleExt(address _token, PricingStrategy _pricingStrategy, address _multisigWallet, uint _start, uint _end, uint _minimumFundingGoal, uint _maximumSellableTokens, bool _isUpdatable, bool _isWhiteListed) CrowdsaleExt(_token, _pricingStrategy, _multisigWallet, _start, _end, _minimumFundingGoal, _isUpdatable, _isWhiteListed) {\n maximumSellableTokens = _maximumSellableTokens;\n }\n // Crowdsale maximumSellableTokens has been changed\n event MaximumSellableTokensChanged(uint newMaximumSellableTokens);\n /**\n * Called from invest() to confirm if the curret investment does not break our cap rule.\n */\n function isBreakingCap(uint weiAmount, uint tokenAmount, uint weiRaisedTotal, uint tokensSoldTotal) constant returns (bool limitBroken) {\n return tokensSoldTotal > maximumSellableTokens;\n }\n function isBreakingInvestorCap(address addr, uint tokenAmount) constant returns (bool limitBroken) {\n if (!isWhiteListed) throw;\n uint maxCap = earlyParticipantWhitelist[addr].maxCap;\n return (tokenAmountOf[addr].plus(tokenAmount)) > maxCap;\n }\n function isCrowdsaleFull() public constant returns (bool) {\n return tokensSold >= maximumSellableTokens;\n }\n /**\n * Dynamically create tokens and assign them to the investor.\n */\n function assignTokens(address receiver, uint tokenAmount) private {\n MintableTokenExt mintableToken = MintableTokenExt(token);\n mintableToken.mint(receiver, tokenAmount);\n }\n function setMaximumSellableTokens(uint tokens) onlyOwner {\n if (finalized) throw;\n if (!isUpdatable) throw;\n CrowdsaleExt lastCrowdsaleCntrct = CrowdsaleExt(lastCrowdsale);\n if (lastCrowdsaleCntrct.finalized()) throw;\n maximumSellableTokens = tokens;\n MaximumSellableTokensChanged(maximumSellableTokens);\n }\n}", "expected_bytecode": "606060405236156102fb5763ffffffff60e060020a6000350416630226401d811461030957806303ca0eed1461032b57806303f9c7931461034f578063045b1a0c1461036557806304fc7c6d14610398578063062b01ce146103ed5780630a09284a146104115780630cc91bb9146104335780630e1d2ec81461045c57806313f4e977146104805780631865c57d146104a257806319b667da146104d65780631a49803b146104f45780631aae34601461052b57806321d5c0f6146105595780632c2de40a1461058557806332013ac31461059a5780633ad075ea146105be5780634042b66f146105e05780634551dd59146106025780634bb278f3146106265780634f97f97f1461063857806350c6773414610664578063518ab2a81461068257806357dc2658146106a4578063590e1ae3146106c25780635b7633d0146106d45780635da89ac0146107005780635ed7ca5b146107225780636203f09f146107345780636e50eb3f1461075657806378b99c241461076b578063797d94371461079757806379e0f59a146107b95780637c2e08a3146108b65780637f7d711e146108da578063831ed348146108f157806387612102146109035780638d51faec1461090d5780638da5cb5b146109225780639075becf1461094e57806397b150ca1461097a57806399e9376c146109a85780639d3c663f146109c7578063a6f2ae3a146109f7578063a7ba44c314610a01578063af46868214610a25578063b3f05b9714610a47578063b9b8af0b14610a6b578063bede2cac14610a8f578063bf5fc2ee14610abe578063cb16e6d014610ad3578063cb3e64fd14610b0f578063d222dc0414610b21578063d245da2814610b45578063d5d0902114610b6c578063d7e64c0014610b90578063dee846c514610bb2578063ebdfa45514610bd0578063ed68ff2c14610bf2578063ef674e6614610c15578063ef86944314610c39578063f2fde38b14610c64578063f3283fba14610c82578063f486972614610ca0578063f7c00e2f14610ccb578063fc0c546a14610ced575b6103075b60006000fd5b565b005b341561031157fe5b610319610d19565b60408051918252519081900360200190f35b341561033357fe5b61033b610d1f565b604080519115158252519081900360200190f35b610307600160a060020a0360043516610d2d565b005b341561036d57fe5b61033b600160a060020a0360043516602435610d6a565b604080519115158252519081900360200190f35b34156103a057fe5b610307600480803590602001908201803590602001908080602002602001604051908101604052809392919081815260200183836020028082843750949650610e3295505050505050565b005b34156103f557fe5b61033b610f22565b604080519115158252519081900360200190f35b341561041957fe5b610319610fab565b60408051918252519081900360200190f35b341561043b57fe5b610307600160a060020a03600435166024351515604435606435610fb1565b005b341561046457fe5b61033b611075565b604080519115158252519081900360200190f35b341561048857fe5b61031961107e565b60408051918252519081900360200190f35b34156104aa57fe5b6104b2611084565b604051808260078111156104c257fe5b60ff16815260200191505060405180910390f35b34156104de57fe5b610307600160a060020a0360043516611257565b005b610307600160a060020a03600435166fffffffffffffffffffffffffffffffff6024351660ff60443516606435608435611314565b005b341561053357fe5b610319600160a060020a036004351661142a565b60408051918252519081900360200190f35b341561056157fe5b61056961143c565b60408051600160a060020a039092168252519081900360200190f35b341561058d57fe5b61030760043561144b565b005b34156105a257fe5b610307600160a060020a0360043516602435604435611548565b005b34156105c657fe5b610319611882565b60408051918252519081900360200190f35b34156105e857fe5b610319611888565b60408051918252519081900360200190f35b341561060a57fe5b61033b61188e565b604080519115158252519081900360200190f35b341561062e57fe5b610307611894565b005b341561064057fe5b61056961198a565b60408051600160a060020a039092168252519081900360200190f35b341561066c57fe5b610307600160a060020a0360043516611999565b005b341561068a57fe5b610319611a55565b60408051918252519081900360200190f35b34156106ac57fe5b610307600160a060020a0360043516611a5b565b005b34156106ca57fe5b610307611a97565b005b34156106dc57fe5b610569611bf0565b60408051600160a060020a039092168252519081900360200190f35b341561070857fe5b610319611bff565b60408051918252519081900360200190f35b341561072a57fe5b610307611c05565b005b341561073c57fe5b610319611c49565b60408051918252519081900360200190f35b341561075e57fe5b610307600435611c4f565b005b341561077357fe5b610569611eb6565b60408051600160a060020a039092168252519081900360200190f35b341561079f57fe5b610319611ec5565b60408051918252519081900360200190f35b34156107c157fe5b610307600480803590602001908201803590602001908080602002602001604051908101604052809392919081815260200183836020028082843750506040805187358901803560208181028481018201909552818452989a998901989297509082019550935083925085019084908082843750506040805187358901803560208181028481018201909552818452989a998901989297509082019550935083925085019084908082843750506040805187358901803560208181028481018201909552818452989a998901989297509082019550935083925085019084908082843750949650611ecb95505050505050565b005b34156108be57fe5b61033b611f87565b604080519115158252519081900360200190f35b34156108e257fe5b6103076004351515611f93565b005b34156108f957fe5b610307612030565b005b610307612055565b005b341561091557fe5b610307600435612104565b005b341561092a57fe5b61056961212a565b60408051600160a060020a039092168252519081900360200190f35b341561095657fe5b610569612139565b60408051600160a060020a039092168252519081900360200190f35b341561098257fe5b610319600160a060020a0360043516612148565b60408051918252519081900360200190f35b6103076fffffffffffffffffffffffffffffffff6004351661215a565b005b34156109cf57fe5b61033b600435602435604435606435612168565b604080519115158252519081900360200190f35b610307612176565b005b3415610a0957fe5b61033b612182565b604080519115158252519081900360200190f35b3415610a2d57fe5b6103196121fa565b60408051918252519081900360200190f35b3415610a4f57fe5b61033b612200565b604080519115158252519081900360200190f35b3415610a7357fe5b61033b612209565b604080519115158252519081900360200190f35b3415610a9757fe5b610569600435612219565b60408051600160a060020a039092168252519081900360200190f35b3415610ac657fe5b61030760043561224b565b005b3415610adb57fe5b610aef600160a060020a0360043516612366565b604080519315158452602084019290925282820152519081900360600190f35b3415610b1757fe5b61030761238b565b005b3415610b2957fe5b61033b6123e3565b604080519115158252519081900360200190f35b3415610b4d57fe5b610307600160a060020a03600435811690602435166044356123f3565b005b3415610b7457fe5b61033b61257b565b604080519115158252519081900360200190f35b3415610b9857fe5b610319612587565b60408051918252519081900360200190f35b3415610bba57fe5b610307600160a060020a036004351661258d565b005b3415610bd857fe5b6103196125f8565b60408051918252519081900360200190f35b3415610bfa57fe5b6103076004351515600160a060020a03602435166125fe565b005b3415610c1d57fe5b61033b6126c2565b604080519115158252519081900360200190f35b610307600160a060020a03600435166fffffffffffffffffffffffffffffffff602435166126d1565b005b3415610c6c57fe5b610307600160a060020a0360043516612717565b005b3415610c8a57fe5b610307600160a060020a03600435166127a2565b005b6103076fffffffffffffffffffffffffffffffff6004351660ff602435166044356064356127f0565b005b3415610cd357fe5b610319612804565b60408051918252519081900360200190f35b3415610cf557fe5b61056961280a565b60408051600160a060020a039092168252519081900360200190f35b60185481565b600f54610100900460ff1681565b600f54610100900460ff1615610d435760006000fd5b60125460a060020a900460ff1615610d5b5760006000fd5b610d66816000612819565b5b50565b600f54600090819062010000900460ff161515610d875760006000fd5b50600160a060020a03831660009081526017602090815260408083206002015460158352818420548251840194909452815160e060020a6366098d4f0281526004810194909452602484018690529051909283927354ca5a7c536dbed5897b78d30a93dcd0e46fbdac926366098d4f926044808201939291829003018186803b1515610e0f57fe5b6102c65a03f41515610e1d57fe5b505050604051805190501191505b5092915050565b6000805433600160a060020a03908116911614610e4f5760006000fd5b610e57612030565b5060005b8151811015610f1c576010546011541415610e84576010805460010190610e829082613141565b505b8181815181101515610e9257fe5b602090810290910101516011805460018101909155601080549091908110610eb657fe5b906000526020600020900160005b6101000a815481600160a060020a030219169083600160a060020a031602179055506001825103811415610f1257610f128282815181101515610f0357fe5b90602001906020020151611a5b565b5b5b600101610e5b565b5b5b5050565b600354604080516000602091820181905282517f8e768288000000000000000000000000000000000000000000000000000000008152600160a060020a033081166004830152935191949390931692638e76828892602480830193919282900301818787803b1515610f9057fe5b6102c65a03f11515610f9e57fe5b5050604051519150505b90565b60085481565b60005433600160a060020a03908116911614610fcd5760006000fd5b600f5462010000900460ff161515610fe55760006000fd5b604080516060810182528415158082526020808301868152838501868152600160a060020a038a166000818152601785528790209551865460ff19169015151786559151600186015551600290940193909355835192835282015281517fa54714518c5d275fdcd3d2a461e4858e4e8cb04fb93cd0bca9d6d34115f26440929181900390910190a15b5b50505050565b60165460ff1681565b60065481565b600f5460009060ff161561109a57506006610fa8565b600454600160a060020a031615156110b457506001610fa8565b600460009054906101000a9004600160a060020a0316600160a060020a03166382771c8e6000604051602001526040518163ffffffff1660e060020a028152600401809050602060405180830381600087803b151561110f57fe5b6102c65a03f1151561111d57fe5b5050604051511515905061113357506001610fa8565b600354604080516000602091820181905282517f8e768288000000000000000000000000000000000000000000000000000000008152600160a060020a03308116600483015293519390941693638e768288936024808301949391928390030190829087803b15156111a157fe5b6102c65a03f115156111af57fe5b505060405151151590506111c557506001610fa8565b6007544210156111d757506002610fa8565b60085442111580156111ee57506111ec61257b565b155b156111fb57506003610fa8565b611203611f87565b1561121057506004610fa8565b611218611f87565b15801561122757506000600a54115b80156112375750600a54600d5410155b1561124457506007610fa8565b506005610fa8565b5b5b5b5b5b5b5b5b90565b60005433600160a060020a039081169116146112735760006000fd5b60048054600160a060020a031916600160a060020a0383811691909117808355604080516000602091820181905282517f614cb9040000000000000000000000000000000000000000000000000000000081529251939094169463614cb9049483820194929383900390910190829087803b15156112ed57fe5b6102c65a03f115156112fb57fe5b50506040515115159050610d665760006000fd5b5b5b50565b60006002866000604051602001526040518082600160a060020a0316600160a060020a03166c0100000000000000000000000002815260140191505060206040518083038160008661646e5a03f1151561136a57fe5b5050604080518051601354600083815260208085018652938501819052845183815260ff8a1681860152808601899052606081018890529451929550600160a060020a03909116936001936080808301949193601f1983019383900390910191908661646e5a03f115156113da57fe5b5050604051601f190151600160a060020a0316146113f85760006000fd5b6fffffffffffffffffffffffffffffffff851615156114175760006000fd5b6114218686612819565b5b505050505050565b60146020526000908152604090205481565b600454600160a060020a031681565b6000805433600160a060020a039081169116146114685760006000fd5b600f5460ff16156114795760006000fd5b60165460ff16151561148b5760006000fd5b601260009054906101000a9004600160a060020a0316905080600160a060020a031663b3f05b976000604051602001526040518163ffffffff1660e060020a028152600401809050602060405180830381600087803b15156114e957fe5b6102c65a03f115156114f757fe5b50506040515115905061150a5760006000fd5b60198290556040805183815290517f7df545c7a1df0d2a1ba979e94124b026facab86a15ed46b6b4a732d995f9e1829181900360200190a15b5b5050565b60008054819033600160a060020a039081169116146115675760006000fd5b600254604080516000602091820181905282517f313ce5670000000000000000000000000000000000000000000000000000000081529251600160a060020a039094169363313ce5679360048082019493918390030190829087803b15156115cb57fe5b6102c65a03f115156115d957fe5b50505060405180519050600a0a840291508383029050600a547354ca5a7c536dbed5897b78d30a93dcd0e46fbdac6366098d4f9091836000604051602001526040518363ffffffff1660e060020a028152600401808381526020018281526020019250505060206040518083038186803b151561165257fe5b6102c65a03f4151561166057fe5b5050604080518051600a556009546000602092830152825160e060020a6366098d4f02815260048101919091526024810186905291517354ca5a7c536dbed5897b78d30a93dcd0e46fbdac93506366098d4f926044808201939291829003018186803b15156116cb57fe5b6102c65a03f415156116d957fe5b5050604080518051600955600160a060020a0388166000908152601460209081528382205492810191909152825160e060020a6366098d4f02815260048101929092526024820185905291517354ca5a7c536dbed5897b78d30a93dcd0e46fbdac93506366098d4f92604480840193919291829003018186803b151561175b57fe5b6102c65a03f4151561176957fe5b5050604080518051600160a060020a03891660009081526014602090815284822092909255601582528381205492820152825160e060020a6366098d4f02815260048101929092526024820186905291517354ca5a7c536dbed5897b78d30a93dcd0e46fbdac93506366098d4f92604480840193919291829003018186803b15156117f057fe5b6102c65a03f415156117fe57fe5b50506040805151600160a060020a0388166000908152601560205291909120555061182985836130bd565b60408051600160a060020a0387168152602081018390528082018490526000606082015290517f0396f60aaad038749091d273dc13aaabc63db6e2271c7bad442d5cf25cc433509181900360800190a15b5b5050505050565b60195481565b600a5481565b60015b90565b6004805b6118a0611084565b60078111156118ab57fe5b146118b65760006000fd5b60005433600160a060020a039081169116146118d25760006000fd5b60005460a060020a900460ff16156118ea5760006000fd5b600f5460ff16156118fb5760006000fd5b600454600160a060020a0316156119765760048054604080517f0bf318a30000000000000000000000000000000000000000000000000000000081529051600160a060020a0390921692630bf318a392828201926000929082900301818387803b151561196457fe5b6102c65a03f1151561197257fe5b5050505b600f805460ff191660011790555b5b5b5b50565b601254600160a060020a031681565b60005433600160a060020a039081169116146119b55760006000fd5b60038054600160a060020a031916600160a060020a038381169190911791829055604080516000602091820181905282517f04bbc255000000000000000000000000000000000000000000000000000000008152925194909316936304bbc255936004808501948390030190829087803b15156112ed57fe5b6102c65a03f115156112fb57fe5b50506040515115159050610d665760006000fd5b5b5b50565b60095481565b60005433600160a060020a03908116911614611a775760006000fd5b60128054600160a060020a031916600160a060020a0383161790555b5b50565b60006007805b611aa5611084565b6007811115611ab057fe5b14611abb5760006000fd5b600160a060020a0333166000908152601460205260409020549150811515611ae35760006000fd5b600160a060020a0333166000908152601460209081526040808320839055600e548151830193909352805160e060020a6366098d4f028152600481019390935260248301859052517354ca5a7c536dbed5897b78d30a93dcd0e46fbdac926366098d4f926044808301939192829003018186803b1515611b5f57fe5b6102c65a03f41515611b6d57fe5b5050604080518051600e55600160a060020a03331681526020810185905281517fbb28353e4598c3b9199101a66e0989549b659a59a54d2c27fbb183f1932c8e6d93509081900390910190a1604051600160a060020a0333169083156108fc029084906000818181858888f193505050501515610f1c5760006000fd5b5b5b5050565b601354600160a060020a031681565b600e5481565b60005433600160a060020a03908116911614611c215760006000fd5b6000805474ff0000000000000000000000000000000000000000191660a060020a1790555b5b565b60015481565b60008054819081908190819033600160a060020a03908116911614611c745760006000fd5b600f5460ff1615611c855760006000fd5b60165460ff161515611c975760006000fd5b85421115611ca55760006000fd5b856007541115611cb55760006000fd5b601254604080516000602091820181905282517fb3f05b970000000000000000000000000000000000000000000000000000000081529251600160a060020a039094169850889363b3f05b979360048082019493918390030190829087803b1515611d1c57fe5b6102c65a03f11515611d2a57fe5b505060405151159050611d3d5760006000fd5b60009350600092505b6011548360ff161015611db0576010805460ff8516908110611d6457fe5b906000526020600020900160005b9054906101000a9004600160a060020a0316600160a060020a031630600160a060020a03161415611da4578260ff1693505b5b600190920191611d46565b601154846001011015611e73578360010191505b601154821015611e73576010805483908110611ddc57fe5b906000526020600020900160005b9054906101000a9004600160a060020a0316905080600160a060020a031663af4686826000604051602001526040518163ffffffff1660e060020a028152600401809050602060405180830381600087803b1515611e4457fe5b6102c65a03f11515611e5257fe5b5050604051518711159050611e675760006000fd5b5b600190910190611dc4565b5b60088690556040805187815290517fd34bb772c4ae9baa99db852f622773b31c7827e8ee818449fef20d30980bd3109181900360200190a15b5b505050505050565b600354600160a060020a031681565b600d5481565b6000805433600160a060020a03908116911614611ee85760006000fd5b600f5462010000900460ff161515611f005760006000fd5b5060005b845181101561187a57611f758582815181101515611f1e57fe5b906020019060200201518583815181101515611f3657fe5b906020019060200201518584815181101515611f4e57fe5b906020019060200201518585815181101515611f6657fe5b90602001906020020151610fb1565b5b600101611f04565b5b5b5050505050565b600654600a5410155b90565b60005433600160a060020a03908116911614611faf5760006000fd5b600f805461ff001916610100831515810291909117918290556012546013546040805160ff9490950484161515855260a060020a90920490921615156020840152600160a060020a0390911682820152517f48d826081348f5f00e8a33c9ae8ce89ed4c6e88400b585a478bc203d9e8177d3916060908290030190a15b5b50565b60005433600160a060020a0390811691161461204c5760006000fd5b60006011555b5b565b6005805b612061611084565b600781111561206c57fe5b146120775760006000fd5b3415156120845760006000fd5b600d547354ca5a7c536dbed5897b78d30a93dcd0e46fbdac6366098d4f9091346000604051602001526040518363ffffffff1660e060020a028152600401808381526020018281526020019250505060206040518083038186803b15156120e757fe5b6102c65a03f415156120f557fe5b505060405151600d55505b5b50565b60005433600160a060020a039081169116146121205760006000fd5b60188190555b5b50565b600054600160a060020a031681565b600554600160a060020a031681565b60156020526000908152604090205481565b610d6633826126d1565b5b50565b60195481115b949350505050565b61030533610d2d565b5b565b6000600460009054906101000a9004600160a060020a0316600160a060020a03166382771c8e6000604051602001526040518163ffffffff1660e060020a028152600401809050602060405180830381600087803b1515610f9057fe5b6102c65a03f11515610f9e57fe5b5050604051519150505b90565b60075481565b600f5460ff1681565b60005460a060020a900460ff1681565b601080548290811061222757fe5b906000526020600020900160005b915054906101000a9004600160a060020a031681565b6000805433600160a060020a039081169116146122685760006000fd5b600f5460ff16156122795760006000fd5b60165460ff16151561228b5760006000fd5b814211156122995760006000fd5b6008548211156122a95760006000fd5b601260009054906101000a9004600160a060020a0316905080600160a060020a031663b3f05b976000604051602001526040518163ffffffff1660e060020a028152600401809050602060405180830381600087803b151561230757fe5b6102c65a03f1151561231557fe5b5050604051511590506123285760006000fd5b60078290556040805183815290517fa3f2a813a039e5195c620dabcd490267a9aa5a50e4e1383bc474e9b800f7defe9181900360200190a15b5b5050565b60176020526000908152604090208054600182015460029092015460ff909116919083565b60005433600160a060020a039081169116146123a75760006000fd5b60005460a060020a900460ff1615156123c05760006000fd5b6000805474ff0000000000000000000000000000000000000000191690555b5b5b565b60125460a060020a900460ff1681565b600160a060020a03831660009081526017602052604081206001015482101561241c5760006000fd5b600f5462010000900460ff1615156124345760006000fd5b33600160a060020a031684600160a060020a031614158015612468575033600160a060020a031683600160a060020a031614155b156124735760006000fd5b50600160a060020a038316600090815260176020908152604080832060020154815183019390935280517ff4f3bdc1000000000000000000000000000000000000000000000000000000008152600481018490526024810185905290517354ca5a7c536dbed5897b78d30a93dcd0e46fbdac9263f4f3bdc19260448082019391829003018186803b151561250357fe5b6102c65a03f4151561251157fe5b5050604080518051606082018352600160a060020a038816600081815260176020818152868320805460ff811615158852828801858152988801878152959094529190529351151560ff199091161783559251600183015591516002909101559150505b50505050565b60195460095410155b90565b600c5481565b60005433600160a060020a039081169116146125a95760006000fd5b60118054600181019091556010805483929081106125c357fe5b906000526020600020900160005b6101000a815481600160a060020a030219169083600160a060020a031602179055505b5b50565b60115481565b60005433600160a060020a0390811691161461261a5760006000fd5b6012805474ff0000000000000000000000000000000000000000191660a060020a8415158102919091179182905560138054600160a060020a031916600160a060020a038581169190911791829055600f546040805160ff610100909304831615158152949095041615156020840152168183015290517f48d826081348f5f00e8a33c9ae8ce89ed4c6e88400b585a478bc203d9e8177d3916060908290030190a15b5b5050565b600f5462010000900460ff1681565b60125460a060020a900460ff16156126e95760006000fd5b6fffffffffffffffffffffffffffffffff811615156127085760006000fd5b610f1c8282612819565b5b5050565b60005433600160a060020a039081169116146127335760006000fd5b600160a060020a03811615156127495760006000fd5b60008054604051600160a060020a03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a360008054600160a060020a031916600160a060020a0383161790555b5b50565b60005433600160a060020a039081169116146127be5760006000fd5b600154600c5411156127d05760006000fd5b60058054600160a060020a031916600160a060020a0383161790555b5b50565b61106e3385858585611314565b5b50505050565b600b5481565b600254600160a060020a031681565b600060006000600060006000600060149054906101000a900460ff16156128405760006000fd5b60025b61284b611084565b600781111561285657fe5b14156128625760006000fd5b60035b61286d611084565b600781111561287857fe5b14156102ff57600f5462010000900460ff16156128b757600160a060020a03881660009081526017602052604090205460ff1615156128b75760006000fd5b5b6128c3565b60006000fd5b5b349550600360009054906101000a9004600160a060020a0316600160a060020a03166318a4155e87600b54600a540360095433600260009054906101000a9004600160a060020a0316600160a060020a031663313ce5676000604051602001526040518163ffffffff1660e060020a028152600401809050602060405180830381600087803b151561295257fe5b6102c65a03f1151561296057fe5b505050604051805190506000604051602001526040518663ffffffff1660e060020a0281526004018086815260200185815260200184815260200183600160a060020a0316600160a060020a0316815260200182815260200195505050505050602060405180830381600087803b15156129d657fe5b6102c65a03f115156129e457fe5b5050604051519550508415156129fa5760006000fd5b600f5462010000900460ff1615612a9957600160a060020a03881660009081526017602052604090206001015485108015612a4b5750600160a060020a038816600090815260156020526040902054155b15612a565760006000fd5b600160a060020a038816600090815260176020526040902060020154851115612a7f5760006000fd5b612a898886610d6a565b15612a945760006000fd5b612b42565b600254604080516000602091820181905282517f3fa615b00000000000000000000000000000000000000000000000000000000081529251600160a060020a0390941693633fa615b09360048082019493918390030190829087803b1515612afd57fe5b6102c65a03f11515612b0b57fe5b505060405151861090508015612b375750600160a060020a038816600090815260156020526040902054155b15612b425760006000fd5b5b612b538686600a54600954612168565b15612b5e5760006000fd5b600160a060020a0388166000908152601460209081526040808320548151830193909352805160e060020a6366098d4f028152600481019390935260248301899052517354ca5a7c536dbed5897b78d30a93dcd0e46fbdac926366098d4f926044808301939192829003018186803b1515612bd557fe5b6102c65a03f41515612be357fe5b5050604080518051600160a060020a038c1660009081526014602090815284822092909255601582528381205492820152825160e060020a6366098d4f02815260048101929092526024820189905291517354ca5a7c536dbed5897b78d30a93dcd0e46fbdac93506366098d4f92604480840193919291829003018186803b1515612c6a57fe5b6102c65a03f41515612c7857fe5b5050604080518051600160a060020a038c1660009081526015602090815284822092909255600a5492820152825160e060020a6366098d4f0281526004810192909252602482018a905291517354ca5a7c536dbed5897b78d30a93dcd0e46fbdac93506366098d4f92604480840193919291829003018186803b1515612cfa57fe5b6102c65a03f41515612d0857fe5b5050604080518051600a556009546000602092830152825160e060020a6366098d4f02815260048101919091526024810189905291517354ca5a7c536dbed5897b78d30a93dcd0e46fbdac93506366098d4f926044808201939291829003018186803b1515612d7357fe5b6102c65a03f41515612d8157fe5b50506040805180516009556003546000602092830181905283517ff14ae17d000000000000000000000000000000000000000000000000000000008152600160a060020a038e81166004830152945194909216945063f14ae17d93602480840194939192918390030190829087803b1515612df857fe5b6102c65a03f11515612e0657fe5b505060405151159050612e8f57600b547354ca5a7c536dbed5897b78d30a93dcd0e46fbdac6366098d4f9091886000604051602001526040518363ffffffff1660e060020a028152600401808381526020018281526020019250505060206040518083038186803b1515612e7657fe5b6102c65a03f41515612e8457fe5b505060405151600b55505b600160a060020a0388166000908152601460205260409020541515612eb857600c805460010190555b612ec288866130bd565b600554604051600160a060020a039091169087156108fc029088906000818181858888f193505050501515612ef75760006000fd5b600f5462010000900460ff161561304e5760009350600092505b6011548360ff161015612f7b576010805460ff8516908110612f2f57fe5b906000526020600020900160005b9054906101000a9004600160a060020a0316600160a060020a031630600160a060020a03161415612f6f578260ff1693505b5b600190920191612f11565b60115484600101101561304e578360010191505b60115482101561304e576010805483908110612fa757fe5b906000526020600020900160005b9054604080517fd245da28000000000000000000000000000000000000000000000000000000008152600160a060020a0333811660048301523081166024830152604482018a905291516101009490940a909204169250829163d245da289160648082019260009290919082900301818387803b151561303157fe5b6102c65a03f1151561303f57fe5b5050505b600190910190612f8f565b5b5b60408051600160a060020a038a168152602081018890528082018790526fffffffffffffffffffffffffffffffff8916606082015290517f0396f60aaad038749091d273dc13aaabc63db6e2271c7bad442d5cf25cc433509181900360800190a15b5b5050505050505050565b600254604080517f40c10f19000000000000000000000000000000000000000000000000000000008152600160a060020a038581166004830152602482018590529151919092169182916340c10f199160448082019260009290919082900301818387803b151561312a57fe5b6102c65a03f1151561142157fe5b5050505b505050565b81548183558181151161313c5760008381526020902061313c91810190830161316b565b5b505050565b610fa891905b808211156131855760008155600101613171565b5090565b905600a165627a7a72305820ed633862024bdaf3d7c3ea3a96bea1c3ba68b7f2eaea220072e1a137aac61e3e0029000000000000000000000000ea097a2b1db00627b2fa17460ad260c016016977000000000000000000000000b4bba71e32c3628f0896639da4c5364519a8ca08000000000000000000000000c7e98536e016bdf82203e8d4df5f31b88d5e3ab70000000000000000000000000000000000000000000000000000000059d3b4000000000000000000000000000000000000000000000000000000000059f89dc40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f04ef12cb04cf15800000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000", + "tx_input": "6060604052600560015560006009556000600a556000600b556000600c556000600d556000600e55600060115534156200003557fe5b604051610120806200342483398101604090815281516020830151918301516060840151608085015160a086015160c087015160e0880151610100909801519597949593949293919290915b88888888888887875b5b60008054600160a060020a03191633600160a060020a03161790555b60008054600160a060020a03338116600160a060020a03199283161790925560028054928b1692909116919091179055620000f087640100000000620019996200019c82021704565b60058054600160a060020a031916600160a060020a0388811691909117918290551615156200011f5760006000fd5b8415156200012d5760006000fd5b6007859055831515620001405760006000fd5b6008849055600754849010620001565760006000fd5b60068390556016805460ff1916831515179055600f805462ff0000191662010000831515021790555b505050601988905550505050505b5050505050505050506200025c565b60005433600160a060020a03908116911614620001b95760006000fd5b60038054600160a060020a031916600160a060020a038381169190911791829055604080516000602091820181905282517f04bbc255000000000000000000000000000000000000000000000000000000008152925194909316936304bbc255936004808501948390030190829087803b15156200023357fe5b6102c65a03f115156200024257fe5b50506040515115159050620002575760006000fd5b5b5b50565b6131b8806200026c6000396000f300606060405236156102fb5763ffffffff60e060020a6000350416630226401d811461030957806303ca0eed1461032b57806303f9c7931461034f578063045b1a0c1461036557806304fc7c6d14610398578063062b01ce146103ed5780630a09284a146104115780630cc91bb9146104335780630e1d2ec81461045c57806313f4e977146104805780631865c57d146104a257806319b667da146104d65780631a49803b146104f45780631aae34601461052b57806321d5c0f6146105595780632c2de40a1461058557806332013ac31461059a5780633ad075ea146105be5780634042b66f146105e05780634551dd59146106025780634bb278f3146106265780634f97f97f1461063857806350c6773414610664578063518ab2a81461068257806357dc2658146106a4578063590e1ae3146106c25780635b7633d0146106d45780635da89ac0146107005780635ed7ca5b146107225780636203f09f146107345780636e50eb3f1461075657806378b99c241461076b578063797d94371461079757806379e0f59a146107b95780637c2e08a3146108b65780637f7d711e146108da578063831ed348146108f157806387612102146109035780638d51faec1461090d5780638da5cb5b146109225780639075becf1461094e57806397b150ca1461097a57806399e9376c146109a85780639d3c663f146109c7578063a6f2ae3a146109f7578063a7ba44c314610a01578063af46868214610a25578063b3f05b9714610a47578063b9b8af0b14610a6b578063bede2cac14610a8f578063bf5fc2ee14610abe578063cb16e6d014610ad3578063cb3e64fd14610b0f578063d222dc0414610b21578063d245da2814610b45578063d5d0902114610b6c578063d7e64c0014610b90578063dee846c514610bb2578063ebdfa45514610bd0578063ed68ff2c14610bf2578063ef674e6614610c15578063ef86944314610c39578063f2fde38b14610c64578063f3283fba14610c82578063f486972614610ca0578063f7c00e2f14610ccb578063fc0c546a14610ced575b6103075b60006000fd5b565b005b341561031157fe5b610319610d19565b60408051918252519081900360200190f35b341561033357fe5b61033b610d1f565b604080519115158252519081900360200190f35b610307600160a060020a0360043516610d2d565b005b341561036d57fe5b61033b600160a060020a0360043516602435610d6a565b604080519115158252519081900360200190f35b34156103a057fe5b610307600480803590602001908201803590602001908080602002602001604051908101604052809392919081815260200183836020028082843750949650610e3295505050505050565b005b34156103f557fe5b61033b610f22565b604080519115158252519081900360200190f35b341561041957fe5b610319610fab565b60408051918252519081900360200190f35b341561043b57fe5b610307600160a060020a03600435166024351515604435606435610fb1565b005b341561046457fe5b61033b611075565b604080519115158252519081900360200190f35b341561048857fe5b61031961107e565b60408051918252519081900360200190f35b34156104aa57fe5b6104b2611084565b604051808260078111156104c257fe5b60ff16815260200191505060405180910390f35b34156104de57fe5b610307600160a060020a0360043516611257565b005b610307600160a060020a03600435166fffffffffffffffffffffffffffffffff6024351660ff60443516606435608435611314565b005b341561053357fe5b610319600160a060020a036004351661142a565b60408051918252519081900360200190f35b341561056157fe5b61056961143c565b60408051600160a060020a039092168252519081900360200190f35b341561058d57fe5b61030760043561144b565b005b34156105a257fe5b610307600160a060020a0360043516602435604435611548565b005b34156105c657fe5b610319611882565b60408051918252519081900360200190f35b34156105e857fe5b610319611888565b60408051918252519081900360200190f35b341561060a57fe5b61033b61188e565b604080519115158252519081900360200190f35b341561062e57fe5b610307611894565b005b341561064057fe5b61056961198a565b60408051600160a060020a039092168252519081900360200190f35b341561066c57fe5b610307600160a060020a0360043516611999565b005b341561068a57fe5b610319611a55565b60408051918252519081900360200190f35b34156106ac57fe5b610307600160a060020a0360043516611a5b565b005b34156106ca57fe5b610307611a97565b005b34156106dc57fe5b610569611bf0565b60408051600160a060020a039092168252519081900360200190f35b341561070857fe5b610319611bff565b60408051918252519081900360200190f35b341561072a57fe5b610307611c05565b005b341561073c57fe5b610319611c49565b60408051918252519081900360200190f35b341561075e57fe5b610307600435611c4f565b005b341561077357fe5b610569611eb6565b60408051600160a060020a039092168252519081900360200190f35b341561079f57fe5b610319611ec5565b60408051918252519081900360200190f35b34156107c157fe5b610307600480803590602001908201803590602001908080602002602001604051908101604052809392919081815260200183836020028082843750506040805187358901803560208181028481018201909552818452989a998901989297509082019550935083925085019084908082843750506040805187358901803560208181028481018201909552818452989a998901989297509082019550935083925085019084908082843750506040805187358901803560208181028481018201909552818452989a998901989297509082019550935083925085019084908082843750949650611ecb95505050505050565b005b34156108be57fe5b61033b611f87565b604080519115158252519081900360200190f35b34156108e257fe5b6103076004351515611f93565b005b34156108f957fe5b610307612030565b005b610307612055565b005b341561091557fe5b610307600435612104565b005b341561092a57fe5b61056961212a565b60408051600160a060020a039092168252519081900360200190f35b341561095657fe5b610569612139565b60408051600160a060020a039092168252519081900360200190f35b341561098257fe5b610319600160a060020a0360043516612148565b60408051918252519081900360200190f35b6103076fffffffffffffffffffffffffffffffff6004351661215a565b005b34156109cf57fe5b61033b600435602435604435606435612168565b604080519115158252519081900360200190f35b610307612176565b005b3415610a0957fe5b61033b612182565b604080519115158252519081900360200190f35b3415610a2d57fe5b6103196121fa565b60408051918252519081900360200190f35b3415610a4f57fe5b61033b612200565b604080519115158252519081900360200190f35b3415610a7357fe5b61033b612209565b604080519115158252519081900360200190f35b3415610a9757fe5b610569600435612219565b60408051600160a060020a039092168252519081900360200190f35b3415610ac657fe5b61030760043561224b565b005b3415610adb57fe5b610aef600160a060020a0360043516612366565b604080519315158452602084019290925282820152519081900360600190f35b3415610b1757fe5b61030761238b565b005b3415610b2957fe5b61033b6123e3565b604080519115158252519081900360200190f35b3415610b4d57fe5b610307600160a060020a03600435811690602435166044356123f3565b005b3415610b7457fe5b61033b61257b565b604080519115158252519081900360200190f35b3415610b9857fe5b610319612587565b60408051918252519081900360200190f35b3415610bba57fe5b610307600160a060020a036004351661258d565b005b3415610bd857fe5b6103196125f8565b60408051918252519081900360200190f35b3415610bfa57fe5b6103076004351515600160a060020a03602435166125fe565b005b3415610c1d57fe5b61033b6126c2565b604080519115158252519081900360200190f35b610307600160a060020a03600435166fffffffffffffffffffffffffffffffff602435166126d1565b005b3415610c6c57fe5b610307600160a060020a0360043516612717565b005b3415610c8a57fe5b610307600160a060020a03600435166127a2565b005b6103076fffffffffffffffffffffffffffffffff6004351660ff602435166044356064356127f0565b005b3415610cd357fe5b610319612804565b60408051918252519081900360200190f35b3415610cf557fe5b61056961280a565b60408051600160a060020a039092168252519081900360200190f35b60185481565b600f54610100900460ff1681565b600f54610100900460ff1615610d435760006000fd5b60125460a060020a900460ff1615610d5b5760006000fd5b610d66816000612819565b5b50565b600f54600090819062010000900460ff161515610d875760006000fd5b50600160a060020a03831660009081526017602090815260408083206002015460158352818420548251840194909452815160e060020a6366098d4f0281526004810194909452602484018690529051909283927354ca5a7c536dbed5897b78d30a93dcd0e46fbdac926366098d4f926044808201939291829003018186803b1515610e0f57fe5b6102c65a03f41515610e1d57fe5b505050604051805190501191505b5092915050565b6000805433600160a060020a03908116911614610e4f5760006000fd5b610e57612030565b5060005b8151811015610f1c576010546011541415610e84576010805460010190610e829082613141565b505b8181815181101515610e9257fe5b602090810290910101516011805460018101909155601080549091908110610eb657fe5b906000526020600020900160005b6101000a815481600160a060020a030219169083600160a060020a031602179055506001825103811415610f1257610f128282815181101515610f0357fe5b90602001906020020151611a5b565b5b5b600101610e5b565b5b5b5050565b600354604080516000602091820181905282517f8e768288000000000000000000000000000000000000000000000000000000008152600160a060020a033081166004830152935191949390931692638e76828892602480830193919282900301818787803b1515610f9057fe5b6102c65a03f11515610f9e57fe5b5050604051519150505b90565b60085481565b60005433600160a060020a03908116911614610fcd5760006000fd5b600f5462010000900460ff161515610fe55760006000fd5b604080516060810182528415158082526020808301868152838501868152600160a060020a038a1660008415158082526020808301868152838501868152600160a060020a038a1660008", "external_libraries": { "SafeMathLibExt": "0x54ca5a7c536dbed5897b78d30a93dcd0e46fbdac" }, diff --git a/apps/explorer/test/support/fixture/smart_contract/contract_from_factory.sol b/apps/explorer/test/support/fixture/smart_contract/contract_from_factory.sol new file mode 100644 index 0000000000..bf85d70c51 --- /dev/null +++ b/apps/explorer/test/support/fixture/smart_contract/contract_from_factory.sol @@ -0,0 +1,18 @@ +pragma solidity 0.4.26; + +contract Factory { + address[] newContracts; + + function createContract (bytes32 name) public { + address newContract = new ContractFromFactory(name); + newContracts.push(newContract); + } +} + +contract ContractFromFactory { + bytes32 public Name; + + constructor(bytes32 name) public { + Name = name; + } +} \ No newline at end of file diff --git a/apps/explorer/test/support/fixture/smart_contract/contract_with_lib.json b/apps/explorer/test/support/fixture/smart_contract/contract_with_lib.json index aba02b0ec4..cd625952b0 100644 --- a/apps/explorer/test/support/fixture/smart_contract/contract_with_lib.json +++ b/apps/explorer/test/support/fixture/smart_contract/contract_with_lib.json @@ -3,11 +3,11 @@ "compiler_version": "v0.5.11+commit.c082d0b4", "contract": "pragma solidity 0.5.11;library BadSafeMath { function add(uint256 a, uint256 b) public pure returns (uint256) { uint256 c = a + 2 * b; require(c >= a, \"SafeMath: addition overflow\"); return c; }}contract SimpleStorage { uint256 storedData = 10; using BadSafeMath for uint256; function increment(uint256 x) public { storedData = storedData.add(x); } function set(uint256 x) public { storedData = x; } function get() public view returns (uint256) { return storedData; }}", "expected_bytecode": "608060405234801561001057600080fd5b50600436106100415760003560e01c806360fe47b1146100465780636d4ce63c146100655780637cf5dab01461007f575b600080fd5b6100636004803603602081101561005c57600080fd5b503561009c565b005b61006d6100a1565b60408051918252519081900360200190f35b6100636004803603602081101561009557600080fd5b50356100a7565b600055565b60005490565b600054733662e222908fa35f013bee37695d0510098b6d7363771602f79091836040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b15801561010257600080fd5b505af4158015610116573d6000803e3d6000fd5b505050506040513d602081101561012c57600080fd5b50516000555056fea265627a7a723158203e59bfb9a5a2e55d38231922c86d8b2ec9b66cb2f6595613674bc4e15290b60764736f6c634300050b0032", + "tx_input": "6080604052600a60005534801561001557600080fd5b50610169806100256000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806360fe47b1146100465780636d4ce63c146100655780637cf5dab01461007f575b600080fd5b6100636004803603602081101561005c57600080fd5b503561009c565b005b61006d6100a1565b60408051918252519081900360200190f35b6100636004803603602081101561009557600080fd5b50356100a7565b600055565b60005490565b600054733662e222908fa35f013bee37695d0510098b6d7363771602f79091836040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b15801561010257600080fd5b505af4158015610116573d6000803e3d6000fd5b505050506040513d602081101561012c57600080fd5b50516000555056fea265627a7a723158207809bc828bbcd3de3e6b6483facb0c5902901fc8827283c749c8ea0702eb871f64736f6c634300050b0032", "external_libraries": { "BadSafeMath": "0x3662e222908fa35f013bee37695d0510098b6d73" }, "name": "SimpleStorage", "optimize": true } - ] - \ No newline at end of file + ] \ No newline at end of file diff --git a/apps/explorer/test/support/fixture/smart_contract/contract_with_lib.sol b/apps/explorer/test/support/fixture/smart_contract/contract_with_lib.sol new file mode 100644 index 0000000000..7b80cef397 --- /dev/null +++ b/apps/explorer/test/support/fixture/smart_contract/contract_with_lib.sol @@ -0,0 +1,25 @@ +pragma solidity 0.5.11; + +library BadSafeMath { + function add(uint256 a, uint256 b) public pure returns (uint256) { + uint256 c = a + 2 * b; + require(c >= a, "SafeMath: addition overflow"); + return c; + } +} + +contract SimpleStorage { + uint256 storedData = 10; + using BadSafeMath for uint256; + function increment(uint256 x) public { + storedData = storedData.add(x); + } + + function set(uint256 x) public { + storedData = x; + } + + function get() public view returns (uint256) { + return storedData; + } +} \ No newline at end of file diff --git a/apps/explorer/test/support/fixture/smart_contract/solidity_5.11_new_whisper_metadata.json b/apps/explorer/test/support/fixture/smart_contract/solidity_5.11_new_whisper_metadata.json index bd0301ef78..04985d44a4 100644 --- a/apps/explorer/test/support/fixture/smart_contract/solidity_5.11_new_whisper_metadata.json +++ b/apps/explorer/test/support/fixture/smart_contract/solidity_5.11_new_whisper_metadata.json @@ -1,5 +1,6 @@ { "bytecode": "608060405260043610610105576000357c0100000000000000000000000000000000000000000000000000000000900480638da5cb5b116100a7578063d4ee1d9011610076578063d4ee1d90146105dc578063dc39d06d14610633578063dd62ed3e146106a6578063f2fde38b1461072b57610105565b80638da5cb5b1461037857806395d89b41146103cf578063a9059cbb1461045f578063cae9ca51146104d257610105565b806323b872dd116100e357806323b872dd14610238578063313ce567146102cb57806370a08231146102fc57806379ba50971461036157610105565b806306fdde031461010a578063095ea7b31461019a57806318160ddd1461020d575b600080fd5b34801561011657600080fd5b5061011f61077c565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561015f578082015181840152602081019050610144565b50505050905090810190601f16801561018c5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156101a657600080fd5b506101f3600480360360408110156101bd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061081a565b604051808215151515815260200191505060405180910390f35b34801561021957600080fd5b5061022261090c565b6040518082815260200191505060405180910390f35b34801561024457600080fd5b506102b16004803603606081101561025b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610967565b604051808215151515815260200191505060405180910390f35b3480156102d757600080fd5b506102e0610c12565b604051808260ff1660ff16815260200191505060405180910390f35b34801561030857600080fd5b5061034b6004803603602081101561031f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610c25565b6040518082815260200191505060405180910390f35b34801561036d57600080fd5b50610376610c6e565b005b34801561038457600080fd5b5061038d610e0b565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156103db57600080fd5b506103e4610e30565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610424578082015181840152602081019050610409565b50505050905090810190601f1680156104515780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561046b57600080fd5b506104b86004803603604081101561048257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610ece565b604051808215151515815260200191505060405180910390f35b3480156104de57600080fd5b506105c2600480360360608110156104f557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019064010000000081111561053c57600080fd5b82018360208201111561054e57600080fd5b8035906020019184600183028401116401000000008311171561057057600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050611069565b604051808215151515815260200191505060405180910390f35b3480156105e857600080fd5b506105f16112b8565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561063f57600080fd5b5061068c6004803603604081101561065657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506112de565b604051808215151515815260200191505060405180910390f35b3480156106b257600080fd5b50610715600480360360408110156106c957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611440565b6040518082815260200191505060405180910390f35b34801561073757600080fd5b5061077a6004803603602081101561074e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506114c7565b005b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156108125780601f106107e757610100808354040283529160200191610812565b820191906000526020600020905b8154815290600101906020018083116107f557829003601f168201915b505050505081565b600081600760003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b6000610962600660008073ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205460055461156490919063ffffffff16565b905090565b60006109bb82600660008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461156490919063ffffffff16565b600660008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610a8d82600760008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461156490919063ffffffff16565b600760008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610b5f82600660008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461157e90919063ffffffff16565b600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190509392505050565b600460009054906101000a900460ff1681565b6000600660008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610cc857600080fd5b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60028054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610ec65780601f10610e9b57610100808354040283529160200191610ec6565b820191906000526020600020905b815481529060010190602001808311610ea957829003601f168201915b505050505081565b6000610f2282600660003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461156490919063ffffffff16565b600660003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610fb782600660008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461157e90919063ffffffff16565b600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b600082600760003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925856040518082815260200191505060405180910390a38373ffffffffffffffffffffffffffffffffffffffff16638f4ffcb1338530866040518563ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018481526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561124657808201518184015260208101905061122b565b50505050905090810190601f1680156112735780820380516001836020036101000a031916815260200191505b5095505050505050600060405180830381600087803b15801561129557600080fd5b505af11580156112a9573d6000803e3d6000fd5b50505050600190509392505050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461133957600080fd5b8273ffffffffffffffffffffffffffffffffffffffff1663a9059cbb6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff16846040518363ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b1580156113fd57600080fd5b505af1158015611411573d6000803e3d6000fd5b505050506040513d602081101561142757600080fd5b8101908080519060200190929190505050905092915050565b6000600760008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461152057600080fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60008282111561157357600080fd5b818303905092915050565b600081830190508281101561159257600080fd5b9291505056fea265627a7a7231582073fbadbc806cc1f3349b3f98d58b62f7fa417e82d98f41bafd3dd9f9be4e4fa764736f6c634300050b0032", + "input": "60806040523480156200001157600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506040518060400160405280600481526020017f5445535400000000000000000000000000000000000000000000000000000000815250600290805190602001906200009f92919062000221565b506040518060400160405280600a81526020017f5465737420546f6b656e0000000000000000000000000000000000000000000081525060039080519060200190620000ed92919062000221565b506012600460006101000a81548160ff021916908360ff160217905550600460009054906101000a900460ff1660ff16600a0a620f424002600581905550600554600660008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef6005546040518082815260200191505060405180910390a3620002d0565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200026457805160ff191683800117855562000295565b8280016001018555821562000295579182015b828111156200029457825182559160200191906001019062000277565b5b509050620002a49190620002a8565b5090565b620002cd91905b80821115620002c9576000816000905550600101620002af565b5090565b90565b6115cd80620002e06000396000f3fe608060405260043610610105576000357c0100000000000000000000000000000000000000000000000000000000900480638da5cb5b116100a7578063d4ee1d9011610076578063d4ee1d90146105dc578063dc39d06d14610633578063dd62ed3e146106a6578063f2fde38b1461072b57610105565b80638da5cb5b1461037857806395d89b41146103cf578063a9059cbb1461045f578063cae9ca51146104d257610105565b806323b872dd116100e357806323b872dd14610238578063313ce567146102cb57806370a08231146102fc57806379ba50971461036157610105565b806306fdde031461010a578063095ea7b31461019a57806318160ddd1461020d575b600080fd5b34801561011657600080fd5b5061011f61077c565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561015f578082015181840152602081019050610144565b50505050905090810190601f16801561018c5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156101a657600080fd5b506101f3600480360360408110156101bd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061081a565b604051808215151515815260200191505060405180910390f35b34801561021957600080fd5b5061022261090c565b6040518082815260200191505060405180910390f35b34801561024457600080fd5b506102b16004803603606081101561025b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610967565b604051808215151515815260200191505060405180910390f35b3480156102d757600080fd5b506102e0610c12565b604051808260ff1660ff16815260200191505060405180910390f35b34801561030857600080fd5b5061034b6004803603602081101561031f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610c25565b6040518082815260200191505060405180910390f35b34801561036d57600080fd5b50610376610c6e565b005b34801561038457600080fd5b5061038d610e0b565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156103db57600080fd5b506103e4610e30565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610424578082015181840152602081019050610409565b50505050905090810190601f1680156104515780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561046b57600080fd5b506104b86004803603604081101561048257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610ece565b604051808215151515815260200191505060405180910390f35b3480156104de57600080fd5b506105c2600480360360608110156104f557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019064010000000081111561053c57600080fd5b82018360208201111561054e57600080fd5b8035906020019184600183028401116401000000008311171561057057600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050611069565b604051808215151515815260200191505060405180910390f35b3480156105e857600080fd5b506105f16112b8565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561063f57600080fd5b5061068c6004803603604081101561065657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506112de565b604051808215151515815260200191505060405180910390f35b3480156106b257600080fd5b50610715600480360360408110156106c957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611440565b6040518082815260200191505060405180910390f35b34801561073757600080fd5b5061077a6004803603602081101561074e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506114c7565b005b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156108125780601f106107e757610100808354040283529160200191610812565b820191906000526020600020905b8154815290600101906020018083116107f557829003601f168201915b505050505081565b600081600760003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b6000610962600660008073ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205460055461156490919063ffffffff16565b905090565b60006109bb82600660008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461156490919063ffffffff16565b600660008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610a8d82600760008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461156490919063ffffffff16565b600760008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610b5f82600660008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461157e90919063ffffffff16565b600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190509392505050565b600460009054906101000a900460ff1681565b6000600660008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610cc857600080fd5b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60028054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610ec65780601f10610e9b57610100808354040283529160200191610ec6565b820191906000526020600020905b815481529060010190602001808311610ea957829003601f168201915b505050505081565b6000610f2282600660003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461156490919063ffffffff16565b600660003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610fb782600660008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461157e90919063ffffffff16565b600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b600082600760003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925856040518082815260200191505060405180910390a38373ffffffffffffffffffffffffffffffffffffffff16638f4ffcb1338530866040518563ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018481526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561124657808201518184015260208101905061122b565b50505050905090810190601f1680156112735780820380516001836020036101000a031916815260200191505b5095505050505050600060405180830381600087803b15801561129557600080fd5b505af11580156112a9573d6000803e3d6000fd5b50505050600190509392505050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461133957600080fd5b8273ffffffffffffffffffffffffffffffffffffffff1663a9059cbb6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff16846040518363ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b1580156113fd57600080fd5b505af1158015611411573d6000803e3d6000fd5b505050506040513d602081101561142757600080fd5b8101908080519060200190929190505050905092915050565b6000600760008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461152057600080fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60008282111561157357600080fd5b818303905092915050565b600081830190508281101561159257600080fd5b9291505056fea265627a7a7231582017ad0fd486aac012abf90ff8ce3c9d571dcbf654bf184da3a8e67daea3e43fbd64736f6c634300050b0032", "compiler_version": "v0.5.11+commit.c082d0b4", "contract": "/**\r\n *Submitted for verification at Etherscan.io on 2019-08-16\r\n*/\r\n\r\npragma solidity ^0.5.0;\r\n\r\n\r\n// ----------------------------------------------------------------------------\r\n\r\n// 'FIXED' 'Example Fixed Supply Token' token contract\r\n\r\n//\r\n\r\n// Symbol : FIXED\r\n\r\n// Name : Example Fixed Supply Token\r\n\r\n// Total supply: 1,000,000.000000000000000000\r\n\r\n// Decimals : 18\r\n\r\n//\r\n\r\n// Enjoy.\r\n\r\n//\r\n\r\n// (c) BokkyPooBah / Bok Consulting Pty Ltd 2018. The MIT Licence.\r\n\r\n// ----------------------------------------------------------------------------\r\n\r\n\r\n\r\n// ----------------------------------------------------------------------------\r\n\r\n// Safe maths\r\n\r\n// ----------------------------------------------------------------------------\r\n\r\nlibrary SafeMath {\r\n\r\n function add(uint a, uint b) internal pure returns (uint c) {\r\n\r\n c = a + b;\r\n\r\n require(c >= a);\r\n\r\n }\r\n\r\n function sub(uint a, uint b) internal pure returns (uint c) {\r\n\r\n require(b <= a);\r\n\r\n c = a - b;\r\n\r\n }\r\n\r\n function mul(uint a, uint b) internal pure returns (uint c) {\r\n\r\n c = a * b;\r\n\r\n require(a == 0 || c / a == b);\r\n\r\n }\r\n\r\n function div(uint a, uint b) internal pure returns (uint c) {\r\n\r\n require(b > 0);\r\n\r\n c = a / b;\r\n\r\n }\r\n\r\n}\r\n\r\n\r\n\r\n// ----------------------------------------------------------------------------\r\n\r\n// ERC Token Standard #20 Interface\r\n\r\n// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md\r\n\r\n// ----------------------------------------------------------------------------\r\n\r\ncontract ERC20Interface {\r\n\r\n function totalSupply() public view returns (uint);\r\n\r\n function balanceOf(address tokenOwner) public view returns (uint balance);\r\n\r\n function allowance(address tokenOwner, address spender) public view returns (uint remaining);\r\n\r\n function transfer(address to, uint tokens) public returns (bool success);\r\n\r\n function approve(address spender, uint tokens) public returns (bool success);\r\n\r\n function transferFrom(address from, address to, uint tokens) public returns (bool success);\r\n\r\n\r\n event Transfer(address indexed from, address indexed to, uint tokens);\r\n\r\n event Approval(address indexed tokenOwner, address indexed spender, uint tokens);\r\n\r\n}\r\n\r\n\r\n\r\n// ----------------------------------------------------------------------------\r\n\r\n// Contract function to receive approval and execute function in one call\r\n\r\n//\r\n\r\n// Borrowed from MiniMeToken\r\n\r\n// ----------------------------------------------------------------------------\r\n\r\ncontract ApproveAndCallFallBack {\r\n\r\n function receiveApproval(address from, uint256 tokens, address token, bytes memory data) public;\r\n\r\n}\r\n\r\n\r\n\r\n// ----------------------------------------------------------------------------\r\n\r\n// Owned contract\r\n\r\n// ----------------------------------------------------------------------------\r\n\r\ncontract Owned {\r\n\r\n address public owner;\r\n\r\n address public newOwner;\r\n\r\n\r\n event OwnershipTransferred(address indexed _from, address indexed _to);\r\n\r\n\r\n constructor() public {\r\n\r\n owner = msg.sender;\r\n\r\n }\r\n\r\n\r\n modifier onlyOwner {\r\n\r\n require(msg.sender == owner);\r\n\r\n _;\r\n\r\n }\r\n\r\n\r\n function transferOwnership(address _newOwner) public onlyOwner {\r\n\r\n newOwner = _newOwner;\r\n\r\n }\r\n\r\n function acceptOwnership() public {\r\n\r\n require(msg.sender == newOwner);\r\n\r\n emit OwnershipTransferred(owner, newOwner);\r\n\r\n owner = newOwner;\r\n\r\n newOwner = address(0);\r\n\r\n }\r\n\r\n}\r\n\r\n\r\n\r\n// ----------------------------------------------------------------------------\r\n\r\n// ERC20 Token, with the addition of symbol, name and decimals and a\r\n\r\n// fixed supply\r\n\r\n// ----------------------------------------------------------------------------\r\n\r\ncontract FixedSupplyToken is ERC20Interface, Owned {\r\n\r\n using SafeMath for uint;\r\n\r\n\r\n string public symbol;\r\n\r\n string public name;\r\n\r\n uint8 public decimals;\r\n\r\n uint _totalSupply;\r\n\r\n\r\n mapping(address => uint) balances;\r\n\r\n mapping(address => mapping(address => uint)) allowed;\r\n\r\n\r\n\r\n // ------------------------------------------------------------------------\r\n\r\n // Constructor\r\n\r\n // ------------------------------------------------------------------------\r\n\r\n constructor() public {\r\n\r\n symbol = \"TEST\";\r\n\r\n name = \"Test Token\";\r\n\r\n decimals = 18;\r\n\r\n _totalSupply = 1000000 * 10**uint(decimals);\r\n\r\n balances[owner] = _totalSupply;\r\n\r\n emit Transfer(address(0), owner, _totalSupply);\r\n\r\n }\r\n\r\n\r\n\r\n // ------------------------------------------------------------------------\r\n\r\n // Total supply\r\n\r\n // ------------------------------------------------------------------------\r\n\r\n function totalSupply() public view returns (uint) {\r\n\r\n return _totalSupply.sub(balances[address(0)]);\r\n\r\n }\r\n\r\n\r\n\r\n // ------------------------------------------------------------------------\r\n\r\n // Get the token balance for account `tokenOwner`\r\n\r\n // ------------------------------------------------------------------------\r\n\r\n function balanceOf(address tokenOwner) public view returns (uint balance) {\r\n\r\n return balances[tokenOwner];\r\n\r\n }\r\n\r\n\r\n\r\n // ------------------------------------------------------------------------\r\n\r\n // Transfer the balance from token owner's account to `to` account\r\n\r\n // - Owner's account must have sufficient balance to transfer\r\n\r\n // - 0 value transfers are allowed\r\n\r\n // ------------------------------------------------------------------------\r\n\r\n function transfer(address to, uint tokens) public returns (bool success) {\r\n\r\n balances[msg.sender] = balances[msg.sender].sub(tokens);\r\n\r\n balances[to] = balances[to].add(tokens);\r\n\r\n emit Transfer(msg.sender, to, tokens);\r\n\r\n return true;\r\n\r\n }\r\n\r\n\r\n\r\n // ------------------------------------------------------------------------\r\n\r\n // Token owner can approve for `spender` to transferFrom(...) `tokens`\r\n\r\n // from the token owner's account\r\n\r\n //\r\n\r\n // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md\r\n\r\n // recommends that there are no checks for the approval double-spend attack\r\n\r\n // as this should be implemented in user interfaces\r\n\r\n // ------------------------------------------------------------------------\r\n\r\n function approve(address spender, uint tokens) public returns (bool success) {\r\n\r\n allowed[msg.sender][spender] = tokens;\r\n\r\n emit Approval(msg.sender, spender, tokens);\r\n\r\n return true;\r\n\r\n }\r\n\r\n\r\n\r\n // ------------------------------------------------------------------------\r\n\r\n // Transfer `tokens` from the `from` account to the `to` account\r\n\r\n //\r\n\r\n // The calling account must already have sufficient tokens approve(...)-d\r\n\r\n // for spending from the `from` account and\r\n\r\n // - From account must have sufficient balance to transfer\r\n\r\n // - Spender must have sufficient allowance to transfer\r\n\r\n // - 0 value transfers are allowed\r\n\r\n // ------------------------------------------------------------------------\r\n\r\n function transferFrom(address from, address to, uint tokens) public returns (bool success) {\r\n\r\n balances[from] = balances[from].sub(tokens);\r\n\r\n allowed[from][msg.sender] = allowed[from][msg.sender].sub(tokens);\r\n\r\n balances[to] = balances[to].add(tokens);\r\n\r\n emit Transfer(from, to, tokens);\r\n\r\n return true;\r\n\r\n }\r\n\r\n\r\n\r\n // ------------------------------------------------------------------------\r\n\r\n // Returns the amount of tokens approved by the owner that can be\r\n\r\n // transferred to the spender's account\r\n\r\n // ------------------------------------------------------------------------\r\n\r\n function allowance(address tokenOwner, address spender) public view returns (uint remaining) {\r\n\r\n return allowed[tokenOwner][spender];\r\n\r\n }\r\n\r\n\r\n\r\n // ------------------------------------------------------------------------\r\n\r\n // Token owner can approve for `spender` to transferFrom(...) `tokens`\r\n\r\n // from the token owner's account. The `spender` contract function\r\n\r\n // `receiveApproval(...)` is then executed\r\n\r\n // ------------------------------------------------------------------------\r\n\r\n function approveAndCall(address spender, uint tokens, bytes memory data) public returns (bool success) {\r\n\r\n allowed[msg.sender][spender] = tokens;\r\n\r\n emit Approval(msg.sender, spender, tokens);\r\n\r\n ApproveAndCallFallBack(spender).receiveApproval(msg.sender, tokens, address(this), data);\r\n\r\n return true;\r\n\r\n }\r\n\r\n\r\n\r\n // ------------------------------------------------------------------------\r\n\r\n // Don't accept ETH\r\n\r\n // ------------------------------------------------------------------------\r\n\r\n function () external payable {\r\n\r\n revert();\r\n\r\n }\r\n\r\n\r\n\r\n // ------------------------------------------------------------------------\r\n\r\n // Owner can transfer out any accidentally sent ERC20 tokens\r\n\r\n // ------------------------------------------------------------------------\r\n\r\n function transferAnyERC20Token(address tokenAddress, uint tokens) public onlyOwner returns (bool success) {\r\n\r\n return ERC20Interface(tokenAddress).transfer(owner, tokens);\r\n\r\n }\r\n\r\n}", "name": "FixedSupplyToken", diff --git a/apps/explorer/test/support/fixture/smart_contract/solidity_5.11_new_whisper_metadata.sol b/apps/explorer/test/support/fixture/smart_contract/solidity_5.11_new_whisper_metadata.sol new file mode 100644 index 0000000000..c72c22650c --- /dev/null +++ b/apps/explorer/test/support/fixture/smart_contract/solidity_5.11_new_whisper_metadata.sol @@ -0,0 +1,412 @@ +/** *Submitted for verification at Etherscan.io on 2019-08-16 +*/ + +pragma solidity ^0.5.0; + + +// ---------------------------------------------------------------------------- + +// 'FIXED' 'Example Fixed Supply Token' token contract + +// + +// Symbol : FIXED + +// Name : Example Fixed Supply Token + +// Total supply: 1,000,000.000000000000000000 + +// Decimals : 18 + +// + +// Enjoy. + +// + +// (c) BokkyPooBah / Bok Consulting Pty Ltd 2018. The MIT Licence. + +// ---------------------------------------------------------------------------- + + + +// ---------------------------------------------------------------------------- + +// Safe maths + +// ---------------------------------------------------------------------------- + +library SafeMath { + + function add(uint a, uint b) internal pure returns (uint c) { + + c = a + b; + + require(c >= a); + + } + + function sub(uint a, uint b) internal pure returns (uint c) { + + require(b <= a); + + c = a - b; + + } + + function mul(uint a, uint b) internal pure returns (uint c) { + + c = a * b; + + require(a == 0 || c / a == b); + + } + + function div(uint a, uint b) internal pure returns (uint c) { + + require(b > 0); + + c = a / b; + + } + +} + + + +// ---------------------------------------------------------------------------- + +// ERC Token Standard #20 Interface + +// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md + +// ---------------------------------------------------------------------------- + +contract ERC20Interface { + + function totalSupply() public view returns (uint); + + function balanceOf(address tokenOwner) public view returns (uint balance); + + function allowance(address tokenOwner, address spender) public view returns (uint remaining); + + function transfer(address to, uint tokens) public returns (bool success); + + function approve(address spender, uint tokens) public returns (bool success); + + function transferFrom(address from, address to, uint tokens) public returns (bool success); + + + event Transfer(address indexed from, address indexed to, uint tokens); + + event Approval(address indexed tokenOwner, address indexed spender, uint tokens); + +} + + + +// ---------------------------------------------------------------------------- + +// Contract function to receive approval and execute function in one call + +// + +// Borrowed from MiniMeToken + +// ---------------------------------------------------------------------------- + +contract ApproveAndCallFallBack { + + function receiveApproval(address from, uint256 tokens, address token, bytes memory data) public; + +} + + + +// ---------------------------------------------------------------------------- + +// Owned contract + +// ---------------------------------------------------------------------------- + +contract Owned { + + address public owner; + + address public newOwner; + + + event OwnershipTransferred(address indexed _from, address indexed _to); + + + constructor() public { + + owner = msg.sender; + + } + + + modifier onlyOwner { + + require(msg.sender == owner); + + _; + + } + + + function transferOwnership(address _newOwner) public onlyOwner { + + newOwner = _newOwner; + + } + + function acceptOwnership() public { + + require(msg.sender == newOwner); + + emit OwnershipTransferred(owner, newOwner); + + owner = newOwner; + + newOwner = address(0); + + } + +} + + + +// ---------------------------------------------------------------------------- + +// ERC20 Token, with the addition of symbol, name and decimals and a + +// fixed supply + +// ---------------------------------------------------------------------------- + +contract FixedSupplyToken is ERC20Interface, Owned { + + using SafeMath for uint; + + + string public symbol; + + string public name; + + uint8 public decimals; + + uint _totalSupply; + + + mapping(address => uint) balances; + + mapping(address => mapping(address => uint)) allowed; + + + + // ------------------------------------------------------------------------ + + // Constructor + + // ------------------------------------------------------------------------ + + constructor() public { + + symbol = "TEST"; + + name = "Test Token"; + + decimals = 18; + + _totalSupply = 1000000 * 10**uint(decimals); + + balances[owner] = _totalSupply; + + emit Transfer(address(0), owner, _totalSupply); + + } + + + + // ------------------------------------------------------------------------ + + // Total supply + + // ------------------------------------------------------------------------ + + function totalSupply() public view returns (uint) { + + return _totalSupply.sub(balances[address(0)]); + + } + + + + // ------------------------------------------------------------------------ + + // Get the token balance for account `tokenOwner` + + // ------------------------------------------------------------------------ + + function balanceOf(address tokenOwner) public view returns (uint balance) { + + return balances[tokenOwner]; + + } + + + + // ------------------------------------------------------------------------ + + // Transfer the balance from token owner's account to `to` account + + // - Owner's account must have sufficient balance to transfer + + // - 0 value transfers are allowed + + // ------------------------------------------------------------------------ + + function transfer(address to, uint tokens) public returns (bool success) { + + balances[msg.sender] = balances[msg.sender].sub(tokens); + + balances[to] = balances[to].add(tokens); + + emit Transfer(msg.sender, to, tokens); + + return true; + + } + + + + // ------------------------------------------------------------------------ + + // Token owner can approve for `spender` to transferFrom(...) `tokens` + + // from the token owner's account + + // + + // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md + + // recommends that there are no checks for the approval double-spend attack + + // as this should be implemented in user interfaces + + // ------------------------------------------------------------------------ + + function approve(address spender, uint tokens) public returns (bool success) { + + allowed[msg.sender][spender] = tokens; + + emit Approval(msg.sender, spender, tokens); + + return true; + + } + + + + // ------------------------------------------------------------------------ + + // Transfer `tokens` from the `from` account to the `to` account + + // + + // The calling account must already have sufficient tokens approve(...)-d + + // for spending from the `from` account and + + // - From account must have sufficient balance to transfer + + // - Spender must have sufficient allowance to transfer + + // - 0 value transfers are allowed + + // ------------------------------------------------------------------------ + + function transferFrom(address from, address to, uint tokens) public returns (bool success) { + + balances[from] = balances[from].sub(tokens); + + allowed[from][msg.sender] = allowed[from][msg.sender].sub(tokens); + + balances[to] = balances[to].add(tokens); + + emit Transfer(from, to, tokens); + + return true; + + } + + + + // ------------------------------------------------------------------------ + + // Returns the amount of tokens approved by the owner that can be + + // transferred to the spender's account + + // ------------------------------------------------------------------------ + + function allowance(address tokenOwner, address spender) public view returns (uint remaining) { + + return allowed[tokenOwner][spender]; + + } + + + + // ------------------------------------------------------------------------ + + // Token owner can approve for `spender` to transferFrom(...) `tokens` + + // from the token owner's account. The `spender` contract function + + // `receiveApproval(...)` is then executed + + // ------------------------------------------------------------------------ + + function approveAndCall(address spender, uint tokens, bytes memory data) public returns (bool success) { + + allowed[msg.sender][spender] = tokens; + + emit Approval(msg.sender, spender, tokens); + + ApproveAndCallFallBack(spender).receiveApproval(msg.sender, tokens, address(this), data); + + return true; + + } + + + + // ------------------------------------------------------------------------ + + // Don't accept ETH + + // ------------------------------------------------------------------------ + + function () external payable { + + revert(); + + } + + + + // ------------------------------------------------------------------------ + + // Owner can transfer out any accidentally sent ERC20 tokens + + // ------------------------------------------------------------------------ + + function transferAnyERC20Token(address tokenAddress, uint tokens) public onlyOwner returns (bool success) { + + return ERC20Interface(tokenAddress).transfer(owner, tokens); + + } + +} \ No newline at end of file From e1cdd757aa142aa6fb5df11d887a295353fae563 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Fri, 15 May 2020 12:36:02 +0300 Subject: [PATCH 02/51] Verification: check compiler version --- CHANGELOG.md | 5 +- .../lib/explorer/chain/smart_contract.ex | 1 + .../lib/explorer/smart_contract/verifier.ex | 151 +++++++--- .../verifier/constructor_arguments.ex | 141 ++++++++- .../explorer/smart_contract/verifier_test.exs | 283 +++++++++++++++++- 5 files changed, 534 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 475e409e26..883d97f951 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,10 @@ ### Features ### Fixes -- [#3106](https://github.com/poanetwork/blockscout/pull/3106) - Verify contract using creation tx input +- [#3112](https://github.com/poanetwork/blockscout/pull/3112) - Fix verification of contracts, compiled with nightly builds of solc compiler +- [#3112](https://github.com/poanetwork/blockscout/pull/3112) - Check compiler version at contract verification +- [#3106](https://github.com/poanetwork/blockscout/pull/3106) - Fix verification of contracts with `immutable` declaration +- [#3106](https://github.com/poanetwork/blockscout/pull/3106) - Fix verification of contracts, created from factory (from internal transaction) ### Chore diff --git a/apps/explorer/lib/explorer/chain/smart_contract.ex b/apps/explorer/lib/explorer/chain/smart_contract.ex index 0db7d7e613..9d4c03134a 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract.ex @@ -326,6 +326,7 @@ defmodule Explorer.Chain.SmartContract do defp upsert_contract_methods(changeset), do: changeset defp error_message(:compilation), do: "There was an error compiling your contract." + defp error_message(:compiler_version), do: "Compiler version does not match, please try again." defp error_message(:generated_bytecode), do: "Bytecode does not match, please try again." defp error_message(:constructor_arguments), do: "Constructor arguments do not match, please try again." defp error_message(:name), do: "Wrong contract name, please try again." diff --git a/apps/explorer/lib/explorer/smart_contract/verifier.ex b/apps/explorer/lib/explorer/smart_contract/verifier.ex index 979fb1128d..1b4eb88a3e 100644 --- a/apps/explorer/lib/explorer/smart_contract/verifier.ex +++ b/apps/explorer/lib/explorer/smart_contract/verifier.ex @@ -1,3 +1,4 @@ +# credo:disable-for-this-file defmodule Explorer.SmartContract.Verifier do @moduledoc """ Module responsible to verify the Smart Contract. @@ -16,7 +17,7 @@ defmodule Explorer.SmartContract.Verifier do @metadata_hash_prefix_0_5_11 "a265627a7a72315820" @metadata_hash_prefix_0_6_0 "a264697066735822" - @metadata_hash_common_suffix "64736f6c6343" + @metadata_hash_common_suffix "64736f6c63" def evaluate_authenticity(_, %{"name" => ""}), do: {:error, :name} @@ -83,19 +84,28 @@ defmodule Explorer.SmartContract.Verifier do contract_source_code, contract_name ) do - %{"metadata_hash" => _generated_metadata_hash, "bytecode" => generated_bytecode} = - extract_bytecode_and_metadata_hash(bytecode) + %{ + "metadata_hash" => _generated_metadata_hash, + "bytecode" => generated_bytecode, + "compiler_version" => generated_compiler_version + } = extract_bytecode_and_metadata_hash(bytecode) "0x" <> blockchain_created_tx_input = address_hash |> Chain.smart_contract_creation_tx_bytecode() - %{"metadata_hash" => _metadata_hash, "bytecode" => blockchain_bytecode_without_whisper} = - extract_bytecode_and_metadata_hash(blockchain_created_tx_input) + %{ + "metadata_hash" => _metadata_hash, + "bytecode" => blockchain_bytecode_without_whisper, + "compiler_version" => compiler_version + } = extract_bytecode_and_metadata_hash(blockchain_created_tx_input) empty_constructor_arguments = arguments_data == "" or arguments_data == nil cond do + generated_compiler_version != compiler_version -> + {:error, :compiler_version} + generated_bytecode != blockchain_bytecode_without_whisper && !try_library_verification(generated_bytecode, blockchain_bytecode_without_whisper) -> {:error, :generated_bytecode} @@ -147,54 +157,86 @@ defmodule Explorer.SmartContract.Verifier do https://solidity.readthedocs.io/en/v0.5.3/metadata.html#encoding-of-the-metadata-hash-in-the-bytecode """ def extract_bytecode_and_metadata_hash("0x" <> code) do - %{"metadata_hash" => metadata_hash, "bytecode" => bytecode} = extract_bytecode_and_metadata_hash(code) - %{"metadata_hash" => metadata_hash, "bytecode" => "0x" <> bytecode} + %{"metadata_hash" => metadata_hash, "bytecode" => bytecode, "compiler_version" => compiler_version} = + extract_bytecode_and_metadata_hash(code) + + %{"metadata_hash" => metadata_hash, "bytecode" => "0x" <> bytecode, "compiler_version" => compiler_version} end def extract_bytecode_and_metadata_hash(code) do - do_extract_bytecode_and_metadata_hash([], String.downcase(code), "") + do_extract_bytecode_and_metadata_hash([], String.downcase(code), nil, nil) end - defp do_extract_bytecode_and_metadata_hash(extracted, remaining, metadata_hash) do + defp do_extract_bytecode_and_metadata_hash(extracted, remaining, metadata_hash, compiler_version) do case remaining do <<>> -> - bytecode = - extracted - |> Enum.reverse() - |> :binary.list_to_bin() - - %{"metadata_hash" => metadata_hash, "bytecode" => bytecode} + do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version) @metadata_hash_prefix_0_4_23 <> <> <> "0029" <> _constructor_arguments -> - bytecode = - extracted - |> Enum.reverse() - |> :binary.list_to_bin() - - %{"metadata_hash" => metadata_hash, "bytecode" => bytecode} + do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version) # Solidity >= 0.5.9; https://github.com/ethereum/solidity/blob/aa4ee3a1559ebc0354926af962efb3fcc7dc15bd/docs/metadata.rst @metadata_hash_prefix_0_5_10 <> <> <> - @metadata_hash_common_suffix <> <<_::binary-size(6)>> <> "0032" <> _constructor_arguments -> - bytecode = - extracted - |> Enum.reverse() - |> :binary.list_to_bin() + @metadata_hash_common_suffix <> + "43" <> <> <> "0032" <> _constructor_arguments -> + do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version) + + @metadata_hash_prefix_0_5_10 <> + <> <> + @metadata_hash_common_suffix <> + "7826" <> <> <> "0057" <> _constructor_arguments -> + do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version) + + @metadata_hash_prefix_0_5_10 <> + <> <> + @metadata_hash_common_suffix <> + "7827" <> <> <> "0057" <> _constructor_arguments -> + do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version) - %{"metadata_hash" => metadata_hash, "bytecode" => bytecode} + @metadata_hash_prefix_0_5_10 <> + <> <> + @metadata_hash_common_suffix <> + "7828" <> <> <> "0058" <> _constructor_arguments -> + do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version) + + @metadata_hash_prefix_0_5_10 <> + <> <> + @metadata_hash_common_suffix <> + "7829" <> <> <> "0059" <> _constructor_arguments -> + do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version) # Solidity >= 0.5.11 https://github.com/ethereum/solidity/blob/develop/Changelog.md#0511-2019-08-12 # Metadata: Update the swarm hash to the current specification, changes bzzr0 to bzzr1 and urls to use bzz-raw:// @metadata_hash_prefix_0_5_11 <> <> <> - @metadata_hash_common_suffix <> <<_::binary-size(6)>> <> "0032" <> _constructor_arguments -> - bytecode = - extracted - |> Enum.reverse() - |> :binary.list_to_bin() + @metadata_hash_common_suffix <> + "43" <> <> <> "0032" <> _constructor_arguments -> + do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version) + + @metadata_hash_prefix_0_5_11 <> + <> <> + @metadata_hash_common_suffix <> + "7826" <> <> <> "0057" <> _constructor_arguments -> + do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version) + + @metadata_hash_prefix_0_5_11 <> + <> <> + @metadata_hash_common_suffix <> + "7827" <> <> <> "0057" <> _constructor_arguments -> + do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version) - %{"metadata_hash" => metadata_hash, "bytecode" => bytecode} + @metadata_hash_prefix_0_5_11 <> + <> <> + @metadata_hash_common_suffix <> + "7828" <> <> <> "0058" <> _constructor_arguments -> + do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version) + + @metadata_hash_prefix_0_5_11 <> + <> <> + @metadata_hash_common_suffix <> + "7829" <> <> <> "0059" <> _constructor_arguments -> + do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version) # Solidity >= 0.6.0 https://github.com/ethereum/solidity/blob/develop/Changelog.md#060-2019-12-17 # https://github.com/ethereum/solidity/blob/26b700771e9cc9c956f0503a05de69a1be427963/docs/metadata.rst#encoding-of-the-metadata-hash-in-the-bytecode @@ -208,19 +250,48 @@ defmodule Explorer.SmartContract.Verifier do # Fixing PR has been created https://github.com/ethereum/solidity/pull/8174 @metadata_hash_prefix_0_6_0 <> <> <> - @metadata_hash_common_suffix <> <<_::binary-size(6)>> <> "0033" <> _constructor_arguments -> - bytecode = - extracted - |> Enum.reverse() - |> :binary.list_to_bin() + @metadata_hash_common_suffix <> + "43" <> <> <> "0033" <> _constructor_arguments -> + do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version) + + @metadata_hash_prefix_0_6_0 <> + <> <> + @metadata_hash_common_suffix <> + "7826" <> <> <> "0057" <> _constructor_arguments -> + do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version) + + @metadata_hash_prefix_0_6_0 <> + <> <> + @metadata_hash_common_suffix <> + "7827" <> <> <> "0057" <> _constructor_arguments -> + do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version) + + @metadata_hash_prefix_0_6_0 <> + <> <> + @metadata_hash_common_suffix <> + "7828" <> <> <> "0058" <> _constructor_arguments -> + do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version) - %{"metadata_hash" => metadata_hash, "bytecode" => bytecode} + @metadata_hash_prefix_0_6_0 <> + <> <> + @metadata_hash_common_suffix <> + "7829" <> <> <> "0059" <> _constructor_arguments -> + do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version) <> <> rest -> - do_extract_bytecode_and_metadata_hash([next | extracted], rest, metadata_hash) + do_extract_bytecode_and_metadata_hash([next | extracted], rest, metadata_hash, compiler_version) end end + defp do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version) do + bytecode = + extracted + |> Enum.reverse() + |> :binary.list_to_bin() + + %{"metadata_hash" => metadata_hash, "bytecode" => bytecode, "compiler_version" => compiler_version} + end + def previous_evm_versions(current_evm_version) do index = Enum.find_index(CodeCompiler.allowed_evm_versions(), fn el -> el == current_evm_version end) diff --git a/apps/explorer/lib/explorer/smart_contract/verifier/constructor_arguments.ex b/apps/explorer/lib/explorer/smart_contract/verifier/constructor_arguments.ex index a647034094..8edc758693 100644 --- a/apps/explorer/lib/explorer/smart_contract/verifier/constructor_arguments.ex +++ b/apps/explorer/lib/explorer/smart_contract/verifier/constructor_arguments.ex @@ -1,3 +1,4 @@ +# credo:disable-for-this-file defmodule Explorer.SmartContract.Verifier.ConstructorArguments do @moduledoc """ Smart contract contrstructor arguments verification logic. @@ -10,7 +11,7 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do @metadata_hash_prefix_0_5_11 "a265627a7a72315820" @metadata_hash_prefix_0_6_0 "a264697066735822" - @metadata_hash_common_suffix "64736f6c6343" + @metadata_hash_common_suffix "64736f6c63" def verify(address_hash, contract_code, arguments_data, contract_source_code, contract_name) do arguments_data = arguments_data |> String.trim_trailing() |> String.trim_leading("0x") @@ -53,7 +54,51 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do # Solidity >= 0.5.10 https://solidity.readthedocs.io/en/v0.5.10/metadata.html @metadata_hash_prefix_0_5_10 <> <<_::binary-size(64)>> <> - @metadata_hash_common_suffix <> <<_::binary-size(6)>> <> "0032" <> constructor_arguments -> + @metadata_hash_common_suffix <> "43" <> <<_::binary-size(6)>> <> "0032" <> constructor_arguments -> + split_constructor_arguments_and_extract_check_func( + constructor_arguments, + check_func, + contract_source_code, + contract_name, + @metadata_hash_prefix_0_5_10 + ) + + @metadata_hash_prefix_0_5_10 <> + <<_::binary-size(64)>> <> + @metadata_hash_common_suffix <> "7826" <> <<_::binary-size(76)>> <> "0057" <> constructor_arguments -> + split_constructor_arguments_and_extract_check_func( + constructor_arguments, + check_func, + contract_source_code, + contract_name, + @metadata_hash_prefix_0_5_10 + ) + + @metadata_hash_prefix_0_5_10 <> + <<_::binary-size(64)>> <> + @metadata_hash_common_suffix <> "7827" <> <<_::binary-size(78)>> <> "0057" <> constructor_arguments -> + split_constructor_arguments_and_extract_check_func( + constructor_arguments, + check_func, + contract_source_code, + contract_name, + @metadata_hash_prefix_0_5_10 + ) + + @metadata_hash_prefix_0_5_10 <> + <<_::binary-size(64)>> <> + @metadata_hash_common_suffix <> "7828" <> <<_::binary-size(80)>> <> "0058" <> constructor_arguments -> + split_constructor_arguments_and_extract_check_func( + constructor_arguments, + check_func, + contract_source_code, + contract_name, + @metadata_hash_prefix_0_5_10 + ) + + @metadata_hash_prefix_0_5_10 <> + <<_::binary-size(64)>> <> + @metadata_hash_common_suffix <> "7829" <> <<_::binary-size(82)>> <> "0059" <> constructor_arguments -> split_constructor_arguments_and_extract_check_func( constructor_arguments, check_func, @@ -66,7 +111,51 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do # Metadata: Update the swarm hash to the current specification, changes bzzr0 to bzzr1 and urls to use bzz-raw:// @metadata_hash_prefix_0_5_11 <> <<_::binary-size(64)>> <> - @metadata_hash_common_suffix <> <<_::binary-size(6)>> <> "0032" <> constructor_arguments -> + @metadata_hash_common_suffix <> "43" <> <<_::binary-size(6)>> <> "0032" <> constructor_arguments -> + split_constructor_arguments_and_extract_check_func( + constructor_arguments, + check_func, + contract_source_code, + contract_name, + @metadata_hash_prefix_0_5_11 + ) + + @metadata_hash_prefix_0_5_11 <> + <<_::binary-size(64)>> <> + @metadata_hash_common_suffix <> "7826" <> <<_::binary-size(76)>> <> "0057" <> constructor_arguments -> + split_constructor_arguments_and_extract_check_func( + constructor_arguments, + check_func, + contract_source_code, + contract_name, + @metadata_hash_prefix_0_5_11 + ) + + @metadata_hash_prefix_0_5_11 <> + <<_::binary-size(64)>> <> + @metadata_hash_common_suffix <> "7827" <> <<_::binary-size(78)>> <> "0057" <> constructor_arguments -> + split_constructor_arguments_and_extract_check_func( + constructor_arguments, + check_func, + contract_source_code, + contract_name, + @metadata_hash_prefix_0_5_11 + ) + + @metadata_hash_prefix_0_5_11 <> + <<_::binary-size(64)>> <> + @metadata_hash_common_suffix <> "7828" <> <<_::binary-size(80)>> <> "0058" <> constructor_arguments -> + split_constructor_arguments_and_extract_check_func( + constructor_arguments, + check_func, + contract_source_code, + contract_name, + @metadata_hash_prefix_0_5_11 + ) + + @metadata_hash_prefix_0_5_11 <> + <<_::binary-size(64)>> <> + @metadata_hash_common_suffix <> "7829" <> <<_::binary-size(82)>> <> "0059" <> constructor_arguments -> split_constructor_arguments_and_extract_check_func( constructor_arguments, check_func, @@ -87,7 +176,51 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do # Fixing PR has been created https://github.com/ethereum/solidity/pull/8174 @metadata_hash_prefix_0_6_0 <> <<_::binary-size(68)>> <> - @metadata_hash_common_suffix <> <<_::binary-size(6)>> <> "0033" <> constructor_arguments -> + @metadata_hash_common_suffix <> "43" <> <<_::binary-size(6)>> <> "0033" <> constructor_arguments -> + split_constructor_arguments_and_extract_check_func( + constructor_arguments, + check_func, + contract_source_code, + contract_name, + @metadata_hash_prefix_0_6_0 + ) + + @metadata_hash_prefix_0_6_0 <> + <<_::binary-size(68)>> <> + @metadata_hash_common_suffix <> "7826" <> <<_::binary-size(76)>> <> "0057" <> constructor_arguments -> + split_constructor_arguments_and_extract_check_func( + constructor_arguments, + check_func, + contract_source_code, + contract_name, + @metadata_hash_prefix_0_6_0 + ) + + @metadata_hash_prefix_0_6_0 <> + <<_::binary-size(68)>> <> + @metadata_hash_common_suffix <> "7827" <> <<_::binary-size(78)>> <> "0057" <> constructor_arguments -> + split_constructor_arguments_and_extract_check_func( + constructor_arguments, + check_func, + contract_source_code, + contract_name, + @metadata_hash_prefix_0_6_0 + ) + + @metadata_hash_prefix_0_6_0 <> + <<_::binary-size(68)>> <> + @metadata_hash_common_suffix <> "7828" <> <<_::binary-size(80)>> <> "0058" <> constructor_arguments -> + split_constructor_arguments_and_extract_check_func( + constructor_arguments, + check_func, + contract_source_code, + contract_name, + @metadata_hash_prefix_0_6_0 + ) + + @metadata_hash_prefix_0_6_0 <> + <<_::binary-size(68)>> <> + @metadata_hash_common_suffix <> "7829" <> <<_::binary-size(82)>> <> "0059" <> constructor_arguments -> split_constructor_arguments_and_extract_check_func( constructor_arguments, check_func, diff --git a/apps/explorer/test/explorer/smart_contract/verifier_test.exs b/apps/explorer/test/explorer/smart_contract/verifier_test.exs index 0500fc4a3d..8199a5600c 100644 --- a/apps/explorer/test/explorer/smart_contract/verifier_test.exs +++ b/apps/explorer/test/explorer/smart_contract/verifier_test.exs @@ -7,6 +7,57 @@ defmodule Explorer.SmartContract.VerifierTest do alias Explorer.SmartContract.Verifier alias Explorer.Factory + @code_0_4 """ + pragma solidity ^0.4.0; + contract Incrementer { + event Incremented(address indexed sender, uint256 newValue); + uint256 public value; + address public lastSender; + constructor(uint256 initialValue) public { + value = initialValue; + lastSender = msg.sender; + } + function inc(uint256 delta) public { + value = value + delta; + lastSender = msg.sender; + } + } + """ + + @code_0_5 """ + pragma solidity ^0.5.0; + contract Incrementer { + event Incremented(address indexed sender, uint256 newValue); + uint256 public value; + address public lastSender; + constructor(uint256 initialValue) public { + value = initialValue; + lastSender = msg.sender; + } + function inc(uint256 delta) public { + value = value + delta; + lastSender = msg.sender; + } + } + """ + + @code_0_6 """ + pragma solidity ^0.6.0; + contract Incrementer { + event Incremented(address indexed sender, uint256 newValue); + uint256 public value; + address public lastSender; + constructor(uint256 initialValue) public { + value = initialValue; + lastSender = msg.sender; + } + function inc(uint256 delta) public { + value = value + delta; + lastSender = msg.sender; + } + } + """ + describe "evaluate_authenticity/2" do setup do {:ok, contract_code_info: Factory.contract_code_info()} @@ -301,7 +352,7 @@ defmodule Explorer.SmartContract.VerifierTest do bytecode = "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600" - %{"metadata_hash" => _metadata_hash, "bytecode" => bytecode_from_code} = + %{"metadata_hash" => _metadata_hash, "bytecode" => bytecode_from_code, "compiler_version" => _compiler_version} = Verifier.extract_bytecode_and_metadata_hash(code) assert bytecode == bytecode_from_code @@ -320,7 +371,7 @@ defmodule Explorer.SmartContract.VerifierTest do bytecode = "0x608060405234801561001057600080fd5b5060df80610010029f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600" - %{"metadata_hash" => _metadata_hash, "bytecode" => bytecode_from_code} = + %{"metadata_hash" => _metadata_hash, "bytecode" => bytecode_from_code, "compiler_version" => _compiler_version} = Verifier.extract_bytecode_and_metadata_hash(code) assert bytecode == bytecode_from_code @@ -425,4 +476,232 @@ defmodule Explorer.SmartContract.VerifierTest do assert abi != nil end end + + describe "compiler version tests" do + test "verification is failed if wrong version of compiler" do + bytecode_0_5_10 = + "0x608060405234801561001057600080fd5b506040516102453803806102458339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101a98061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72305820fb47165501c50aae8ccb0394b15f4302606e0ba55eb6d59fe12eca19ba494d5e64736f6c634300050a0032" + + constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a" + contract_address = insert(:contract_address, contract_code: bytecode_0_5_10) + bytecode_construtor_arguments = "#{bytecode_0_5_10}#{constructor_arguments}" + + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: bytecode_construtor_arguments + ) + |> with_block() + + params = %{ + "contract_source_code" => @code_0_5, + "compiler_version" => "v0.5.11+commit.c082d0b4", + "evm_version" => "homestead", + "name" => "Incrementer", + "optimization" => false, + "constructor_arguments" => constructor_arguments + } + + response = Verifier.evaluate_authenticity(contract_address.hash, params) + assert {:error, :compiler_version} = response + end + + test "verification is successful if proper version of compiler" do + bytecode_0_5_10 = + "0x608060405234801561001057600080fd5b506040516102453803806102458339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101a98061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72305820fb47165501c50aae8ccb0394b15f4302606e0ba55eb6d59fe12eca19ba494d5e64736f6c634300050a0032" + + constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a" + contract_address = insert(:contract_address, contract_code: bytecode_0_5_10) + bytecode_construtor_arguments = "#{bytecode_0_5_10}#{constructor_arguments}" + + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: bytecode_construtor_arguments + ) + |> with_block() + + params = %{ + "contract_source_code" => @code_0_5, + "compiler_version" => "v0.5.10+commit.5a6ea5b1", + "evm_version" => "homestead", + "name" => "Incrementer", + "optimization" => false, + "constructor_arguments" => constructor_arguments + } + + assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) + assert abi != nil + end + end + + describe "verification with nightly builds" do + test "verification is successful if proper nightly version of compiler ~0.4" do + bytecode_v0_4_24_nightly_2018_4_26_commit_ef2111a2 = + "0x608060405234801561001057600080fd5b5060405160208061023d833981018060405281019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101b28061008b6000396000f300608060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063256fec881461005c5780633fa4f245146100b3578063812600df146100de575b600080fd5b34801561006857600080fd5b5061007161010b565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156100bf57600080fd5b506100c8610131565b6040518082815260200191505060405180910390f35b3480156100ea57600080fd5b5061010960048036038101908080359060200190929190505050610137565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505600a165627a7a723058202d622d653be0a507f7ac0bc89d8934ccdbaf5e127abd603c3864a462149885070029" + + constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a" + contract_address = insert(:contract_address, contract_code: bytecode_v0_4_24_nightly_2018_4_26_commit_ef2111a2) + bytecode_construtor_arguments = "#{bytecode_v0_4_24_nightly_2018_4_26_commit_ef2111a2}#{constructor_arguments}" + + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: bytecode_construtor_arguments + ) + |> with_block() + + params = %{ + "contract_source_code" => @code_0_4, + "compiler_version" => "v0.4.24-nightly.2018.4.26+commit.ef2111a2", + "evm_version" => "homestead", + "name" => "Incrementer", + "optimization" => false, + "constructor_arguments" => constructor_arguments + } + + assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) + assert abi != nil + end + + test "verification is successful if proper nightly version of compiler ~0.5.10" do + bytecode_0_5_10_nightly_2019_6_4_commit_95e6b2e4 = + "0x608060405234801561001057600080fd5b5060405161026a38038061026a8339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101ce8061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a723058208d4e3fa9b2179a8384e617e388dde334be1b44e7b11b42ab964ab1050e7cedca64736f6c637827302e352e31302d6e696768746c792e323031392e362e342b636f6d6d69742e39356536623265340057" + + constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a" + contract_address = insert(:contract_address, contract_code: bytecode_0_5_10_nightly_2019_6_4_commit_95e6b2e4) + bytecode_construtor_arguments = "#{bytecode_0_5_10_nightly_2019_6_4_commit_95e6b2e4}#{constructor_arguments}" + + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: bytecode_construtor_arguments + ) + |> with_block() + + params = %{ + "contract_source_code" => @code_0_5, + "compiler_version" => "v0.5.10-nightly.2019.6.4+commit.95e6b2e4", + "evm_version" => "homestead", + "name" => "Incrementer", + "optimization" => false, + "constructor_arguments" => constructor_arguments + } + + assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) + assert abi != nil + end + + test "verification is successful if proper nightly version of compiler ~0.5.11" do + bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753 = + "0x608060405234801561001057600080fd5b5060405161026b38038061026b8339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101cf8061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72305820f7420b8c3b16d83ce728d8c279f0f887c4dcd7bfcd38c484acc9cdb82fde785764736f6c637828302e352e31312d6e696768746c792e323031392e362e32352b636f6d6d69742e31636338343735330058" + + constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a" + contract_address = insert(:contract_address, contract_code: bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753) + bytecode_construtor_arguments = "#{bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753}#{constructor_arguments}" + + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: bytecode_construtor_arguments + ) + |> with_block() + + params = %{ + "contract_source_code" => @code_0_5, + "compiler_version" => "v0.5.11-nightly.2019.6.25+commit.1cc84753", + "evm_version" => "homestead", + "name" => "Incrementer", + "optimization" => false, + "constructor_arguments" => constructor_arguments + } + + assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) + assert abi != nil + end + + test "verification is successful if proper nightly version of compiler ~0.5.14" do + bytecode_0_5_14_nightly_2019_12_10_commit_45aa7a88 = + "0x608060405234801561001057600080fd5b5060405161026c38038061026c8339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101d08061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72315820ec5a7ce04b1c2f97a3d3e61ae1b5cb06585e81c504542fd9668a8ead654da72764736f6c637829302e352e31342d6e696768746c792e323031392e31322e31302b636f6d6d69742e34356161376138380059" + + constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a" + contract_address = insert(:contract_address, contract_code: bytecode_0_5_14_nightly_2019_12_10_commit_45aa7a88) + bytecode_construtor_arguments = "#{bytecode_0_5_14_nightly_2019_12_10_commit_45aa7a88}#{constructor_arguments}" + + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: bytecode_construtor_arguments + ) + |> with_block() + + params = %{ + "contract_source_code" => @code_0_5, + "compiler_version" => "v0.5.14-nightly.2019.12.10+commit.45aa7a88", + "evm_version" => "homestead", + "name" => "Incrementer", + "optimization" => false, + "constructor_arguments" => constructor_arguments + } + + assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) + assert abi != nil + end + + test "verification is successful if proper nightly version of compiler ~0.6.0" do + bytecode_0_6_1_nightly_2020_1_2_commit_d082b9b8 = + "0x608060405234801561001057600080fd5b5060405161026a38038061026a8339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101ce8061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea264697066735822122029b5dde5889a195ed02cebb1a638ae3754be34464b9a2bc8b48b6286636031fb64736f6c637826302e362e312d6e696768746c792e323032302e312e322b636f6d6d69742e64303832623962380057" + + constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a" + contract_address = insert(:contract_address, contract_code: bytecode_0_6_1_nightly_2020_1_2_commit_d082b9b8) + bytecode_construtor_arguments = "#{bytecode_0_6_1_nightly_2020_1_2_commit_d082b9b8}#{constructor_arguments}" + + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: bytecode_construtor_arguments + ) + |> with_block() + + params = %{ + "contract_source_code" => @code_0_6, + "compiler_version" => "v0.6.1-nightly.2020.1.2+commit.d082b9b8", + "evm_version" => "homestead", + "name" => "Incrementer", + "optimization" => false, + "constructor_arguments" => constructor_arguments + } + + assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) + assert abi != nil + end + + test "verification is failed if wrong nightly version of compiler ~0.5.11" do + bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753 = + "0x608060405234801561001057600080fd5b5060405161026b38038061026b8339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101cf8061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72305820f7420b8c3b16d83ce728d8c279f0f887c4dcd7bfcd38c484acc9cdb82fde785764736f6c637828302e352e31312d6e696768746c792e323031392e362e32352b636f6d6d69742e31636338343735330058" + + constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a" + contract_address = insert(:contract_address, contract_code: bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753) + bytecode_construtor_arguments = "#{bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753}#{constructor_arguments}" + + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: bytecode_construtor_arguments + ) + |> with_block() + + params = %{ + "contract_source_code" => @code_0_5, + "compiler_version" => "v0.5.11-nightly.2019.8.10+commit.f5f2bbb2", + "evm_version" => "homestead", + "name" => "Incrementer", + "optimization" => false, + "constructor_arguments" => constructor_arguments + } + + response = Verifier.evaluate_authenticity(contract_address.hash, params) + assert {:error, :compiler_version} = response + end + end end From 5eeeb6dcabcd464e892a33080cacb81eb77164d7 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Mon, 18 May 2020 11:00:39 +0300 Subject: [PATCH 03/51] Performance fix: index on blocks [miner_hash, number] --- CHANGELOG.md | 1 + ...8075748_create_index_blocks_miner_hash_number_index.exs | 7 +++++++ 2 files changed, 8 insertions(+) create mode 100644 apps/explorer/priv/repo/migrations/20200518075748_create_index_blocks_miner_hash_number_index.exs diff --git a/CHANGELOG.md b/CHANGELOG.md index 287e54992b..b1147ec9f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Features ### Fixes +- [#3114](https://github.com/poanetwork/blockscout/pull/3114) - Fix performance of "Blocks validated" page ### Chore diff --git a/apps/explorer/priv/repo/migrations/20200518075748_create_index_blocks_miner_hash_number_index.exs b/apps/explorer/priv/repo/migrations/20200518075748_create_index_blocks_miner_hash_number_index.exs new file mode 100644 index 0000000000..f48ff9bbf0 --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20200518075748_create_index_blocks_miner_hash_number_index.exs @@ -0,0 +1,7 @@ +defmodule Explorer.Repo.Migrations.CreateIndexBlocksMinerHashNumberIndex do + use Ecto.Migration + + def change do + create_if_not_exists(index(:blocks, [:miner_hash, :number])) + end +end From a1e9e6f85f7638b4f889993de06508235d5f03ab Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Mon, 18 May 2020 16:14:21 +0300 Subject: [PATCH 04/51] Fix verification of contracts created from factory --- CHANGELOG.md | 2 +- apps/explorer/lib/explorer/chain.ex | 2 + .../explorer/smart_contract/verifier_test.exs | 77 ++++++++++++++++++- 3 files changed, 79 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f1c72a368..8c86a3c153 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ - [#3112](https://github.com/poanetwork/blockscout/pull/3112) - Fix verification of contracts, compiled with nightly builds of solc compiler - [#3112](https://github.com/poanetwork/blockscout/pull/3112) - Check compiler version at contract verification - [#3106](https://github.com/poanetwork/blockscout/pull/3106) - Fix verification of contracts with `immutable` declaration -- [#3106](https://github.com/poanetwork/blockscout/pull/3106) - Fix verification of contracts, created from factory (from internal transaction) +- [#3106](https://github.com/poanetwork/blockscout/pull/3106), [#3115](https://github.com/poanetwork/blockscout/pull/3115) - Fix verification of contracts, created from factory (from internal transaction) ### Chore diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index bf81d2edee..79650b48e7 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -2734,7 +2734,9 @@ defmodule Explorer.Chain do creation_int_tx_query = from( itx in InternalTransaction, + join: t in assoc(itx, :transaction), where: itx.created_contract_address_hash == ^address_hash, + where: t.status == ^1, select: itx.init ) diff --git a/apps/explorer/test/explorer/smart_contract/verifier_test.exs b/apps/explorer/test/explorer/smart_contract/verifier_test.exs index 8199a5600c..73be0078fa 100644 --- a/apps/explorer/test/explorer/smart_contract/verifier_test.exs +++ b/apps/explorer/test/explorer/smart_contract/verifier_test.exs @@ -445,10 +445,14 @@ defmodule Explorer.SmartContract.VerifierTest do contract_address = insert(:contract_address, contract_code: bytecode) + transaction_success_details = [ + status: :ok + ] + transaction = :transaction |> insert() - |> with_block() + |> with_block(transaction_success_details) :internal_transaction |> insert( @@ -475,6 +479,77 @@ defmodule Explorer.SmartContract.VerifierTest do assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) assert abi != nil end + + test "verifies smart-contract created from another contract using successful tx" do + path = File.cwd!() <> "/test/support/fixture/smart_contract/contract_from_factory.sol" + contract = File.read!(path) + + constructor_arguments = "4e1477bdc40fc2458bf646f96f269502658277779fdf0f4080fe798a2d45bc37" + + bytecode = + "0x608060405260043610603e5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416638052474d81146043575b600080fd5b348015604e57600080fd5b5060556067565b60408051918252519081900360200190f35b600054815600a165627a7a72305820a1a0ec90e133c3064fab0ae82aa02a020224ea39d2b5421b6788f800bdde02f60029" + + init = + "0x608060405234801561001057600080fd5b506040516020806100cc83398101604052516000556099806100336000396000f300608060405260043610603e5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416638052474d81146043575b600080fd5b348015604e57600080fd5b5060556067565b60408051918252519081900360200190f35b600054815600a165627a7a72305820a1a0ec90e133c3064fab0ae82aa02a020224ea39d2b5421b6788f800bdde02f600294e1477bdc40fc2458bf646f96f269502658277779fdf0f4080fe798a2d45bc37" + + contract_address = insert(:contract_address, contract_code: bytecode) + + transaction_success_details = [ + status: :ok + ] + + transaction_success = + :transaction + |> insert() + |> with_block(transaction_success_details) + + transaction_failure_details = [ + status: :error + ] + + transaction_failure = + :transaction + |> insert() + |> with_block(transaction_failure_details) + + :internal_transaction + |> insert( + created_contract_address_hash: contract_address.hash, + init: init, + type: "create", + created_contract_code: bytecode, + input: nil, + transaction_hash: transaction_success.hash, + index: 0, + block_hash: transaction_success.block_hash, + block_index: 0 + ) + + :internal_transaction + |> insert( + created_contract_address_hash: contract_address.hash, + init: init, + type: "create", + created_contract_code: bytecode, + input: nil, + transaction_hash: transaction_failure.hash, + index: 0, + block_hash: transaction_failure.block_hash, + block_index: 0 + ) + + params = %{ + "contract_source_code" => contract, + "compiler_version" => "v0.4.26+commit.4563c3fc", + "evm_version" => "default", + "name" => "ContractFromFactory", + "optimization" => true, + "constructor_arguments" => constructor_arguments + } + + assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) + assert abi != nil + end end describe "compiler version tests" do From a8775fbbfafefbe6a13a744f4bca544a92b1feac Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Thu, 21 May 2020 18:03:02 +0300 Subject: [PATCH 05/51] Fix performance of Inventory tab --- CHANGELOG.md | 1 + apps/explorer/lib/explorer/chain/token_transfer.ex | 3 ++- apps/explorer/test/explorer/chain/token_transfer_test.exs | 8 +++++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c86a3c153..d6e4ae3401 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Features ### Fixes +- [#3073](https://github.com/poanetwork/blockscout/issues/3073) - Fix performance of Inventory tab loading for ERC-721 tokens - [#3114](https://github.com/poanetwork/blockscout/pull/3114) - Fix performance of "Blocks validated" page - [#3112](https://github.com/poanetwork/blockscout/pull/3112) - Fix verification of contracts, compiled with nightly builds of solc compiler - [#3112](https://github.com/poanetwork/blockscout/pull/3112) - Check compiler version at contract verification diff --git a/apps/explorer/lib/explorer/chain/token_transfer.ex b/apps/explorer/lib/explorer/chain/token_transfer.ex index 228d0ce660..fc2a5f27dc 100644 --- a/apps/explorer/lib/explorer/chain/token_transfer.ex +++ b/apps/explorer/lib/explorer/chain/token_transfer.ex @@ -281,6 +281,7 @@ defmodule Explorer.Chain.TokenTransfer do end @doc """ + Innventory tab query. A token ERC-721 is considered unique because it corresponds to the possession of a specific asset. @@ -293,7 +294,7 @@ defmodule Explorer.Chain.TokenTransfer do tt in TokenTransfer, left_join: instance in Instance, on: tt.token_contract_address_hash == instance.token_contract_address_hash and tt.token_id == instance.token_id, - where: tt.token_contract_address_hash == ^contract_address_hash, + where: instance.token_contract_address_hash == ^contract_address_hash, order_by: [desc: tt.block_number], distinct: tt.token_id, preload: [:to_address], diff --git a/apps/explorer/test/explorer/chain/token_transfer_test.exs b/apps/explorer/test/explorer/chain/token_transfer_test.exs index b388fafcbe..f82ab71323 100644 --- a/apps/explorer/test/explorer/chain/token_transfer_test.exs +++ b/apps/explorer/test/explorer/chain/token_transfer_test.exs @@ -156,6 +156,12 @@ defmodule Explorer.Chain.TokenTransferTest do |> insert() |> with_block(insert(:block, number: 1)) + insert( + :token_instance, + token_id: 42, + token_contract_address_hash: token_contract_address.hash + ) + insert( :token_transfer, to_address: build(:address), @@ -168,7 +174,7 @@ defmodule Explorer.Chain.TokenTransferTest do another_transaction = :transaction |> insert() - |> with_block(insert(:block, number: 2)) + |> with_block(insert(:block, number: 3)) last_owner = insert( From b827d1c029b7d92715e55958a4a84b4a3ca29c6d Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Thu, 21 May 2020 20:01:48 +0300 Subject: [PATCH 06/51] Add index --- ...n_contract_address_hash_token_id_block_number_index.exs | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 apps/explorer/priv/repo/migrations/20200521170002_create_token_transfers_token_contract_address_hash_token_id_block_number_index.exs diff --git a/apps/explorer/priv/repo/migrations/20200521170002_create_token_transfers_token_contract_address_hash_token_id_block_number_index.exs b/apps/explorer/priv/repo/migrations/20200521170002_create_token_transfers_token_contract_address_hash_token_id_block_number_index.exs new file mode 100644 index 0000000000..f18fc168c2 --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20200521170002_create_token_transfers_token_contract_address_hash_token_id_block_number_index.exs @@ -0,0 +1,7 @@ +defmodule Explorer.Repo.Migrations.CreateTokenTransfersTokenContractAddressHashTokenIdBlockNumberIndex do + use Ecto.Migration + + def change do + create_if_not_exists(index(:token_transfers, [:token_contract_address_hash, :token_id, :block_number])) + end +end From 374cfead7746e13549da4721daa65ecbc738f2f0 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Thu, 21 May 2020 21:01:01 +0300 Subject: [PATCH 07/51] Inventory: distinct: [desc: tt.token_id] --- apps/explorer/lib/explorer/chain/token_transfer.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/explorer/lib/explorer/chain/token_transfer.ex b/apps/explorer/lib/explorer/chain/token_transfer.ex index fc2a5f27dc..344e8187e8 100644 --- a/apps/explorer/lib/explorer/chain/token_transfer.ex +++ b/apps/explorer/lib/explorer/chain/token_transfer.ex @@ -296,7 +296,7 @@ defmodule Explorer.Chain.TokenTransfer do on: tt.token_contract_address_hash == instance.token_contract_address_hash and tt.token_id == instance.token_id, where: instance.token_contract_address_hash == ^contract_address_hash, order_by: [desc: tt.block_number], - distinct: tt.token_id, + distinct: [desc: tt.token_id], preload: [:to_address], select: %{tt | instance: instance} ) From 6f7df4286521c7e338fcbacc04bb1b393c6c857e Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Fri, 22 May 2020 00:19:47 +0300 Subject: [PATCH 08/51] Modified direction in index --- ..._token_contract_address_hash_token_id_block_number_index.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/explorer/priv/repo/migrations/20200521170002_create_token_transfers_token_contract_address_hash_token_id_block_number_index.exs b/apps/explorer/priv/repo/migrations/20200521170002_create_token_transfers_token_contract_address_hash_token_id_block_number_index.exs index f18fc168c2..eb136b3f22 100644 --- a/apps/explorer/priv/repo/migrations/20200521170002_create_token_transfers_token_contract_address_hash_token_id_block_number_index.exs +++ b/apps/explorer/priv/repo/migrations/20200521170002_create_token_transfers_token_contract_address_hash_token_id_block_number_index.exs @@ -2,6 +2,6 @@ defmodule Explorer.Repo.Migrations.CreateTokenTransfersTokenContractAddressHashT use Ecto.Migration def change do - create_if_not_exists(index(:token_transfers, [:token_contract_address_hash, :token_id, :block_number])) + create_if_not_exists(index(:token_transfers, [:token_contract_address_hash, "token_id DESC", "block_number DESC"])) end end From c1a0e17c832f7adbc353f9984e2655b50621bba4 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Fri, 22 May 2020 14:06:00 +0300 Subject: [PATCH 09/51] Response from block JSON RPC method without totalDifficulty (uncle blocks) --- CHANGELOG.md | 1 + .../lib/ethereum_jsonrpc/block.ex | 43 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6e4ae3401..822a064f5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Features ### Fixes +- [#3121](https://github.com/poanetwork/blockscout/pull/3121) - Geth: handle response from eth_getblockbyhash JSON RPC method without totalDifficulty (uncle blocks) - [#3073](https://github.com/poanetwork/blockscout/issues/3073) - Fix performance of Inventory tab loading for ERC-721 tokens - [#3114](https://github.com/poanetwork/blockscout/pull/3114) - Fix performance of "Blocks validated" page - [#3112](https://github.com/poanetwork/blockscout/pull/3112) - Fix verification of contracts, compiled with nightly builds of solc compiler diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex index de83b3601e..20e7db22c8 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex @@ -235,6 +235,49 @@ defmodule EthereumJSONRPC.Block do } end + # Geth: a response from eth_getblockbyhash for uncle blocks is without `totalDifficulty` param + 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, + "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, + transactions_root: transactions_root, + uncles: uncles + } + end + @doc """ Get `t:EthereumJSONRPC.Transactions.elixir/0` from `t:elixir/0` From 23454a2a48de94de7e45865d2348f560f2c4b3d4 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Fri, 22 May 2020 15:37:43 +0300 Subject: [PATCH 10/51] Upper border of tx speed display --- .../lib/block_scout_web/views/transaction_view.ex | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex index 252df60bf5..e16061d28b 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex @@ -143,8 +143,9 @@ defmodule BlockScoutWeb.TransactionView do :pending end - def processing_time_duration(%Transaction{earliest_processing_start: nil}) do - :unknown + def processing_time_duration(%Transaction{earliest_processing_start: nil} = tx) do + avg_time = Timex.Duration.to_seconds(Explorer.Counters.AverageBlockTime.average_block_time()) + {:ok, "<= #{avg_time} seconds"} end def processing_time_duration(%Transaction{ From 26a651cdc499b36ef4fff7751da9e3b4a2d5af35 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Fri, 22 May 2020 15:49:12 +0300 Subject: [PATCH 11/51] Exclude balance percentage calculation from burn address --- CHANGELOG.md | 3 ++- .../lib/block_scout_web/views/address_view.ex | 11 ++++++++++ apps/block_scout_web/priv/gettext/default.pot | 22 +++++++++---------- .../priv/gettext/en/LC_MESSAGES/default.po | 22 +++++++++---------- 4 files changed, 35 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6e4ae3401..84a7bb5e72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,8 @@ ### Features ### Fixes -- [#3073](https://github.com/poanetwork/blockscout/issues/3073) - Fix performance of Inventory tab loading for ERC-721 tokens +- [#3122](https://github.com/poanetwork/blockscout/pull/3122) - Exclude balance percentage calculation for burn address on accounts page +- [#3119](https://github.com/poanetwork/blockscout/pull/3119), [#3120](https://github.com/poanetwork/blockscout/pull/3120) - Fix performance of Inventory tab loading for ERC-721 tokens - [#3114](https://github.com/poanetwork/blockscout/pull/3114) - Fix performance of "Blocks validated" page - [#3112](https://github.com/poanetwork/blockscout/pull/3112) - Fix verification of contracts, compiled with nightly builds of solc compiler - [#3112](https://github.com/poanetwork/blockscout/pull/3112) - Check compiler version at contract verification diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_view.ex index 3e536bb0b3..1b14c7b424 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_view.ex @@ -116,6 +116,17 @@ defmodule BlockScoutWeb.AddressView do def balance_percentage(_, nil), do: "" + def balance_percentage( + %Address{ + hash: %Explorer.Chain.Hash{ + byte_count: 20, + bytes: <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>> + } + }, + _ + ), + do: "" + def balance_percentage(%Address{fetched_coin_balance: balance}, total_supply) do if total_supply > 0 do balance diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot index de30d52252..0b467a05bf 100644 --- a/apps/block_scout_web/priv/gettext/default.pot +++ b/apps/block_scout_web/priv/gettext/default.pot @@ -903,7 +903,7 @@ msgstr "" #: lib/block_scout_web/templates/tokens/transfer/index.html.eex:14 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:4 #: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:7 -#: lib/block_scout_web/views/address_view.ex:314 +#: lib/block_scout_web/views/address_view.ex:325 #: lib/block_scout_web/views/tokens/instance/overview_view.ex:90 #: lib/block_scout_web/views/tokens/overview_view.ex:35 #: lib/block_scout_web/views/transaction_view.ex:379 @@ -1041,7 +1041,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/chain/show.html.eex:52 #: lib/block_scout_web/templates/layout/app.html.eex:30 -#: lib/block_scout_web/views/address_view.ex:127 +#: lib/block_scout_web/views/address_view.ex:138 msgid "Market Cap" msgstr "" @@ -1807,7 +1807,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address/_tabs.html.eex:37 #: lib/block_scout_web/templates/address_validation/index.html.eex:13 -#: lib/block_scout_web/views/address_view.ex:319 +#: lib/block_scout_web/views/address_view.ex:330 msgid "Blocks Validated" msgstr "" @@ -1817,18 +1817,18 @@ msgstr "" #: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:187 #: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:126 #: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:149 -#: lib/block_scout_web/views/address_view.ex:315 +#: lib/block_scout_web/views/address_view.ex:326 msgid "Code" msgstr "" #, elixir-format #: lib/block_scout_web/templates/address/_tabs.html.eex:26 -#: lib/block_scout_web/views/address_view.ex:318 +#: lib/block_scout_web/views/address_view.ex:329 msgid "Coin Balance History" msgstr "" #, elixir-format -#: lib/block_scout_web/views/address_view.ex:316 +#: lib/block_scout_web/views/address_view.ex:327 msgid "Decompiled Code" msgstr "" @@ -1837,7 +1837,7 @@ msgstr "" #: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:19 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:11 #: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:6 -#: lib/block_scout_web/views/address_view.ex:313 +#: lib/block_scout_web/views/address_view.ex:324 #: lib/block_scout_web/views/transaction_view.ex:380 msgid "Internal Transactions" msgstr "" @@ -1847,7 +1847,7 @@ msgstr "" #: lib/block_scout_web/templates/address_logs/index.html.eex:8 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:17 #: lib/block_scout_web/templates/transaction_log/index.html.eex:8 -#: lib/block_scout_web/views/address_view.ex:320 +#: lib/block_scout_web/views/address_view.ex:331 #: lib/block_scout_web/views/transaction_view.ex:381 msgid "Logs" msgstr "" @@ -1855,7 +1855,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address/_tabs.html.eex:62 #: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:25 -#: lib/block_scout_web/views/address_view.ex:317 +#: lib/block_scout_web/views/address_view.ex:328 #: lib/block_scout_web/views/tokens/overview_view.ex:37 msgid "Read Contract" msgstr "" @@ -1864,7 +1864,7 @@ msgstr "" #: lib/block_scout_web/templates/address/_tabs.html.eex:14 #: lib/block_scout_web/templates/address_token/index.html.eex:8 #: lib/block_scout_web/templates/address_token_transfer/index.html.eex:11 -#: lib/block_scout_web/views/address_view.ex:311 +#: lib/block_scout_web/views/address_view.ex:322 msgid "Tokens" msgstr "" @@ -1876,7 +1876,7 @@ msgstr "" #: lib/block_scout_web/templates/block_transaction/index.html.eex:18 #: lib/block_scout_web/templates/chain/show.html.eex:184 #: lib/block_scout_web/templates/layout/_topnav.html.eex:50 -#: lib/block_scout_web/views/address_view.ex:312 +#: lib/block_scout_web/views/address_view.ex:323 msgid "Transactions" msgstr "" diff --git a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po index de30d52252..0b467a05bf 100644 --- a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po +++ b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po @@ -903,7 +903,7 @@ msgstr "" #: lib/block_scout_web/templates/tokens/transfer/index.html.eex:14 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:4 #: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:7 -#: lib/block_scout_web/views/address_view.ex:314 +#: lib/block_scout_web/views/address_view.ex:325 #: lib/block_scout_web/views/tokens/instance/overview_view.ex:90 #: lib/block_scout_web/views/tokens/overview_view.ex:35 #: lib/block_scout_web/views/transaction_view.ex:379 @@ -1041,7 +1041,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/chain/show.html.eex:52 #: lib/block_scout_web/templates/layout/app.html.eex:30 -#: lib/block_scout_web/views/address_view.ex:127 +#: lib/block_scout_web/views/address_view.ex:138 msgid "Market Cap" msgstr "" @@ -1807,7 +1807,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address/_tabs.html.eex:37 #: lib/block_scout_web/templates/address_validation/index.html.eex:13 -#: lib/block_scout_web/views/address_view.ex:319 +#: lib/block_scout_web/views/address_view.ex:330 msgid "Blocks Validated" msgstr "" @@ -1817,18 +1817,18 @@ msgstr "" #: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:187 #: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:126 #: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:149 -#: lib/block_scout_web/views/address_view.ex:315 +#: lib/block_scout_web/views/address_view.ex:326 msgid "Code" msgstr "" #, elixir-format #: lib/block_scout_web/templates/address/_tabs.html.eex:26 -#: lib/block_scout_web/views/address_view.ex:318 +#: lib/block_scout_web/views/address_view.ex:329 msgid "Coin Balance History" msgstr "" #, elixir-format -#: lib/block_scout_web/views/address_view.ex:316 +#: lib/block_scout_web/views/address_view.ex:327 msgid "Decompiled Code" msgstr "" @@ -1837,7 +1837,7 @@ msgstr "" #: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:19 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:11 #: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:6 -#: lib/block_scout_web/views/address_view.ex:313 +#: lib/block_scout_web/views/address_view.ex:324 #: lib/block_scout_web/views/transaction_view.ex:380 msgid "Internal Transactions" msgstr "" @@ -1847,7 +1847,7 @@ msgstr "" #: lib/block_scout_web/templates/address_logs/index.html.eex:8 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:17 #: lib/block_scout_web/templates/transaction_log/index.html.eex:8 -#: lib/block_scout_web/views/address_view.ex:320 +#: lib/block_scout_web/views/address_view.ex:331 #: lib/block_scout_web/views/transaction_view.ex:381 msgid "Logs" msgstr "" @@ -1855,7 +1855,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address/_tabs.html.eex:62 #: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:25 -#: lib/block_scout_web/views/address_view.ex:317 +#: lib/block_scout_web/views/address_view.ex:328 #: lib/block_scout_web/views/tokens/overview_view.ex:37 msgid "Read Contract" msgstr "" @@ -1864,7 +1864,7 @@ msgstr "" #: lib/block_scout_web/templates/address/_tabs.html.eex:14 #: lib/block_scout_web/templates/address_token/index.html.eex:8 #: lib/block_scout_web/templates/address_token_transfer/index.html.eex:11 -#: lib/block_scout_web/views/address_view.ex:311 +#: lib/block_scout_web/views/address_view.ex:322 msgid "Tokens" msgstr "" @@ -1876,7 +1876,7 @@ msgstr "" #: lib/block_scout_web/templates/block_transaction/index.html.eex:18 #: lib/block_scout_web/templates/chain/show.html.eex:184 #: lib/block_scout_web/templates/layout/_topnav.html.eex:50 -#: lib/block_scout_web/views/address_view.ex:312 +#: lib/block_scout_web/views/address_view.ex:323 msgid "Transactions" msgstr "" From 054bfa177a3c95466360eb84260c79527b145706 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Sat, 23 May 2020 08:13:46 +0300 Subject: [PATCH 12/51] use patched version of ex_abi --- apps/ethereum_jsonrpc/mix.exs | 2 +- mix.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/ethereum_jsonrpc/mix.exs b/apps/ethereum_jsonrpc/mix.exs index 064b50938f..b3b0c5eb75 100644 --- a/apps/ethereum_jsonrpc/mix.exs +++ b/apps/ethereum_jsonrpc/mix.exs @@ -84,7 +84,7 @@ defmodule EthereumJsonrpc.MixProject do # Convert unix timestamps in JSONRPC to DateTimes {:timex, "~> 3.6"}, # Encode/decode function names and arguments - {:ex_abi, [git: "https://github.com/poanetwork/ex_abi.git", branch: "master"]}, + {:ex_abi, git: "https://github.com/ayrat555/ex_abi-1", branch: "ayrat555/re-write-encoder"}, # `:verify_fun` for `Socket.Web.connect` {:ssl_verify_fun, "~> 1.1"}, # `EthereumJSONRPC.WebSocket` diff --git a/mix.lock b/mix.lock index 8e8b9e3f38..dd5ccbc5ea 100644 --- a/mix.lock +++ b/mix.lock @@ -32,7 +32,7 @@ "ecto": {:hex, :ecto, "3.3.1", "82ab74298065bf0c64ca299f6c6785e68ea5d6b980883ee80b044499df35aba1", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "e6c614dfe3bcff2d575ce16d815dbd43f4ee1844599a83de1eea81976a31c174"}, "ecto_sql": {:hex, :ecto_sql, "3.3.2", "92804e0de69bb63e621273c3492252cb08a29475c05d40eeb6f41ad2d483cfd3", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b82d89d4e6a9f7f7f04783b07e8b0af968e0be2f01ee4b39047fe727c5c07471"}, "elixir_make": {:hex, :elixir_make, "0.6.0", "38349f3e29aff4864352084fc736fa7fa0f2995a819a737554f7ebd28b85aaab", [:mix], [], "hexpm", "d522695b93b7f0b4c0fcb2dfe73a6b905b1c301226a5a55cb42e5b14d509e050"}, - "ex_abi": {:git, "https://github.com/poanetwork/ex_abi.git", "ef450816ef4ca98648125577b675ab584251a883", [branch: "master"]}, + "ex_abi": {:git, "https://github.com/ayrat555/ex_abi-1", "b012098e71129b3ff7394685e1aaa5b7180e9b53", [branch: "ayrat555/re-write-encoder"]}, "ex_cldr": {:hex, :ex_cldr, "2.7.2", "d79a1af6ed12630a15175d2b88d4381b22db5d6f835c5da8de132f0cf712b233", [:mix], [{:cldr_utils, "~> 2.1", [hex: :cldr_utils, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:gettext, "~> 0.13", [hex: :gettext, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 0.5", [hex: :nimble_parsec, repo: "hexpm", optional: false]}, {:plug, "~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "52344684e4e0ff046e1929ae019c0b3c6b122c77c948a43be30684015f2036e2"}, "ex_cldr_currencies": {:hex, :ex_cldr_currencies, "2.3.0", "bffae489416b8b05d4683403263f5d62aae17de70c24ff915a533541fea514de", [:mix], [{:ex_cldr, "~> 2.6", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "7d8ba3738b24d9c7d31bc63b131785b444ed05388fd1cc0958216eb5992d79d6"}, "ex_cldr_lists": {:hex, :ex_cldr_lists, "2.2.0", "b99f8752d098fc6ba5f083bbd0b25d0d01e36b0042155cf6abd5f205306ba849", [:mix], [{:ex_cldr, "~> 2.6", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:ex_cldr_numbers, "~> 2.6", [hex: :ex_cldr_numbers, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "89e25d539a4126472560da2e28b6f1aeb859f5afc0778d6b594029c4226d1775"}, From 5d374476fd0f6d7fa7ce79ace12c4f8a14d5f6b6 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Sat, 23 May 2020 08:34:43 +0300 Subject: [PATCH 13/51] remove a dirty hack for decoding abi of string --- apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/encoder.ex | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/encoder.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/encoder.ex index de0f4d1ca3..8cefe10850 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/encoder.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/encoder.ex @@ -58,16 +58,6 @@ defmodule EthereumJSONRPC.Encoder do end) end - def decode_result(result, %{returns: r} = fs) when r in [:string, [:string]] do - case decode_result(result, %{fs | returns: {:tuple, [:string]}}) do - {id, {:ok, [{string}]}} -> - {id, {:ok, [string]}} - - error -> - error - end - end - def decode_result(%{id: id, result: result}, function_selector) do types_list = List.wrap(function_selector.returns) From 79b0f6f9ac778c8f24a0e2428bd57a2a0f991f2d Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Sat, 23 May 2020 08:41:57 +0300 Subject: [PATCH 14/51] remove IO.inspect --- .../chain/import/runner/address/token_balances_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/explorer/test/explorer/chain/import/runner/address/token_balances_test.exs b/apps/explorer/test/explorer/chain/import/runner/address/token_balances_test.exs index a40cd8599e..a35fbcc578 100644 --- a/apps/explorer/test/explorer/chain/import/runner/address/token_balances_test.exs +++ b/apps/explorer/test/explorer/chain/import/runner/address/token_balances_test.exs @@ -93,7 +93,7 @@ defmodule Explorer.Chain.Import.Runner.Address.TokenBalancesTest do value_fetched_at: DateTime.utc_now() } - run_changes(new_changes, options) |> IO.inspect() + run_changes(new_changes, options) end end From 0bea4fe3a7502377bd435dcfd9cdbf583abc5286 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Fri, 22 May 2020 15:49:12 +0300 Subject: [PATCH 15/51] Exclude balance percentage calculation from burn address --- CHANGELOG.md | 3 ++- .../lib/block_scout_web/views/address_view.ex | 11 ++++++++++ apps/block_scout_web/priv/gettext/default.pot | 22 +++++++++---------- .../priv/gettext/en/LC_MESSAGES/default.po | 22 +++++++++---------- 4 files changed, 35 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6e4ae3401..84a7bb5e72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,8 @@ ### Features ### Fixes -- [#3073](https://github.com/poanetwork/blockscout/issues/3073) - Fix performance of Inventory tab loading for ERC-721 tokens +- [#3122](https://github.com/poanetwork/blockscout/pull/3122) - Exclude balance percentage calculation for burn address on accounts page +- [#3119](https://github.com/poanetwork/blockscout/pull/3119), [#3120](https://github.com/poanetwork/blockscout/pull/3120) - Fix performance of Inventory tab loading for ERC-721 tokens - [#3114](https://github.com/poanetwork/blockscout/pull/3114) - Fix performance of "Blocks validated" page - [#3112](https://github.com/poanetwork/blockscout/pull/3112) - Fix verification of contracts, compiled with nightly builds of solc compiler - [#3112](https://github.com/poanetwork/blockscout/pull/3112) - Check compiler version at contract verification diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_view.ex index 3e536bb0b3..1b14c7b424 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_view.ex @@ -116,6 +116,17 @@ defmodule BlockScoutWeb.AddressView do def balance_percentage(_, nil), do: "" + def balance_percentage( + %Address{ + hash: %Explorer.Chain.Hash{ + byte_count: 20, + bytes: <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>> + } + }, + _ + ), + do: "" + def balance_percentage(%Address{fetched_coin_balance: balance}, total_supply) do if total_supply > 0 do balance diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot index de30d52252..0b467a05bf 100644 --- a/apps/block_scout_web/priv/gettext/default.pot +++ b/apps/block_scout_web/priv/gettext/default.pot @@ -903,7 +903,7 @@ msgstr "" #: lib/block_scout_web/templates/tokens/transfer/index.html.eex:14 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:4 #: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:7 -#: lib/block_scout_web/views/address_view.ex:314 +#: lib/block_scout_web/views/address_view.ex:325 #: lib/block_scout_web/views/tokens/instance/overview_view.ex:90 #: lib/block_scout_web/views/tokens/overview_view.ex:35 #: lib/block_scout_web/views/transaction_view.ex:379 @@ -1041,7 +1041,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/chain/show.html.eex:52 #: lib/block_scout_web/templates/layout/app.html.eex:30 -#: lib/block_scout_web/views/address_view.ex:127 +#: lib/block_scout_web/views/address_view.ex:138 msgid "Market Cap" msgstr "" @@ -1807,7 +1807,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address/_tabs.html.eex:37 #: lib/block_scout_web/templates/address_validation/index.html.eex:13 -#: lib/block_scout_web/views/address_view.ex:319 +#: lib/block_scout_web/views/address_view.ex:330 msgid "Blocks Validated" msgstr "" @@ -1817,18 +1817,18 @@ msgstr "" #: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:187 #: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:126 #: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:149 -#: lib/block_scout_web/views/address_view.ex:315 +#: lib/block_scout_web/views/address_view.ex:326 msgid "Code" msgstr "" #, elixir-format #: lib/block_scout_web/templates/address/_tabs.html.eex:26 -#: lib/block_scout_web/views/address_view.ex:318 +#: lib/block_scout_web/views/address_view.ex:329 msgid "Coin Balance History" msgstr "" #, elixir-format -#: lib/block_scout_web/views/address_view.ex:316 +#: lib/block_scout_web/views/address_view.ex:327 msgid "Decompiled Code" msgstr "" @@ -1837,7 +1837,7 @@ msgstr "" #: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:19 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:11 #: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:6 -#: lib/block_scout_web/views/address_view.ex:313 +#: lib/block_scout_web/views/address_view.ex:324 #: lib/block_scout_web/views/transaction_view.ex:380 msgid "Internal Transactions" msgstr "" @@ -1847,7 +1847,7 @@ msgstr "" #: lib/block_scout_web/templates/address_logs/index.html.eex:8 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:17 #: lib/block_scout_web/templates/transaction_log/index.html.eex:8 -#: lib/block_scout_web/views/address_view.ex:320 +#: lib/block_scout_web/views/address_view.ex:331 #: lib/block_scout_web/views/transaction_view.ex:381 msgid "Logs" msgstr "" @@ -1855,7 +1855,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address/_tabs.html.eex:62 #: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:25 -#: lib/block_scout_web/views/address_view.ex:317 +#: lib/block_scout_web/views/address_view.ex:328 #: lib/block_scout_web/views/tokens/overview_view.ex:37 msgid "Read Contract" msgstr "" @@ -1864,7 +1864,7 @@ msgstr "" #: lib/block_scout_web/templates/address/_tabs.html.eex:14 #: lib/block_scout_web/templates/address_token/index.html.eex:8 #: lib/block_scout_web/templates/address_token_transfer/index.html.eex:11 -#: lib/block_scout_web/views/address_view.ex:311 +#: lib/block_scout_web/views/address_view.ex:322 msgid "Tokens" msgstr "" @@ -1876,7 +1876,7 @@ msgstr "" #: lib/block_scout_web/templates/block_transaction/index.html.eex:18 #: lib/block_scout_web/templates/chain/show.html.eex:184 #: lib/block_scout_web/templates/layout/_topnav.html.eex:50 -#: lib/block_scout_web/views/address_view.ex:312 +#: lib/block_scout_web/views/address_view.ex:323 msgid "Transactions" msgstr "" diff --git a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po index de30d52252..0b467a05bf 100644 --- a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po +++ b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po @@ -903,7 +903,7 @@ msgstr "" #: lib/block_scout_web/templates/tokens/transfer/index.html.eex:14 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:4 #: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:7 -#: lib/block_scout_web/views/address_view.ex:314 +#: lib/block_scout_web/views/address_view.ex:325 #: lib/block_scout_web/views/tokens/instance/overview_view.ex:90 #: lib/block_scout_web/views/tokens/overview_view.ex:35 #: lib/block_scout_web/views/transaction_view.ex:379 @@ -1041,7 +1041,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/chain/show.html.eex:52 #: lib/block_scout_web/templates/layout/app.html.eex:30 -#: lib/block_scout_web/views/address_view.ex:127 +#: lib/block_scout_web/views/address_view.ex:138 msgid "Market Cap" msgstr "" @@ -1807,7 +1807,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address/_tabs.html.eex:37 #: lib/block_scout_web/templates/address_validation/index.html.eex:13 -#: lib/block_scout_web/views/address_view.ex:319 +#: lib/block_scout_web/views/address_view.ex:330 msgid "Blocks Validated" msgstr "" @@ -1817,18 +1817,18 @@ msgstr "" #: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:187 #: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:126 #: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:149 -#: lib/block_scout_web/views/address_view.ex:315 +#: lib/block_scout_web/views/address_view.ex:326 msgid "Code" msgstr "" #, elixir-format #: lib/block_scout_web/templates/address/_tabs.html.eex:26 -#: lib/block_scout_web/views/address_view.ex:318 +#: lib/block_scout_web/views/address_view.ex:329 msgid "Coin Balance History" msgstr "" #, elixir-format -#: lib/block_scout_web/views/address_view.ex:316 +#: lib/block_scout_web/views/address_view.ex:327 msgid "Decompiled Code" msgstr "" @@ -1837,7 +1837,7 @@ msgstr "" #: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:19 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:11 #: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:6 -#: lib/block_scout_web/views/address_view.ex:313 +#: lib/block_scout_web/views/address_view.ex:324 #: lib/block_scout_web/views/transaction_view.ex:380 msgid "Internal Transactions" msgstr "" @@ -1847,7 +1847,7 @@ msgstr "" #: lib/block_scout_web/templates/address_logs/index.html.eex:8 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:17 #: lib/block_scout_web/templates/transaction_log/index.html.eex:8 -#: lib/block_scout_web/views/address_view.ex:320 +#: lib/block_scout_web/views/address_view.ex:331 #: lib/block_scout_web/views/transaction_view.ex:381 msgid "Logs" msgstr "" @@ -1855,7 +1855,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address/_tabs.html.eex:62 #: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:25 -#: lib/block_scout_web/views/address_view.ex:317 +#: lib/block_scout_web/views/address_view.ex:328 #: lib/block_scout_web/views/tokens/overview_view.ex:37 msgid "Read Contract" msgstr "" @@ -1864,7 +1864,7 @@ msgstr "" #: lib/block_scout_web/templates/address/_tabs.html.eex:14 #: lib/block_scout_web/templates/address_token/index.html.eex:8 #: lib/block_scout_web/templates/address_token_transfer/index.html.eex:11 -#: lib/block_scout_web/views/address_view.ex:311 +#: lib/block_scout_web/views/address_view.ex:322 msgid "Tokens" msgstr "" @@ -1876,7 +1876,7 @@ msgstr "" #: lib/block_scout_web/templates/block_transaction/index.html.eex:18 #: lib/block_scout_web/templates/chain/show.html.eex:184 #: lib/block_scout_web/templates/layout/_topnav.html.eex:50 -#: lib/block_scout_web/views/address_view.ex:312 +#: lib/block_scout_web/views/address_view.ex:323 msgid "Transactions" msgstr "" From 97c6b442b342fb5d26c7f3cea2c349f545619c86 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Mon, 25 May 2020 10:59:38 +0300 Subject: [PATCH 16/51] Add CHANGELOG entry --- CHANGELOG.md | 1 + .../lib/block_scout_web/views/transaction_view.ex | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 84a7bb5e72..db327f0c91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - [#3106](https://github.com/poanetwork/blockscout/pull/3106), [#3115](https://github.com/poanetwork/blockscout/pull/3115) - Fix verification of contracts, created from factory (from internal transaction) ### Chore +- [#3124](https://github.com/poanetwork/blockscout/pull/3124) - Display upper border for tx speed if the value cannot be calculated ## 3.1.2-beta diff --git a/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex index e16061d28b..7653640041 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex @@ -143,7 +143,7 @@ defmodule BlockScoutWeb.TransactionView do :pending end - def processing_time_duration(%Transaction{earliest_processing_start: nil} = tx) do + def processing_time_duration(%Transaction{earliest_processing_start: nil}) do avg_time = Timex.Duration.to_seconds(Explorer.Counters.AverageBlockTime.average_block_time()) {:ok, "<= #{avg_time} seconds"} end From e504efb1f5d452c3ef9ef7f40f10b4e2bb60f6f4 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Mon, 25 May 2020 11:18:04 +0300 Subject: [PATCH 17/51] Update CHANGELOG.md --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5cedd79f29..7fe8e7c168 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,6 @@ - [#3122](https://github.com/poanetwork/blockscout/pull/3122) - Exclude balance percentage calculation for burn address on accounts page - [#3121](https://github.com/poanetwork/blockscout/pull/3121) - Geth: handle response from eth_getblockbyhash JSON RPC method without totalDifficulty (uncle blocks) - [#3119](https://github.com/poanetwork/blockscout/pull/3119), [#3120](https://github.com/poanetwork/blockscout/pull/3120) - Fix performance of Inventory tab loading for ERC-721 tokens -- [#3073](https://github.com/poanetwork/blockscout/issues/3073) - Fix performance of Inventory tab loading for ERC-721 tokens - [#3114](https://github.com/poanetwork/blockscout/pull/3114) - Fix performance of "Blocks validated" page - [#3112](https://github.com/poanetwork/blockscout/pull/3112) - Fix verification of contracts, compiled with nightly builds of solc compiler - [#3112](https://github.com/poanetwork/blockscout/pull/3112) - Check compiler version at contract verification From 2a2a51084669a9ac15edacacfe77e85bde632fc7 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Mon, 25 May 2020 11:30:34 +0300 Subject: [PATCH 18/51] Fix tests --- .../templates/transaction/overview.html.eex | 2 - .../block_scout_web/views/transaction_view.ex | 6 +- apps/block_scout_web/priv/gettext/default.pot | 74 +++++++++---------- .../priv/gettext/en/LC_MESSAGES/default.po | 74 +++++++++---------- 4 files changed, 79 insertions(+), 77 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex index cca53f0865..63c7c0ca25 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex @@ -94,8 +94,6 @@ <%= case processing_time_duration(@transaction) do %> <% :pending -> %> <% nil %> - <% :unknown -> %> - <% nil %> <% {:ok, interval_string} -> %>
<%= gettext "Transaction Speed" %>
diff --git a/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex index 7653640041..c495791de5 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex @@ -6,6 +6,7 @@ defmodule BlockScoutWeb.TransactionView do alias Explorer.{Chain, Repo} alias Explorer.Chain.Block.Reward alias Explorer.Chain.{Address, Block, InternalTransaction, Transaction, Wei} + alias Explorer.Counters.AverageBlockTime alias Explorer.ExchangeRates.Token alias Timex.Duration @@ -144,7 +145,10 @@ defmodule BlockScoutWeb.TransactionView do end def processing_time_duration(%Transaction{earliest_processing_start: nil}) do - avg_time = Timex.Duration.to_seconds(Explorer.Counters.AverageBlockTime.average_block_time()) + avg_time = + AverageBlockTime.average_block_time() + |> Duration.to_seconds() + {:ok, "<= #{avg_time} seconds"} end diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot index 0b467a05bf..054a7f3a73 100644 --- a/apps/block_scout_web/priv/gettext/default.pot +++ b/apps/block_scout_web/priv/gettext/default.pot @@ -13,7 +13,7 @@ msgstr[0] "" msgstr[1] "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:196 +#: lib/block_scout_web/templates/transaction/overview.html.eex:194 msgid " Token Transfer" msgstr "" @@ -59,7 +59,7 @@ msgid "%{subnetwork} Explorer - BlockScout" msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:236 +#: lib/block_scout_web/views/transaction_view.ex:241 msgid "(Awaiting internal transactions for status)" msgstr "" @@ -218,7 +218,7 @@ msgid "Block Number" msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:32 +#: lib/block_scout_web/views/transaction_view.ex:33 msgid "Block Pending" msgstr "" @@ -355,12 +355,12 @@ msgid "Contract Byte Code" msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:325 +#: lib/block_scout_web/views/transaction_view.ex:330 msgid "Contract Call" msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:322 +#: lib/block_scout_web/views/transaction_view.ex:327 msgid "Contract Creation" msgstr "" @@ -558,12 +558,12 @@ msgid "During times when the network is busy (i.e during ICOs) it can take a whi msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:136 +#: lib/block_scout_web/views/transaction_view.ex:137 msgid "ERC-20 " msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:137 +#: lib/block_scout_web/views/transaction_view.ex:138 msgid "ERC-721 " msgstr "" @@ -621,12 +621,12 @@ msgid "Error trying to fetch balances." msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:240 +#: lib/block_scout_web/views/transaction_view.ex:245 msgid "Error: %{reason}" msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:238 +#: lib/block_scout_web/views/transaction_view.ex:243 msgid "Error: (Awaiting internal transactions for reason)" msgstr "" @@ -646,8 +646,8 @@ msgstr "" #: lib/block_scout_web/templates/layout/app.html.eex:32 #: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:20 #: lib/block_scout_web/templates/transaction/_tile.html.eex:29 -#: lib/block_scout_web/templates/transaction/overview.html.eex:181 -#: lib/block_scout_web/templates/transaction/overview.html.eex:255 +#: lib/block_scout_web/templates/transaction/overview.html.eex:179 +#: lib/block_scout_web/templates/transaction/overview.html.eex:253 #: lib/block_scout_web/views/wei_helpers.ex:78 msgid "Ether" msgstr "" @@ -753,8 +753,8 @@ msgid "Hash" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:115 -#: lib/block_scout_web/templates/transaction/overview.html.eex:119 +#: lib/block_scout_web/templates/transaction/overview.html.eex:113 +#: lib/block_scout_web/templates/transaction/overview.html.eex:117 msgid "Hex (Default)" msgstr "" @@ -776,7 +776,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/transaction/_emission_reward_tile.html.eex:8 -#: lib/block_scout_web/views/transaction_view.ex:237 +#: lib/block_scout_web/views/transaction_view.ex:242 msgid "Success" msgstr "" @@ -891,7 +891,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:12 #: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:10 -#: lib/block_scout_web/views/transaction_view.ex:318 +#: lib/block_scout_web/views/transaction_view.ex:323 msgid "Token Transfer" msgstr "" @@ -906,7 +906,7 @@ msgstr "" #: lib/block_scout_web/views/address_view.ex:325 #: lib/block_scout_web/views/tokens/instance/overview_view.ex:90 #: lib/block_scout_web/views/tokens/overview_view.ex:35 -#: lib/block_scout_web/views/transaction_view.ex:379 +#: lib/block_scout_web/views/transaction_view.ex:384 msgid "Token Transfers" msgstr "" @@ -922,7 +922,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address_logs/_logs.html.eex:18 -#: lib/block_scout_web/views/transaction_view.ex:328 +#: lib/block_scout_web/views/transaction_view.ex:333 msgid "Transaction" msgstr "" @@ -1005,7 +1005,7 @@ msgid "License ID" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:283 +#: lib/block_scout_web/templates/transaction/overview.html.eex:281 msgid "Limit" msgstr "" @@ -1046,7 +1046,7 @@ msgid "Market Cap" msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:221 +#: lib/block_scout_web/views/transaction_view.ex:226 msgid "Max of" msgstr "" @@ -1159,8 +1159,8 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/layout/_topnav.html.eex:59 -#: lib/block_scout_web/views/transaction_view.ex:235 -#: lib/block_scout_web/views/transaction_view.ex:269 +#: lib/block_scout_web/views/transaction_view.ex:240 +#: lib/block_scout_web/views/transaction_view.ex:274 msgid "Pending" msgstr "" @@ -1201,14 +1201,14 @@ msgid "RPC" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:109 +#: lib/block_scout_web/templates/transaction/overview.html.eex:107 msgid "Raw Input" msgstr "" #, elixir-format #: lib/block_scout_web/templates/transaction/_tabs.html.eex:24 #: lib/block_scout_web/templates/transaction_raw_trace/index.html.eex:7 -#: lib/block_scout_web/views/transaction_view.ex:382 +#: lib/block_scout_web/views/transaction_view.ex:387 msgid "Raw Trace" msgstr "" @@ -1488,7 +1488,7 @@ msgid "Transaction Inputs" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:101 +#: lib/block_scout_web/templates/transaction/overview.html.eex:99 msgid "Transaction Speed" msgstr "" @@ -1518,7 +1518,7 @@ msgid "Type" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:122 +#: lib/block_scout_web/templates/transaction/overview.html.eex:120 msgid "UTF-8" msgstr "" @@ -1544,7 +1544,7 @@ msgid "Use the search box to find a hosted network, or select from the list of a msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:277 +#: lib/block_scout_web/templates/transaction/overview.html.eex:275 msgid "Used" msgstr "" @@ -1574,8 +1574,8 @@ msgid "Validator Info" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:181 -#: lib/block_scout_web/templates/transaction/overview.html.eex:255 +#: lib/block_scout_web/templates/transaction/overview.html.eex:179 +#: lib/block_scout_web/templates/transaction/overview.html.eex:253 msgid "Value" msgstr "" @@ -1752,7 +1752,7 @@ msgid "Decimals" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:273 +#: lib/block_scout_web/templates/transaction/overview.html.eex:271 msgid "Gas" msgstr "" @@ -1799,8 +1799,8 @@ msgid "Transfers" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:134 -#: lib/block_scout_web/templates/transaction/overview.html.eex:147 +#: lib/block_scout_web/templates/transaction/overview.html.eex:132 +#: lib/block_scout_web/templates/transaction/overview.html.eex:145 msgid "Copy Txn Input" msgstr "" @@ -1838,7 +1838,7 @@ msgstr "" #: lib/block_scout_web/templates/transaction/_tabs.html.eex:11 #: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:6 #: lib/block_scout_web/views/address_view.ex:324 -#: lib/block_scout_web/views/transaction_view.ex:380 +#: lib/block_scout_web/views/transaction_view.ex:385 msgid "Internal Transactions" msgstr "" @@ -1848,7 +1848,7 @@ msgstr "" #: lib/block_scout_web/templates/transaction/_tabs.html.eex:17 #: lib/block_scout_web/templates/transaction_log/index.html.eex:8 #: lib/block_scout_web/views/address_view.ex:331 -#: lib/block_scout_web/views/transaction_view.ex:381 +#: lib/block_scout_web/views/transaction_view.ex:386 msgid "Logs" msgstr "" @@ -1881,26 +1881,26 @@ msgid "Transactions" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:232 +#: lib/block_scout_web/templates/transaction/overview.html.eex:230 msgid " Token Burning" msgstr "" #, elixir-format #: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:8 #: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:6 -#: lib/block_scout_web/views/transaction_view.ex:317 +#: lib/block_scout_web/views/transaction_view.ex:322 msgid "Token Burning" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:214 +#: lib/block_scout_web/templates/transaction/overview.html.eex:212 msgid " Token Minting" msgstr "" #, elixir-format #: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:10 #: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:8 -#: lib/block_scout_web/views/transaction_view.ex:316 +#: lib/block_scout_web/views/transaction_view.ex:321 msgid "Token Minting" msgstr "" diff --git a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po index 0b467a05bf..054a7f3a73 100644 --- a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po +++ b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po @@ -13,7 +13,7 @@ msgstr[0] "" msgstr[1] "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:196 +#: lib/block_scout_web/templates/transaction/overview.html.eex:194 msgid " Token Transfer" msgstr "" @@ -59,7 +59,7 @@ msgid "%{subnetwork} Explorer - BlockScout" msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:236 +#: lib/block_scout_web/views/transaction_view.ex:241 msgid "(Awaiting internal transactions for status)" msgstr "" @@ -218,7 +218,7 @@ msgid "Block Number" msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:32 +#: lib/block_scout_web/views/transaction_view.ex:33 msgid "Block Pending" msgstr "" @@ -355,12 +355,12 @@ msgid "Contract Byte Code" msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:325 +#: lib/block_scout_web/views/transaction_view.ex:330 msgid "Contract Call" msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:322 +#: lib/block_scout_web/views/transaction_view.ex:327 msgid "Contract Creation" msgstr "" @@ -558,12 +558,12 @@ msgid "During times when the network is busy (i.e during ICOs) it can take a whi msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:136 +#: lib/block_scout_web/views/transaction_view.ex:137 msgid "ERC-20 " msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:137 +#: lib/block_scout_web/views/transaction_view.ex:138 msgid "ERC-721 " msgstr "" @@ -621,12 +621,12 @@ msgid "Error trying to fetch balances." msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:240 +#: lib/block_scout_web/views/transaction_view.ex:245 msgid "Error: %{reason}" msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:238 +#: lib/block_scout_web/views/transaction_view.ex:243 msgid "Error: (Awaiting internal transactions for reason)" msgstr "" @@ -646,8 +646,8 @@ msgstr "" #: lib/block_scout_web/templates/layout/app.html.eex:32 #: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:20 #: lib/block_scout_web/templates/transaction/_tile.html.eex:29 -#: lib/block_scout_web/templates/transaction/overview.html.eex:181 -#: lib/block_scout_web/templates/transaction/overview.html.eex:255 +#: lib/block_scout_web/templates/transaction/overview.html.eex:179 +#: lib/block_scout_web/templates/transaction/overview.html.eex:253 #: lib/block_scout_web/views/wei_helpers.ex:78 msgid "Ether" msgstr "" @@ -753,8 +753,8 @@ msgid "Hash" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:115 -#: lib/block_scout_web/templates/transaction/overview.html.eex:119 +#: lib/block_scout_web/templates/transaction/overview.html.eex:113 +#: lib/block_scout_web/templates/transaction/overview.html.eex:117 msgid "Hex (Default)" msgstr "" @@ -776,7 +776,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/transaction/_emission_reward_tile.html.eex:8 -#: lib/block_scout_web/views/transaction_view.ex:237 +#: lib/block_scout_web/views/transaction_view.ex:242 msgid "Success" msgstr "" @@ -891,7 +891,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:12 #: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:10 -#: lib/block_scout_web/views/transaction_view.ex:318 +#: lib/block_scout_web/views/transaction_view.ex:323 msgid "Token Transfer" msgstr "" @@ -906,7 +906,7 @@ msgstr "" #: lib/block_scout_web/views/address_view.ex:325 #: lib/block_scout_web/views/tokens/instance/overview_view.ex:90 #: lib/block_scout_web/views/tokens/overview_view.ex:35 -#: lib/block_scout_web/views/transaction_view.ex:379 +#: lib/block_scout_web/views/transaction_view.ex:384 msgid "Token Transfers" msgstr "" @@ -922,7 +922,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address_logs/_logs.html.eex:18 -#: lib/block_scout_web/views/transaction_view.ex:328 +#: lib/block_scout_web/views/transaction_view.ex:333 msgid "Transaction" msgstr "" @@ -1005,7 +1005,7 @@ msgid "License ID" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:283 +#: lib/block_scout_web/templates/transaction/overview.html.eex:281 msgid "Limit" msgstr "" @@ -1046,7 +1046,7 @@ msgid "Market Cap" msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:221 +#: lib/block_scout_web/views/transaction_view.ex:226 msgid "Max of" msgstr "" @@ -1159,8 +1159,8 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/layout/_topnav.html.eex:59 -#: lib/block_scout_web/views/transaction_view.ex:235 -#: lib/block_scout_web/views/transaction_view.ex:269 +#: lib/block_scout_web/views/transaction_view.ex:240 +#: lib/block_scout_web/views/transaction_view.ex:274 msgid "Pending" msgstr "" @@ -1201,14 +1201,14 @@ msgid "RPC" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:109 +#: lib/block_scout_web/templates/transaction/overview.html.eex:107 msgid "Raw Input" msgstr "" #, elixir-format #: lib/block_scout_web/templates/transaction/_tabs.html.eex:24 #: lib/block_scout_web/templates/transaction_raw_trace/index.html.eex:7 -#: lib/block_scout_web/views/transaction_view.ex:382 +#: lib/block_scout_web/views/transaction_view.ex:387 msgid "Raw Trace" msgstr "" @@ -1488,7 +1488,7 @@ msgid "Transaction Inputs" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:101 +#: lib/block_scout_web/templates/transaction/overview.html.eex:99 msgid "Transaction Speed" msgstr "" @@ -1518,7 +1518,7 @@ msgid "Type" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:122 +#: lib/block_scout_web/templates/transaction/overview.html.eex:120 msgid "UTF-8" msgstr "" @@ -1544,7 +1544,7 @@ msgid "Use the search box to find a hosted network, or select from the list of a msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:277 +#: lib/block_scout_web/templates/transaction/overview.html.eex:275 msgid "Used" msgstr "" @@ -1574,8 +1574,8 @@ msgid "Validator Info" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:181 -#: lib/block_scout_web/templates/transaction/overview.html.eex:255 +#: lib/block_scout_web/templates/transaction/overview.html.eex:179 +#: lib/block_scout_web/templates/transaction/overview.html.eex:253 msgid "Value" msgstr "" @@ -1752,7 +1752,7 @@ msgid "Decimals" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:273 +#: lib/block_scout_web/templates/transaction/overview.html.eex:271 msgid "Gas" msgstr "" @@ -1799,8 +1799,8 @@ msgid "Transfers" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:134 -#: lib/block_scout_web/templates/transaction/overview.html.eex:147 +#: lib/block_scout_web/templates/transaction/overview.html.eex:132 +#: lib/block_scout_web/templates/transaction/overview.html.eex:145 msgid "Copy Txn Input" msgstr "" @@ -1838,7 +1838,7 @@ msgstr "" #: lib/block_scout_web/templates/transaction/_tabs.html.eex:11 #: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:6 #: lib/block_scout_web/views/address_view.ex:324 -#: lib/block_scout_web/views/transaction_view.ex:380 +#: lib/block_scout_web/views/transaction_view.ex:385 msgid "Internal Transactions" msgstr "" @@ -1848,7 +1848,7 @@ msgstr "" #: lib/block_scout_web/templates/transaction/_tabs.html.eex:17 #: lib/block_scout_web/templates/transaction_log/index.html.eex:8 #: lib/block_scout_web/views/address_view.ex:331 -#: lib/block_scout_web/views/transaction_view.ex:381 +#: lib/block_scout_web/views/transaction_view.ex:386 msgid "Logs" msgstr "" @@ -1881,26 +1881,26 @@ msgid "Transactions" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:232 +#: lib/block_scout_web/templates/transaction/overview.html.eex:230 msgid " Token Burning" msgstr "" #, elixir-format #: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:8 #: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:6 -#: lib/block_scout_web/views/transaction_view.ex:317 +#: lib/block_scout_web/views/transaction_view.ex:322 msgid "Token Burning" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:214 +#: lib/block_scout_web/templates/transaction/overview.html.eex:212 msgid " Token Minting" msgstr "" #, elixir-format #: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:10 #: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:8 -#: lib/block_scout_web/views/transaction_view.ex:316 +#: lib/block_scout_web/views/transaction_view.ex:321 msgid "Token Minting" msgstr "" From a68ec64a4b12c09d1d714eb856d13b931aee0120 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Mon, 25 May 2020 13:49:45 +0300 Subject: [PATCH 19/51] Fix tests --- .../templates/transaction/overview.html.eex | 2 + .../block_scout_web/views/transaction_view.ex | 14 ++-- apps/block_scout_web/priv/gettext/default.pot | 68 +++++++++---------- .../priv/gettext/en/LC_MESSAGES/default.po | 68 +++++++++---------- 4 files changed, 80 insertions(+), 72 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex index 63c7c0ca25..cca53f0865 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex @@ -94,6 +94,8 @@ <%= case processing_time_duration(@transaction) do %> <% :pending -> %> <% nil %> + <% :unknown -> %> + <% nil %> <% {:ok, interval_string} -> %>
<%= gettext "Transaction Speed" %>
diff --git a/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex index c495791de5..1c01ab80a3 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex @@ -145,11 +145,17 @@ defmodule BlockScoutWeb.TransactionView do end def processing_time_duration(%Transaction{earliest_processing_start: nil}) do - avg_time = - AverageBlockTime.average_block_time() - |> Duration.to_seconds() + avg_time = AverageBlockTime.average_block_time() - {:ok, "<= #{avg_time} seconds"} + if avg_time == {:error, :disabled} do + :unknown + else + avg_time_in_secs = + avg_time + |> Duration.to_seconds() + + {:ok, "<= #{avg_time_in_secs} seconds"} + end end def processing_time_duration(%Transaction{ diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot index 054a7f3a73..6aa0491090 100644 --- a/apps/block_scout_web/priv/gettext/default.pot +++ b/apps/block_scout_web/priv/gettext/default.pot @@ -13,7 +13,7 @@ msgstr[0] "" msgstr[1] "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:194 +#: lib/block_scout_web/templates/transaction/overview.html.eex:196 msgid " Token Transfer" msgstr "" @@ -59,7 +59,7 @@ msgid "%{subnetwork} Explorer - BlockScout" msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:241 +#: lib/block_scout_web/views/transaction_view.ex:247 msgid "(Awaiting internal transactions for status)" msgstr "" @@ -355,12 +355,12 @@ msgid "Contract Byte Code" msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:330 +#: lib/block_scout_web/views/transaction_view.ex:336 msgid "Contract Call" msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:327 +#: lib/block_scout_web/views/transaction_view.ex:333 msgid "Contract Creation" msgstr "" @@ -621,12 +621,12 @@ msgid "Error trying to fetch balances." msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:245 +#: lib/block_scout_web/views/transaction_view.ex:251 msgid "Error: %{reason}" msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:243 +#: lib/block_scout_web/views/transaction_view.ex:249 msgid "Error: (Awaiting internal transactions for reason)" msgstr "" @@ -646,8 +646,8 @@ msgstr "" #: lib/block_scout_web/templates/layout/app.html.eex:32 #: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:20 #: lib/block_scout_web/templates/transaction/_tile.html.eex:29 -#: lib/block_scout_web/templates/transaction/overview.html.eex:179 -#: lib/block_scout_web/templates/transaction/overview.html.eex:253 +#: lib/block_scout_web/templates/transaction/overview.html.eex:181 +#: lib/block_scout_web/templates/transaction/overview.html.eex:255 #: lib/block_scout_web/views/wei_helpers.ex:78 msgid "Ether" msgstr "" @@ -753,8 +753,8 @@ msgid "Hash" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:113 -#: lib/block_scout_web/templates/transaction/overview.html.eex:117 +#: lib/block_scout_web/templates/transaction/overview.html.eex:115 +#: lib/block_scout_web/templates/transaction/overview.html.eex:119 msgid "Hex (Default)" msgstr "" @@ -776,7 +776,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/transaction/_emission_reward_tile.html.eex:8 -#: lib/block_scout_web/views/transaction_view.ex:242 +#: lib/block_scout_web/views/transaction_view.ex:248 msgid "Success" msgstr "" @@ -891,7 +891,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:12 #: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:10 -#: lib/block_scout_web/views/transaction_view.ex:323 +#: lib/block_scout_web/views/transaction_view.ex:329 msgid "Token Transfer" msgstr "" @@ -906,7 +906,7 @@ msgstr "" #: lib/block_scout_web/views/address_view.ex:325 #: lib/block_scout_web/views/tokens/instance/overview_view.ex:90 #: lib/block_scout_web/views/tokens/overview_view.ex:35 -#: lib/block_scout_web/views/transaction_view.ex:384 +#: lib/block_scout_web/views/transaction_view.ex:390 msgid "Token Transfers" msgstr "" @@ -922,7 +922,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address_logs/_logs.html.eex:18 -#: lib/block_scout_web/views/transaction_view.ex:333 +#: lib/block_scout_web/views/transaction_view.ex:339 msgid "Transaction" msgstr "" @@ -1005,7 +1005,7 @@ msgid "License ID" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:281 +#: lib/block_scout_web/templates/transaction/overview.html.eex:283 msgid "Limit" msgstr "" @@ -1046,7 +1046,7 @@ msgid "Market Cap" msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:226 +#: lib/block_scout_web/views/transaction_view.ex:232 msgid "Max of" msgstr "" @@ -1159,8 +1159,8 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/layout/_topnav.html.eex:59 -#: lib/block_scout_web/views/transaction_view.ex:240 -#: lib/block_scout_web/views/transaction_view.ex:274 +#: lib/block_scout_web/views/transaction_view.ex:246 +#: lib/block_scout_web/views/transaction_view.ex:280 msgid "Pending" msgstr "" @@ -1201,14 +1201,14 @@ msgid "RPC" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:107 +#: lib/block_scout_web/templates/transaction/overview.html.eex:109 msgid "Raw Input" msgstr "" #, elixir-format #: lib/block_scout_web/templates/transaction/_tabs.html.eex:24 #: lib/block_scout_web/templates/transaction_raw_trace/index.html.eex:7 -#: lib/block_scout_web/views/transaction_view.ex:387 +#: lib/block_scout_web/views/transaction_view.ex:393 msgid "Raw Trace" msgstr "" @@ -1488,7 +1488,7 @@ msgid "Transaction Inputs" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:99 +#: lib/block_scout_web/templates/transaction/overview.html.eex:101 msgid "Transaction Speed" msgstr "" @@ -1518,7 +1518,7 @@ msgid "Type" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:120 +#: lib/block_scout_web/templates/transaction/overview.html.eex:122 msgid "UTF-8" msgstr "" @@ -1544,7 +1544,7 @@ msgid "Use the search box to find a hosted network, or select from the list of a msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:275 +#: lib/block_scout_web/templates/transaction/overview.html.eex:277 msgid "Used" msgstr "" @@ -1574,8 +1574,8 @@ msgid "Validator Info" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:179 -#: lib/block_scout_web/templates/transaction/overview.html.eex:253 +#: lib/block_scout_web/templates/transaction/overview.html.eex:181 +#: lib/block_scout_web/templates/transaction/overview.html.eex:255 msgid "Value" msgstr "" @@ -1752,7 +1752,7 @@ msgid "Decimals" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:271 +#: lib/block_scout_web/templates/transaction/overview.html.eex:273 msgid "Gas" msgstr "" @@ -1799,8 +1799,8 @@ msgid "Transfers" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:132 -#: lib/block_scout_web/templates/transaction/overview.html.eex:145 +#: lib/block_scout_web/templates/transaction/overview.html.eex:134 +#: lib/block_scout_web/templates/transaction/overview.html.eex:147 msgid "Copy Txn Input" msgstr "" @@ -1838,7 +1838,7 @@ msgstr "" #: lib/block_scout_web/templates/transaction/_tabs.html.eex:11 #: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:6 #: lib/block_scout_web/views/address_view.ex:324 -#: lib/block_scout_web/views/transaction_view.ex:385 +#: lib/block_scout_web/views/transaction_view.ex:391 msgid "Internal Transactions" msgstr "" @@ -1848,7 +1848,7 @@ msgstr "" #: lib/block_scout_web/templates/transaction/_tabs.html.eex:17 #: lib/block_scout_web/templates/transaction_log/index.html.eex:8 #: lib/block_scout_web/views/address_view.ex:331 -#: lib/block_scout_web/views/transaction_view.ex:386 +#: lib/block_scout_web/views/transaction_view.ex:392 msgid "Logs" msgstr "" @@ -1881,26 +1881,26 @@ msgid "Transactions" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:230 +#: lib/block_scout_web/templates/transaction/overview.html.eex:232 msgid " Token Burning" msgstr "" #, elixir-format #: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:8 #: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:6 -#: lib/block_scout_web/views/transaction_view.ex:322 +#: lib/block_scout_web/views/transaction_view.ex:328 msgid "Token Burning" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:212 +#: lib/block_scout_web/templates/transaction/overview.html.eex:214 msgid " Token Minting" msgstr "" #, elixir-format #: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:10 #: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:8 -#: lib/block_scout_web/views/transaction_view.ex:321 +#: lib/block_scout_web/views/transaction_view.ex:327 msgid "Token Minting" msgstr "" diff --git a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po index 054a7f3a73..6aa0491090 100644 --- a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po +++ b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po @@ -13,7 +13,7 @@ msgstr[0] "" msgstr[1] "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:194 +#: lib/block_scout_web/templates/transaction/overview.html.eex:196 msgid " Token Transfer" msgstr "" @@ -59,7 +59,7 @@ msgid "%{subnetwork} Explorer - BlockScout" msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:241 +#: lib/block_scout_web/views/transaction_view.ex:247 msgid "(Awaiting internal transactions for status)" msgstr "" @@ -355,12 +355,12 @@ msgid "Contract Byte Code" msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:330 +#: lib/block_scout_web/views/transaction_view.ex:336 msgid "Contract Call" msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:327 +#: lib/block_scout_web/views/transaction_view.ex:333 msgid "Contract Creation" msgstr "" @@ -621,12 +621,12 @@ msgid "Error trying to fetch balances." msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:245 +#: lib/block_scout_web/views/transaction_view.ex:251 msgid "Error: %{reason}" msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:243 +#: lib/block_scout_web/views/transaction_view.ex:249 msgid "Error: (Awaiting internal transactions for reason)" msgstr "" @@ -646,8 +646,8 @@ msgstr "" #: lib/block_scout_web/templates/layout/app.html.eex:32 #: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:20 #: lib/block_scout_web/templates/transaction/_tile.html.eex:29 -#: lib/block_scout_web/templates/transaction/overview.html.eex:179 -#: lib/block_scout_web/templates/transaction/overview.html.eex:253 +#: lib/block_scout_web/templates/transaction/overview.html.eex:181 +#: lib/block_scout_web/templates/transaction/overview.html.eex:255 #: lib/block_scout_web/views/wei_helpers.ex:78 msgid "Ether" msgstr "" @@ -753,8 +753,8 @@ msgid "Hash" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:113 -#: lib/block_scout_web/templates/transaction/overview.html.eex:117 +#: lib/block_scout_web/templates/transaction/overview.html.eex:115 +#: lib/block_scout_web/templates/transaction/overview.html.eex:119 msgid "Hex (Default)" msgstr "" @@ -776,7 +776,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/transaction/_emission_reward_tile.html.eex:8 -#: lib/block_scout_web/views/transaction_view.ex:242 +#: lib/block_scout_web/views/transaction_view.ex:248 msgid "Success" msgstr "" @@ -891,7 +891,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:12 #: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:10 -#: lib/block_scout_web/views/transaction_view.ex:323 +#: lib/block_scout_web/views/transaction_view.ex:329 msgid "Token Transfer" msgstr "" @@ -906,7 +906,7 @@ msgstr "" #: lib/block_scout_web/views/address_view.ex:325 #: lib/block_scout_web/views/tokens/instance/overview_view.ex:90 #: lib/block_scout_web/views/tokens/overview_view.ex:35 -#: lib/block_scout_web/views/transaction_view.ex:384 +#: lib/block_scout_web/views/transaction_view.ex:390 msgid "Token Transfers" msgstr "" @@ -922,7 +922,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address_logs/_logs.html.eex:18 -#: lib/block_scout_web/views/transaction_view.ex:333 +#: lib/block_scout_web/views/transaction_view.ex:339 msgid "Transaction" msgstr "" @@ -1005,7 +1005,7 @@ msgid "License ID" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:281 +#: lib/block_scout_web/templates/transaction/overview.html.eex:283 msgid "Limit" msgstr "" @@ -1046,7 +1046,7 @@ msgid "Market Cap" msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:226 +#: lib/block_scout_web/views/transaction_view.ex:232 msgid "Max of" msgstr "" @@ -1159,8 +1159,8 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/layout/_topnav.html.eex:59 -#: lib/block_scout_web/views/transaction_view.ex:240 -#: lib/block_scout_web/views/transaction_view.ex:274 +#: lib/block_scout_web/views/transaction_view.ex:246 +#: lib/block_scout_web/views/transaction_view.ex:280 msgid "Pending" msgstr "" @@ -1201,14 +1201,14 @@ msgid "RPC" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:107 +#: lib/block_scout_web/templates/transaction/overview.html.eex:109 msgid "Raw Input" msgstr "" #, elixir-format #: lib/block_scout_web/templates/transaction/_tabs.html.eex:24 #: lib/block_scout_web/templates/transaction_raw_trace/index.html.eex:7 -#: lib/block_scout_web/views/transaction_view.ex:387 +#: lib/block_scout_web/views/transaction_view.ex:393 msgid "Raw Trace" msgstr "" @@ -1488,7 +1488,7 @@ msgid "Transaction Inputs" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:99 +#: lib/block_scout_web/templates/transaction/overview.html.eex:101 msgid "Transaction Speed" msgstr "" @@ -1518,7 +1518,7 @@ msgid "Type" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:120 +#: lib/block_scout_web/templates/transaction/overview.html.eex:122 msgid "UTF-8" msgstr "" @@ -1544,7 +1544,7 @@ msgid "Use the search box to find a hosted network, or select from the list of a msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:275 +#: lib/block_scout_web/templates/transaction/overview.html.eex:277 msgid "Used" msgstr "" @@ -1574,8 +1574,8 @@ msgid "Validator Info" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:179 -#: lib/block_scout_web/templates/transaction/overview.html.eex:253 +#: lib/block_scout_web/templates/transaction/overview.html.eex:181 +#: lib/block_scout_web/templates/transaction/overview.html.eex:255 msgid "Value" msgstr "" @@ -1752,7 +1752,7 @@ msgid "Decimals" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:271 +#: lib/block_scout_web/templates/transaction/overview.html.eex:273 msgid "Gas" msgstr "" @@ -1799,8 +1799,8 @@ msgid "Transfers" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:132 -#: lib/block_scout_web/templates/transaction/overview.html.eex:145 +#: lib/block_scout_web/templates/transaction/overview.html.eex:134 +#: lib/block_scout_web/templates/transaction/overview.html.eex:147 msgid "Copy Txn Input" msgstr "" @@ -1838,7 +1838,7 @@ msgstr "" #: lib/block_scout_web/templates/transaction/_tabs.html.eex:11 #: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:6 #: lib/block_scout_web/views/address_view.ex:324 -#: lib/block_scout_web/views/transaction_view.ex:385 +#: lib/block_scout_web/views/transaction_view.ex:391 msgid "Internal Transactions" msgstr "" @@ -1848,7 +1848,7 @@ msgstr "" #: lib/block_scout_web/templates/transaction/_tabs.html.eex:17 #: lib/block_scout_web/templates/transaction_log/index.html.eex:8 #: lib/block_scout_web/views/address_view.ex:331 -#: lib/block_scout_web/views/transaction_view.ex:386 +#: lib/block_scout_web/views/transaction_view.ex:392 msgid "Logs" msgstr "" @@ -1881,26 +1881,26 @@ msgid "Transactions" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:230 +#: lib/block_scout_web/templates/transaction/overview.html.eex:232 msgid " Token Burning" msgstr "" #, elixir-format #: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:8 #: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:6 -#: lib/block_scout_web/views/transaction_view.ex:322 +#: lib/block_scout_web/views/transaction_view.ex:328 msgid "Token Burning" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:212 +#: lib/block_scout_web/templates/transaction/overview.html.eex:214 msgid " Token Minting" msgstr "" #, elixir-format #: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:10 #: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:8 -#: lib/block_scout_web/views/transaction_view.ex:321 +#: lib/block_scout_web/views/transaction_view.ex:327 msgid "Token Minting" msgstr "" From 14fbf27606e251cc2ed31f903048d0d55a9ec396 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Mon, 25 May 2020 18:47:57 +0300 Subject: [PATCH 20/51] Store ddresses coin balances changes daily --- CHANGELOG.md | 1 + apps/explorer/lib/explorer/chain.ex | 16 +- .../chain/address/coin_balance_daily.ex | 66 +++++ .../runner/address/coin_balances_daily.ex | 138 +++++++++++ .../chain/import/stage/address_referencing.ex | 3 +- ...0525115811_address_coin_balances_daily.exs | 17 ++ apps/indexer/lib/indexer/block/fetcher.ex | 19 +- .../lib/indexer/block/realtime/fetcher.ex | 23 +- .../lib/indexer/fetcher/block_reward.ex | 3 +- .../lib/indexer/fetcher/coin_balance.ex | 18 +- .../lib/indexer/fetcher/coin_balance_daily.ex | 226 ++++++++++++++++++ apps/indexer/lib/indexer/supervisor.ex | 3 + .../transform/address_coin_balances_daily.ex | 136 +++++++++++ 13 files changed, 643 insertions(+), 26 deletions(-) create mode 100644 apps/explorer/lib/explorer/chain/address/coin_balance_daily.ex create mode 100644 apps/explorer/lib/explorer/chain/import/runner/address/coin_balances_daily.ex create mode 100644 apps/explorer/priv/repo/migrations/20200525115811_address_coin_balances_daily.exs create mode 100644 apps/indexer/lib/indexer/fetcher/coin_balance_daily.ex create mode 100644 apps/indexer/lib/indexer/transform/address_coin_balances_daily.ex diff --git a/CHANGELOG.md b/CHANGELOG.md index 91567e98d2..dd2cf1adb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Features ### Fixes +- [#3125](https://github.com/poanetwork/blockscout/pull/3125) - Fix performance of coin balance history chart - [#3122](https://github.com/poanetwork/blockscout/pull/3122) - Exclude balance percentage calculation for burn address on accounts page - [#3121](https://github.com/poanetwork/blockscout/pull/3121) - Geth: handle response from eth_getblockbyhash JSON RPC method without totalDifficulty (uncle blocks) - [#3119](https://github.com/poanetwork/blockscout/pull/3119), [#3120](https://github.com/poanetwork/blockscout/pull/3120) - Fix performance of Inventory tab loading for ERC-721 tokens diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 79650b48e7..df569c4f88 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -33,6 +33,7 @@ defmodule Explorer.Chain do alias Explorer.Chain.{ Address, Address.CoinBalance, + Address.CoinBalanceDaily, Address.CurrentTokenBalance, Address.TokenBalance, Block, @@ -3488,25 +3489,12 @@ defmodule Explorer.Chain do @spec address_to_balances_by_day(Hash.Address.t()) :: [balance_by_day] def address_to_balances_by_day(address_hash) do - latest_block_timestamp = - address_hash - |> CoinBalance.last_coin_balance_timestamp() - |> Repo.one() - address_hash - |> CoinBalance.balances_by_day(latest_block_timestamp) + |> CoinBalanceDaily.balances_by_day() |> Repo.all() - |> replace_last_value(latest_block_timestamp) |> normalize_balances_by_day() end - # https://github.com/poanetwork/blockscout/issues/2658 - defp replace_last_value(items, %{value: value, timestamp: timestamp}) do - List.replace_at(items, -1, %{date: Date.convert!(timestamp, Calendar.ISO), value: value}) - end - - defp replace_last_value(items, _), do: items - defp normalize_balances_by_day(balances_by_day) do result = balances_by_day diff --git a/apps/explorer/lib/explorer/chain/address/coin_balance_daily.ex b/apps/explorer/lib/explorer/chain/address/coin_balance_daily.ex new file mode 100644 index 0000000000..063f1d8235 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/address/coin_balance_daily.ex @@ -0,0 +1,66 @@ +defmodule Explorer.Chain.Address.CoinBalanceDaily do + @moduledoc """ + Maximum `t:Explorer.Chain.Wei.t/0` `value` of `t:Explorer.Chain.Address.t/0` at the day. + This table is used to display coinn balance history chart. + """ + + use Explorer.Schema + + alias Explorer.Chain.{Address, Hash, Wei} + alias Explorer.Chain.Address.CoinBalanceDaily + + @optional_fields ~w(value)a + @required_fields ~w(address_hash day)a + @allowed_fields @optional_fields ++ @required_fields + + @typedoc """ + * `address` - the `t:Explorer.Chain.Address.t/0`. + * `address_hash` - foreign key for `address`. + * `day` - the `t:Date.t/0`. + * `inserted_at` - When the balance was first inserted into the database. + * `updated_at` - When the balance was last updated. + * `value` - the max balance (`value`) of `address` during the `day`. + """ + @type t :: %__MODULE__{ + address: %Ecto.Association.NotLoaded{} | Address.t(), + address_hash: Hash.Address.t(), + day: Date.t(), + inserted_at: DateTime.t(), + updated_at: DateTime.t(), + value: Wei.t() | nil + } + + @primary_key false + schema "address_coin_balances_daily" do + field(:day, :date) + field(:value, Wei) + + timestamps() + + belongs_to(:address, Address, foreign_key: :address_hash, references: :hash, type: Hash.Address) + end + + @doc """ + Builds an `Ecto.Query` to fetch a series of balances by day for the given account. Each element in the series + corresponds to the maximum balance in that day. Only the last 90 days of data are used. + """ + def balances_by_day(address_hash) do + CoinBalanceDaily + |> where([cbd], cbd.address_hash == ^address_hash) + |> limit_time_interval() + |> order_by([cbd], cbd.day) + |> select([cbd], %{date: cbd.day, value: cbd.value}) + end + + def limit_time_interval(query) do + query |> where([cbd], cbd.day >= fragment("date_trunc('day', now()) - interval '90 days'")) + end + + def changeset(%__MODULE__{} = balance, params) do + balance + |> cast(params, @allowed_fields) + |> validate_required(@required_fields) + |> foreign_key_constraint(:address_hash) + |> unique_constraint(:day, name: :address_coin_balances_daily_address_hash_day_index) + end +end diff --git a/apps/explorer/lib/explorer/chain/import/runner/address/coin_balances_daily.ex b/apps/explorer/lib/explorer/chain/import/runner/address/coin_balances_daily.ex new file mode 100644 index 0000000000..e9e747c069 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/import/runner/address/coin_balances_daily.ex @@ -0,0 +1,138 @@ +defmodule Explorer.Chain.Import.Runner.Address.CoinBalancesDaily do + @moduledoc """ + Bulk imports `t:Explorer.Chain.Address.CoinBalancesDaily.t/0`. + """ + + require Ecto.Query + + import Ecto.Query, only: [from: 2] + + alias Ecto.{Changeset, Multi, Repo} + alias Explorer.Chain.Address.CoinBalanceDaily + alias Explorer.Chain.{Hash, Import, Wei} + + @behaviour Import.Runner + + # milliseconds + @timeout 60_000 + + @type imported :: [ + %{required(:address_hash) => Hash.Address.t(), required(:day) => Date.t()} + ] + + @impl Import.Runner + def ecto_schema_module, do: CoinBalanceDaily + + @impl Import.Runner + def option_key, do: :address_coin_balances_daily + + @impl Import.Runner + def imported_table_row do + %{ + value_type: "[%{address_hash: Explorer.Chain.Hash.t(), day: Date.t()}]", + value_description: "List of maps of the `t:#{ecto_schema_module()}.t/0` `address_hash` and `day`" + } + end + + @impl Import.Runner + def run(multi, changes_list, %{timestamps: timestamps} = options) do + insert_options = + options + |> Map.get(option_key(), %{}) + |> Map.take(~w(on_conflict timeout)a) + |> Map.put_new(:timeout, @timeout) + |> Map.put(:timestamps, timestamps) + + Multi.run(multi, :address_coin_balances_daily, fn repo, _ -> + insert(repo, changes_list, insert_options) + end) + end + + @impl Import.Runner + def timeout, do: @timeout + + @spec insert( + Repo.t(), + [ + %{ + required(:address_hash) => Hash.Address.t(), + required(:day) => Date.t(), + required(:value) => Wei.t() + } + ], + %{ + optional(:on_conflict) => Import.Runner.on_conflict(), + required(:timeout) => timeout, + required(:timestamps) => Import.timestamps() + } + ) :: + {:ok, [%{required(:address_hash) => Hash.Address.t(), required(:day) => Date.t()}]} + | {:error, [Changeset.t()]} + defp insert(repo, changes_list, %{timeout: timeout, timestamps: timestamps} = options) when is_list(changes_list) do + on_conflict = Map.get_lazy(options, :on_conflict, &default_on_conflict/0) + + combined_changes_list = + changes_list + |> Enum.reduce([], fn change, acc -> + if Enum.empty?(acc) do + [change | acc] + else + target_item = + Enum.find(acc, fn item -> + item.day == change.day && item.address_hash == change.address_hash + end) + + if target_item do + if change.value > target_item.value do + acc_updated = List.delete(acc, target_item) + [change | acc_updated] + else + acc + end + else + [change | acc] + end + end + end) + + # Enforce CoinBalanceDaily ShareLocks order (see docs: sharelocks.md) + ordered_changes_list = Enum.sort_by(combined_changes_list, &{&1.address_hash, &1.day}) + + {:ok, _} = + Import.insert_changes_list( + repo, + ordered_changes_list, + conflict_target: [:address_hash, :day], + on_conflict: on_conflict, + for: CoinBalanceDaily, + timeout: timeout, + timestamps: timestamps + ) + + {:ok, Enum.map(ordered_changes_list, &Map.take(&1, ~w(address_hash day)a))} + end + + def default_on_conflict do + from( + balance in CoinBalanceDaily, + update: [ + set: [ + value: + fragment( + """ + CASE WHEN EXCLUDED.value IS NOT NULL THEN + EXCLUDED.value + ELSE + ? + END + """, + balance.value + ), + inserted_at: fragment("LEAST(EXCLUDED.inserted_at, ?)", balance.inserted_at), + updated_at: fragment("GREATEST(EXCLUDED.updated_at, ?)", balance.updated_at) + ] + ], + where: fragment("EXCLUDED.value IS NOT NULL") + ) + end +end diff --git a/apps/explorer/lib/explorer/chain/import/stage/address_referencing.ex b/apps/explorer/lib/explorer/chain/import/stage/address_referencing.ex index 2575a0eab8..a428d51b0c 100644 --- a/apps/explorer/lib/explorer/chain/import/stage/address_referencing.ex +++ b/apps/explorer/lib/explorer/chain/import/stage/address_referencing.ex @@ -14,7 +14,8 @@ defmodule Explorer.Chain.Import.Stage.AddressReferencing do Runner.Address.CoinBalances, Runner.Blocks, Runner.StakingPools, - Runner.StakingPoolsDelegators + Runner.StakingPoolsDelegators, + Runner.Address.CoinBalancesDaily ] @impl Stage diff --git a/apps/explorer/priv/repo/migrations/20200525115811_address_coin_balances_daily.exs b/apps/explorer/priv/repo/migrations/20200525115811_address_coin_balances_daily.exs new file mode 100644 index 0000000000..dc03899679 --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20200525115811_address_coin_balances_daily.exs @@ -0,0 +1,17 @@ +defmodule Explorer.Repo.Migrations.AddressCoinBalancesDaily do + use Ecto.Migration + + def change do + create table(:address_coin_balances_daily, primary_key: false) do + add(:address_hash, references(:addresses, column: :hash, type: :bytea), null: false) + add(:day, :date, null: false) + + # null until fetched + add(:value, :numeric, precision: 100, default: fragment("NULL"), null: true) + + timestamps(null: false, type: :utc_datetime_usec) + end + + create(unique_index(:address_coin_balances_daily, [:address_hash, :day])) + end +end diff --git a/apps/indexer/lib/indexer/block/fetcher.ex b/apps/indexer/lib/indexer/block/fetcher.ex index 2d4d399eba..c84e9acd48 100644 --- a/apps/indexer/lib/indexer/block/fetcher.ex +++ b/apps/indexer/lib/indexer/block/fetcher.ex @@ -20,6 +20,7 @@ defmodule Indexer.Block.Fetcher do alias Indexer.Fetcher.{ BlockReward, CoinBalance, + CoinBalanceDaily, ContractCode, InternalTransaction, ReplacedTransaction, @@ -55,6 +56,7 @@ defmodule Indexer.Block.Fetcher do address_hash_to_fetched_balance_block_number: address_hash_to_fetched_balance_block_number, addresses: Import.Runner.options(), address_coin_balances: Import.Runner.options(), + address_coin_balances_daily: Import.Runner.options(), address_token_balances: Import.Runner.options(), blocks: Import.Runner.options(), block_second_degree_relations: Import.Runner.options(), @@ -164,6 +166,7 @@ defmodule Indexer.Block.Fetcher do %{ addresses: %{params: addresses}, address_coin_balances: %{params: coin_balances_params_set}, + address_coin_balances_daily: %{params: coin_balances_params_set}, address_token_balances: %{params: address_token_balances}, blocks: %{params: blocks}, block_second_degree_relations: %{params: block_second_degree_relations_params}, @@ -245,12 +248,18 @@ defmodule Indexer.Block.Fetcher do def async_import_coin_balances(%{addresses: addresses}, %{ address_hash_to_fetched_balance_block_number: address_hash_to_block_number }) do - addresses - |> Enum.map(fn %Address{hash: address_hash} -> - block_number = Map.fetch!(address_hash_to_block_number, to_string(address_hash)) - %{address_hash: address_hash, block_number: block_number} - end) + coin_balances_import_params = + addresses + |> Enum.map(fn %Address{hash: address_hash} -> + block_number = Map.fetch!(address_hash_to_block_number, to_string(address_hash)) + %{address_hash: address_hash, block_number: block_number} + end) + + coin_balances_import_params |> CoinBalance.async_fetch_balances() + + coin_balances_import_params + |> CoinBalanceDaily.async_fetch_balances() end def async_import_coin_balances(_, _), do: :ok diff --git a/apps/indexer/lib/indexer/block/realtime/fetcher.ex b/apps/indexer/lib/indexer/block/realtime/fetcher.ex index 533a79c74c..0533484b7f 100644 --- a/apps/indexer/lib/indexer/block/realtime/fetcher.ex +++ b/apps/indexer/lib/indexer/block/realtime/fetcher.ex @@ -26,7 +26,7 @@ defmodule Indexer.Block.Realtime.Fetcher do ] alias Ecto.Changeset - alias EthereumJSONRPC.{FetchedBalances, Subscription} + alias EthereumJSONRPC.{Blocks, FetchedBalances, Subscription} alias Explorer.Chain alias Explorer.Chain.Cache.Accounts alias Explorer.Counters.AverageBlockTime @@ -172,6 +172,7 @@ defmodule Indexer.Block.Realtime.Fetcher do block_fetcher, %{ address_coin_balances: %{params: address_coin_balances_params}, + address_coin_balances_daily: %{params: address_coin_balances_daily_params}, address_hash_to_fetched_balance_block_number: address_hash_to_block_number, addresses: %{params: addresses_params}, block_rewards: block_rewards @@ -182,7 +183,8 @@ defmodule Indexer.Block.Realtime.Fetcher do balances(block_fetcher, %{ address_hash_to_block_number: address_hash_to_block_number, addresses_params: addresses_params, - balances_params: address_coin_balances_params + balances_params: address_coin_balances_params, + balances_daily_params: address_coin_balances_daily_params })}, {block_reward_errors, chain_import_block_rewards} = Map.pop(block_rewards, :errors), chain_import_options = @@ -381,7 +383,22 @@ defmodule Indexer.Block.Realtime.Fetcher do importable_balances_params = Enum.map(params_list, &Map.put(&1, :value_fetched_at, value_fetched_at)) - {:ok, %{addresses_params: merged_addresses_params, balances_params: importable_balances_params}} + block_number = Enum.at(params_list, 0)[:block_number] + + {:ok, %Blocks{blocks_params: blocks_params}} = + EthereumJSONRPC.fetch_blocks_by_range(block_number..block_number, json_rpc_named_arguments) + + block_timestamp = Enum.at(blocks_params, 0).timestamp + day = DateTime.to_date(block_timestamp) + + importable_balances_daily_params = Enum.map(params_list, &Map.put(&1, :day, day)) + + {:ok, + %{ + addresses_params: merged_addresses_params, + balances_params: importable_balances_params, + balances_daily_params: importable_balances_daily_params + }} {:error, _} = error -> error diff --git a/apps/indexer/lib/indexer/fetcher/block_reward.ex b/apps/indexer/lib/indexer/fetcher/block_reward.ex index ed593bb655..2075c33d99 100644 --- a/apps/indexer/lib/indexer/fetcher/block_reward.ex +++ b/apps/indexer/lib/indexer/fetcher/block_reward.ex @@ -20,7 +20,7 @@ defmodule Indexer.Fetcher.BlockReward do alias Explorer.Chain.Cache.Accounts alias Indexer.{BufferedTask, Tracer} alias Indexer.Fetcher.BlockReward.Supervisor, as: BlockRewardSupervisor - alias Indexer.Fetcher.CoinBalance + alias Indexer.Fetcher.{CoinBalance, CoinBalanceDaily} alias Indexer.Transform.{AddressCoinBalances, Addresses} @behaviour BufferedTask @@ -135,6 +135,7 @@ defmodule Indexer.Fetcher.BlockReward do Accounts.drop(addresses) CoinBalance.async_fetch_balances(address_coin_balances) + CoinBalanceDaily.async_fetch_balances(address_coin_balances) retry_errors(errors) diff --git a/apps/indexer/lib/indexer/fetcher/coin_balance.ex b/apps/indexer/lib/indexer/fetcher/coin_balance.ex index f8fadfcb53..32f37459a0 100644 --- a/apps/indexer/lib/indexer/fetcher/coin_balance.ex +++ b/apps/indexer/lib/indexer/fetcher/coin_balance.ex @@ -77,12 +77,17 @@ defmodule Indexer.Fetcher.CoinBalance do # `{address, block}`, so take unique params only unique_entries = Enum.uniq(entries) - unique_entry_count = Enum.count(unique_entries) + unique_filtered_entries = + Enum.filter(unique_entries, fn {_hash, block_number} -> + block_number > first_block_to_index() + end) + + unique_entry_count = Enum.count(unique_filtered_entries) Logger.metadata(count: unique_entry_count) Logger.debug(fn -> "fetching" end) - unique_entries + unique_filtered_entries |> Enum.map(&entry_to_params/1) |> EthereumJSONRPC.fetch_balances(json_rpc_named_arguments) |> case do @@ -101,6 +106,15 @@ defmodule Indexer.Fetcher.CoinBalance do end end + defp first_block_to_index do + string_value = Application.get_env(:indexer, :first_block) + + case Integer.parse(string_value) do + {integer, ""} -> integer + _ -> 0 + end + end + defp entry_to_params({address_hash_bytes, block_number}) when is_integer(block_number) do {:ok, address_hash} = Hash.Address.cast(address_hash_bytes) %{block_quantity: integer_to_quantity(block_number), hash_data: to_string(address_hash)} diff --git a/apps/indexer/lib/indexer/fetcher/coin_balance_daily.ex b/apps/indexer/lib/indexer/fetcher/coin_balance_daily.ex new file mode 100644 index 0000000000..f15f599a9b --- /dev/null +++ b/apps/indexer/lib/indexer/fetcher/coin_balance_daily.ex @@ -0,0 +1,226 @@ +defmodule Indexer.Fetcher.CoinBalanceDaily do + @moduledoc """ + Fetches `t:Explorer.Chain.Address.CoinBalanceDaily.t/0`. + """ + + use Indexer.Fetcher + use Spandex.Decorators + + require Logger + + import EthereumJSONRPC, only: [integer_to_quantity: 1, quantity_to_integer: 1] + + alias EthereumJSONRPC.{Blocks, FetchedBalances} + alias Explorer.Chain + alias Explorer.Chain.Cache.Accounts + alias Explorer.Chain.Hash + alias Indexer.{BufferedTask, Tracer} + + @behaviour BufferedTask + + @defaults [ + flush_interval: :timer.seconds(3), + max_batch_size: 500, + max_concurrency: 4, + task_supervisor: Indexer.Fetcher.CoinBalanceDaily.TaskSupervisor, + metadata: [fetcher: :coin_balance_daily] + ] + + @doc """ + Asynchronously fetches balances for each address `hash` at the `day`. + """ + @spec async_fetch_balances([ + %{required(:address_hash) => Hash.Address.t(), required(:day) => Date.t()} + ]) :: :ok + def async_fetch_balances(balance_fields) when is_list(balance_fields) do + entries = Enum.map(balance_fields, &entry/1) + + BufferedTask.buffer(__MODULE__, entries) + end + + @doc false + # credo:disable-for-next-line Credo.Check.Design.DuplicatedCode + def child_spec([init_options, gen_server_options]) do + {state, mergeable_init_options} = Keyword.pop(init_options, :json_rpc_named_arguments) + + unless state do + raise ArgumentError, + ":json_rpc_named_arguments must be provided to `#{__MODULE__}.child_spec " <> + "to allow for json_rpc calls when running." + end + + merged_init_options = + @defaults + |> Keyword.merge(mergeable_init_options) + |> Keyword.put(:state, state) + + Supervisor.child_spec({BufferedTask, [{__MODULE__, merged_init_options}, gen_server_options]}, id: __MODULE__) + end + + @impl BufferedTask + def init(initial, reducer, _) do + {:ok, final} = + Chain.stream_unfetched_balances(initial, fn address_fields, acc -> + address_fields + |> entry() + |> reducer.(acc) + end) + + final + end + + @impl BufferedTask + @decorate trace(name: "fetch", resource: "Indexer.Fetcher.CoinBalanceDaily.run/2", service: :indexer, tracer: Tracer) + def run(entries, json_rpc_named_arguments) do + # the same address may be used more than once in the same block, but we only want one `Balance` for a given + # `{address, block}`, so take unique params only + unique_entries = Enum.uniq(entries) + + unique_filtered_entries = + Enum.filter(unique_entries, fn {_hash, block_number} -> + block_number > first_block_to_index() + end) + + unique_entry_count = Enum.count(unique_filtered_entries) + Logger.metadata(count: unique_entry_count) + + Logger.debug(fn -> "fetching" end) + + unique_filtered_entries + |> Enum.map(&entry_to_params/1) + |> EthereumJSONRPC.fetch_balances(json_rpc_named_arguments) + |> case do + {:ok, fetched_balances} -> + run_fetched_balances(fetched_balances, unique_entries) + + {:error, reason} -> + Logger.error( + fn -> + ["failed to fetch: ", inspect(reason)] + end, + error_count: unique_entry_count + ) + + {:retry, unique_entries} + end + end + + defp first_block_to_index do + string_value = Application.get_env(:indexer, :first_block) + + case Integer.parse(string_value) do + {integer, ""} -> integer + _ -> 0 + end + end + + defp entry_to_params({address_hash_bytes, block_number}) when is_integer(block_number) do + {:ok, address_hash} = Hash.Address.cast(address_hash_bytes) + %{block_quantity: integer_to_quantity(block_number), hash_data: to_string(address_hash)} + end + + defp entry(%{address_hash: %Hash{bytes: address_hash_bytes}, block_number: block_number}) do + {address_hash_bytes, block_number} + end + + def balances_params_to_address_params(balances_params) do + balances_params + |> Enum.group_by(fn %{address_hash: address_hash} -> address_hash end) + |> Map.values() + |> Stream.map(&Enum.max_by(&1, fn %{block_number: block_number} -> block_number end)) + |> Enum.map(fn %{address_hash: address_hash, block_number: block_number, value: value} -> + %{ + hash: address_hash, + fetched_coin_balance_block_number: block_number, + fetched_coin_balance: value + } + end) + end + + def import_fetched_balances(%FetchedBalances{params_list: params_list}, broadcast_type \\ false) do + day = + if Enum.empty?(params_list) do + nil + else + json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) + block_number = Enum.at(params_list, 0)[:block_number] + + {:ok, %Blocks{blocks_params: blocks_params}} = + EthereumJSONRPC.fetch_blocks_by_range(block_number..block_number, json_rpc_named_arguments) + + block_timestamp = Enum.at(blocks_params, 0).timestamp + + DateTime.to_date(block_timestamp) + end + + importable_balances_daily_params = + Enum.map(params_list, fn param -> + param + |> Map.put(:day, day) + end) + + addresses_params = balances_params_to_address_params(importable_balances_daily_params) + + Chain.import(%{ + addresses: %{params: addresses_params, with: :balance_changeset}, + address_coin_balances_daily: %{params: importable_balances_daily_params}, + broadcast: broadcast_type + }) + end + + defp run_fetched_balances(%FetchedBalances{errors: errors} = fetched_balances, _) do + {:ok, imported} = import_fetched_balances(fetched_balances) + + Accounts.drop(imported[:addresses]) + + retry(errors) + end + + defp retry([]), do: :ok + + defp retry(errors) when is_list(errors) do + retried_entries = fetched_balances_errors_to_entries(errors) + + Logger.error( + fn -> + [ + "failed to fetch: ", + fetched_balance_errors_to_iodata(errors) + ] + end, + error_count: Enum.count(retried_entries) + ) + + {:retry, retried_entries} + end + + defp fetched_balances_errors_to_entries(errors) when is_list(errors) do + Enum.map(errors, &fetched_balance_error_to_entry/1) + end + + defp fetched_balance_error_to_entry(%{data: %{block_quantity: block_quantity, day: day, hash_data: hash_data}}) + when is_binary(block_quantity) and is_binary(hash_data) do + {:ok, %Hash{bytes: address_hash_bytes}} = Hash.Address.cast(hash_data) + block_number = quantity_to_integer(block_quantity) + {address_hash_bytes, block_number, day} + end + + defp fetched_balance_errors_to_iodata(errors) when is_list(errors) do + fetched_balance_errors_to_iodata(errors, []) + end + + defp fetched_balance_errors_to_iodata([], iodata), do: iodata + + defp fetched_balance_errors_to_iodata([error | errors], iodata) do + fetched_balance_errors_to_iodata(errors, [iodata | fetched_balance_error_to_iodata(error)]) + end + + defp fetched_balance_error_to_iodata(%{ + code: code, + message: message, + data: %{day: day, hash_data: hash_data} + }) + when is_integer(code) and is_binary(message) and is_binary(day) and is_binary(hash_data) do + [hash_data, "@", day, ": (", to_string(code), ") ", message, ?\n] + end +end diff --git a/apps/indexer/lib/indexer/supervisor.ex b/apps/indexer/lib/indexer/supervisor.ex index f22b6de487..b5f6b96f2e 100644 --- a/apps/indexer/lib/indexer/supervisor.ex +++ b/apps/indexer/lib/indexer/supervisor.ex @@ -11,6 +11,7 @@ defmodule Indexer.Supervisor do alias Indexer.Fetcher.{ BlockReward, CoinBalance, + CoinBalanceDaily, CoinBalanceOnDemand, ContractCode, InternalTransaction, @@ -109,6 +110,8 @@ defmodule Indexer.Supervisor do [[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]]}, {CoinBalance.Supervisor, [[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]]}, + {CoinBalanceDaily.Supervisor, + [[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]]}, {Token.Supervisor, [[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]]}, {TokenInstance.Supervisor, [[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]]}, diff --git a/apps/indexer/lib/indexer/transform/address_coin_balances_daily.ex b/apps/indexer/lib/indexer/transform/address_coin_balances_daily.ex new file mode 100644 index 0000000000..de8c9832a5 --- /dev/null +++ b/apps/indexer/lib/indexer/transform/address_coin_balances_daily.ex @@ -0,0 +1,136 @@ +defmodule Indexer.Transform.AddressCoinBalancesDaily do + @moduledoc """ + Extracts `Explorer.Chain.Address.CoinBalanceDaily` params from other schema's params. + """ + + alias EthereumJSONRPC.Blocks + + def params_set(%{} = import_options) do + Enum.reduce(import_options, MapSet.new(), &reducer/2) + end + + defp reducer({:beneficiary_params, beneficiary_params}, acc) when is_list(beneficiary_params) do + Enum.into(beneficiary_params, acc, fn %{ + address_hash: address_hash, + block_number: block_number + } + when is_binary(address_hash) and is_integer(block_number) -> + json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) + + {:ok, %Blocks{blocks_params: blocks_params}} = + EthereumJSONRPC.fetch_blocks_by_range(block_number..block_number, json_rpc_named_arguments) + + block_timestamp = Enum.at(blocks_params, 0).timestamp + day = DateTime.to_date(block_timestamp) + + %{address_hash: address_hash, day: day} + end) + end + + defp reducer({:blocks_params, blocks_params}, acc) when is_list(blocks_params) do + # a block MUST have a miner_hash and number + Enum.into(blocks_params, acc, fn %{miner_hash: address_hash, number: block_number, timestamp: block_timestamp} + when is_binary(address_hash) and is_integer(block_number) -> + day = DateTime.to_date(block_timestamp) + %{address_hash: address_hash, day: day} + end) + end + + defp reducer({:internal_transactions_params, internal_transactions_params}, initial) + when is_list(internal_transactions_params) do + Enum.reduce(internal_transactions_params, initial, &internal_transactions_params_reducer/2) + end + + defp reducer({:logs_params, logs_params}, acc) when is_list(logs_params) do + # a log MUST have address_hash and block_number + logs_params + |> Enum.into(acc, fn + %{address_hash: address_hash, block_number: block_number} + when is_binary(address_hash) and is_integer(block_number) -> + json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) + + {:ok, %Blocks{blocks_params: blocks_params}} = + EthereumJSONRPC.fetch_blocks_by_range(block_number..block_number, json_rpc_named_arguments) + + block_timestamp = Enum.at(blocks_params, 0).timestamp + day = DateTime.to_date(block_timestamp) + %{address_hash: address_hash, day: day} + + %{type: "pending"} -> + nil + end) + |> Enum.reject(fn val -> is_nil(val) end) + |> MapSet.new() + end + + defp reducer({:transactions_params, transactions_params}, initial) when is_list(transactions_params) do + Enum.reduce(transactions_params, initial, &transactions_params_reducer/2) + end + + defp reducer({:block_second_degree_relations_params, block_second_degree_relations_params}, initial) + when is_list(block_second_degree_relations_params), + do: initial + + defp internal_transactions_params_reducer( + %{block_number: block_number} = internal_transaction_params, + acc + ) + when is_integer(block_number) do + case internal_transaction_params do + %{type: "call"} -> + acc + + %{type: "create", error: _} -> + acc + + %{type: "create", created_contract_address_hash: address_hash} when is_binary(address_hash) -> + json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) + + {:ok, %Blocks{blocks_params: blocks_params}} = + EthereumJSONRPC.fetch_blocks_by_range(block_number..block_number, json_rpc_named_arguments) + + block_timestamp = Enum.at(blocks_params, 0).timestamp + day = DateTime.to_date(block_timestamp) + MapSet.put(acc, %{address_hash: address_hash, day: day}) + + %{type: "selfdestruct", from_address_hash: from_address_hash, to_address_hash: to_address_hash} + when is_binary(from_address_hash) and is_binary(to_address_hash) -> + json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) + + {:ok, %Blocks{blocks_params: blocks_params}} = + EthereumJSONRPC.fetch_blocks_by_range(block_number..block_number, json_rpc_named_arguments) + + block_timestamp = Enum.at(blocks_params, 0).timestamp + day = DateTime.to_date(block_timestamp) + + acc + |> MapSet.put(%{address_hash: from_address_hash, day: day}) + |> MapSet.put(%{address_hash: to_address_hash, day: day}) + end + end + + defp transactions_params_reducer( + %{block_number: block_number, from_address_hash: from_address_hash} = transaction_params, + initial + ) + when is_binary(from_address_hash) do + # a transaction MUST have a `from_address_hash` + json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) + + {:ok, %Blocks{blocks_params: blocks_params}} = + EthereumJSONRPC.fetch_blocks_by_range(block_number..block_number, json_rpc_named_arguments) + + block_timestamp = Enum.at(blocks_params, 0).timestamp + day = DateTime.to_date(block_timestamp) + acc = MapSet.put(initial, %{address_hash: from_address_hash, day: day}) + + # `to_address_hash` is optional + case transaction_params do + %{to_address_hash: to_address_hash} when is_binary(to_address_hash) -> + MapSet.put(acc, %{address_hash: to_address_hash, day: day}) + + _ -> + acc + end + end +end From 4f591a569955a156cf2a80029297c99819075c0c Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Tue, 26 May 2020 14:37:04 +0300 Subject: [PATCH 21/51] Fetch balance only for blocks which are greatrer or equal block with FIRST_BLOCK number --- CHANGELOG.md | 1 + .../lib/indexer/fetcher/coin_balance.ex | 22 +++++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91567e98d2..ac179e3aef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Features ### Fixes +- [#3126](https://github.com/poanetwork/blockscout/pull/3126) - Fetch balance only for blocks which are greater or equal block with FIRST_BLOCK number - [#3122](https://github.com/poanetwork/blockscout/pull/3122) - Exclude balance percentage calculation for burn address on accounts page - [#3121](https://github.com/poanetwork/blockscout/pull/3121) - Geth: handle response from eth_getblockbyhash JSON RPC method without totalDifficulty (uncle blocks) - [#3119](https://github.com/poanetwork/blockscout/pull/3119), [#3120](https://github.com/poanetwork/blockscout/pull/3120) - Fix performance of Inventory tab loading for ERC-721 tokens diff --git a/apps/indexer/lib/indexer/fetcher/coin_balance.ex b/apps/indexer/lib/indexer/fetcher/coin_balance.ex index f8fadfcb53..06c4bf6fa3 100644 --- a/apps/indexer/lib/indexer/fetcher/coin_balance.ex +++ b/apps/indexer/lib/indexer/fetcher/coin_balance.ex @@ -77,17 +77,22 @@ defmodule Indexer.Fetcher.CoinBalance do # `{address, block}`, so take unique params only unique_entries = Enum.uniq(entries) - unique_entry_count = Enum.count(unique_entries) + unique_filtered_entries = + Enum.filter(unique_entries, fn {_hash, block_number} -> + block_number >= first_block_to_index() + end) + + unique_entry_count = Enum.count(unique_filtered_entries) Logger.metadata(count: unique_entry_count) Logger.debug(fn -> "fetching" end) - unique_entries + unique_filtered_entries |> Enum.map(&entry_to_params/1) |> EthereumJSONRPC.fetch_balances(json_rpc_named_arguments) |> case do {:ok, fetched_balances} -> - run_fetched_balances(fetched_balances, unique_entries) + run_fetched_balances(fetched_balances, unique_filtered_entries) {:error, reason} -> Logger.error( @@ -97,7 +102,16 @@ defmodule Indexer.Fetcher.CoinBalance do error_count: unique_entry_count ) - {:retry, unique_entries} + {:retry, unique_filtered_entries} + end + end + + defp first_block_to_index do + string_value = Application.get_env(:indexer, :first_block) + + case Integer.parse(string_value) do + {integer, ""} -> integer + _ -> 0 end end From 14079ef12341884ecbd0bb8c5a94996a833db713 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Tue, 26 May 2020 16:40:33 +0300 Subject: [PATCH 22/51] Fix merging conflict --- apps/indexer/lib/indexer/fetcher/coin_balance.ex | 9 --------- 1 file changed, 9 deletions(-) diff --git a/apps/indexer/lib/indexer/fetcher/coin_balance.ex b/apps/indexer/lib/indexer/fetcher/coin_balance.ex index da06fc2732..06c4bf6fa3 100644 --- a/apps/indexer/lib/indexer/fetcher/coin_balance.ex +++ b/apps/indexer/lib/indexer/fetcher/coin_balance.ex @@ -115,15 +115,6 @@ defmodule Indexer.Fetcher.CoinBalance do end end - defp first_block_to_index do - string_value = Application.get_env(:indexer, :first_block) - - case Integer.parse(string_value) do - {integer, ""} -> integer - _ -> 0 - end - end - defp entry_to_params({address_hash_bytes, block_number}) when is_integer(block_number) do {:ok, address_hash} = Hash.Address.cast(address_hash_bytes) %{block_quantity: integer_to_quantity(block_number), hash_data: to_string(address_hash)} From 0eb253dd2ff4c1d1381cd93b9788a80f7fc6b1bc Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Tue, 26 May 2020 18:21:49 +0300 Subject: [PATCH 23/51] Token instance metadata retriever refinement: process metadata if only image URL is passed to tokenURI --- CHANGELOG.md | 1 + apps/block_scout_web/mix.exs | 2 +- apps/ethereum_jsonrpc/mix.exs | 2 +- .../explorer/token/instance_metadata_retriever.ex | 12 +++++++++--- apps/explorer/mix.exs | 2 +- mix.lock | 2 +- 6 files changed, 14 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac179e3aef..24c0b8228f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Features ### Fixes +- [#3128](https://github.com/poanetwork/blockscout/pull/3128) - Token instance metadata retriever refinement: process metadata if only image URL is passed to token URI - [#3126](https://github.com/poanetwork/blockscout/pull/3126) - Fetch balance only for blocks which are greater or equal block with FIRST_BLOCK number - [#3122](https://github.com/poanetwork/blockscout/pull/3122) - Exclude balance percentage calculation for burn address on accounts page - [#3121](https://github.com/poanetwork/blockscout/pull/3121) - Geth: handle response from eth_getblockbyhash JSON RPC method without totalDifficulty (uncle blocks) diff --git a/apps/block_scout_web/mix.exs b/apps/block_scout_web/mix.exs index 234d21f0b1..39077461d0 100644 --- a/apps/block_scout_web/mix.exs +++ b/apps/block_scout_web/mix.exs @@ -90,7 +90,7 @@ defmodule BlockScoutWeb.Mixfile do {:floki, "~> 0.20.1", only: :test}, {:flow, "~> 0.12"}, {:gettext, "~> 0.16.1"}, - {:httpoison, "~> 1.0"}, + {:httpoison, "~> 1.6"}, {:indexer, in_umbrella: true, runtime: false}, # JSON parser and generator {:jason, "~> 1.0"}, diff --git a/apps/ethereum_jsonrpc/mix.exs b/apps/ethereum_jsonrpc/mix.exs index 064b50938f..10743241ee 100644 --- a/apps/ethereum_jsonrpc/mix.exs +++ b/apps/ethereum_jsonrpc/mix.exs @@ -70,7 +70,7 @@ defmodule EthereumJsonrpc.MixProject do # Code coverage {:excoveralls, "~> 0.10.0", only: [:test], github: "KronicDeth/excoveralls", branch: "circle-workflows"}, # JSONRPC HTTP Post calls - {:httpoison, "~> 1.0"}, + {:httpoison, "~> 1.6"}, # Decode/Encode JSON for JSONRPC {:jason, "~> 1.0"}, # Log errors and application output to separate files diff --git a/apps/explorer/lib/explorer/token/instance_metadata_retriever.ex b/apps/explorer/lib/explorer/token/instance_metadata_retriever.ex index bde63ee1e9..69e92e6dde 100644 --- a/apps/explorer/lib/explorer/token/instance_metadata_retriever.ex +++ b/apps/explorer/lib/explorer/token/instance_metadata_retriever.ex @@ -98,10 +98,16 @@ defmodule Explorer.Token.InstanceMetadataRetriever do defp fetch_metadata(token_uri) do case HTTPoison.get(token_uri) do - {:ok, %Response{body: body, status_code: 200}} -> - {:ok, json} = decode_json(body) + {:ok, %Response{body: body, status_code: 200, headers: headers}} -> + if Enum.member?(headers, {"Content-Type", "image/png"}) do + json = %{"image" => %{"type" => "image", "description" => token_uri}} - check_type(json) + check_type(json) + else + {:ok, json} = decode_json(body) + + check_type(json) + end {:ok, %Response{body: body}} -> {:error, body} diff --git a/apps/explorer/mix.exs b/apps/explorer/mix.exs index 99626e0945..1c4cbd8e5e 100644 --- a/apps/explorer/mix.exs +++ b/apps/explorer/mix.exs @@ -84,7 +84,7 @@ defmodule Explorer.Mixfile do # Code coverage {:excoveralls, "~> 0.10.0", only: [:test], github: "KronicDeth/excoveralls", branch: "circle-workflows"}, {:exvcr, "~> 0.10", only: :test}, - {:httpoison, "~> 1.0"}, + {:httpoison, "~> 1.6"}, {:jason, "~> 1.0"}, {:junit_formatter, ">= 0.0.0", only: [:test], runtime: false}, # Log errors and application output to separate files diff --git a/mix.lock b/mix.lock index 8e8b9e3f38..88d77427f3 100644 --- a/mix.lock +++ b/mix.lock @@ -55,7 +55,7 @@ "gettext": {:hex, :gettext, "0.16.1", "e2130b25eebcbe02bb343b119a07ae2c7e28bd4b146c4a154da2ffb2b3507af2", [:mix], [], "hexpm", "dd3a7ea5e3e87ee9df29452dd9560709b4c7cc8141537d0b070155038d92bdf1"}, "hackney": {:hex, :hackney, "1.15.2", "07e33c794f8f8964ee86cebec1a8ed88db5070e52e904b8f12209773c1036085", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.5", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "e0100f8ef7d1124222c11ad362c857d3df7cb5f4204054f9f0f4a728666591fc"}, "html_entities": {:hex, :html_entities, "0.4.0", "f2fee876858cf6aaa9db608820a3209e45a087c5177332799592142b50e89a6b", [:mix], [], "hexpm", "3e3d7156a272950373ce5a4018b1490bea26676f8d6a7d409f6fac8568b8cb9a"}, - "httpoison": {:hex, :httpoison, "1.0.0", "1f02f827148d945d40b24f0b0a89afe40bfe037171a6cf70f2486976d86921cd", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "07967c56199f716ce9adb27415ccea1bd76c44f777dd0a6d4166c3d932f37fdf"}, + "httpoison": {:hex, :httpoison, "1.6.2", "ace7c8d3a361cebccbed19c283c349b3d26991eff73a1eaaa8abae2e3c8089b6", [:mix], [{:hackney, "~> 1.15 and >= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "aa2c74bd271af34239a3948779612f87df2422c2fdcfdbcec28d9c105f0773fe"}, "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "4bdd305eb64e18b0273864920695cb18d7a2021f31a11b9c5fbcd9a253f936e2"}, "jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fdf843bca858203ae1de16da2ee206f53416bbda5dc8c9e78f43243de4bc3afe"}, "jsx": {:hex, :jsx, "2.8.3", "a05252d381885240744d955fbe3cf810504eb2567164824e19303ea59eef62cf", [:mix, :rebar3], [], "hexpm", "fc3499fed7a726995aa659143a248534adc754ebd16ccd437cd93b649a95091f"}, From fb38c8ac6b2b7cb6c9b3a0c0668459fcff63c452 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Tue, 26 May 2020 19:54:22 +0300 Subject: [PATCH 24/51] Fix JSONN structure --- apps/explorer/lib/explorer/token/instance_metadata_retriever.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/explorer/lib/explorer/token/instance_metadata_retriever.ex b/apps/explorer/lib/explorer/token/instance_metadata_retriever.ex index 69e92e6dde..b9d8adfc36 100644 --- a/apps/explorer/lib/explorer/token/instance_metadata_retriever.ex +++ b/apps/explorer/lib/explorer/token/instance_metadata_retriever.ex @@ -100,7 +100,7 @@ defmodule Explorer.Token.InstanceMetadataRetriever do case HTTPoison.get(token_uri) do {:ok, %Response{body: body, status_code: 200, headers: headers}} -> if Enum.member?(headers, {"Content-Type", "image/png"}) do - json = %{"image" => %{"type" => "image", "description" => token_uri}} + json = %{"image" => token_uri} check_type(json) else From 082d921510fa24439a8091ed8478e51b943ffb86 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Tue, 26 May 2020 22:26:58 +0300 Subject: [PATCH 25/51] Token image from image as an object --- .../views/tokens/instance/overview_view.ex | 10 +++++++++- apps/block_scout_web/priv/gettext/default.pot | 4 ++-- .../priv/gettext/en/LC_MESSAGES/default.po | 4 ++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex index 02937fec9f..ab0058f4a8 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex @@ -26,7 +26,7 @@ defmodule BlockScoutWeb.Tokens.Instance.OverviewView do instance.metadata["image_url"] instance.metadata && instance.metadata["image"] -> - instance.metadata["image"] + retrieve_image(instance.metadata["image"]) instance.metadata && instance.metadata["properties"]["image"]["description"] -> instance.metadata["properties"]["image"]["description"] @@ -87,6 +87,14 @@ defmodule BlockScoutWeb.Tokens.Instance.OverviewView do |> tab_name() end + defp retrieve_image(image) when is_map(image) do + image["description"] + end + + defp retrieve_image(image) do + image + end + defp tab_name(["token_transfers"]), do: gettext("Token Transfers") defp tab_name(["metadata"]), do: gettext("Metadata") end diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot index 6aa0491090..b4edc9e73c 100644 --- a/apps/block_scout_web/priv/gettext/default.pot +++ b/apps/block_scout_web/priv/gettext/default.pot @@ -904,7 +904,7 @@ msgstr "" #: lib/block_scout_web/templates/transaction/_tabs.html.eex:4 #: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:7 #: lib/block_scout_web/views/address_view.ex:325 -#: lib/block_scout_web/views/tokens/instance/overview_view.ex:90 +#: lib/block_scout_web/views/tokens/instance/overview_view.ex:98 #: lib/block_scout_web/views/tokens/overview_view.ex:35 #: lib/block_scout_web/views/transaction_view.ex:390 msgid "Token Transfers" @@ -1779,7 +1779,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/tokens/instance/metadata/index.html.eex:18 #: lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex:10 -#: lib/block_scout_web/views/tokens/instance/overview_view.ex:91 +#: lib/block_scout_web/views/tokens/instance/overview_view.ex:99 msgid "Metadata" msgstr "" diff --git a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po index 6aa0491090..b4edc9e73c 100644 --- a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po +++ b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po @@ -904,7 +904,7 @@ msgstr "" #: lib/block_scout_web/templates/transaction/_tabs.html.eex:4 #: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:7 #: lib/block_scout_web/views/address_view.ex:325 -#: lib/block_scout_web/views/tokens/instance/overview_view.ex:90 +#: lib/block_scout_web/views/tokens/instance/overview_view.ex:98 #: lib/block_scout_web/views/tokens/overview_view.ex:35 #: lib/block_scout_web/views/transaction_view.ex:390 msgid "Token Transfers" @@ -1779,7 +1779,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/tokens/instance/metadata/index.html.eex:18 #: lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex:10 -#: lib/block_scout_web/views/tokens/instance/overview_view.ex:91 +#: lib/block_scout_web/views/tokens/instance/overview_view.ex:99 msgid "Metadata" msgstr "" From 93a2fd496060345d532dfcf1656c1f323b9509d0 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Wed, 27 May 2020 10:52:03 +0300 Subject: [PATCH 26/51] Take into account FIRST_BLOCK for block rewards fetching --- CHANGELOG.md | 3 ++- apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex | 22 ++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24c0b8228f..1e1a97a65a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,8 @@ ### Features ### Fixes -- [#3128](https://github.com/poanetwork/blockscout/pull/3128) - Token instance metadata retriever refinement: process metadata if only image URL is passed to token URI +- [#3130](https://github.com/poanetwork/blockscout/pull/3130) - Take into account FIRST_BLOCK for block rewards fetching +- [#3128](https://github.com/poanetwork/blockscout/pull/3128) - Token instance metadata retriever refinement: add processing of token metadata if only image URL is passed to token URI - [#3126](https://github.com/poanetwork/blockscout/pull/3126) - Fetch balance only for blocks which are greater or equal block with FIRST_BLOCK number - [#3122](https://github.com/poanetwork/blockscout/pull/3122) - Exclude balance percentage calculation for burn address on accounts page - [#3121](https://github.com/poanetwork/blockscout/pull/3121) - Geth: handle response from eth_getblockbyhash JSON RPC method without totalDifficulty (uncle blocks) diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex index dfe5fa4ea5..89e56b6125 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex @@ -215,7 +215,18 @@ defmodule EthereumJSONRPC do @spec fetch_beneficiaries([block_number], json_rpc_named_arguments) :: {:ok, FetchedBeneficiaries.t()} | {:error, reason :: term} | :ignore def fetch_beneficiaries(block_numbers, json_rpc_named_arguments) when is_list(block_numbers) do - Keyword.fetch!(json_rpc_named_arguments, :variant).fetch_beneficiaries(block_numbers, json_rpc_named_arguments) + min_block = first_block_to_fetch() + + filtered_block_numbers = + block_numbers + |> Enum.filter(fn block_number -> + block_number >= min_block + end) + + Keyword.fetch!(json_rpc_named_arguments, :variant).fetch_beneficiaries( + filtered_block_numbers, + json_rpc_named_arguments + ) end @doc """ @@ -464,4 +475,13 @@ defmodule EthereumJSONRPC do {:ok, Blocks.from_responses(responses, id_to_params)} end end + + defp first_block_to_fetch do + string_value = Application.get_env(:indexer, :first_block) + + case Integer.parse(string_value) do + {integer, ""} -> integer + _ -> 0 + end + end end From 1b3a80b448ef253fcdff0b980fe649d1248f6761 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Wed, 27 May 2020 12:24:55 +0300 Subject: [PATCH 27/51] Fix performance of coin supply API endpoints --- CHANGELOG.md | 1 + apps/explorer/lib/explorer/chain.ex | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e1a97a65a..39968689cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Features ### Fixes +- [#3132](https://github.com/poanetwork/blockscout/pull/3132) - Fix performance of coin supply API endpoints - [#3130](https://github.com/poanetwork/blockscout/pull/3130) - Take into account FIRST_BLOCK for block rewards fetching - [#3128](https://github.com/poanetwork/blockscout/pull/3128) - Token instance metadata retriever refinement: add processing of token metadata if only image URL is passed to token URI - [#3126](https://github.com/poanetwork/blockscout/pull/3126) - Fetch balance only for blocks which are greater or equal block with FIRST_BLOCK number diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 79650b48e7..f1d409a8f6 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -1517,7 +1517,8 @@ defmodule Explorer.Chain do from( a0 in Address, select: fragment("SUM(a0.fetched_coin_balance)"), - where: a0.hash != ^burn_address_hash + where: a0.hash != ^burn_address_hash, + where: a0.fetched_coin_balance > 0 ) Repo.one!(query) || 0 @@ -1528,7 +1529,8 @@ defmodule Explorer.Chain do query = from( a0 in Address, - select: fragment("SUM(a0.fetched_coin_balance)") + select: fragment("SUM(a0.fetched_coin_balance)"), + where: a0.fetched_coin_balance > 0 ) Repo.one!(query) || 0 From 6626502e90399b8933d0df681a5c3c3e210a5010 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Wed, 27 May 2020 13:25:51 +0300 Subject: [PATCH 28/51] Take into account FIRST_BLOCK in trace_ReplayBlockTransactions requests --- CHANGELOG.md | 1 + apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e1a97a65a..4a02f740f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Features ### Fixes +- [#3133](https://github.com/poanetwork/blockscout/pull/3133) - Take into account FIRST_BLOCK in trace_ReplayBlockTransactions requests - [#3130](https://github.com/poanetwork/blockscout/pull/3130) - Take into account FIRST_BLOCK for block rewards fetching - [#3128](https://github.com/poanetwork/blockscout/pull/3128) - Token instance metadata retriever refinement: add processing of token metadata if only image URL is passed to token URI - [#3126](https://github.com/poanetwork/blockscout/pull/3126) - Fetch balance only for blocks which are greater or equal block with FIRST_BLOCK number diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex index 89e56b6125..a772f615c5 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex @@ -305,9 +305,17 @@ defmodule EthereumJSONRPC do @doc """ Fetches internal transactions for entire blocks from variant API. """ - def fetch_block_internal_transactions(params_list, json_rpc_named_arguments) when is_list(params_list) do + def fetch_block_internal_transactions(block_numbers, json_rpc_named_arguments) when is_list(block_numbers) do + min_block = first_block_to_fetch() + + filtered_block_numbers = + block_numbers + |> Enum.filter(fn block_number -> + block_number >= min_block + end) + Keyword.fetch!(json_rpc_named_arguments, :variant).fetch_block_internal_transactions( - params_list, + filtered_block_numbers, json_rpc_named_arguments ) end From e07f2a3b28ecf94e1be867ac2bbd366f5fe1f715 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Wed, 27 May 2020 13:45:40 +0300 Subject: [PATCH 29/51] Fix coinn supply query: Or the value is incompatible or it must be interpolated (using ^) so it may be cast accordingly in query --- apps/explorer/lib/explorer/chain.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index f1d409a8f6..434f00246a 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -1518,7 +1518,7 @@ defmodule Explorer.Chain do a0 in Address, select: fragment("SUM(a0.fetched_coin_balance)"), where: a0.hash != ^burn_address_hash, - where: a0.fetched_coin_balance > 0 + where: a0.fetched_coin_balance > ^0 ) Repo.one!(query) || 0 @@ -1530,7 +1530,7 @@ defmodule Explorer.Chain do from( a0 in Address, select: fragment("SUM(a0.fetched_coin_balance)"), - where: a0.fetched_coin_balance > 0 + where: a0.fetched_coin_balance > ^0 ) Repo.one!(query) || 0 From 0e20429ec4871945208eb225f4686f4c99d4d441 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Wed, 27 May 2020 19:52:11 +0300 Subject: [PATCH 30/51] Last values of fetched counters from DB if cache is empty --- CHANGELOG.md | 1 + .../controllers/api/rpc/stats_controller.ex | 9 +++++- apps/explorer/lib/explorer/chain.ex | 23 ++++++++++++++ .../chain/cache/address_sum_minus_burnt.ex | 7 +++++ .../explorer/counters/last_fetched_counter.ex | 30 +++++++++++++++++++ .../20200527144742_add_counters_table.exs | 12 ++++++++ 6 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 apps/explorer/lib/explorer/counters/last_fetched_counter.ex create mode 100644 apps/explorer/priv/repo/migrations/20200527144742_add_counters_table.exs diff --git a/CHANGELOG.md b/CHANGELOG.md index 36cfb607c0..06271da0a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - [#3106](https://github.com/poanetwork/blockscout/pull/3106), [#3115](https://github.com/poanetwork/blockscout/pull/3115) - Fix verification of contracts, created from factory (from internal transaction) ### Chore +- [#3134](https://github.com/poanetwork/blockscout/pull/3134) - Get last value of fetched coinsupply API endpoint from DB if cache is empty - [#3124](https://github.com/poanetwork/blockscout/pull/3124) - Display upper border for tx speed if the value cannot be calculated diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/stats_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/stats_controller.ex index 7a1920b790..be8230145b 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/stats_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/stats_controller.ex @@ -44,8 +44,15 @@ defmodule BlockScoutWeb.API.RPC.StatsController do def coinsupply(conn, _params) do cached_coin_total_supply_wei = AddressSumMinusBurnt.get_sum_minus_burnt() + coin_total_supply_wei = + if Decimal.cmp(cached_coin_total_supply_wei, 0) == :gt do + cached_coin_total_supply_wei + else + Chain.get_last_fetched_counter("sum_coin_total_supply_minus_burnt") + end + cached_coin_total_supply = - %Wei{value: Decimal.new(cached_coin_total_supply_wei)} + %Wei{value: Decimal.new(coin_total_supply_wei)} |> Wei.to(:ether) render(conn, "coinsupply.json", cached_coin_total_supply) diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 434f00246a..35fea6f42e 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -28,6 +28,8 @@ defmodule Explorer.Chain do alias Ecto.Adapters.SQL alias Ecto.{Changeset, Multi} + alias Explorer.Counters.LastFetchedCounter + alias Explorer.Chain alias Explorer.Chain.{ @@ -2162,6 +2164,27 @@ defmodule Explorer.Chain do end end + @spec upsert_last_fetched_counter(map()) :: {:ok, LastFetchedCounter.t()} | {:error, Ecto.Changeset.t()} + def upsert_last_fetched_counter(params) do + changeset = LastFetchedCounter.changeset(%LastFetchedCounter{}, params) + + Repo.insert(changeset, + on_conflict: :replace_all, + conflict_target: [:counter_type] + ) + end + + def get_last_fetched_counter(type) do + query = + from( + last_fetched_counter in LastFetchedCounter, + where: last_fetched_counter.counter_type == ^type, + select: last_fetched_counter.value + ) + + Repo.one!(query) + end + defp block_status({number, timestamp}) do now = DateTime.utc_now() last_block_period = DateTime.diff(now, timestamp, :millisecond) diff --git a/apps/explorer/lib/explorer/chain/cache/address_sum_minus_burnt.ex b/apps/explorer/lib/explorer/chain/cache/address_sum_minus_burnt.ex index f8eb22a405..635b19241e 100644 --- a/apps/explorer/lib/explorer/chain/cache/address_sum_minus_burnt.ex +++ b/apps/explorer/lib/explorer/chain/cache/address_sum_minus_burnt.ex @@ -31,6 +31,13 @@ defmodule Explorer.Chain.Cache.AddressSumMinusBurnt do try do result = Chain.fetch_sum_coin_total_supply_minus_burnt() + params = %{ + counter_type: "sum_coin_total_supply_minus_burnt", + value: result + } + + Chain.upsert_last_fetched_counter(params) + set_sum_minus_burnt(result) rescue e -> diff --git a/apps/explorer/lib/explorer/counters/last_fetched_counter.ex b/apps/explorer/lib/explorer/counters/last_fetched_counter.ex new file mode 100644 index 0000000000..e4d684c2f1 --- /dev/null +++ b/apps/explorer/lib/explorer/counters/last_fetched_counter.ex @@ -0,0 +1,30 @@ +defmodule Explorer.Counters.LastFetchedCounter do + @moduledoc """ + Stores last fetched counters. + """ + + alias Explorer.Counters.LastFetchedCounter + use Explorer.Schema + + import Ecto.Changeset + + @type t :: %LastFetchedCounter{ + counter_type: String.t(), + value: Decimal.t() + } + + @primary_key false + schema "last_fetched_counters" do + field(:counter_type, :string) + field(:value, :decimal) + + timestamps() + end + + @doc false + def changeset(struct, params \\ %{}) do + struct + |> cast(params, [:counter_type, :value]) + |> validate_required([:counter_type]) + end +end diff --git a/apps/explorer/priv/repo/migrations/20200527144742_add_counters_table.exs b/apps/explorer/priv/repo/migrations/20200527144742_add_counters_table.exs new file mode 100644 index 0000000000..e09b9ee1e7 --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20200527144742_add_counters_table.exs @@ -0,0 +1,12 @@ +defmodule Explorer.Repo.Migrations.AddCountersTable do + use Ecto.Migration + + def change do + create table(:last_fetched_counters, primary_key: false) do + add(:counter_type, :string, primary_key: true, null: false) + add(:value, :numeric, precision: 100, null: true) + + timestamps(null: false, type: :utc_datetime_usec) + end + end +end From 61d9f2dc61de147dcf6760b3da5118897dd3d25d Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Wed, 27 May 2020 20:40:28 +0300 Subject: [PATCH 31/51] Return for last fetched counnter if no rows inn the DB --- apps/explorer/lib/explorer/chain.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 35fea6f42e..9f5d4fb34a 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -2182,7 +2182,7 @@ defmodule Explorer.Chain do select: last_fetched_counter.value ) - Repo.one!(query) + Repo.one!(query) || 0 end defp block_status({number, timestamp}) do From 111491b257fc864cd0b81bffac7efddd9819c01d Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Thu, 28 May 2020 11:27:48 +0300 Subject: [PATCH 32/51] use version of ex_abi from hex.pm --- apps/ethereum_jsonrpc/mix.exs | 2 +- mix.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/ethereum_jsonrpc/mix.exs b/apps/ethereum_jsonrpc/mix.exs index b3b0c5eb75..ef816a392f 100644 --- a/apps/ethereum_jsonrpc/mix.exs +++ b/apps/ethereum_jsonrpc/mix.exs @@ -84,7 +84,7 @@ defmodule EthereumJsonrpc.MixProject do # Convert unix timestamps in JSONRPC to DateTimes {:timex, "~> 3.6"}, # Encode/decode function names and arguments - {:ex_abi, git: "https://github.com/ayrat555/ex_abi-1", branch: "ayrat555/re-write-encoder"}, + {:ex_abi, "~> 0.4"}, # `:verify_fun` for `Socket.Web.connect` {:ssl_verify_fun, "~> 1.1"}, # `EthereumJSONRPC.WebSocket` diff --git a/mix.lock b/mix.lock index dd5ccbc5ea..cacf0537c0 100644 --- a/mix.lock +++ b/mix.lock @@ -32,7 +32,7 @@ "ecto": {:hex, :ecto, "3.3.1", "82ab74298065bf0c64ca299f6c6785e68ea5d6b980883ee80b044499df35aba1", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "e6c614dfe3bcff2d575ce16d815dbd43f4ee1844599a83de1eea81976a31c174"}, "ecto_sql": {:hex, :ecto_sql, "3.3.2", "92804e0de69bb63e621273c3492252cb08a29475c05d40eeb6f41ad2d483cfd3", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b82d89d4e6a9f7f7f04783b07e8b0af968e0be2f01ee4b39047fe727c5c07471"}, "elixir_make": {:hex, :elixir_make, "0.6.0", "38349f3e29aff4864352084fc736fa7fa0f2995a819a737554f7ebd28b85aaab", [:mix], [], "hexpm", "d522695b93b7f0b4c0fcb2dfe73a6b905b1c301226a5a55cb42e5b14d509e050"}, - "ex_abi": {:git, "https://github.com/ayrat555/ex_abi-1", "b012098e71129b3ff7394685e1aaa5b7180e9b53", [branch: "ayrat555/re-write-encoder"]}, + "ex_abi": {:hex, :ex_abi, "0.4.0", "ff7e7f5b56c228b117e1f54e80c668a8f0424c275f233a50373548b70d99bd5c", [:mix], [{:exth_crypto, "~> 0.1.6", [hex: :exth_crypto, repo: "hexpm", optional: false]}], "hexpm", "2d33499de38c54531103e58530d0453863fb6149106327f691001873b0556e68"}, "ex_cldr": {:hex, :ex_cldr, "2.7.2", "d79a1af6ed12630a15175d2b88d4381b22db5d6f835c5da8de132f0cf712b233", [:mix], [{:cldr_utils, "~> 2.1", [hex: :cldr_utils, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:gettext, "~> 0.13", [hex: :gettext, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 0.5", [hex: :nimble_parsec, repo: "hexpm", optional: false]}, {:plug, "~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "52344684e4e0ff046e1929ae019c0b3c6b122c77c948a43be30684015f2036e2"}, "ex_cldr_currencies": {:hex, :ex_cldr_currencies, "2.3.0", "bffae489416b8b05d4683403263f5d62aae17de70c24ff915a533541fea514de", [:mix], [{:ex_cldr, "~> 2.6", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "7d8ba3738b24d9c7d31bc63b131785b444ed05388fd1cc0958216eb5992d79d6"}, "ex_cldr_lists": {:hex, :ex_cldr_lists, "2.2.0", "b99f8752d098fc6ba5f083bbd0b25d0d01e36b0042155cf6abd5f205306ba849", [:mix], [{:ex_cldr, "~> 2.6", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:ex_cldr_numbers, "~> 2.6", [hex: :ex_cldr_numbers, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "89e25d539a4126472560da2e28b6f1aeb859f5afc0778d6b594029c4226d1775"}, From 3c37a46e02c5e005d1cf85d6d4ea0da2c2cf291b Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Thu, 28 May 2020 16:17:09 +0300 Subject: [PATCH 33/51] RSK hardfork: cumulativeDifficulty --- CHANGELOG.md | 1 + apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 06271da0a7..e15b48c25f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - [#3106](https://github.com/poanetwork/blockscout/pull/3106), [#3115](https://github.com/poanetwork/blockscout/pull/3115) - Fix verification of contracts, created from factory (from internal transaction) ### Chore +- [#3137](https://github.com/poanetwork/blockscout/pull/3137) - RSK Papyrus Release v2.0.1 hardfork: cumulativeDifficulty - [#3134](https://github.com/poanetwork/blockscout/pull/3134) - Get last value of fetched coinsupply API endpoint from DB if cache is empty - [#3124](https://github.com/poanetwork/blockscout/pull/3124) - Display upper border for tx speed if the value cannot be calculated diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex index 20e7db22c8..ce44f792a7 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex @@ -480,7 +480,7 @@ defmodule EthereumJSONRPC.Block do end defp entry_to_elixir({key, quantity}) - when key in ~w(difficulty gasLimit gasUsed minimumGasPrice number size totalDifficulty paidFees) and + when key in ~w(difficulty gasLimit gasUsed minimumGasPrice number size cumulativeDifficulty totalDifficulty paidFees) and not is_nil(quantity) do {key, quantity_to_integer(quantity)} end From c6693596d5eeaa496a32f829b4cccc888f1831b5 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Thu, 28 May 2020 13:29:54 +0300 Subject: [PATCH 34/51] Finalize coin balance history chart update --- apps/block_scout_web/config/config.exs | 4 + ...ss_coin_balance_by_day_controller_test.exs | 2 + .../api/rpc/address_controller_test.exs | 71 ++++++ apps/explorer/lib/explorer/chain.ex | 13 + .../chain/address/coin_balance_daily.ex | 17 +- .../runner/address/coin_balances_daily.ex | 3 +- .../explorer/chain_spec/parity/importer.ex | 22 +- .../chain_spec/parity/importer_test.exs | 78 +++++- apps/explorer/test/explorer/chain_test.exs | 4 + apps/explorer/test/support/factory.ex | 13 + apps/indexer/lib/indexer/block/fetcher.ex | 28 ++- .../lib/indexer/block/realtime/fetcher.ex | 40 +++- .../lib/indexer/fetcher/block_reward.ex | 9 +- .../lib/indexer/fetcher/coin_balance.ex | 79 +++++- .../lib/indexer/fetcher/coin_balance_daily.ex | 226 ------------------ .../indexer/fetcher/coin_balance_on_demand.ex | 55 ++++- apps/indexer/lib/indexer/supervisor.ex | 3 - .../transform/address_coin_balances_daily.ex | 59 +++-- .../test/indexer/block/fetcher_test.exs | 135 ++++++++++- .../indexer/block/realtime/fetcher_test.exs | 120 +++++++++- .../indexer/fetcher/block_reward_test.exs | 126 ++++++++++ .../fetcher/coin_balance_on_demand_test.exs | 65 +++++ .../indexer/fetcher/coin_balance_test.exs | 137 ++++++++++- 23 files changed, 1018 insertions(+), 291 deletions(-) delete mode 100644 apps/indexer/lib/indexer/fetcher/coin_balance_daily.ex diff --git a/apps/block_scout_web/config/config.exs b/apps/block_scout_web/config/config.exs index 9fd1da1f08..b5bd4087f6 100644 --- a/apps/block_scout_web/config/config.exs +++ b/apps/block_scout_web/config/config.exs @@ -84,6 +84,10 @@ config :block_scout_web, BlockScoutWeb.Chain.TransactionHistoryChartController, # days history_size: 30 +config :block_scout_web, BlockScoutWeb.Chain.Address.CoinBalance, + # days + coin_balance_history_days: System.get_env("COIN_BALANCE_HISTORY_DAYS", "10") + config :ex_cldr, default_locale: "en", default_backend: BlockScoutWeb.Cldr diff --git a/apps/block_scout_web/test/block_scout_web/controllers/address_coin_balance_by_day_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/address_coin_balance_by_day_controller_test.exs index 3778793300..8d94957b9c 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/address_coin_balance_by_day_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/address_coin_balance_by_day_controller_test.exs @@ -11,6 +11,8 @@ defmodule BlockScoutWeb.AddressCoinBalanceByDayControllerTest do block_one_day_ago = insert(:block, timestamp: Timex.shift(noon, days: -1), number: 1) insert(:fetched_balance, address_hash: address.hash, value: 1000, block_number: block.number) insert(:fetched_balance, address_hash: address.hash, value: 2000, block_number: block_one_day_ago.number) + insert(:fetched_balance_daily, address_hash: address.hash, value: 1000, day: noon) + insert(:fetched_balance_daily, address_hash: address.hash, value: 2000, day: Timex.shift(noon, days: -1)) conn = get(conn, address_coin_balance_by_day_path(conn, :index, Address.checksum(address)), %{"type" => "JSON"}) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs index 1f18b5422d..5378ca6cfe 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs @@ -111,6 +111,30 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do address_hash = to_string(address.hash) + expect(EthereumJSONRPC.Mox, :json_rpc, 1, fn [ + %{ + id: id, + method: "eth_getBalance", + params: [^mining_address_hash, "0x65"] + } + ], + _options -> + {:ok, [%{id: id, jsonrpc: "2.0", result: "0x02"}]} + end) + + res = eth_block_number_fake_response("0x65") + + expect(EthereumJSONRPC.Mox, :json_rpc, 1, fn [ + %{ + id: 0, + method: "eth_getBlockByNumber", + params: ["0x65", true] + } + ], + _ -> + {:ok, [res]} + end) + expect(EthereumJSONRPC.Mox, :json_rpc, 1, fn [ %{ id: id, @@ -122,6 +146,17 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do {:ok, [%{id: id, jsonrpc: "2.0", result: "0x02"}]} end) + expect(EthereumJSONRPC.Mox, :json_rpc, 1, fn [ + %{ + id: 0, + method: "eth_getBlockByNumber", + params: ["0x65", true] + } + ], + _ -> + {:ok, [res]} + end) + response = conn |> get("/api", params) @@ -2719,4 +2754,40 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do |> put_in(["properties", "result"], result) |> ExJsonSchema.Schema.resolve() end + + defp eth_block_number_fake_response(block_quantity) do + %{ + id: 0, + jsonrpc: "2.0", + result: %{ + "author" => "0x0000000000000000000000000000000000000000", + "difficulty" => "0x20000", + "extraData" => "0x", + "gasLimit" => "0x663be0", + "gasUsed" => "0x0", + "hash" => "0x5b28c1bfd3a15230c9a46b399cd0f9a6920d432e85381cc6a140b06e8410112f", + "logsBloom" => + "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner" => "0x0000000000000000000000000000000000000000", + "number" => block_quantity, + "parentHash" => "0x0000000000000000000000000000000000000000000000000000000000000000", + "receiptsRoot" => "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "sealFields" => [ + "0x80", + "0xb8410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ], + "sha3Uncles" => "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "signature" => + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "size" => "0x215", + "stateRoot" => "0xfad4af258fd11939fae0c6c6eec9d340b1caac0b0196fd9a1bc3f489c5bf00b3", + "step" => "0", + "timestamp" => "0x0", + "totalDifficulty" => "0x20000", + "transactions" => [], + "transactionsRoot" => "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncles" => [] + } + } + end end diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index bdc2062c8f..ed300bebc7 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -3514,12 +3514,25 @@ defmodule Explorer.Chain do @spec address_to_balances_by_day(Hash.Address.t()) :: [balance_by_day] def address_to_balances_by_day(address_hash) do + latest_block_timestamp = + address_hash + |> CoinBalance.last_coin_balance_timestamp() + |> Repo.one() + address_hash |> CoinBalanceDaily.balances_by_day() |> Repo.all() + |> replace_last_value(latest_block_timestamp) |> normalize_balances_by_day() end + # https://github.com/poanetwork/blockscout/issues/2658 + defp replace_last_value(items, %{value: value, timestamp: timestamp}) do + List.replace_at(items, -1, %{date: Date.convert!(timestamp, Calendar.ISO), value: value}) + end + + defp replace_last_value(items, _), do: items + defp normalize_balances_by_day(balances_by_day) do result = balances_by_day diff --git a/apps/explorer/lib/explorer/chain/address/coin_balance_daily.ex b/apps/explorer/lib/explorer/chain/address/coin_balance_daily.ex index 063f1d8235..4cc68b8970 100644 --- a/apps/explorer/lib/explorer/chain/address/coin_balance_daily.ex +++ b/apps/explorer/lib/explorer/chain/address/coin_balance_daily.ex @@ -42,18 +42,27 @@ defmodule Explorer.Chain.Address.CoinBalanceDaily do @doc """ Builds an `Ecto.Query` to fetch a series of balances by day for the given account. Each element in the series - corresponds to the maximum balance in that day. Only the last 90 days of data are used. + corresponds to the maximum balance in that day. Only the last `n` days of data are used. + `n` is configurable via COIN_BALANCE_HISTORY_DAYS ENV var. """ def balances_by_day(address_hash) do + {days_to_consider, _} = + Application.get_env(:block_scout_web, BlockScoutWeb.Chain.Address.CoinBalance)[:coin_balance_history_days] + |> Integer.parse() + CoinBalanceDaily |> where([cbd], cbd.address_hash == ^address_hash) - |> limit_time_interval() + |> limit_time_interval(days_to_consider) |> order_by([cbd], cbd.day) |> select([cbd], %{date: cbd.day, value: cbd.value}) end - def limit_time_interval(query) do - query |> where([cbd], cbd.day >= fragment("date_trunc('day', now()) - interval '90 days'")) + def limit_time_interval(query, days_to_consider) do + query + |> where( + [cbd], + cbd.day >= fragment("date_trunc('day', now() - CAST(? AS INTERVAL))", ^%Postgrex.Interval{days: days_to_consider}) + ) end def changeset(%__MODULE__{} = balance, params) do diff --git a/apps/explorer/lib/explorer/chain/import/runner/address/coin_balances_daily.ex b/apps/explorer/lib/explorer/chain/import/runner/address/coin_balances_daily.ex index e9e747c069..d8f279d9ad 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/address/coin_balances_daily.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/address/coin_balances_daily.ex @@ -120,12 +120,13 @@ defmodule Explorer.Chain.Import.Runner.Address.CoinBalancesDaily do value: fragment( """ - CASE WHEN EXCLUDED.value IS NOT NULL THEN + CASE WHEN EXCLUDED.value IS NOT NULL AND EXCLUDED.value > ? THEN EXCLUDED.value ELSE ? END """, + balance.value, balance.value ), inserted_at: fragment("LEAST(EXCLUDED.inserted_at, ?)", balance.inserted_at), diff --git a/apps/explorer/lib/explorer/chain_spec/parity/importer.ex b/apps/explorer/lib/explorer/chain_spec/parity/importer.ex index ba4c7a3e37..cdd2da1eed 100644 --- a/apps/explorer/lib/explorer/chain_spec/parity/importer.ex +++ b/apps/explorer/lib/explorer/chain_spec/parity/importer.ex @@ -5,6 +5,7 @@ defmodule Explorer.ChainSpec.Parity.Importer do require Logger + alias EthereumJSONRPC.Blocks alias Explorer.{Chain, Repo} alias Explorer.Chain.Block.{EmissionReward, Range} alias Explorer.Chain.Hash.Address, as: AddressHash @@ -33,6 +34,21 @@ defmodule Explorer.ChainSpec.Parity.Importer do end) |> Enum.to_list() + json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) + + {:ok, %Blocks{blocks_params: [%{timestamp: timestamp}]}} = + EthereumJSONRPC.fetch_blocks_by_range(1..1, json_rpc_named_arguments) + + day = DateTime.to_date(timestamp) + + balance_daily_params = + chain_spec + |> genesis_accounts() + |> Stream.map(fn balance_map -> + Map.put(balance_map, :day, day) + end) + |> Enum.to_list() + address_params = balance_params |> Stream.map(fn %{address_hash: hash} = map -> @@ -40,7 +56,11 @@ defmodule Explorer.ChainSpec.Parity.Importer do end) |> Enum.to_list() - params = %{address_coin_balances: %{params: balance_params}, addresses: %{params: address_params}} + params = %{ + address_coin_balances: %{params: balance_params}, + address_coin_balances_daily: %{params: balance_daily_params}, + addresses: %{params: address_params} + } Chain.import(params) end diff --git a/apps/explorer/test/explorer/chain_spec/parity/importer_test.exs b/apps/explorer/test/explorer/chain_spec/parity/importer_test.exs index ba97a94731..2f19675c43 100644 --- a/apps/explorer/test/explorer/chain_spec/parity/importer_test.exs +++ b/apps/explorer/test/explorer/chain_spec/parity/importer_test.exs @@ -1,12 +1,17 @@ defmodule Explorer.ChainSpec.Parity.ImporterTest do use Explorer.DataCase - alias Explorer.Chain.Address.CoinBalance + import Mox + import EthereumJSONRPC, only: [integer_to_quantity: 1] + + alias Explorer.Chain.Address.{CoinBalance, CoinBalanceDaily} alias Explorer.Chain.Block.{EmissionReward, Range} alias Explorer.Chain.{Address, Hash, Wei} alias Explorer.ChainSpec.Parity.Importer alias Explorer.Repo + setup :set_mox_global + @chain_spec "#{File.cwd!()}/test/support/fixture/chain_spec/foundation.json" |> File.read!() |> Jason.decode!() @@ -141,14 +146,37 @@ defmodule Explorer.ChainSpec.Parity.ImporterTest do describe "import_genesis_accounts/1" do test "imports accounts" do + block_quantity = integer_to_quantity(1) + res = eth_block_number_fake_response(block_quantity) + + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn [ + %{id: 0, jsonrpc: "2.0", method: "eth_getBlockByNumber", params: ["0x1", true]} + ], + _ -> + {:ok, [res]} + end) + {:ok, %{address_coin_balances: address_coin_balances}} = Importer.import_genesis_accounts(@chain_spec) assert Enum.count(address_coin_balances) == 403 assert CoinBalance |> Repo.all() |> Enum.count() == 403 + assert CoinBalanceDaily |> Repo.all() |> Enum.count() == 403 assert Address |> Repo.all() |> Enum.count() == 403 end test "imports contract code" do + block_quantity = integer_to_quantity(1) + res = eth_block_number_fake_response(block_quantity) + + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn [ + %{id: 0, jsonrpc: "2.0", method: "eth_getBlockByNumber", params: ["0x1", true]} + ], + [] -> + {:ok, [res]} + end) + code = "0x608060405234801561001057600080fd5b50600436106100cf5760003560e01c806391ad27b41161008c57806398d5fdca1161006657806398d5fdca14610262578063a97e5c9314610280578063df5dd1a5146102dc578063eebd48b014610320576100cf565b806391ad27b4146101e457806391b7f5ed14610202578063955d14cd14610244576100cf565b80630aa6f2fe146100d457806320ba81ee1461011657806322a90082146101345780634c2c987c14610176578063764cbcd1146101985780637837efdc146101da575b600080fd5b610100600480360360208110156100ea57600080fd5b8101908080359060200190929190505050610353565b6040518082815260200191505060405180910390f35b61011e6103c4565b6040518082815260200191505060405180910390f35b6101606004803603602081101561014a57600080fd5b81019080803590602001909291905050506103ce565b6040518082815260200191505060405180910390f35b61017e61043f565b604051808215151515815260200191505060405180910390f35b6101c4600480360360208110156101ae57600080fd5b8101908080359060200190929190505050610456565b6040518082815260200191505060405180910390f35b6101e26104c7565b005b6101ec6104d2565b6040518082815260200191505060405180910390f35b61022e6004803603602081101561021857600080fd5b81019080803590602001909291905050506104dc565b6040518082815260200191505060405180910390f35b61024c6106a2565b6040518082815260200191505060405180910390f35b61026a6106ac565b6040518082815260200191505060405180910390f35b6102c26004803603602081101561029657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506106b6565b604051808215151515815260200191505060405180910390f35b61031e600480360360208110156102f257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506106d3565b005b61032861073d565b6040518085815260200184815260200183815260200182815260200194505050505060405180910390f35b600061035e336106b6565b6103b3576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526030815260200180610b4f6030913960400191505060405180910390fd5b816004819055506004549050919050565b6000600454905090565b60006103d9336106b6565b61042e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526030815260200180610b4f6030913960400191505060405180910390fd5b816003819055506003549050919050565b6000600560009054906101000a900460ff16905090565b6000610461336106b6565b6104b6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526030815260200180610b4f6030913960400191505060405180910390fd5b816002819055506002549050919050565b6104d033610771565b565b6000600354905090565b60006104e7336106b6565b61053c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526030815260200180610b4f6030913960400191505060405180910390fd5b600082116105b2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260098152602001807f7072696365203c3d30000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b6105ba6104d2565b6105c26106a2565b01421015610638576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f54494d455f4c4f434b5f494e434f4d504c45544500000000000000000000000081525060200191505060405180910390fd5b610641826107cb565b5061064b42610456565b503373ffffffffffffffffffffffffffffffffffffffff167f95dce27040c59c8b1c445b284f81a3aaae6eecd7d08d5c7684faee64cdb514a1836040518082815260200191505060405180910390a2819050919050565b6000600254905090565b6000600154905090565b60006106cc82600061083c90919063ffffffff16565b9050919050565b6106dc336106b6565b610731576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526030815260200180610b4f6030913960400191505060405180910390fd5b61073a8161091a565b50565b60008060008061074b6106ac565b6107536104d2565b61075b6103c4565b6107636106a2565b935093509350935090919293565b61078581600061097390919063ffffffff16565b8073ffffffffffffffffffffffffffffffffffffffff167f9c8e7d83025bef8a04c664b2f753f64b8814bdb7e27291d7e50935f18cc3c71260405160405180910390a250565b60006107d6336106b6565b61082b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526030815260200180610b4f6030913960400191505060405180910390fd5b816001819055506001549050919050565b60008073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156108c3576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180610b2d6022913960400191505060405180910390fd5b8260000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b61092e816000610a3090919063ffffffff16565b8073ffffffffffffffffffffffffffffffffffffffff167e47706786c922d17b39285dc59d696bafea72c0b003d3841ae1202076f4c2e460405160405180910390a250565b61097d828261083c565b6109d2576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180610b0c6021913960400191505060405180910390fd5b60008260000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055505050565b610a3a828261083c565b15610aad576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f526f6c65733a206163636f756e7420616c72656164792068617320726f6c650081525060200191505060405180910390fd5b60018260000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550505056fe526f6c65733a206163636f756e7420646f6573206e6f74206861766520726f6c65526f6c65733a206163636f756e7420697320746865207a65726f20616464726573734f7261636c65526f6c653a2063616c6c657220646f6573206e6f74206861766520746865204f7261636c6520726f6c65a265627a7a72315820df30730da57a5061c487e0b37e84e80308fa443e2e80ee9117a13fa8149caf4164736f6c634300050b0032" @@ -169,11 +197,59 @@ defmodule Explorer.ChainSpec.Parity.ImporterTest do end test "imports coin balances without 0x" do + block_quantity = integer_to_quantity(1) + res = eth_block_number_fake_response(block_quantity) + + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn [ + %{id: 0, jsonrpc: "2.0", method: "eth_getBlockByNumber", params: ["0x1", true]} + ], + [] -> + {:ok, [res]} + end) + {:ok, %{address_coin_balances: address_coin_balances}} = Importer.import_genesis_accounts(@chain_classic_spec) assert Enum.count(address_coin_balances) == 8894 assert CoinBalance |> Repo.all() |> Enum.count() == 8894 + assert CoinBalanceDaily |> Repo.all() |> Enum.count() == 8894 assert Address |> Repo.all() |> Enum.count() == 8894 end end + + defp eth_block_number_fake_response(block_quantity) do + %{ + id: 0, + jsonrpc: "2.0", + result: %{ + "author" => "0x0000000000000000000000000000000000000000", + "difficulty" => "0x20000", + "extraData" => "0x", + "gasLimit" => "0x663be0", + "gasUsed" => "0x0", + "hash" => "0x5b28c1bfd3a15230c9a46b399cd0f9a6920d432e85381cc6a140b06e8410112f", + "logsBloom" => + "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner" => "0x0000000000000000000000000000000000000000", + "number" => block_quantity, + "parentHash" => "0x0000000000000000000000000000000000000000000000000000000000000000", + "receiptsRoot" => "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "sealFields" => [ + "0x80", + "0xb8410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ], + "sha3Uncles" => "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "signature" => + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "size" => "0x215", + "stateRoot" => "0xfad4af258fd11939fae0c6c6eec9d340b1caac0b0196fd9a1bc3f489c5bf00b3", + "step" => "0", + "timestamp" => "0x0", + "totalDifficulty" => "0x20000", + "transactions" => [], + "transactionsRoot" => "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncles" => [] + } + } + end end diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs index ade78e9f64..158e11fe58 100644 --- a/apps/explorer/test/explorer/chain_test.exs +++ b/apps/explorer/test/explorer/chain_test.exs @@ -4719,6 +4719,8 @@ defmodule Explorer.ChainTest do block_one_day_ago = insert(:block, timestamp: yesterday, number: 49) insert(:fetched_balance, address_hash: address.hash, value: 1000, block_number: block.number) insert(:fetched_balance, address_hash: address.hash, value: 2000, block_number: block_one_day_ago.number) + insert(:fetched_balance_daily, address_hash: address.hash, value: 1000, day: noon) + insert(:fetched_balance_daily, address_hash: address.hash, value: 2000, day: yesterday) balances = Chain.address_to_balances_by_day(address.hash) @@ -4735,6 +4737,7 @@ defmodule Explorer.ChainTest do yesterday = Timex.shift(noon, days: -1) block_one_day_ago = insert(:block, timestamp: yesterday) insert(:fetched_balance, address_hash: address.hash, value: 1000, block_number: block_one_day_ago.number) + insert(:fetched_balance_daily, address_hash: address.hash, value: 1000, day: yesterday) balances = Chain.address_to_balances_by_day(address.hash) @@ -4754,6 +4757,7 @@ defmodule Explorer.ChainTest do block_past = insert(:block, timestamp: past, number: 2) insert(:fetched_balance, address_hash: address.hash, value: 0, block_number: block_past.number) + insert(:fetched_balance_daily, address_hash: address.hash, value: 0, day: today) [balance] = Chain.address_to_balances_by_day(address.hash) diff --git a/apps/explorer/test/support/factory.ex b/apps/explorer/test/support/factory.ex index 09420fc1c4..fa932c1e1d 100644 --- a/apps/explorer/test/support/factory.ex +++ b/apps/explorer/test/support/factory.ex @@ -16,6 +16,7 @@ defmodule Explorer.Factory do Address.CurrentTokenBalance, Address.TokenBalance, Address.CoinBalance, + Address.CoinBalanceDaily, Block, ContractMethod, Data, @@ -56,6 +57,13 @@ defmodule Explorer.Factory do } end + def unfetched_balance_daily_factory do + %CoinBalanceDaily{ + address_hash: address_hash(), + day: Timex.shift(Timex.now(), days: Enum.random(0..100) * -1) + } + end + def update_balance_value(%CoinBalance{address_hash: address_hash, block_number: block_number}, value) do Repo.update_all( from( @@ -71,6 +79,11 @@ defmodule Explorer.Factory do |> struct!(value: Enum.random(1..100_000)) end + def fetched_balance_daily_factory do + unfetched_balance_daily_factory() + |> struct!(value: Enum.random(1..100_000)) + end + def contract_address_factory do %Address{ hash: address_hash(), diff --git a/apps/indexer/lib/indexer/block/fetcher.ex b/apps/indexer/lib/indexer/block/fetcher.ex index c84e9acd48..1792b941ea 100644 --- a/apps/indexer/lib/indexer/block/fetcher.ex +++ b/apps/indexer/lib/indexer/block/fetcher.ex @@ -20,7 +20,6 @@ defmodule Indexer.Block.Fetcher do alias Indexer.Fetcher.{ BlockReward, CoinBalance, - CoinBalanceDaily, ContractCode, InternalTransaction, ReplacedTransaction, @@ -35,6 +34,7 @@ defmodule Indexer.Block.Fetcher do alias Indexer.Transform.{ AddressCoinBalances, + AddressCoinBalancesDaily, Addresses, AddressTokenBalances, MintTransfers, @@ -155,6 +155,14 @@ defmodule Indexer.Block.Fetcher do transactions_params: transactions_with_receipts } |> AddressCoinBalances.params_set(), + coin_balances_params_daily_set = + %{ + beneficiary_params: MapSet.to_list(beneficiary_params_set), + blocks_params: blocks, + logs_params: logs, + transactions_params: transactions_with_receipts + } + |> AddressCoinBalancesDaily.params_set(), beneficiaries_with_gas_payment <- beneficiary_params_set |> add_gas_payments(transactions_with_receipts, blocks) @@ -166,7 +174,7 @@ defmodule Indexer.Block.Fetcher do %{ addresses: %{params: addresses}, address_coin_balances: %{params: coin_balances_params_set}, - address_coin_balances_daily: %{params: coin_balances_params_set}, + address_coin_balances_daily: %{params: coin_balances_params_daily_set}, address_token_balances: %{params: address_token_balances}, blocks: %{params: blocks}, block_second_degree_relations: %{params: block_second_degree_relations_params}, @@ -248,18 +256,12 @@ defmodule Indexer.Block.Fetcher do def async_import_coin_balances(%{addresses: addresses}, %{ address_hash_to_fetched_balance_block_number: address_hash_to_block_number }) do - coin_balances_import_params = - addresses - |> Enum.map(fn %Address{hash: address_hash} -> - block_number = Map.fetch!(address_hash_to_block_number, to_string(address_hash)) - %{address_hash: address_hash, block_number: block_number} - end) - - coin_balances_import_params + addresses + |> Enum.map(fn %Address{hash: address_hash} -> + block_number = Map.fetch!(address_hash_to_block_number, to_string(address_hash)) + %{address_hash: address_hash, block_number: block_number} + end) |> CoinBalance.async_fetch_balances() - - coin_balances_import_params - |> CoinBalanceDaily.async_fetch_balances() end def async_import_coin_balances(_, _), do: :ok diff --git a/apps/indexer/lib/indexer/block/realtime/fetcher.ex b/apps/indexer/lib/indexer/block/realtime/fetcher.ex index 0533484b7f..df4daa5837 100644 --- a/apps/indexer/lib/indexer/block/realtime/fetcher.ex +++ b/apps/indexer/lib/indexer/block/realtime/fetcher.ex @@ -178,7 +178,13 @@ defmodule Indexer.Block.Realtime.Fetcher do block_rewards: block_rewards } = options ) do - with {:balances, {:ok, %{addresses_params: balances_addresses_params, balances_params: balances_params}}} <- + with {:balances, + {:ok, + %{ + addresses_params: balances_addresses_params, + balances_params: balances_params, + balances_daily_params: balances_daily_params + }}} <- {:balances, balances(block_fetcher, %{ address_hash_to_block_number: address_hash_to_block_number, @@ -193,7 +199,8 @@ defmodule Indexer.Block.Realtime.Fetcher do |> put_in([:addresses, :params], balances_addresses_params) |> put_in([:blocks, :params, Access.all(), :consensus], true) |> put_in([:block_rewards], chain_import_block_rewards) - |> put_in([Access.key(:address_coin_balances, %{}), :params], balances_params), + |> put_in([Access.key(:address_coin_balances, %{}), :params], balances_params) + |> put_in([Access.key(:address_coin_balances_daily, %{}), :params], balances_daily_params), {:import, {:ok, imported} = ok} <- {:import, Chain.import(chain_import_options)} do async_import_remaining_block_data( imported, @@ -383,15 +390,26 @@ defmodule Indexer.Block.Realtime.Fetcher do importable_balances_params = Enum.map(params_list, &Map.put(&1, :value_fetched_at, value_fetched_at)) - block_number = Enum.at(params_list, 0)[:block_number] - - {:ok, %Blocks{blocks_params: blocks_params}} = - EthereumJSONRPC.fetch_blocks_by_range(block_number..block_number, json_rpc_named_arguments) - - block_timestamp = Enum.at(blocks_params, 0).timestamp - day = DateTime.to_date(block_timestamp) - - importable_balances_daily_params = Enum.map(params_list, &Map.put(&1, :day, day)) + block_numbers = + params_list + |> Enum.map(&Map.get(&1, :block_number)) + |> Enum.sort() + |> Enum.dedup() + + block_timestamp_map = + Enum.reduce(block_numbers, %{}, fn block_number, map -> + {:ok, %Blocks{blocks_params: [%{timestamp: timestamp}]}} = + EthereumJSONRPC.fetch_blocks_by_range(block_number..block_number, json_rpc_named_arguments) + + day = DateTime.to_date(timestamp) + Map.put(map, "#{block_number}", day) + end) + + importable_balances_daily_params = + Enum.map(params_list, fn param -> + day = Map.get(block_timestamp_map, "#{param.block_number}") + Map.put(param, :day, day) + end) {:ok, %{ diff --git a/apps/indexer/lib/indexer/fetcher/block_reward.ex b/apps/indexer/lib/indexer/fetcher/block_reward.ex index 2075c33d99..7bd933eb5f 100644 --- a/apps/indexer/lib/indexer/fetcher/block_reward.ex +++ b/apps/indexer/lib/indexer/fetcher/block_reward.ex @@ -20,8 +20,8 @@ defmodule Indexer.Fetcher.BlockReward do alias Explorer.Chain.Cache.Accounts alias Indexer.{BufferedTask, Tracer} alias Indexer.Fetcher.BlockReward.Supervisor, as: BlockRewardSupervisor - alias Indexer.Fetcher.{CoinBalance, CoinBalanceDaily} - alias Indexer.Transform.{AddressCoinBalances, Addresses} + alias Indexer.Fetcher.CoinBalance + alias Indexer.Transform.{AddressCoinBalances, AddressCoinBalancesDaily, Addresses} @behaviour BufferedTask @@ -135,7 +135,6 @@ defmodule Indexer.Fetcher.BlockReward do Accounts.drop(addresses) CoinBalance.async_fetch_balances(address_coin_balances) - CoinBalanceDaily.async_fetch_balances(address_coin_balances) retry_errors(errors) @@ -246,9 +245,13 @@ defmodule Indexer.Fetcher.BlockReward do addresses_params = Addresses.extract_addresses(%{block_reward_contract_beneficiaries: block_rewards_params}) address_coin_balances_params_set = AddressCoinBalances.params_set(%{beneficiary_params: block_rewards_params}) + address_coin_balances_daily_params_set = + AddressCoinBalancesDaily.params_set(%{beneficiary_params: block_rewards_params}) + Chain.import(%{ addresses: %{params: addresses_params}, address_coin_balances: %{params: address_coin_balances_params_set}, + address_coin_balances_daily: %{params: address_coin_balances_daily_params_set}, block_rewards: %{params: block_rewards_params} }) end diff --git a/apps/indexer/lib/indexer/fetcher/coin_balance.ex b/apps/indexer/lib/indexer/fetcher/coin_balance.ex index 06c4bf6fa3..5212729c2e 100644 --- a/apps/indexer/lib/indexer/fetcher/coin_balance.ex +++ b/apps/indexer/lib/indexer/fetcher/coin_balance.ex @@ -11,7 +11,7 @@ defmodule Indexer.Fetcher.CoinBalance do import EthereumJSONRPC, only: [integer_to_quantity: 1, quantity_to_integer: 1] - alias EthereumJSONRPC.FetchedBalances + alias EthereumJSONRPC.{Blocks, FetchedBalances} alias Explorer.Chain alias Explorer.Chain.{Block, Hash} alias Explorer.Chain.Cache.Accounts @@ -141,11 +141,88 @@ defmodule Indexer.Fetcher.CoinBalance do importable_balances_params = Enum.map(params_list, &Map.put(&1, :value_fetched_at, value_fetched_at)) + json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) + + block_numbers = + params_list + |> Enum.map(&Map.get(&1, :block_number)) + |> Enum.sort() + |> Enum.dedup() + + block_timestamp_map = + Enum.reduce(block_numbers, %{}, fn block_number, map -> + {:ok, %Blocks{blocks_params: [%{timestamp: timestamp}]}} = + EthereumJSONRPC.fetch_blocks_by_range(block_number..block_number, json_rpc_named_arguments) + + day = DateTime.to_date(timestamp) + Map.put(map, "#{block_number}", day) + end) + + importable_balances_daily_params = + params_list + |> Enum.map(fn balance_param -> + day = Map.get(block_timestamp_map, "#{balance_param.block_number}") + + incoming_balance_daily_param = %{ + address_hash: balance_param.address_hash, + day: day, + value: balance_param.value + } + + incoming_balance_daily_param + end) + addresses_params = balances_params_to_address_params(importable_balances_params) Chain.import(%{ addresses: %{params: addresses_params, with: :balance_changeset}, address_coin_balances: %{params: importable_balances_params}, + address_coin_balances_daily: %{params: importable_balances_daily_params}, + broadcast: broadcast_type + }) + end + + def import_fetched_daily_balances(%FetchedBalances{params_list: params_list}, broadcast_type \\ false) do + value_fetched_at = DateTime.utc_now() + + importable_balances_params = Enum.map(params_list, &Map.put(&1, :value_fetched_at, value_fetched_at)) + + json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) + + block_numbers = + params_list + |> Enum.map(&Map.get(&1, :block_number)) + |> Enum.sort() + |> Enum.dedup() + + block_timestamp_map = + Enum.reduce(block_numbers, %{}, fn block_number, map -> + {:ok, %Blocks{blocks_params: [%{timestamp: timestamp}]}} = + EthereumJSONRPC.fetch_blocks_by_range(block_number..block_number, json_rpc_named_arguments) + + day = DateTime.to_date(timestamp) + Map.put(map, "#{block_number}", day) + end) + + importable_balances_daily_params = + params_list + |> Enum.map(fn balance_param -> + day = Map.get(block_timestamp_map, "#{balance_param.block_number}") + + incoming_balance_daily_param = %{ + address_hash: balance_param.address_hash, + day: day, + value: balance_param.value + } + + incoming_balance_daily_param + end) + + addresses_params = balances_params_to_address_params(importable_balances_params) + + Chain.import(%{ + addresses: %{params: addresses_params, with: :balance_changeset}, + address_coin_balances_daily: %{params: importable_balances_daily_params}, broadcast: broadcast_type }) end diff --git a/apps/indexer/lib/indexer/fetcher/coin_balance_daily.ex b/apps/indexer/lib/indexer/fetcher/coin_balance_daily.ex deleted file mode 100644 index f15f599a9b..0000000000 --- a/apps/indexer/lib/indexer/fetcher/coin_balance_daily.ex +++ /dev/null @@ -1,226 +0,0 @@ -defmodule Indexer.Fetcher.CoinBalanceDaily do - @moduledoc """ - Fetches `t:Explorer.Chain.Address.CoinBalanceDaily.t/0`. - """ - - use Indexer.Fetcher - use Spandex.Decorators - - require Logger - - import EthereumJSONRPC, only: [integer_to_quantity: 1, quantity_to_integer: 1] - - alias EthereumJSONRPC.{Blocks, FetchedBalances} - alias Explorer.Chain - alias Explorer.Chain.Cache.Accounts - alias Explorer.Chain.Hash - alias Indexer.{BufferedTask, Tracer} - - @behaviour BufferedTask - - @defaults [ - flush_interval: :timer.seconds(3), - max_batch_size: 500, - max_concurrency: 4, - task_supervisor: Indexer.Fetcher.CoinBalanceDaily.TaskSupervisor, - metadata: [fetcher: :coin_balance_daily] - ] - - @doc """ - Asynchronously fetches balances for each address `hash` at the `day`. - """ - @spec async_fetch_balances([ - %{required(:address_hash) => Hash.Address.t(), required(:day) => Date.t()} - ]) :: :ok - def async_fetch_balances(balance_fields) when is_list(balance_fields) do - entries = Enum.map(balance_fields, &entry/1) - - BufferedTask.buffer(__MODULE__, entries) - end - - @doc false - # credo:disable-for-next-line Credo.Check.Design.DuplicatedCode - def child_spec([init_options, gen_server_options]) do - {state, mergeable_init_options} = Keyword.pop(init_options, :json_rpc_named_arguments) - - unless state do - raise ArgumentError, - ":json_rpc_named_arguments must be provided to `#{__MODULE__}.child_spec " <> - "to allow for json_rpc calls when running." - end - - merged_init_options = - @defaults - |> Keyword.merge(mergeable_init_options) - |> Keyword.put(:state, state) - - Supervisor.child_spec({BufferedTask, [{__MODULE__, merged_init_options}, gen_server_options]}, id: __MODULE__) - end - - @impl BufferedTask - def init(initial, reducer, _) do - {:ok, final} = - Chain.stream_unfetched_balances(initial, fn address_fields, acc -> - address_fields - |> entry() - |> reducer.(acc) - end) - - final - end - - @impl BufferedTask - @decorate trace(name: "fetch", resource: "Indexer.Fetcher.CoinBalanceDaily.run/2", service: :indexer, tracer: Tracer) - def run(entries, json_rpc_named_arguments) do - # the same address may be used more than once in the same block, but we only want one `Balance` for a given - # `{address, block}`, so take unique params only - unique_entries = Enum.uniq(entries) - - unique_filtered_entries = - Enum.filter(unique_entries, fn {_hash, block_number} -> - block_number > first_block_to_index() - end) - - unique_entry_count = Enum.count(unique_filtered_entries) - Logger.metadata(count: unique_entry_count) - - Logger.debug(fn -> "fetching" end) - - unique_filtered_entries - |> Enum.map(&entry_to_params/1) - |> EthereumJSONRPC.fetch_balances(json_rpc_named_arguments) - |> case do - {:ok, fetched_balances} -> - run_fetched_balances(fetched_balances, unique_entries) - - {:error, reason} -> - Logger.error( - fn -> - ["failed to fetch: ", inspect(reason)] - end, - error_count: unique_entry_count - ) - - {:retry, unique_entries} - end - end - - defp first_block_to_index do - string_value = Application.get_env(:indexer, :first_block) - - case Integer.parse(string_value) do - {integer, ""} -> integer - _ -> 0 - end - end - - defp entry_to_params({address_hash_bytes, block_number}) when is_integer(block_number) do - {:ok, address_hash} = Hash.Address.cast(address_hash_bytes) - %{block_quantity: integer_to_quantity(block_number), hash_data: to_string(address_hash)} - end - - defp entry(%{address_hash: %Hash{bytes: address_hash_bytes}, block_number: block_number}) do - {address_hash_bytes, block_number} - end - - def balances_params_to_address_params(balances_params) do - balances_params - |> Enum.group_by(fn %{address_hash: address_hash} -> address_hash end) - |> Map.values() - |> Stream.map(&Enum.max_by(&1, fn %{block_number: block_number} -> block_number end)) - |> Enum.map(fn %{address_hash: address_hash, block_number: block_number, value: value} -> - %{ - hash: address_hash, - fetched_coin_balance_block_number: block_number, - fetched_coin_balance: value - } - end) - end - - def import_fetched_balances(%FetchedBalances{params_list: params_list}, broadcast_type \\ false) do - day = - if Enum.empty?(params_list) do - nil - else - json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) - block_number = Enum.at(params_list, 0)[:block_number] - - {:ok, %Blocks{blocks_params: blocks_params}} = - EthereumJSONRPC.fetch_blocks_by_range(block_number..block_number, json_rpc_named_arguments) - - block_timestamp = Enum.at(blocks_params, 0).timestamp - - DateTime.to_date(block_timestamp) - end - - importable_balances_daily_params = - Enum.map(params_list, fn param -> - param - |> Map.put(:day, day) - end) - - addresses_params = balances_params_to_address_params(importable_balances_daily_params) - - Chain.import(%{ - addresses: %{params: addresses_params, with: :balance_changeset}, - address_coin_balances_daily: %{params: importable_balances_daily_params}, - broadcast: broadcast_type - }) - end - - defp run_fetched_balances(%FetchedBalances{errors: errors} = fetched_balances, _) do - {:ok, imported} = import_fetched_balances(fetched_balances) - - Accounts.drop(imported[:addresses]) - - retry(errors) - end - - defp retry([]), do: :ok - - defp retry(errors) when is_list(errors) do - retried_entries = fetched_balances_errors_to_entries(errors) - - Logger.error( - fn -> - [ - "failed to fetch: ", - fetched_balance_errors_to_iodata(errors) - ] - end, - error_count: Enum.count(retried_entries) - ) - - {:retry, retried_entries} - end - - defp fetched_balances_errors_to_entries(errors) when is_list(errors) do - Enum.map(errors, &fetched_balance_error_to_entry/1) - end - - defp fetched_balance_error_to_entry(%{data: %{block_quantity: block_quantity, day: day, hash_data: hash_data}}) - when is_binary(block_quantity) and is_binary(hash_data) do - {:ok, %Hash{bytes: address_hash_bytes}} = Hash.Address.cast(hash_data) - block_number = quantity_to_integer(block_quantity) - {address_hash_bytes, block_number, day} - end - - defp fetched_balance_errors_to_iodata(errors) when is_list(errors) do - fetched_balance_errors_to_iodata(errors, []) - end - - defp fetched_balance_errors_to_iodata([], iodata), do: iodata - - defp fetched_balance_errors_to_iodata([error | errors], iodata) do - fetched_balance_errors_to_iodata(errors, [iodata | fetched_balance_error_to_iodata(error)]) - end - - defp fetched_balance_error_to_iodata(%{ - code: code, - message: message, - data: %{day: day, hash_data: hash_data} - }) - when is_integer(code) and is_binary(message) and is_binary(day) and is_binary(hash_data) do - [hash_data, "@", day, ": (", to_string(code), ") ", message, ?\n] - end -end diff --git a/apps/indexer/lib/indexer/fetcher/coin_balance_on_demand.ex b/apps/indexer/lib/indexer/fetcher/coin_balance_on_demand.ex index 17014d2ede..3f68f11cb6 100644 --- a/apps/indexer/lib/indexer/fetcher/coin_balance_on_demand.ex +++ b/apps/indexer/lib/indexer/fetcher/coin_balance_on_demand.ex @@ -15,10 +15,10 @@ defmodule Indexer.Fetcher.CoinBalanceOnDemand do import Ecto.Query, only: [from: 2] import EthereumJSONRPC, only: [integer_to_quantity: 1] - alias EthereumJSONRPC.FetchedBalances + alias EthereumJSONRPC.{FetchedBalances} alias Explorer.{Chain, Repo} alias Explorer.Chain.Address - alias Explorer.Chain.Address.CoinBalance + alias Explorer.Chain.Address.{CoinBalance, CoinBalanceDaily} alias Explorer.Chain.Cache.{Accounts, BlockNumber} alias Explorer.Counters.AverageBlockTime alias Indexer.Fetcher.CoinBalance, as: CoinBalanceFetcher @@ -86,6 +86,12 @@ defmodule Indexer.Fetcher.CoinBalanceOnDemand do {:noreply, state} end + def handle_cast({:fetch_and_import_daily_balances, block_number, address}, state) do + fetch_and_import_daily_balances(block_number, address, state.json_rpc_named_arguments) + + {:noreply, state} + end + ## Implementation defp do_trigger_fetch(%Address{fetched_coin_balance_block_number: nil} = address, latest_block_number, _) do @@ -95,6 +101,14 @@ defmodule Indexer.Fetcher.CoinBalanceOnDemand do end defp do_trigger_fetch(address, latest_block_number, stale_balance_window) do + latest_by_day = + from( + cbd in CoinBalanceDaily, + where: cbd.address_hash == ^address.hash, + order_by: [desc: :day], + limit: 1 + ) + latest = from( cb in CoinBalance, @@ -105,15 +119,28 @@ defmodule Indexer.Fetcher.CoinBalanceOnDemand do limit: 1 ) + do_trigger_balance_fetch_query(address, latest_block_number, stale_balance_window, latest, latest_by_day) + end + + defp do_trigger_balance_fetch_query( + address, + latest_block_number, + stale_balance_window, + query_balances, + query_balances_daily + ) do if address.fetched_coin_balance_block_number < stale_balance_window do + do_trigger_balance_daily_fetch_query(address, latest_block_number, query_balances_daily) GenServer.cast(__MODULE__, {:fetch_and_update, latest_block_number, address}) {:stale, latest_block_number} else - case Repo.one(latest) do + case Repo.one(query_balances) do nil -> # There is no recent coin balance to fetch, so we check to see how old the # balance is on the address. If it is too old, we check again, just to be safe. + do_trigger_balance_daily_fetch_query(address, latest_block_number, query_balances_daily) + :current %CoinBalance{value_fetched_at: nil, block_number: block_number} -> @@ -122,11 +149,19 @@ defmodule Indexer.Fetcher.CoinBalanceOnDemand do {:pending, block_number} %CoinBalance{} -> + do_trigger_balance_daily_fetch_query(address, latest_block_number, query_balances_daily) + :current end end end + defp do_trigger_balance_daily_fetch_query(address, latest_block_number, query) do + if Repo.one(query) == nil do + GenServer.cast(__MODULE__, {:fetch_and_import_daily_balances, latest_block_number, address}) + end + end + defp fetch_and_import(block_number, address, json_rpc_named_arguments) do case fetch_balances(block_number, address, json_rpc_named_arguments) do {:ok, fetched_balances} -> do_import(fetched_balances) @@ -134,6 +169,13 @@ defmodule Indexer.Fetcher.CoinBalanceOnDemand do end end + defp fetch_and_import_daily_balances(block_number, address, json_rpc_named_arguments) do + case fetch_balances(block_number, address, json_rpc_named_arguments) do + {:ok, fetched_balances} -> do_import_daily_balances(fetched_balances) + _ -> :ok + end + end + defp fetch_and_update(block_number, address, json_rpc_named_arguments) do case fetch_balances(block_number, address, json_rpc_named_arguments) do {:ok, %{params_list: []}} -> @@ -165,6 +207,13 @@ defmodule Indexer.Fetcher.CoinBalanceOnDemand do end end + defp do_import_daily_balances(%FetchedBalances{} = fetched_balances) do + case CoinBalanceFetcher.import_fetched_daily_balances(fetched_balances, :on_demand) do + {:ok, %{addresses: [address]}} -> {:ok, address} + _ -> :error + end + end + defp latest_block_number do BlockNumber.get_max() end diff --git a/apps/indexer/lib/indexer/supervisor.ex b/apps/indexer/lib/indexer/supervisor.ex index b5f6b96f2e..f22b6de487 100644 --- a/apps/indexer/lib/indexer/supervisor.ex +++ b/apps/indexer/lib/indexer/supervisor.ex @@ -11,7 +11,6 @@ defmodule Indexer.Supervisor do alias Indexer.Fetcher.{ BlockReward, CoinBalance, - CoinBalanceDaily, CoinBalanceOnDemand, ContractCode, InternalTransaction, @@ -110,8 +109,6 @@ defmodule Indexer.Supervisor do [[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]]}, {CoinBalance.Supervisor, [[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]]}, - {CoinBalanceDaily.Supervisor, - [[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]]}, {Token.Supervisor, [[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]]}, {TokenInstance.Supervisor, [[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]]}, diff --git a/apps/indexer/lib/indexer/transform/address_coin_balances_daily.ex b/apps/indexer/lib/indexer/transform/address_coin_balances_daily.ex index de8c9832a5..8382570ebc 100644 --- a/apps/indexer/lib/indexer/transform/address_coin_balances_daily.ex +++ b/apps/indexer/lib/indexer/transform/address_coin_balances_daily.ex @@ -10,18 +10,29 @@ defmodule Indexer.Transform.AddressCoinBalancesDaily do end defp reducer({:beneficiary_params, beneficiary_params}, acc) when is_list(beneficiary_params) do + json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) + + block_numbers = + beneficiary_params + |> Enum.map(&Map.get(&1, :block_number)) + |> Enum.sort() + |> Enum.dedup() + + block_timestamp_map = + Enum.reduce(block_numbers, %{}, fn block_number, map -> + {:ok, %Blocks{blocks_params: [%{timestamp: timestamp}]}} = + EthereumJSONRPC.fetch_blocks_by_range(block_number..block_number, json_rpc_named_arguments) + + day = DateTime.to_date(timestamp) + Map.put(map, "#{block_number}", day) + end) + Enum.into(beneficiary_params, acc, fn %{ address_hash: address_hash, block_number: block_number } when is_binary(address_hash) and is_integer(block_number) -> - json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) - - {:ok, %Blocks{blocks_params: blocks_params}} = - EthereumJSONRPC.fetch_blocks_by_range(block_number..block_number, json_rpc_named_arguments) - - block_timestamp = Enum.at(blocks_params, 0).timestamp - day = DateTime.to_date(block_timestamp) + day = Map.get(block_timestamp_map, "#{block_number}") %{address_hash: address_hash, day: day} end) @@ -43,17 +54,28 @@ defmodule Indexer.Transform.AddressCoinBalancesDaily do defp reducer({:logs_params, logs_params}, acc) when is_list(logs_params) do # a log MUST have address_hash and block_number + json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) + + block_numbers = + logs_params + |> Enum.map(&Map.get(&1, :block_number)) + |> Enum.sort() + |> Enum.dedup() + + block_timestamp_map = + Enum.reduce(block_numbers, %{}, fn block_number, map -> + {:ok, %Blocks{blocks_params: [%{timestamp: timestamp}]}} = + EthereumJSONRPC.fetch_blocks_by_range(block_number..block_number, json_rpc_named_arguments) + + day = DateTime.to_date(timestamp) + Map.put(map, "#{block_number}", day) + end) + logs_params |> Enum.into(acc, fn %{address_hash: address_hash, block_number: block_number} when is_binary(address_hash) and is_integer(block_number) -> - json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) - - {:ok, %Blocks{blocks_params: blocks_params}} = - EthereumJSONRPC.fetch_blocks_by_range(block_number..block_number, json_rpc_named_arguments) - - block_timestamp = Enum.at(blocks_params, 0).timestamp - day = DateTime.to_date(block_timestamp) + day = Map.get(block_timestamp_map, "#{block_number}") %{address_hash: address_hash, day: day} %{type: "pending"} -> @@ -86,10 +108,9 @@ defmodule Indexer.Transform.AddressCoinBalancesDaily do %{type: "create", created_contract_address_hash: address_hash} when is_binary(address_hash) -> json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) - {:ok, %Blocks{blocks_params: blocks_params}} = + {:ok, %Blocks{blocks_params: [%{timestamp: block_timestamp}]}} = EthereumJSONRPC.fetch_blocks_by_range(block_number..block_number, json_rpc_named_arguments) - block_timestamp = Enum.at(blocks_params, 0).timestamp day = DateTime.to_date(block_timestamp) MapSet.put(acc, %{address_hash: address_hash, day: day}) @@ -97,10 +118,9 @@ defmodule Indexer.Transform.AddressCoinBalancesDaily do when is_binary(from_address_hash) and is_binary(to_address_hash) -> json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) - {:ok, %Blocks{blocks_params: blocks_params}} = + {:ok, %Blocks{blocks_params: [%{timestamp: block_timestamp}]}} = EthereumJSONRPC.fetch_blocks_by_range(block_number..block_number, json_rpc_named_arguments) - block_timestamp = Enum.at(blocks_params, 0).timestamp day = DateTime.to_date(block_timestamp) acc @@ -117,10 +137,9 @@ defmodule Indexer.Transform.AddressCoinBalancesDaily do # a transaction MUST have a `from_address_hash` json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) - {:ok, %Blocks{blocks_params: blocks_params}} = + {:ok, %Blocks{blocks_params: [%{timestamp: block_timestamp}]}} = EthereumJSONRPC.fetch_blocks_by_range(block_number..block_number, json_rpc_named_arguments) - block_timestamp = Enum.at(blocks_params, 0).timestamp day = DateTime.to_date(block_timestamp) acc = MapSet.put(initial, %{address_hash: from_address_hash, day: day}) diff --git a/apps/indexer/test/indexer/block/fetcher_test.exs b/apps/indexer/test/indexer/block/fetcher_test.exs index 4bd04dc6dc..f3b1bdd131 100644 --- a/apps/indexer/test/indexer/block/fetcher_test.exs +++ b/apps/indexer/test/indexer/block/fetcher_test.exs @@ -79,6 +79,8 @@ defmodule Indexer.Block.FetcherTest do block_quantity = integer_to_quantity(block_number) miner_hash = "0x0000000000000000000000000000000000000000" + res = eth_block_number_fake_response(block_quantity) + case Keyword.fetch!(json_rpc_named_arguments, :variant) do EthereumJSONRPC.Parity -> EthereumJSONRPC.Mox @@ -137,6 +139,17 @@ defmodule Indexer.Block.FetcherTest do {:ok, [%{id: id, result: []}]} end end) + |> expect(:json_rpc, fn [ + %{ + id: 0, + jsonrpc: "2.0", + method: "eth_getBlockByNumber", + params: [^block_quantity, true] + } + ], + _ -> + {:ok, [res]} + end) EthereumJSONRPC.Geth -> EthereumJSONRPC.Mox @@ -381,10 +394,78 @@ defmodule Indexer.Block.FetcherTest do end) # async requests need to be grouped in one expect because the order is non-deterministic while multiple expect # calls on the same name/arity are used in order - |> expect(:json_rpc, 5, fn json, _options -> + |> expect(:json_rpc, 11, fn json, _options -> [request] = json case request do + %{ + id: 0, + jsonrpc: "2.0", + method: "eth_getBlockByNumber", + params: [^block_quantity, true] + } -> + {:ok, + [ + %{ + id: 0, + jsonrpc: "2.0", + result: %{ + "author" => "0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca", + "difficulty" => "0xfffffffffffffffffffffffffffffffe", + "extraData" => "0xd5830108048650617269747986312e32322e31826c69", + "gasLimit" => "0x69fe20", + "gasUsed" => "0xc512", + "hash" => "0xf6b4b8c88df3ebd252ec476328334dc026cf66606a84fb769b3d3cbccc8471bd", + "logsBloom" => + "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000200000000000000000000020000000000000000200000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner" => "0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca", + "number" => "0x25", + "parentHash" => "0xc37bbad7057945d1bf128c1ff009fb1ad632110bf6a000aac025a80f7766b66e", + "receiptsRoot" => "0xd300311aab7dcc98c05ac3f1893629b2c9082c189a0a0c76f4f63e292ac419d5", + "sealFields" => [ + "0x84120a71de", + "0xb841fcdb570511ec61edda93849bb7c6b3232af60feb2ea74e4035f0143ab66dfdd00f67eb3eda1adddbb6b572db1e0abd39ce00f9b3ccacb9f47973279ff306fe5401" + ], + "sha3Uncles" => "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "signature" => + "fcdb570511ec61edda93849bb7c6b3232af60feb2ea74e4035f0143ab66dfdd00f67eb3eda1adddbb6b572db1e0abd39ce00f9b3ccacb9f47973279ff306fe5401", + "size" => "0x2cf", + "stateRoot" => "0x2cd84079b0d0c267ed387e3895fd1c1dc21ff82717beb1132adac64276886e19", + "step" => "302674398", + "timestamp" => "0x5a343956", + "totalDifficulty" => "0x24ffffffffffffffffffffffffedf78dfd", + "transactions" => [ + %{ + "blockHash" => "0xf6b4b8c88df3ebd252ec476328334dc026cf66606a84fb769b3d3cbccc8471bd", + "blockNumber" => "0x25", + "chainId" => "0x4d", + "condition" => nil, + "creates" => nil, + "from" => from_address_hash, + "gas" => "0x47b760", + "gasPrice" => "0x174876e800", + "hash" => transaction_hash, + "input" => "0x10855269000000000000000000000000862d67cb0773ee3f8ce7ea89b328ffea861ab3ef", + "nonce" => "0x4", + "publicKey" => + "0xe5d196ad4ceada719d9e592f7166d0c75700f6eab2e3c3de34ba751ea786527cb3f6eb96ad9fdfdb9989ff572df50f1c42ef800af9c5207a38b929aff969b5c9", + "r" => "0xa7f8f45cce375bb7af8750416e1b03e0473f93c256da2285d1134fc97a700e01", + "raw" => + "0xf88a0485174876e8008347b760948bf38d4764929064f2d4d3a56520a76ab3df415b80a410855269000000000000000000000000862d67cb0773ee3f8ce7ea89b328ffea861ab3ef81bea0a7f8f45cce375bb7af8750416e1b03e0473f93c256da2285d1134fc97a700e01a01f87a076f13824f4be8963e3dffd7300dae64d5f23c9a062af0c6ead347c135f", + "s" => "0x1f87a076f13824f4be8963e3dffd7300dae64d5f23c9a062af0c6ead347c135f", + "standardV" => "0x1", + "to" => to_address_hash, + "transactionIndex" => "0x0", + "v" => "0xbe", + "value" => "0x0" + } + ], + "transactionsRoot" => "0x68e314a05495f390f9cd0c36267159522e5450d2adf254a74567b452e767bf34", + "uncles" => [] + } + } + ]} + %{id: id, method: "eth_getBalance", params: [^to_address_hash, ^block_quantity]} -> {:ok, [%{id: id, jsonrpc: "2.0", result: "0x1"}]} @@ -662,6 +743,22 @@ defmodule Indexer.Block.FetcherTest do } %{id: id, method: "trace_block"} -> + block_quantity = integer_to_quantity(block_number) + res = eth_block_number_fake_response(block_quantity) + + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn [ + %{ + id: 0, + jsonrpc: "2.0", + method: "eth_getBlockByNumber", + params: [^block_quantity, true] + } + ], + _ -> + {:ok, [res]} + end) + %{ id: id, result: [ @@ -742,4 +839,40 @@ defmodule Indexer.Block.FetcherTest do counts.buffer == 0 and counts.tasks == 0 end) end + + defp eth_block_number_fake_response(block_quantity) do + %{ + id: 0, + jsonrpc: "2.0", + result: %{ + "author" => "0x0000000000000000000000000000000000000000", + "difficulty" => "0x20000", + "extraData" => "0x", + "gasLimit" => "0x663be0", + "gasUsed" => "0x0", + "hash" => "0x5b28c1bfd3a15230c9a46b399cd0f9a6920d432e85381cc6a140b06e8410112f", + "logsBloom" => + "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner" => "0x0000000000000000000000000000000000000000", + "number" => block_quantity, + "parentHash" => "0x0000000000000000000000000000000000000000000000000000000000000000", + "receiptsRoot" => "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "sealFields" => [ + "0x80", + "0xb8410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ], + "sha3Uncles" => "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "signature" => + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "size" => "0x215", + "stateRoot" => "0xfad4af258fd11939fae0c6c6eec9d340b1caac0b0196fd9a1bc3f489c5bf00b3", + "step" => "0", + "timestamp" => "0x0", + "totalDifficulty" => "0x20000", + "transactions" => [], + "transactionsRoot" => "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncles" => [] + } + } + end end diff --git a/apps/indexer/test/indexer/block/realtime/fetcher_test.exs b/apps/indexer/test/indexer/block/realtime/fetcher_test.exs index f222805d26..c4d1e5dc96 100644 --- a/apps/indexer/test/indexer/block/realtime/fetcher_test.exs +++ b/apps/indexer/test/indexer/block/realtime/fetcher_test.exs @@ -205,7 +205,7 @@ defmodule Indexer.Block.Realtime.FetcherTest do } ]} end) - |> expect(:json_rpc, 2, fn + |> expect(:json_rpc, 5, fn [ %{id: 0, jsonrpc: "2.0", method: "trace_block", params: ["0x3C365F"]}, %{id: 1, jsonrpc: "2.0", method: "trace_block", params: ["0x3C3660"]} @@ -217,6 +217,124 @@ defmodule Indexer.Block.Realtime.FetcherTest do %{id: 1, jsonrpc: "2.0", result: []} ]} + [ + %{ + id: 0, + jsonrpc: "2.0", + method: "eth_getBlockByNumber", + params: ["0x3C365F", true] + } + ], + _ -> + {:ok, + [ + %{ + id: 0, + jsonrpc: "2.0", + result: %{ + "author" => "0x5ee341ac44d344ade1ca3a771c59b98eb2a77df2", + "difficulty" => "0xfffffffffffffffffffffffffffffffe", + "extraData" => "0xd583010b088650617269747986312e32372e32826c69", + "gasLimit" => "0x7a1200", + "gasUsed" => "0x2886e", + "hash" => "0xa4ec735cabe1510b5ae081b30f17222580b4588dbec52830529753a688b046cc", + "logsBloom" => + "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner" => "0x5ee341ac44d344ade1ca3a771c59b98eb2a77df2", + "number" => "0x3c365f", + "parentHash" => "0x57f6d66e07488defccd5216c4d2968dd6afd3bd32415e284de3b02af6535e8dc", + "receiptsRoot" => "0x111be72e682cea9c93e02f1ef503fb64aa821b2ef510fd9177c49b37d0af98b5", + "sealFields" => [ + "0x841246c63f", + "0xb841ba3d11db672fd7893d1b7906275fa7c4c7f4fbcc8fa29eab0331480332361516545ef10a36d800ad2be2b449dde8d5703125156a9cf8a035f5a8623463e051b700" + ], + "sha3Uncles" => "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "signature" => + "ba3d11db672fd7893d1b7906275fa7c4c7f4fbcc8fa29eab0331480332361516545ef10a36d800ad2be2b449dde8d5703125156a9cf8a035f5a8623463e051b700", + "size" => "0x33e", + "stateRoot" => "0x7f73f5fb9f891213b671356126c31e9795d038844392c7aa8800ed4f52307209", + "step" => "306628159", + "timestamp" => "0x5b61df3b", + "totalDifficulty" => "0x3c365effffffffffffffffffffffffed7f0362", + "transactions" => [ + %{ + "blockHash" => "0xa4ec735cabe1510b5ae081b30f17222580b4588dbec52830529753a688b046cc", + "blockNumber" => "0x3c365f", + "chainId" => "0x63", + "condition" => nil, + "creates" => nil, + "from" => "0x40b18103537c0f15d5e137dd8ddd019b84949d16", + "gas" => "0x3d9c5", + "gasPrice" => "0x3b9aca00", + "hash" => "0xd3937e70fab3fb2bfe8feefac36815408bf07de3b9e09fe81114b9a6b17f55c8", + "input" => + "0x8841ac11000000000000000000000000000000000000000000000000000000000000006c000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000005", + "nonce" => "0x65b", + "publicKey" => + "0x89c2123ed4b5d141cf1f4b6f5f3d754418f03aea2e870a1c50888d94bf5531f74237e2fea72d0bc198ef213272b62c6869615720757255e6cba087f9db6e759f", + "r" => "0x55a1a93541d7f782f97f6699437bb60fa4606d63760b30c1ee317e648f93995", + "raw" => + "0xf8f582065b843b9aca008303d9c594698bf6943bab687b2756394624aa183f434f65da8901158e4f216242a000b8848841ac11000000000000000000000000000000000000000000000000000000000000006c00000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000581eaa0055a1a93541d7f782f97f6699437bb60fa4606d63760b30c1ee317e648f93995a06affd4da5eca84fbca2b016c980f861e0af1f8d6535e2fe29d8f96dc0ce358f7", + "s" => "0x6affd4da5eca84fbca2b016c980f861e0af1f8d6535e2fe29d8f96dc0ce358f7", + "standardV" => "0x1", + "to" => "0x698bf6943bab687b2756394624aa183f434f65da", + "transactionIndex" => "0x0", + "v" => "0xea", + "value" => "0x1158e4f216242a000" + } + ], + "transactionsRoot" => "0xd7c39a93eafe0bdcbd1324c13dcd674bed8c9fa8adbf8f95bf6a59788985da6f", + "uncles" => ["0xa4ec735cabe1510b5ae081b30f17222580b4588dbec52830529753a688b046cd"] + } + } + ]} + + [ + %{ + id: 0, + jsonrpc: "2.0", + method: "eth_getBlockByNumber", + params: ["0x3C3660", true] + } + ], + _ -> + {:ok, + [ + %{ + id: 0, + jsonrpc: "2.0", + result: %{ + "author" => "0x66c9343c7e8ca673a1fedf9dbf2cd7936dbbf7e3", + "difficulty" => "0xfffffffffffffffffffffffffffffffe", + "extraData" => "0xd583010a068650617269747986312e32362e32826c69", + "gasLimit" => "0x7a1200", + "gasUsed" => "0x0", + "hash" => "0xfb483e511d316fa4072694da3f7abc94b06286406af45061e5e681395bdc6815", + "logsBloom" => + "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner" => "0x66c9343c7e8ca673a1fedf9dbf2cd7936dbbf7e3", + "number" => "0x3c3660", + "parentHash" => "0xa4ec735cabe1510b5ae081b30f17222580b4588dbec52830529753a688b046cc", + "receiptsRoot" => "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "sealFields" => [ + "0x841246c640", + "0xb84114db3fd7526b7ea3635f5c85c30dd8a645453aa2f8afe5fd33fe0ec663c9c7b653b0fb5d8dc7d0b809674fa9dca9887d1636a586bf62191da22255eb068bf20800" + ], + "sha3Uncles" => "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "signature" => + "14db3fd7526b7ea3635f5c85c30dd8a645453aa2f8afe5fd33fe0ec663c9c7b653b0fb5d8dc7d0b809674fa9dca9887d1636a586bf62191da22255eb068bf20800", + "size" => "0x243", + "stateRoot" => "0x3174c461989e9f99e08fa9b4ffb8bce8d9a281c8fc9f80694bb9d3acd4f15559", + "step" => "306628160", + "timestamp" => "0x5b61df40", + "totalDifficulty" => "0x3c365fffffffffffffffffffffffffed7f0360", + "transactions" => [], + "transactionsRoot" => "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncles" => [] + } + } + ]} + [ %{ id: 0, diff --git a/apps/indexer/test/indexer/fetcher/block_reward_test.exs b/apps/indexer/test/indexer/fetcher/block_reward_test.exs index 85d31b6d05..3de7117357 100644 --- a/apps/indexer/test/indexer/fetcher/block_reward_test.exs +++ b/apps/indexer/test/indexer/fetcher/block_reward_test.exs @@ -117,6 +117,21 @@ defmodule Indexer.Fetcher.BlockRewardTest do } end) + res = eth_block_number_fake_response(block_quantity) + + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn [ + %{ + id: 0, + jsonrpc: "2.0", + method: "eth_getBlockByNumber", + params: [^block_quantity, true] + } + ], + _ -> + {:ok, [res]} + end) + assert count(Chain.Block.Reward) == 0 parent = self() @@ -190,6 +205,21 @@ defmodule Indexer.Fetcher.BlockRewardTest do } end) + res = eth_block_number_fake_response(block_quantity) + + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn [ + %{ + id: 0, + jsonrpc: "2.0", + method: "eth_getBlockByNumber", + params: [^block_quantity, true] + } + ], + _ -> + {:ok, [res]} + end) + parent = self() pid = @@ -320,6 +350,21 @@ defmodule Indexer.Fetcher.BlockRewardTest do } end) + res = eth_block_number_fake_response(block_quantity) + + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn [ + %{ + id: 0, + jsonrpc: "2.0", + method: "eth_getBlockByNumber", + params: [^block_quantity, true] + } + ], + _ -> + {:ok, [res]} + end) + assert count(Chain.Block.Reward) == 0 assert count(Chain.Address.CoinBalance) == 0 @@ -408,6 +453,21 @@ defmodule Indexer.Fetcher.BlockRewardTest do } end) + res = eth_block_number_fake_response(block_quantity) + + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn [ + %{ + id: 0, + jsonrpc: "2.0", + method: "eth_getBlockByNumber", + params: [^block_quantity, true] + } + ], + _ -> + {:ok, [res]} + end) + assert count(Chain.Block.Reward) == 0 assert count(Chain.Address.CoinBalance) == 0 @@ -486,6 +546,21 @@ defmodule Indexer.Fetcher.BlockRewardTest do } end) + res = eth_block_number_fake_response(block_quantity) + + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn [ + %{ + id: 0, + jsonrpc: "2.0", + method: "eth_getBlockByNumber", + params: [^block_quantity, true] + } + ], + _ -> + {:ok, [res]} + end) + assert count(Chain.Block.Reward) == 1 assert count(Chain.Address.CoinBalance) == 1 @@ -625,6 +700,21 @@ defmodule Indexer.Fetcher.BlockRewardTest do } end) + res = eth_block_number_fake_response(block_quantity) + + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn [ + %{ + id: 0, + jsonrpc: "2.0", + method: "eth_getBlockByNumber", + params: [^block_quantity, true] + } + ], + _ -> + {:ok, [res]} + end) + assert count(Chain.Block.Reward) == 0 assert count(Chain.Address.CoinBalance) == 0 @@ -684,4 +774,40 @@ defmodule Indexer.Fetcher.BlockRewardTest do do_wait_until(parent, ref, producer) end end + + defp eth_block_number_fake_response(block_quantity) do + %{ + id: 0, + jsonrpc: "2.0", + result: %{ + "author" => "0x0000000000000000000000000000000000000000", + "difficulty" => "0x20000", + "extraData" => "0x", + "gasLimit" => "0x663be0", + "gasUsed" => "0x0", + "hash" => "0x5b28c1bfd3a15230c9a46b399cd0f9a6920d432e85381cc6a140b06e8410112f", + "logsBloom" => + "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner" => "0x0000000000000000000000000000000000000000", + "number" => block_quantity, + "parentHash" => "0x0000000000000000000000000000000000000000000000000000000000000000", + "receiptsRoot" => "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "sealFields" => [ + "0x80", + "0xb8410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ], + "sha3Uncles" => "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "signature" => + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "size" => "0x215", + "stateRoot" => "0xfad4af258fd11939fae0c6c6eec9d340b1caac0b0196fd9a1bc3f489c5bf00b3", + "step" => "0", + "timestamp" => "0x0", + "totalDifficulty" => "0x20000", + "transactions" => [], + "transactionsRoot" => "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncles" => [] + } + } + end end diff --git a/apps/indexer/test/indexer/fetcher/coin_balance_on_demand_test.exs b/apps/indexer/test/indexer/fetcher/coin_balance_on_demand_test.exs index 8f0ae262e4..bab4f54da2 100644 --- a/apps/indexer/test/indexer/fetcher/coin_balance_on_demand_test.exs +++ b/apps/indexer/test/indexer/fetcher/coin_balance_on_demand_test.exs @@ -117,6 +117,21 @@ defmodule Indexer.Fetcher.CoinBalanceOnDemandTest do {:ok, [%{id: id, jsonrpc: "2.0", result: "0x02"}]} end) + res = eth_block_number_fake_response("0x65") + + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn [ + %{ + id: 0, + jsonrpc: "2.0", + method: "eth_getBlockByNumber", + params: ["0x65", true] + } + ], + _ -> + {:ok, [res]} + end) + assert CoinBalanceOnDemand.trigger_fetch(address) == {:stale, 101} {:ok, expected_wei} = Wei.cast(2) @@ -144,6 +159,20 @@ defmodule Indexer.Fetcher.CoinBalanceOnDemandTest do {:ok, [%{id: id, jsonrpc: "2.0", result: "0x02"}]} end) + EthereumJSONRPC.Mox + |> expect(:json_rpc, 1, fn [ + %{ + id: 0, + jsonrpc: "2.0", + method: "eth_getBlockByNumber", + params: ["0x66", true] + } + ], + _ -> + res = eth_block_number_fake_response("0x66") + {:ok, [res]} + end) + assert CoinBalanceOnDemand.trigger_fetch(address) == {:pending, 102} {:ok, expected_wei} = Wei.cast(2) @@ -154,4 +183,40 @@ defmodule Indexer.Fetcher.CoinBalanceOnDemandTest do ) end end + + defp eth_block_number_fake_response(block_quantity) do + %{ + id: 0, + jsonrpc: "2.0", + result: %{ + "author" => "0x0000000000000000000000000000000000000000", + "difficulty" => "0x20000", + "extraData" => "0x", + "gasLimit" => "0x663be0", + "gasUsed" => "0x0", + "hash" => "0x5b28c1bfd3a15230c9a46b399cd0f9a6920d432e85381cc6a140b06e8410112f", + "logsBloom" => + "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner" => "0x0000000000000000000000000000000000000000", + "number" => block_quantity, + "parentHash" => "0x0000000000000000000000000000000000000000000000000000000000000000", + "receiptsRoot" => "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "sealFields" => [ + "0x80", + "0xb8410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ], + "sha3Uncles" => "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "signature" => + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "size" => "0x215", + "stateRoot" => "0xfad4af258fd11939fae0c6c6eec9d340b1caac0b0196fd9a1bc3f489c5bf00b3", + "step" => "0", + "timestamp" => "0x0", + "totalDifficulty" => "0x20000", + "transactions" => [], + "transactionsRoot" => "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncles" => [] + } + } + end end diff --git a/apps/indexer/test/indexer/fetcher/coin_balance_test.exs b/apps/indexer/test/indexer/fetcher/coin_balance_test.exs index 94fbf9f774..e61d54d6f8 100644 --- a/apps/indexer/test/indexer/fetcher/coin_balance_test.exs +++ b/apps/indexer/test/indexer/fetcher/coin_balance_test.exs @@ -58,6 +58,21 @@ defmodule Indexer.Fetcher.CoinBalanceTest do _options -> {:ok, [%{id: id, result: integer_to_quantity(fetched_balance)}]} end) + + res = eth_block_number_fake_response(block_quantity) + + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn [ + %{ + id: 0, + jsonrpc: "2.0", + method: "eth_getBlockByNumber", + params: [^block_quantity, true] + } + ], + _ -> + {:ok, [res]} + end) end {:ok, miner_hash} = Hash.Address.cast(miner_hash_data) @@ -114,6 +129,21 @@ defmodule Indexer.Fetcher.CoinBalanceTest do _options -> {:ok, [%{id: id, result: integer_to_quantity(fetched_balance)}]} end) + + res = eth_block_number_fake_response(block_quantity) + + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn [ + %{ + id: 0, + jsonrpc: "2.0", + method: "eth_getBlockByNumber", + params: [^block_quantity, true] + } + ], + _ -> + {:ok, [res]} + end) end {:ok, miner_hash} = Hash.Address.cast(miner_hash_data) @@ -178,6 +208,21 @@ defmodule Indexer.Fetcher.CoinBalanceTest do _options -> {:ok, [%{id: id, result: integer_to_quantity(fetched_balance)}]} end) + + res = eth_block_number_fake_response(block_quantity) + + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn [ + %{ + id: 0, + jsonrpc: "2.0", + method: "eth_getBlockByNumber", + params: [^block_quantity, true] + } + ], + _ -> + {:ok, [res]} + end) end CoinBalance.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) @@ -254,6 +299,25 @@ defmodule Indexer.Fetcher.CoinBalanceTest do {:ok, %Hash{bytes: address_hash_bytes}} = Hash.Address.cast(hash_data) entries = Enum.map(block_quantities, &{address_hash_bytes, quantity_to_integer(&1)}) + res1 = eth_block_number_fake_response("0x1") + res2 = eth_block_number_fake_response("0x2") + + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn [ + %{id: 0, jsonrpc: "2.0", method: "eth_getBlockByNumber", params: ["0x1", true]} + ], + _ -> + {:ok, [res1]} + end) + + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn [ + %{id: 0, jsonrpc: "2.0", method: "eth_getBlockByNumber", params: ["0x2", true]} + ], + _ -> + {:ok, [res2]} + end) + case CoinBalance.run(entries, json_rpc_named_arguments) do :ok -> balances = Repo.all(from(balance in Address.CoinBalance, where: balance.address_hash == ^hash_data)) @@ -314,16 +378,33 @@ defmodule Indexer.Fetcher.CoinBalanceTest do test "retries none if all imported and no fetch errors", %{json_rpc_named_arguments: json_rpc_named_arguments} do %Hash{bytes: address_hash_bytes} = address_hash() - entries = [{address_hash_bytes, block_number()}] + block_number = block_number() + entries = [{address_hash_bytes, block_number}] expect(EthereumJSONRPC.Mox, :json_rpc, fn [%{id: id, method: "eth_getBalance", params: [_, _]}], _ -> {:ok, [%{id: id, result: "0x1"}]} end) + block_quantity = integer_to_quantity(block_number) + res = eth_block_number_fake_response(block_quantity) + + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn [ + %{ + id: 0, + jsonrpc: "2.0", + method: "eth_getBlockByNumber", + params: [^block_quantity, true] + } + ], + _ -> + {:ok, [res]} + end) + assert :ok = CoinBalance.run(entries, json_rpc_named_arguments) end - test "retries retries fetch errors if all imported", %{json_rpc_named_arguments: json_rpc_named_arguments} do + test "retries fetch errors if all imported", %{json_rpc_named_arguments: json_rpc_named_arguments} do %Hash{bytes: address_hash_bytes} = address_hash() bad_block_number = block_number() good_block_number = block_number() @@ -359,6 +440,22 @@ defmodule Indexer.Fetcher.CoinBalanceTest do {:ok, responses} end) + bad_block_quantity = integer_to_quantity(bad_block_number) + res_bad = eth_block_number_fake_response(bad_block_quantity) + + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn [ + %{ + id: 0, + jsonrpc: "2.0", + method: "eth_getBlockByNumber", + params: [bad_block_quantity, true] + } + ], + [] -> + {:ok, [res_bad]} + end) + assert {:retry, [{^address_hash_bytes, ^bad_block_number}]} = CoinBalance.run( [{address_hash_bytes, good_block_number}, {address_hash_bytes, bad_block_number}], @@ -374,4 +471,40 @@ defmodule Indexer.Fetcher.CoinBalanceTest do Process.sleep(100) wait(producer) end + + defp eth_block_number_fake_response(block_quantity) do + %{ + id: 0, + jsonrpc: "2.0", + result: %{ + "author" => "0x0000000000000000000000000000000000000000", + "difficulty" => "0x20000", + "extraData" => "0x", + "gasLimit" => "0x663be0", + "gasUsed" => "0x0", + "hash" => "0x5b28c1bfd3a15230c9a46b399cd0f9a6920d432e85381cc6a140b06e8410112f", + "logsBloom" => + "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner" => "0x0000000000000000000000000000000000000000", + "number" => block_quantity, + "parentHash" => "0x0000000000000000000000000000000000000000000000000000000000000000", + "receiptsRoot" => "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "sealFields" => [ + "0x80", + "0xb8410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ], + "sha3Uncles" => "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "signature" => + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "size" => "0x215", + "stateRoot" => "0xfad4af258fd11939fae0c6c6eec9d340b1caac0b0196fd9a1bc3f489c5bf00b3", + "step" => "0", + "timestamp" => "0x0", + "totalDifficulty" => "0x20000", + "transactions" => [], + "transactionsRoot" => "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncles" => [] + } + } + end end From 536f78cacc03b3814624442ed576731fbe2fd4ac Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Mon, 1 Jun 2020 21:30:55 +0300 Subject: [PATCH 35/51] Remove orderinng by day in the query --- apps/explorer/lib/explorer/chain/address/coin_balance_daily.ex | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/explorer/lib/explorer/chain/address/coin_balance_daily.ex b/apps/explorer/lib/explorer/chain/address/coin_balance_daily.ex index 4cc68b8970..ca90840771 100644 --- a/apps/explorer/lib/explorer/chain/address/coin_balance_daily.ex +++ b/apps/explorer/lib/explorer/chain/address/coin_balance_daily.ex @@ -53,7 +53,6 @@ defmodule Explorer.Chain.Address.CoinBalanceDaily do CoinBalanceDaily |> where([cbd], cbd.address_hash == ^address_hash) |> limit_time_interval(days_to_consider) - |> order_by([cbd], cbd.day) |> select([cbd], %{date: cbd.day, value: cbd.value}) end From 83156769f6fbc1b109ce6b8aa867607ec7259d0c Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Mon, 1 Jun 2020 22:28:18 +0300 Subject: [PATCH 36/51] Balances by day processing: orrering by day --- apps/explorer/lib/explorer/chain.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index ed300bebc7..ea3ace7278 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -3522,6 +3522,7 @@ defmodule Explorer.Chain do address_hash |> CoinBalanceDaily.balances_by_day() |> Repo.all() + |> Enum.sort(&(&1.date <= &2.date)) |> replace_last_value(latest_block_timestamp) |> normalize_balances_by_day() end From 75145508b18e0d1c99cf778e5999fda423701866 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Mon, 1 Jun 2020 23:16:17 +0300 Subject: [PATCH 37/51] Refine processing of balance by day --- apps/explorer/lib/explorer/chain.ex | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index ea3ace7278..481ae4a730 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -3522,7 +3522,7 @@ defmodule Explorer.Chain do address_hash |> CoinBalanceDaily.balances_by_day() |> Repo.all() - |> Enum.sort(&(&1.date <= &2.date)) + |> Enum.sort_by(fn %{date: d} -> {d.year, d.month, d.day} end) |> replace_last_value(latest_block_timestamp) |> normalize_balances_by_day() end @@ -3537,7 +3537,6 @@ defmodule Explorer.Chain do defp normalize_balances_by_day(balances_by_day) do result = balances_by_day - |> Enum.map(fn day -> Map.take(day, [:date, :value]) end) |> Enum.filter(fn day -> day.value end) |> Enum.map(fn day -> Map.update!(day, :date, &to_string(&1)) end) |> Enum.map(fn day -> Map.update!(day, :value, &Wei.to(&1, :ether)) end) From 4cd5568bb54a51c27bbcdf49eb8f91789c5b14e7 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Tue, 2 Jun 2020 15:02:27 +0300 Subject: [PATCH 38/51] Fix performance of the balance changing history under the chart --- CHANGELOG.md | 2 + .../address_coin_balance_controller.ex | 12 +++- apps/explorer/lib/explorer/chain.ex | 70 ++++++++++++++++--- .../explorer/chain/address/coin_balance.ex | 5 +- apps/explorer/test/explorer/chain_test.exs | 26 ------- 5 files changed, 75 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8dd7817284..22ca5eda24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,10 @@ ## Current ### Features +- [#3125](https://github.com/poanetwork/blockscout/pull/3125) - Availability to configure a number of days to consider at coin balance history chart via environment variable ### Fixes +- [#3140](https://github.com/poanetwork/blockscout/pull/3140) - Fix performance of the balance changing history list loading - [#3133](https://github.com/poanetwork/blockscout/pull/3133) - Take into account FIRST_BLOCK in trace_ReplayBlockTransactions requests - [#3132](https://github.com/poanetwork/blockscout/pull/3132) - Fix performance of coin supply API endpoints - [#3130](https://github.com/poanetwork/blockscout/pull/3130) - Take into account FIRST_BLOCK for block rewards fetching diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_coin_balance_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_coin_balance_controller.ex index 538f8fd7f4..b4e21e3393 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_coin_balance_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_coin_balance_controller.ex @@ -23,6 +23,16 @@ defmodule BlockScoutWeb.AddressCoinBalanceController do {coin_balances, next_page} = split_list_by_page(coin_balances_plus_one) + deduplicated_coin_balances = + coin_balances + |> Enum.dedup_by(fn record -> + if record.delta == Decimal.new(0) do + :dup + else + System.unique_integer() + end + end) + next_page_url = case next_page_params(next_page, coin_balances, params) do nil -> @@ -38,7 +48,7 @@ defmodule BlockScoutWeb.AddressCoinBalanceController do end coin_balances_json = - Enum.map(coin_balances, fn coin_balance -> + Enum.map(deduplicated_coin_balances, fn coin_balance -> View.render_to_string( AddressCoinBalanceView, "_coin_balances.html", diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 481ae4a730..f89e94a365 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -3493,17 +3493,62 @@ defmodule Explorer.Chain do def address_to_coin_balances(address_hash, options) do paging_options = Keyword.get(options, :paging_options, @default_paging_options) - address_hash - |> CoinBalance.fetch_coin_balances(paging_options) - |> page_coin_balances(paging_options) - |> Repo.all() - |> Enum.dedup_by(fn record -> - if record.delta == Decimal.new(0) do - :dup + balances_raw = + address_hash + |> CoinBalance.fetch_coin_balances(paging_options) + |> page_coin_balances(paging_options) + |> Repo.all() + + min_block_number = + balances_raw + |> Enum.min_by(fn balance -> balance.block_number end) + |> Map.get(:block_number) + + max_block_number = + balances_raw + |> Enum.max_by(fn balance -> balance.block_number end) + |> Map.get(:block_number) + + min_block_timestamp = find_block_timestamp(min_block_number) + max_block_timestamp = find_block_timestamp(max_block_number) + + min_block_unix_timestamp = + min_block_timestamp + |> Timex.to_unix() + + max_block_unix_timestamp = + max_block_timestamp + |> Timex.to_unix() + + blocks_delta = max_block_number - min_block_number + + balances_with_dates = + if blocks_delta > 0 do + balances_raw + |> Enum.map(fn balance -> + date = + trunc( + min_block_unix_timestamp + + (balance.block_number - min_block_number) * (max_block_unix_timestamp - min_block_unix_timestamp) / + blocks_delta + ) + + formatted_date = Timex.from_unix(date) + %{balance | block_timestamp: formatted_date} + end) else - System.unique_integer() + balances_raw + |> Enum.map(fn balance -> + date = min_block_unix_timestamp + + formatted_date = Timex.from_unix(date) + %{balance | block_timestamp: formatted_date} + end) end - end) + + balances_with_dates + |> Enum.filter(fn balance -> balance.value end) + |> Enum.sort(fn balance1, balance2 -> balance1.block_timestamp >= balance2.block_timestamp end) end def get_coin_balance(address_hash, block_number) do @@ -4334,4 +4379,11 @@ defmodule Explorer.Chain do block_index end end + + defp find_block_timestamp(number) do + Block + |> where([b], b.number == ^number) + |> select([b], b.timestamp) + |> Repo.one() + end end diff --git a/apps/explorer/lib/explorer/chain/address/coin_balance.ex b/apps/explorer/lib/explorer/chain/address/coin_balance.ex index f3647a8f54..f523ea0023 100644 --- a/apps/explorer/lib/explorer/chain/address/coin_balance.ex +++ b/apps/explorer/lib/explorer/chain/address/coin_balance.ex @@ -76,12 +76,9 @@ defmodule Explorer.Chain.Address.CoinBalance do cb in CoinBalance, where: cb.address_hash == ^address_hash, where: not is_nil(cb.value), - inner_join: b in Block, - on: cb.block_number == b.number, order_by: [desc: :block_number], limit: ^page_size, - select_merge: %{delta: fragment("value - coalesce(lag(value, 1) over (order by block_number), 0)")}, - select_merge: %{block_timestamp: b.timestamp} + select_merge: %{delta: fragment("value - coalesce(lag(value, 1) over (order by block_number), 0)")} ) end diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs index 158e11fe58..76774460c1 100644 --- a/apps/explorer/test/explorer/chain_test.exs +++ b/apps/explorer/test/explorer/chain_test.exs @@ -5025,32 +5025,6 @@ defmodule Explorer.ChainTest do end end - describe "address_to_coin_balances/2" do - test "deduplicates records by zero delta" do - address = insert(:address) - - 1..5 - |> Enum.each(fn block_number -> - insert(:block, number: block_number) - insert(:fetched_balance, value: 1, block_number: block_number, address_hash: address.hash) - end) - - insert(:block, number: 6) - insert(:fetched_balance, value: 2, block_number: 6, address_hash: address.hash) - - assert [first, second, third] = Chain.address_to_coin_balances(address.hash, []) - - assert first.block_number == 6 - assert first.delta == Decimal.new(1) - - assert second.block_number == 5 - assert second.delta == Decimal.new(0) - - assert third.block_number == 1 - assert third.delta == Decimal.new(1) - end - end - describe "extract_db_name/1" do test "extracts correct db name" do db_url = "postgresql://viktor:@localhost:5432/blockscout-dev-1" From b1a71d37c35abb99811e71a26c343b29946d7c3e Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Tue, 2 Jun 2020 16:22:00 +0300 Subject: [PATCH 39/51] Change order in lag function for calculating deltas of coin balance change --- apps/explorer/lib/explorer/chain/address/coin_balance.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/explorer/lib/explorer/chain/address/coin_balance.ex b/apps/explorer/lib/explorer/chain/address/coin_balance.ex index f523ea0023..6c9289e741 100644 --- a/apps/explorer/lib/explorer/chain/address/coin_balance.ex +++ b/apps/explorer/lib/explorer/chain/address/coin_balance.ex @@ -78,7 +78,7 @@ defmodule Explorer.Chain.Address.CoinBalance do where: not is_nil(cb.value), order_by: [desc: :block_number], limit: ^page_size, - select_merge: %{delta: fragment("value - coalesce(lag(value, 1) over (order by block_number), 0)")} + select_merge: %{delta: fragment("coalesce(lag(value, 1) over (order by block_number desc), 0) - value")} ) end From 4abef9d535d006ab6046ec683bac1f5dccefb69f Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Thu, 28 May 2020 16:17:09 +0300 Subject: [PATCH 40/51] RSK hardfork: cumulativeDifficulty --- CHANGELOG.md | 1 + apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 22ca5eda24..1aa1af4fbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ - [#3106](https://github.com/poanetwork/blockscout/pull/3106), [#3115](https://github.com/poanetwork/blockscout/pull/3115) - Fix verification of contracts, created from factory (from internal transaction) ### Chore +- [#3137](https://github.com/poanetwork/blockscout/pull/3137) - RSK Papyrus Release v2.0.1 hardfork: cumulativeDifficulty - [#3134](https://github.com/poanetwork/blockscout/pull/3134) - Get last value of fetched coinsupply API endpoint from DB if cache is empty - [#3124](https://github.com/poanetwork/blockscout/pull/3124) - Display upper border for tx speed if the value cannot be calculated diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex index 20e7db22c8..ce44f792a7 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex @@ -480,7 +480,7 @@ defmodule EthereumJSONRPC.Block do end defp entry_to_elixir({key, quantity}) - when key in ~w(difficulty gasLimit gasUsed minimumGasPrice number size totalDifficulty paidFees) and + when key in ~w(difficulty gasLimit gasUsed minimumGasPrice number size cumulativeDifficulty totalDifficulty paidFees) and not is_nil(quantity) do {key, quantity_to_integer(quantity)} end From 5d65fc4c576c5200b5a7049c073406568fd7e7ad Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Tue, 2 Jun 2020 16:42:36 +0300 Subject: [PATCH 41/51] Replace lag function with lead to calculate coin balance deltas --- apps/explorer/lib/explorer/chain/address/coin_balance.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/explorer/lib/explorer/chain/address/coin_balance.ex b/apps/explorer/lib/explorer/chain/address/coin_balance.ex index 6c9289e741..8e410e993f 100644 --- a/apps/explorer/lib/explorer/chain/address/coin_balance.ex +++ b/apps/explorer/lib/explorer/chain/address/coin_balance.ex @@ -78,7 +78,7 @@ defmodule Explorer.Chain.Address.CoinBalance do where: not is_nil(cb.value), order_by: [desc: :block_number], limit: ^page_size, - select_merge: %{delta: fragment("coalesce(lag(value, 1) over (order by block_number desc), 0) - value")} + select_merge: %{delta: fragment("value - coalesce(lead(value, 1) over (order by block_number desc), 0)")} ) end From a7f0af53e0876e4cf5caef9d449d10b7c5d243aa Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Tue, 2 Jun 2020 21:57:32 +0300 Subject: [PATCH 42/51] Speedup last coin balance timestamp query --- .../lib/explorer/chain/address/coin_balance.ex | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/apps/explorer/lib/explorer/chain/address/coin_balance.ex b/apps/explorer/lib/explorer/chain/address/coin_balance.ex index 8e410e993f..ca693b84a8 100644 --- a/apps/explorer/lib/explorer/chain/address/coin_balance.ex +++ b/apps/explorer/lib/explorer/chain/address/coin_balance.ex @@ -105,11 +105,18 @@ defmodule Explorer.Chain.Address.CoinBalance do end def last_coin_balance_timestamp(address_hash) do - CoinBalance - |> join(:inner, [cb], b in Block, on: cb.block_number == b.number) - |> where([cb], cb.address_hash == ^address_hash) - |> last(:block_number) - |> select([cb, b], %{timestamp: b.timestamp, value: cb.value}) + coin_balance_query = + CoinBalance + |> where([cb], cb.address_hash == ^address_hash) + |> last(:block_number) + |> select([cb, b], %{block_number: cb.block_number, value: cb.value}) + + from( + cb in subquery(coin_balance_query), + inner_join: b in Block, + on: cb.block_number == b.number, + select: %{timestamp: b.timestamp, value: cb.value} + ) end def changeset(%__MODULE__{} = balance, params) do From ac37938e7dff37a12f8219daf3109ec8198aa15d Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Tue, 2 Jun 2020 21:57:32 +0300 Subject: [PATCH 43/51] Speedup last coin balance timestamp query --- CHANGELOG.md | 1 + .../lib/explorer/chain/address/coin_balance.ex | 17 ++++++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1aa1af4fbb..c222d86c10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - [#3125](https://github.com/poanetwork/blockscout/pull/3125) - Availability to configure a number of days to consider at coin balance history chart via environment variable ### Fixes +- [#3142](https://github.com/poanetwork/blockscout/pull/3142) - Speed-up last coin balance timestamp query (coin balance history page performance improvement) - [#3140](https://github.com/poanetwork/blockscout/pull/3140) - Fix performance of the balance changing history list loading - [#3133](https://github.com/poanetwork/blockscout/pull/3133) - Take into account FIRST_BLOCK in trace_ReplayBlockTransactions requests - [#3132](https://github.com/poanetwork/blockscout/pull/3132) - Fix performance of coin supply API endpoints diff --git a/apps/explorer/lib/explorer/chain/address/coin_balance.ex b/apps/explorer/lib/explorer/chain/address/coin_balance.ex index 8e410e993f..ca693b84a8 100644 --- a/apps/explorer/lib/explorer/chain/address/coin_balance.ex +++ b/apps/explorer/lib/explorer/chain/address/coin_balance.ex @@ -105,11 +105,18 @@ defmodule Explorer.Chain.Address.CoinBalance do end def last_coin_balance_timestamp(address_hash) do - CoinBalance - |> join(:inner, [cb], b in Block, on: cb.block_number == b.number) - |> where([cb], cb.address_hash == ^address_hash) - |> last(:block_number) - |> select([cb, b], %{timestamp: b.timestamp, value: cb.value}) + coin_balance_query = + CoinBalance + |> where([cb], cb.address_hash == ^address_hash) + |> last(:block_number) + |> select([cb, b], %{block_number: cb.block_number, value: cb.value}) + + from( + cb in subquery(coin_balance_query), + inner_join: b in Block, + on: cb.block_number == b.number, + select: %{timestamp: b.timestamp, value: cb.value} + ) end def changeset(%__MODULE__{} = balance, params) do From 42896e766114bd3ec0a3c2a37302759e0319ee11 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Thu, 4 Jun 2020 22:12:47 +0300 Subject: [PATCH 44/51] Fix coin balance history page if no balance changes --- .../address_coin_balance_controller.ex | 12 +-- apps/explorer/lib/explorer/chain.ex | 99 ++++++++++--------- .../explorer/chain/address/coin_balance.ex | 48 ++++++--- 3 files changed, 90 insertions(+), 69 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_coin_balance_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_coin_balance_controller.ex index b4e21e3393..538f8fd7f4 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_coin_balance_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_coin_balance_controller.ex @@ -23,16 +23,6 @@ defmodule BlockScoutWeb.AddressCoinBalanceController do {coin_balances, next_page} = split_list_by_page(coin_balances_plus_one) - deduplicated_coin_balances = - coin_balances - |> Enum.dedup_by(fn record -> - if record.delta == Decimal.new(0) do - :dup - else - System.unique_integer() - end - end) - next_page_url = case next_page_params(next_page, coin_balances, params) do nil -> @@ -48,7 +38,7 @@ defmodule BlockScoutWeb.AddressCoinBalanceController do end coin_balances_json = - Enum.map(deduplicated_coin_balances, fn coin_balance -> + Enum.map(coin_balances, fn coin_balance -> View.render_to_string( AddressCoinBalanceView, "_coin_balances.html", diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index f89e94a365..9fc94e6af0 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -3499,56 +3499,63 @@ defmodule Explorer.Chain do |> page_coin_balances(paging_options) |> Repo.all() - min_block_number = + if Enum.empty?(balances_raw) do balances_raw - |> Enum.min_by(fn balance -> balance.block_number end) - |> Map.get(:block_number) - - max_block_number = - balances_raw - |> Enum.max_by(fn balance -> balance.block_number end) - |> Map.get(:block_number) - - min_block_timestamp = find_block_timestamp(min_block_number) - max_block_timestamp = find_block_timestamp(max_block_number) - - min_block_unix_timestamp = - min_block_timestamp - |> Timex.to_unix() - - max_block_unix_timestamp = - max_block_timestamp - |> Timex.to_unix() - - blocks_delta = max_block_number - min_block_number - - balances_with_dates = - if blocks_delta > 0 do - balances_raw - |> Enum.map(fn balance -> - date = - trunc( - min_block_unix_timestamp + - (balance.block_number - min_block_number) * (max_block_unix_timestamp - min_block_unix_timestamp) / - blocks_delta - ) - - formatted_date = Timex.from_unix(date) - %{balance | block_timestamp: formatted_date} - end) - else + else + balances_raw_filtered = balances_raw - |> Enum.map(fn balance -> - date = min_block_unix_timestamp + |> Enum.filter(fn balance -> balance.value end) + + min_block_number = + balances_raw_filtered + |> Enum.min_by(fn balance -> balance.block_number end, fn -> %{} end) + |> Map.get(:block_number) + + max_block_number = + balances_raw_filtered + |> Enum.max_by(fn balance -> balance.block_number end, fn -> %{} end) + |> Map.get(:block_number) + + min_block_timestamp = find_block_timestamp(min_block_number) + max_block_timestamp = find_block_timestamp(max_block_number) + + min_block_unix_timestamp = + min_block_timestamp + |> Timex.to_unix() + + max_block_unix_timestamp = + max_block_timestamp + |> Timex.to_unix() + + blocks_delta = max_block_number - min_block_number + + balances_with_dates = + if blocks_delta > 0 do + balances_raw_filtered + |> Enum.map(fn balance -> + date = + trunc( + min_block_unix_timestamp + + (balance.block_number - min_block_number) * (max_block_unix_timestamp - min_block_unix_timestamp) / + blocks_delta + ) + + formatted_date = Timex.from_unix(date) + %{balance | block_timestamp: formatted_date} + end) + else + balances_raw_filtered + |> Enum.map(fn balance -> + date = min_block_unix_timestamp - formatted_date = Timex.from_unix(date) - %{balance | block_timestamp: formatted_date} - end) - end + formatted_date = Timex.from_unix(date) + %{balance | block_timestamp: formatted_date} + end) + end - balances_with_dates - |> Enum.filter(fn balance -> balance.value end) - |> Enum.sort(fn balance1, balance2 -> balance1.block_timestamp >= balance2.block_timestamp end) + balances_with_dates + |> Enum.sort(fn balance1, balance2 -> balance1.block_number >= balance2.block_number end) + end end def get_coin_balance(address_hash, block_number) do diff --git a/apps/explorer/lib/explorer/chain/address/coin_balance.ex b/apps/explorer/lib/explorer/chain/address/coin_balance.ex index ca693b84a8..cada366173 100644 --- a/apps/explorer/lib/explorer/chain/address/coin_balance.ex +++ b/apps/explorer/lib/explorer/chain/address/coin_balance.ex @@ -72,13 +72,18 @@ defmodule Explorer.Chain.Address.CoinBalance do The last coin balance from an Address is the last block indexed. """ def fetch_coin_balances(address_hash, %PagingOptions{page_size: page_size}) do - from( - cb in CoinBalance, - where: cb.address_hash == ^address_hash, - where: not is_nil(cb.value), - order_by: [desc: :block_number], - limit: ^page_size, - select_merge: %{delta: fragment("value - coalesce(lead(value, 1) over (order by block_number desc), 0)")} + query = + from( + cb in CoinBalance, + where: cb.address_hash == ^address_hash, + where: not is_nil(cb.value), + order_by: [desc: :block_number], + select_merge: %{delta: fragment("value - coalesce(lead(value, 1) over (order by block_number desc), 0)")} + ) + + from(balance in subquery(query), + where: balance.delta != 0, + limit: ^page_size ) end @@ -87,21 +92,40 @@ defmodule Explorer.Chain.Address.CoinBalance do corresponds to the maximum balance in that day. Only the last 90 days of data are used. """ def balances_by_day(address_hash, block_timestamp \\ nil) do + {days_to_consider, _} = + Application.get_env(:block_scout_web, BlockScoutWeb.Chain.Address.CoinBalance)[:coin_balance_history_days] + |> Integer.parse() + CoinBalance |> join(:inner, [cb], b in Block, on: cb.block_number == b.number) |> where([cb], cb.address_hash == ^address_hash) - |> limit_time_interval(block_timestamp) + |> limit_time_interval(days_to_consider, block_timestamp) |> group_by([cb, b], fragment("date_trunc('day', ?)", b.timestamp)) |> order_by([cb, b], fragment("date_trunc('day', ?)", b.timestamp)) |> select([cb, b], %{date: type(fragment("date_trunc('day', ?)", b.timestamp), :date), value: max(cb.value)}) end - def limit_time_interval(query, nil) do - query |> where([cb, b], b.timestamp >= fragment("date_trunc('day', now()) - interval '90 days'")) + def limit_time_interval(query, days_to_consider, nil) do + query + |> where( + [cb, b], + b.timestamp >= + fragment("date_trunc('day', now() - CAST(? AS INTERVAL))", ^%Postgrex.Interval{days: days_to_consider}) + ) end - def limit_time_interval(query, %{timestamp: timestamp}) do - query |> where([cb, b], b.timestamp >= fragment("(? AT TIME ZONE ?) - interval '90 days'", ^timestamp, ^"Etc/UTC")) + def limit_time_interval(query, days_to_consider, %{timestamp: timestamp}) do + query + |> where( + [cb, b], + b.timestamp >= + fragment( + "(? AT TIME ZONE ?) - CAST(? AS INTERVAL)", + ^timestamp, + ^"Etc/UTC", + ^%Postgrex.Interval{days: days_to_consider} + ) + ) end def last_coin_balance_timestamp(address_hash) do From dd60c427b0902cb771eebf6f080328e2c5f4430f Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Fri, 5 Jun 2020 16:33:22 +0300 Subject: [PATCH 45/51] Add CHANGELOG entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c222d86c10..65627e9e12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - [#3125](https://github.com/poanetwork/blockscout/pull/3125) - Availability to configure a number of days to consider at coin balance history chart via environment variable ### Fixes +- [#3146](https://github.com/poanetwork/blockscout/pull/3146) - Fix coin balance history page: order of items, fix if no balance changes - [#3142](https://github.com/poanetwork/blockscout/pull/3142) - Speed-up last coin balance timestamp query (coin balance history page performance improvement) - [#3140](https://github.com/poanetwork/blockscout/pull/3140) - Fix performance of the balance changing history list loading - [#3133](https://github.com/poanetwork/blockscout/pull/3133) - Take into account FIRST_BLOCK in trace_ReplayBlockTransactions requests From a2e667f5cc13c9a9a36b083cf132a0b6c98b41f8 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Fri, 5 Jun 2020 17:01:49 +0300 Subject: [PATCH 46/51] Release 3.1.3 --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65627e9e12..7cebfb1c7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ ## Current +### Features + +### Fixes + +### Chore + + +## 3.1.3-beta + ### Features - [#3125](https://github.com/poanetwork/blockscout/pull/3125) - Availability to configure a number of days to consider at coin balance history chart via environment variable From 0404df18b907dbe25d347ae906c10da3f6677cc0 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Mon, 8 Jun 2020 13:30:06 +0300 Subject: [PATCH 47/51] Display and store revert reason on demand --- CHANGELOG.md | 1 + .../api/rpc/transaction_controller.ex | 19 ++++- .../lib/block_scout_web/etherscan.ex | 12 ++- .../templates/transaction/overview.html.eex | 8 ++ .../views/api/rpc/transaction_view.ex | 1 + .../block_scout_web/views/transaction_view.ex | 4 + apps/block_scout_web/priv/gettext/default.pot | 79 ++++++++++--------- .../priv/gettext/en/LC_MESSAGES/default.po | 79 ++++++++++--------- .../lib/ethereum_jsonrpc/transaction.ex | 16 +++- apps/explorer/lib/explorer/chain.ex | 67 ++++++++++++++++ .../lib/explorer/chain/transaction.ex | 8 +- apps/explorer/lib/explorer/etherscan.ex | 1 + ...22_alter_transactions_add_error_reason.exs | 9 +++ 13 files changed, 223 insertions(+), 81 deletions(-) create mode 100644 apps/explorer/priv/repo/migrations/20200608075122_alter_transactions_add_error_reason.exs diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cebfb1c7f..2e35153b36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## Current ### Features +- [#3149](https://github.com/poanetwork/blockscout/pull/3149) - Display and store revert reason of tx on demand at transaction details page and at gettxinfo API endpoint. ### Fixes diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/transaction_controller.ex index 565ea4b6a5..c109714009 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/transaction_controller.ex @@ -5,16 +5,31 @@ defmodule BlockScoutWeb.API.RPC.TransactionController do alias Explorer.Chain + alias Explorer.Chain.Transaction + def gettxinfo(conn, params) do with {:txhash_param, {:ok, txhash_param}} <- fetch_txhash(params), {:format, {:ok, transaction_hash}} <- to_transaction_hash(txhash_param), - {:transaction, {:ok, transaction}} <- transaction_from_hash(transaction_hash), + {:transaction, {:ok, %Transaction{revert_reason: revert_reason, error: error} = transaction}} <- + transaction_from_hash(transaction_hash), paging_options <- paging_options(params) do logs = Chain.transaction_to_logs(transaction_hash, paging_options) {logs, next_page} = split_list_by_page(logs) + transaction_updated = + if error == "Reverted" do + if String.strip(revert_reason) == "" do + revert_reason_from_node = Chain.fetch_tx_revert_reason(transaction) + %Transaction{transaction | revert_reason: revert_reason_from_node} + else + transaction + end + else + transaction + end + render(conn, :gettxinfo, %{ - transaction: transaction, + transaction: transaction_updated, block_height: Chain.block_height(), logs: logs, next_page_params: next_page_params(next_page, logs, params) diff --git a/apps/block_scout_web/lib/block_scout_web/etherscan.ex b/apps/block_scout_web/lib/block_scout_web/etherscan.ex index 019795dc69..f419f05eee 100644 --- a/apps/block_scout_web/lib/block_scout_web/etherscan.ex +++ b/apps/block_scout_web/lib/block_scout_web/etherscan.ex @@ -470,7 +470,8 @@ defmodule BlockScoutWeb.Etherscan do "success" => true, "timeStamp" => "1541018182", "to" => "0x000000000000000000000000000000000000000d", - "value" => "67612" + "value" => "67612", + revertReason: "No credit of that type" } } @@ -609,6 +610,12 @@ defmodule BlockScoutWeb.Etherscan do example: ~s("18") } + @revert_reason_type %{ + type: "revert_reason", + definition: "Revert reason of transaction.", + example: ~s("No credit of that type") + } + @logs_details %{ name: "Log Detail", fields: %{ @@ -1010,7 +1017,8 @@ defmodule BlockScoutWeb.Etherscan do logs: %{ type: "array", array_type: @logs_details - } + }, + revertReason: @revert_reason_type } } diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex index cca53f0865..5ee1fac85e 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex @@ -53,6 +53,14 @@
+ <%= if status == {:error, "Reverted"} do %> +
+
<%= gettext "Revert reason" %>
+
+ <%= BlockScoutWeb.TransactionView.transaction_revert_reason(@transaction) %> +
+
+ <% end %>
<%= gettext "Block Number" %>
diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/transaction_view.ex index b9e8f7430d..4b81b11091 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/transaction_view.ex @@ -70,6 +70,7 @@ defmodule BlockScoutWeb.API.RPC.TransactionView do "gasUsed" => "#{transaction.gas_used}", "gasPrice" => "#{transaction.gas_price.value}", "logs" => Enum.map(logs, &prepare_log/1), + "revertReason" => "#{transaction.revert_reason}", "next_page_params" => next_page_params } end diff --git a/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex index 1c01ab80a3..c128083916 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex @@ -237,6 +237,10 @@ defmodule BlockScoutWeb.TransactionView do Chain.transaction_to_status(transaction) end + def transaction_revert_reason(transaction) do + Chain.transaction_to_revert_reason(transaction) + end + def empty_exchange_rate?(exchange_rate) do Token.null?(exchange_rate) end diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot index b4edc9e73c..9157634e2c 100644 --- a/apps/block_scout_web/priv/gettext/default.pot +++ b/apps/block_scout_web/priv/gettext/default.pot @@ -13,7 +13,7 @@ msgstr[0] "" msgstr[1] "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:196 +#: lib/block_scout_web/templates/transaction/overview.html.eex:204 msgid " Token Transfer" msgstr "" @@ -59,7 +59,7 @@ msgid "%{subnetwork} Explorer - BlockScout" msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:247 +#: lib/block_scout_web/views/transaction_view.ex:251 msgid "(Awaiting internal transactions for status)" msgstr "" @@ -193,7 +193,7 @@ msgid "Block %{block_number} - %{subnetwork} Explorer" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:72 +#: lib/block_scout_web/templates/transaction/overview.html.eex:80 msgid "Block Confirmations" msgstr "" @@ -213,7 +213,7 @@ msgid "Block Mined, awaiting import..." msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:58 +#: lib/block_scout_web/templates/transaction/overview.html.eex:66 msgid "Block Number" msgstr "" @@ -355,12 +355,12 @@ msgid "Contract Byte Code" msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:336 +#: lib/block_scout_web/views/transaction_view.ex:340 msgid "Contract Call" msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:333 +#: lib/block_scout_web/views/transaction_view.ex:337 msgid "Contract Creation" msgstr "" @@ -621,12 +621,12 @@ msgid "Error trying to fetch balances." msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:251 +#: lib/block_scout_web/views/transaction_view.ex:255 msgid "Error: %{reason}" msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:249 +#: lib/block_scout_web/views/transaction_view.ex:253 msgid "Error: (Awaiting internal transactions for reason)" msgstr "" @@ -646,8 +646,8 @@ msgstr "" #: lib/block_scout_web/templates/layout/app.html.eex:32 #: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:20 #: lib/block_scout_web/templates/transaction/_tile.html.eex:29 -#: lib/block_scout_web/templates/transaction/overview.html.eex:181 -#: lib/block_scout_web/templates/transaction/overview.html.eex:255 +#: lib/block_scout_web/templates/transaction/overview.html.eex:189 +#: lib/block_scout_web/templates/transaction/overview.html.eex:263 #: lib/block_scout_web/views/wei_helpers.ex:78 msgid "Ether" msgstr "" @@ -666,7 +666,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/block/overview.html.eex:73 -#: lib/block_scout_web/templates/transaction/overview.html.eex:79 +#: lib/block_scout_web/templates/transaction/overview.html.eex:87 msgid "Nonce" msgstr "" @@ -753,8 +753,8 @@ msgid "Hash" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:115 -#: lib/block_scout_web/templates/transaction/overview.html.eex:119 +#: lib/block_scout_web/templates/transaction/overview.html.eex:123 +#: lib/block_scout_web/templates/transaction/overview.html.eex:127 msgid "Hex (Default)" msgstr "" @@ -776,7 +776,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/transaction/_emission_reward_tile.html.eex:8 -#: lib/block_scout_web/views/transaction_view.ex:248 +#: lib/block_scout_web/views/transaction_view.ex:252 msgid "Success" msgstr "" @@ -789,7 +789,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:21 #: lib/block_scout_web/templates/transaction/_tile.html.eex:32 -#: lib/block_scout_web/templates/transaction/overview.html.eex:84 +#: lib/block_scout_web/templates/transaction/overview.html.eex:92 msgid "TX Fee" msgstr "" @@ -891,7 +891,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:12 #: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:10 -#: lib/block_scout_web/views/transaction_view.ex:329 +#: lib/block_scout_web/views/transaction_view.ex:333 msgid "Token Transfer" msgstr "" @@ -906,7 +906,7 @@ msgstr "" #: lib/block_scout_web/views/address_view.ex:325 #: lib/block_scout_web/views/tokens/instance/overview_view.ex:98 #: lib/block_scout_web/views/tokens/overview_view.ex:35 -#: lib/block_scout_web/views/transaction_view.ex:390 +#: lib/block_scout_web/views/transaction_view.ex:394 msgid "Token Transfers" msgstr "" @@ -922,7 +922,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address_logs/_logs.html.eex:18 -#: lib/block_scout_web/views/transaction_view.ex:339 +#: lib/block_scout_web/views/transaction_view.ex:343 msgid "Transaction" msgstr "" @@ -1005,7 +1005,7 @@ msgid "License ID" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:283 +#: lib/block_scout_web/templates/transaction/overview.html.eex:291 msgid "Limit" msgstr "" @@ -1159,8 +1159,8 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/layout/_topnav.html.eex:59 -#: lib/block_scout_web/views/transaction_view.ex:246 -#: lib/block_scout_web/views/transaction_view.ex:280 +#: lib/block_scout_web/views/transaction_view.ex:250 +#: lib/block_scout_web/views/transaction_view.ex:284 msgid "Pending" msgstr "" @@ -1201,14 +1201,14 @@ msgid "RPC" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:109 +#: lib/block_scout_web/templates/transaction/overview.html.eex:117 msgid "Raw Input" msgstr "" #, elixir-format #: lib/block_scout_web/templates/transaction/_tabs.html.eex:24 #: lib/block_scout_web/templates/transaction_raw_trace/index.html.eex:7 -#: lib/block_scout_web/views/transaction_view.ex:393 +#: lib/block_scout_web/views/transaction_view.ex:397 msgid "Raw Trace" msgstr "" @@ -1488,7 +1488,7 @@ msgid "Transaction Inputs" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:101 +#: lib/block_scout_web/templates/transaction/overview.html.eex:109 msgid "Transaction Speed" msgstr "" @@ -1518,7 +1518,7 @@ msgid "Type" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:122 +#: lib/block_scout_web/templates/transaction/overview.html.eex:130 msgid "UTF-8" msgstr "" @@ -1544,7 +1544,7 @@ msgid "Use the search box to find a hosted network, or select from the list of a msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:277 +#: lib/block_scout_web/templates/transaction/overview.html.eex:285 msgid "Used" msgstr "" @@ -1574,8 +1574,8 @@ msgid "Validator Info" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:181 -#: lib/block_scout_web/templates/transaction/overview.html.eex:255 +#: lib/block_scout_web/templates/transaction/overview.html.eex:189 +#: lib/block_scout_web/templates/transaction/overview.html.eex:263 msgid "Value" msgstr "" @@ -1752,7 +1752,7 @@ msgid "Decimals" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:273 +#: lib/block_scout_web/templates/transaction/overview.html.eex:281 msgid "Gas" msgstr "" @@ -1799,8 +1799,8 @@ msgid "Transfers" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:134 -#: lib/block_scout_web/templates/transaction/overview.html.eex:147 +#: lib/block_scout_web/templates/transaction/overview.html.eex:142 +#: lib/block_scout_web/templates/transaction/overview.html.eex:155 msgid "Copy Txn Input" msgstr "" @@ -1838,7 +1838,7 @@ msgstr "" #: lib/block_scout_web/templates/transaction/_tabs.html.eex:11 #: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:6 #: lib/block_scout_web/views/address_view.ex:324 -#: lib/block_scout_web/views/transaction_view.ex:391 +#: lib/block_scout_web/views/transaction_view.ex:395 msgid "Internal Transactions" msgstr "" @@ -1848,7 +1848,7 @@ msgstr "" #: lib/block_scout_web/templates/transaction/_tabs.html.eex:17 #: lib/block_scout_web/templates/transaction_log/index.html.eex:8 #: lib/block_scout_web/views/address_view.ex:331 -#: lib/block_scout_web/views/transaction_view.ex:392 +#: lib/block_scout_web/views/transaction_view.ex:396 msgid "Logs" msgstr "" @@ -1881,26 +1881,26 @@ msgid "Transactions" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:232 +#: lib/block_scout_web/templates/transaction/overview.html.eex:240 msgid " Token Burning" msgstr "" #, elixir-format #: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:8 #: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:6 -#: lib/block_scout_web/views/transaction_view.ex:328 +#: lib/block_scout_web/views/transaction_view.ex:332 msgid "Token Burning" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:214 +#: lib/block_scout_web/templates/transaction/overview.html.eex:222 msgid " Token Minting" msgstr "" #, elixir-format #: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:10 #: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:8 -#: lib/block_scout_web/views/transaction_view.ex:327 +#: lib/block_scout_web/views/transaction_view.ex:331 msgid "Token Minting" msgstr "" @@ -1908,3 +1908,8 @@ msgstr "" #: lib/block_scout_web/views/block_view.ex:62 msgid "Chore Reward" msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/transaction/overview.html.eex:58 +msgid "Revert reason" +msgstr "" diff --git a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po index b4edc9e73c..9157634e2c 100644 --- a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po +++ b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po @@ -13,7 +13,7 @@ msgstr[0] "" msgstr[1] "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:196 +#: lib/block_scout_web/templates/transaction/overview.html.eex:204 msgid " Token Transfer" msgstr "" @@ -59,7 +59,7 @@ msgid "%{subnetwork} Explorer - BlockScout" msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:247 +#: lib/block_scout_web/views/transaction_view.ex:251 msgid "(Awaiting internal transactions for status)" msgstr "" @@ -193,7 +193,7 @@ msgid "Block %{block_number} - %{subnetwork} Explorer" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:72 +#: lib/block_scout_web/templates/transaction/overview.html.eex:80 msgid "Block Confirmations" msgstr "" @@ -213,7 +213,7 @@ msgid "Block Mined, awaiting import..." msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:58 +#: lib/block_scout_web/templates/transaction/overview.html.eex:66 msgid "Block Number" msgstr "" @@ -355,12 +355,12 @@ msgid "Contract Byte Code" msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:336 +#: lib/block_scout_web/views/transaction_view.ex:340 msgid "Contract Call" msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:333 +#: lib/block_scout_web/views/transaction_view.ex:337 msgid "Contract Creation" msgstr "" @@ -621,12 +621,12 @@ msgid "Error trying to fetch balances." msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:251 +#: lib/block_scout_web/views/transaction_view.ex:255 msgid "Error: %{reason}" msgstr "" #, elixir-format -#: lib/block_scout_web/views/transaction_view.ex:249 +#: lib/block_scout_web/views/transaction_view.ex:253 msgid "Error: (Awaiting internal transactions for reason)" msgstr "" @@ -646,8 +646,8 @@ msgstr "" #: lib/block_scout_web/templates/layout/app.html.eex:32 #: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:20 #: lib/block_scout_web/templates/transaction/_tile.html.eex:29 -#: lib/block_scout_web/templates/transaction/overview.html.eex:181 -#: lib/block_scout_web/templates/transaction/overview.html.eex:255 +#: lib/block_scout_web/templates/transaction/overview.html.eex:189 +#: lib/block_scout_web/templates/transaction/overview.html.eex:263 #: lib/block_scout_web/views/wei_helpers.ex:78 msgid "Ether" msgstr "" @@ -666,7 +666,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/block/overview.html.eex:73 -#: lib/block_scout_web/templates/transaction/overview.html.eex:79 +#: lib/block_scout_web/templates/transaction/overview.html.eex:87 msgid "Nonce" msgstr "" @@ -753,8 +753,8 @@ msgid "Hash" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:115 -#: lib/block_scout_web/templates/transaction/overview.html.eex:119 +#: lib/block_scout_web/templates/transaction/overview.html.eex:123 +#: lib/block_scout_web/templates/transaction/overview.html.eex:127 msgid "Hex (Default)" msgstr "" @@ -776,7 +776,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/transaction/_emission_reward_tile.html.eex:8 -#: lib/block_scout_web/views/transaction_view.ex:248 +#: lib/block_scout_web/views/transaction_view.ex:252 msgid "Success" msgstr "" @@ -789,7 +789,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:21 #: lib/block_scout_web/templates/transaction/_tile.html.eex:32 -#: lib/block_scout_web/templates/transaction/overview.html.eex:84 +#: lib/block_scout_web/templates/transaction/overview.html.eex:92 msgid "TX Fee" msgstr "" @@ -891,7 +891,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:12 #: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:10 -#: lib/block_scout_web/views/transaction_view.ex:329 +#: lib/block_scout_web/views/transaction_view.ex:333 msgid "Token Transfer" msgstr "" @@ -906,7 +906,7 @@ msgstr "" #: lib/block_scout_web/views/address_view.ex:325 #: lib/block_scout_web/views/tokens/instance/overview_view.ex:98 #: lib/block_scout_web/views/tokens/overview_view.ex:35 -#: lib/block_scout_web/views/transaction_view.ex:390 +#: lib/block_scout_web/views/transaction_view.ex:394 msgid "Token Transfers" msgstr "" @@ -922,7 +922,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address_logs/_logs.html.eex:18 -#: lib/block_scout_web/views/transaction_view.ex:339 +#: lib/block_scout_web/views/transaction_view.ex:343 msgid "Transaction" msgstr "" @@ -1005,7 +1005,7 @@ msgid "License ID" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:283 +#: lib/block_scout_web/templates/transaction/overview.html.eex:291 msgid "Limit" msgstr "" @@ -1159,8 +1159,8 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/layout/_topnav.html.eex:59 -#: lib/block_scout_web/views/transaction_view.ex:246 -#: lib/block_scout_web/views/transaction_view.ex:280 +#: lib/block_scout_web/views/transaction_view.ex:250 +#: lib/block_scout_web/views/transaction_view.ex:284 msgid "Pending" msgstr "" @@ -1201,14 +1201,14 @@ msgid "RPC" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:109 +#: lib/block_scout_web/templates/transaction/overview.html.eex:117 msgid "Raw Input" msgstr "" #, elixir-format #: lib/block_scout_web/templates/transaction/_tabs.html.eex:24 #: lib/block_scout_web/templates/transaction_raw_trace/index.html.eex:7 -#: lib/block_scout_web/views/transaction_view.ex:393 +#: lib/block_scout_web/views/transaction_view.ex:397 msgid "Raw Trace" msgstr "" @@ -1488,7 +1488,7 @@ msgid "Transaction Inputs" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:101 +#: lib/block_scout_web/templates/transaction/overview.html.eex:109 msgid "Transaction Speed" msgstr "" @@ -1518,7 +1518,7 @@ msgid "Type" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:122 +#: lib/block_scout_web/templates/transaction/overview.html.eex:130 msgid "UTF-8" msgstr "" @@ -1544,7 +1544,7 @@ msgid "Use the search box to find a hosted network, or select from the list of a msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:277 +#: lib/block_scout_web/templates/transaction/overview.html.eex:285 msgid "Used" msgstr "" @@ -1574,8 +1574,8 @@ msgid "Validator Info" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:181 -#: lib/block_scout_web/templates/transaction/overview.html.eex:255 +#: lib/block_scout_web/templates/transaction/overview.html.eex:189 +#: lib/block_scout_web/templates/transaction/overview.html.eex:263 msgid "Value" msgstr "" @@ -1752,7 +1752,7 @@ msgid "Decimals" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:273 +#: lib/block_scout_web/templates/transaction/overview.html.eex:281 msgid "Gas" msgstr "" @@ -1799,8 +1799,8 @@ msgid "Transfers" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:134 -#: lib/block_scout_web/templates/transaction/overview.html.eex:147 +#: lib/block_scout_web/templates/transaction/overview.html.eex:142 +#: lib/block_scout_web/templates/transaction/overview.html.eex:155 msgid "Copy Txn Input" msgstr "" @@ -1838,7 +1838,7 @@ msgstr "" #: lib/block_scout_web/templates/transaction/_tabs.html.eex:11 #: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:6 #: lib/block_scout_web/views/address_view.ex:324 -#: lib/block_scout_web/views/transaction_view.ex:391 +#: lib/block_scout_web/views/transaction_view.ex:395 msgid "Internal Transactions" msgstr "" @@ -1848,7 +1848,7 @@ msgstr "" #: lib/block_scout_web/templates/transaction/_tabs.html.eex:17 #: lib/block_scout_web/templates/transaction_log/index.html.eex:8 #: lib/block_scout_web/views/address_view.ex:331 -#: lib/block_scout_web/views/transaction_view.ex:392 +#: lib/block_scout_web/views/transaction_view.ex:396 msgid "Logs" msgstr "" @@ -1881,26 +1881,26 @@ msgid "Transactions" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:232 +#: lib/block_scout_web/templates/transaction/overview.html.eex:240 msgid " Token Burning" msgstr "" #, elixir-format #: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:8 #: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:6 -#: lib/block_scout_web/views/transaction_view.ex:328 +#: lib/block_scout_web/views/transaction_view.ex:332 msgid "Token Burning" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/overview.html.eex:214 +#: lib/block_scout_web/templates/transaction/overview.html.eex:222 msgid " Token Minting" msgstr "" #, elixir-format #: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:10 #: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:8 -#: lib/block_scout_web/views/transaction_view.ex:327 +#: lib/block_scout_web/views/transaction_view.ex:331 msgid "Token Minting" msgstr "" @@ -1908,3 +1908,8 @@ msgstr "" #: lib/block_scout_web/views/block_view.ex:62 msgid "Chore Reward" msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/transaction/overview.html.eex:58 +msgid "Revert reason" +msgstr "" diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex index 031b150384..54d812c4c4 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex @@ -9,7 +9,7 @@ defmodule EthereumJSONRPC.Transaction do """ require Logger - import EthereumJSONRPC, only: [quantity_to_integer: 1] + import EthereumJSONRPC, only: [quantity_to_integer: 1, integer_to_quantity: 1, request: 1] alias EthereumJSONRPC @@ -313,6 +313,20 @@ defmodule EthereumJSONRPC.Transaction do nil end + def eth_call_request(id, block_number, data, to, from, gas, gas_price, value) do + block = + case block_number do + nil -> "latest" + block_number -> integer_to_quantity(block_number) + end + + request(%{ + id: id, + method: "eth_call", + params: [%{to: to, from: from, data: data, gas: gas, gas_price: gas_price, value: value}, block] + }) + end + # double check that no new keys are being missed by requiring explicit match for passthrough # `t:EthereumJSONRPC.address/0` and `t:EthereumJSONRPC.hash/0` pass through as `Explorer.Chain` can verify correct # hash format diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 9fc94e6af0..93f655a190 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -28,6 +28,8 @@ defmodule Explorer.Chain do alias Ecto.Adapters.SQL alias Ecto.{Changeset, Multi} + alias EthereumJSONRPC.Transaction, as: EthereumJSONRPCTransaction + alias Explorer.Counters.LastFetchedCounter alias Explorer.Chain @@ -2715,6 +2717,71 @@ defmodule Explorer.Chain do def transaction_to_status(%Transaction{status: :error, error: error}) when is_binary(error), do: {:error, error} + def transaction_to_revert_reason(%Transaction{revert_reason: revert_reason} = transaction) do + revert_reason = + if revert_reason == nil do + fetch_tx_revert_reason(transaction) + else + revert_reason + end + + revert_reason + end + + def fetch_tx_revert_reason( + %Transaction{ + block_number: block_number, + to_address_hash: to_address_hash, + from_address_hash: from_address_hash, + input: data, + gas: gas, + gas_price: gas_price, + value: value + } = transaction + ) do + json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) + + req = + EthereumJSONRPCTransaction.eth_call_request( + 0, + block_number, + data, + to_address_hash, + from_address_hash, + gas, + Wei.hex_format(gas_price), + Wei.hex_format(value) + ) + + data = + case EthereumJSONRPC.json_rpc(req, json_rpc_named_arguments) do + {:error, %{data: data}} -> + data + + _ -> + nil + end + + if data == nil do + nil + else + revert_reason_parts = String.split(data, "revert: ") + + formatted_revert_reason = + if Enum.count(revert_reason_parts) > 1 do + Enum.at(revert_reason_parts, 1) + else + data + end + + transaction + |> Changeset.change(%{revert_reason: formatted_revert_reason}) + |> Repo.update() + + formatted_revert_reason + end + end + @doc """ The `t:Explorer.Chain.Transaction.t/0` or `t:Explorer.Chain.InternalTransaction.t/0` `value` of the `transaction` in `unit`. diff --git a/apps/explorer/lib/explorer/chain/transaction.ex b/apps/explorer/lib/explorer/chain/transaction.ex index 66127868c6..9d60d58edd 100644 --- a/apps/explorer/lib/explorer/chain/transaction.ex +++ b/apps/explorer/lib/explorer/chain/transaction.ex @@ -30,7 +30,7 @@ defmodule Explorer.Chain.Transaction do @optional_attrs ~w(block_hash block_number created_contract_address_hash cumulative_gas_used earliest_processing_start error gas_used index created_contract_code_indexed_at status - to_address_hash)a + to_address_hash revert_reason)a @required_attrs ~w(from_address_hash gas gas_price hash input nonce r s v value)a @@ -106,6 +106,7 @@ defmodule Explorer.Chain.Transaction do * `internal_transactions` - transactions (value transfers) created while executing contract used for this transaction * `created_contract_code_indexed_at` - when created `address` code was fetched by `Indexer` + * `revert_reason` - revert reason of transaction | `status` | `contract_creation_address_hash` | `input` | Token Transfer? | `internal_transactions_indexed_at` | `internal_transactions` | Description | |----------|----------------------------------|------------|-----------------|-------------------------------------------|-------------------------|-----------------------------------------------------------------------------------------------------| @@ -129,6 +130,7 @@ defmodule Explorer.Chain.Transaction do * `uncles` - uncle blocks where `forks` were collated * `v` - The V field of the signature. * `value` - wei transferred from `from_address` to `to_address` + * `revert_reason` - revert reason of transaction """ @type t :: %__MODULE__{ block: %Ecto.Association.NotLoaded{} | Block.t() | nil, @@ -159,7 +161,8 @@ defmodule Explorer.Chain.Transaction do to_address_hash: Hash.Address.t() | nil, uncles: %Ecto.Association.NotLoaded{} | [Block.t()], v: v(), - value: Wei.t() + value: Wei.t(), + revert_reason: String.t() } @derive {Poison.Encoder, @@ -199,6 +202,7 @@ defmodule Explorer.Chain.Transaction do field(:status, Status) field(:v, :decimal) field(:value, Wei) + field(:revert_reason, :string) # A transient field for deriving old block hash during transaction upserts. # Used to force refetch of a block in case a transaction is re-collated diff --git a/apps/explorer/lib/explorer/etherscan.ex b/apps/explorer/lib/explorer/etherscan.ex index 1caf9f7e64..82fd5a45f9 100644 --- a/apps/explorer/lib/explorer/etherscan.ex +++ b/apps/explorer/lib/explorer/etherscan.ex @@ -332,6 +332,7 @@ defmodule Explorer.Etherscan do status to_address_hash value + revert_reason )a defp list_transactions(address_hash, max_block_number, options) do diff --git a/apps/explorer/priv/repo/migrations/20200608075122_alter_transactions_add_error_reason.exs b/apps/explorer/priv/repo/migrations/20200608075122_alter_transactions_add_error_reason.exs new file mode 100644 index 0000000000..6f0ab6320e --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20200608075122_alter_transactions_add_error_reason.exs @@ -0,0 +1,9 @@ +defmodule Explorer.Repo.Migrations.AlterTransactionsAddErrorReason do + use Ecto.Migration + + def change do + alter table(:transactions) do + add(:revert_reason, :text) + end + end +end From 964d1a2b464be986ab4dce37e3e3d7a9c04747ed Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Mon, 8 Jun 2020 15:44:30 +0300 Subject: [PATCH 48/51] Update test --- .../controllers/api/rpc/transaction_controller_test.exs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/transaction_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/transaction_controller_test.exs index 2b3f6fff5e..5c5dd11f27 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/transaction_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/transaction_controller_test.exs @@ -520,7 +520,8 @@ defmodule BlockScoutWeb.API.RPC.TransactionControllerTest do "index" => "#{log.index}" } ], - "next_page_params" => nil + "next_page_params" => nil, + "revertReason" => "" } schema = From 27964a5e3d32de771d4a19f7895471191ca1b8e1 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Tue, 9 Jun 2020 14:14:34 +0300 Subject: [PATCH 49/51] Add unit tests --- .dialyzer-ignore | 4 +- .../api/rpc/transaction_controller.ex | 5 +- .../api/rpc/transaction_controller_test.exs | 180 ++++++++++++++++++ apps/explorer/lib/explorer/chain.ex | 41 ++-- apps/explorer/test/explorer/chain_test.exs | 28 +++ .../indexer/fetcher/coin_balance_test.exs | 8 +- 6 files changed, 236 insertions(+), 30 deletions(-) diff --git a/.dialyzer-ignore b/.dialyzer-ignore index 1535f582c8..79833b82e3 100644 --- a/.dialyzer-ignore +++ b/.dialyzer-ignore @@ -12,4 +12,6 @@ apps/explorer/lib/explorer/smart_contract/publisher_worker.ex:6: The pattern 'fa apps/explorer/lib/explorer/smart_contract/publisher_worker.ex:6: The test 5 == 'infinity' can never evaluate to 'true' lib/block_scout_web/router.ex:1 lib/phoenix/router.ex:324 -lib/block_scout_web/views/layout_view.ex:143 \ No newline at end of file +lib/block_scout_web/views/layout_view.ex:143 +lib/block_scout_web/controllers/api/rpc/transaction_controller.ex:21 +lib/block_scout_web/controllers/api/rpc/transaction_controller.ex:22 \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/transaction_controller.ex index c109714009..4c7abad3fe 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/transaction_controller.ex @@ -18,9 +18,8 @@ defmodule BlockScoutWeb.API.RPC.TransactionController do transaction_updated = if error == "Reverted" do - if String.strip(revert_reason) == "" do - revert_reason_from_node = Chain.fetch_tx_revert_reason(transaction) - %Transaction{transaction | revert_reason: revert_reason_from_node} + if revert_reason == nil do + %Transaction{transaction | revert_reason: Chain.fetch_tx_revert_reason(transaction)} else transaction end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/transaction_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/transaction_controller_test.exs index 5c5dd11f27..d46071d215 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/transaction_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/transaction_controller_test.exs @@ -1,6 +1,8 @@ defmodule BlockScoutWeb.API.RPC.TransactionControllerTest do use BlockScoutWeb.ConnCase + import Mox + @moduletag capture_log: true describe "gettxreceiptstatus" do @@ -577,6 +579,184 @@ defmodule BlockScoutWeb.API.RPC.TransactionControllerTest do assert response["status"] == "1" assert response["message"] == "OK" end + + test "with a txhash with revert reason from DB", %{conn: conn} do + block = insert(:block, number: 100) + + transaction = + :transaction + |> insert(revert_reason: "No credit of that type") + |> with_block(block) + + insert(:address) + + params = %{ + "module" => "transaction", + "action" => "gettxinfo", + "txhash" => "#{transaction.hash}" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"]["revertReason"] == "No credit of that type" + assert response["status"] == "1" + assert response["message"] == "OK" + end + + test "with a txhash with empty revert reason from DB", %{conn: conn} do + block = insert(:block, number: 100) + + transaction = + :transaction + |> insert(revert_reason: "") + |> with_block(block) + + insert(:address) + + params = %{ + "module" => "transaction", + "action" => "gettxinfo", + "txhash" => "#{transaction.hash}" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"]["revertReason"] == "" + assert response["status"] == "1" + assert response["message"] == "OK" + end + + test "with a txhash with revert reason from the archive node", %{conn: conn} do + block = insert(:block, number: 100, hash: "0x3e51328bccedee581e8ba35190216a61a5d67fd91ca528f3553142c0c7d18391") + + transaction = + :transaction + |> insert( + error: "Reverted", + status: :error, + block_hash: block.hash, + block_number: block.number, + cumulative_gas_used: 884_322, + gas_used: 106_025, + index: 0, + hash: "0xac2a7dab94d965893199e7ee01649e2d66f0787a4c558b3118c09e80d4df8269" + ) + + insert(:address) + + expect( + EthereumJSONRPC.Mox, + :json_rpc, + fn _json, [] -> + {:error, %{code: -32015, message: "VM execution error.", data: "revert: No credit of that type"}} + end + ) + + params = %{ + "module" => "transaction", + "action" => "gettxinfo", + "txhash" => "#{transaction.hash}" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"]["revertReason"] == "No credit of that type" + assert response["status"] == "1" + assert response["message"] == "OK" + end + end + + test "with a txhash with empty revert reason from the archive node", %{conn: conn} do + block = insert(:block, number: 100, hash: "0x3e51328bccedee581e8ba35190216a61a5d67fd91ca528f3553142c0c7d18391") + + transaction = + :transaction + |> insert( + error: "Reverted", + status: :error, + block_hash: block.hash, + block_number: block.number, + cumulative_gas_used: 884_322, + gas_used: 106_025, + index: 0, + hash: "0xac2a7dab94d965893199e7ee01649e2d66f0787a4c558b3118c09e80d4df8269" + ) + + insert(:address) + + expect( + EthereumJSONRPC.Mox, + :json_rpc, + fn _json, [] -> + {:error, %{code: -32015, message: "VM execution error.", data: ""}} + end + ) + + params = %{ + "module" => "transaction", + "action" => "gettxinfo", + "txhash" => "#{transaction.hash}" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"]["revertReason"] == "" + assert response["status"] == "1" + assert response["message"] == "OK" + end + + test "with a txhash with empty revert reason from DB if eth_call doesn't return an error", %{conn: conn} do + block = insert(:block, number: 100, hash: "0x3e51328bccedee581e8ba35190216a61a5d67fd91ca528f3553142c0c7d18391") + + transaction = + :transaction + |> insert( + error: "Reverted", + status: :error, + block_hash: block.hash, + block_number: block.number, + cumulative_gas_used: 884_322, + gas_used: 106_025, + index: 0, + hash: "0xac2a7dab94d965893199e7ee01649e2d66f0787a4c558b3118c09e80d4df8269" + ) + + insert(:address) + + expect( + EthereumJSONRPC.Mox, + :json_rpc, + fn _json, [] -> + {:ok} + end + ) + + params = %{ + "module" => "transaction", + "action" => "gettxinfo", + "txhash" => "#{transaction.hash}" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"]["revertReason"] == "" + assert response["status"] == "1" + assert response["message"] == "OK" end defp resolve_schema(result \\ %{}) do diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 93f655a190..bd520b4e70 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -2717,15 +2717,14 @@ defmodule Explorer.Chain do def transaction_to_status(%Transaction{status: :error, error: error}) when is_binary(error), do: {:error, error} - def transaction_to_revert_reason(%Transaction{revert_reason: revert_reason} = transaction) do - revert_reason = - if revert_reason == nil do - fetch_tx_revert_reason(transaction) - else - revert_reason - end + def transaction_to_revert_reason(transaction) do + %Transaction{revert_reason: revert_reason} = transaction - revert_reason + if revert_reason == nil do + fetch_tx_revert_reason(transaction) + else + revert_reason + end end def fetch_tx_revert_reason( @@ -2748,7 +2747,7 @@ defmodule Explorer.Chain do data, to_address_hash, from_address_hash, - gas, + Decimal.to_integer(gas), Wei.hex_format(gas_price), Wei.hex_format(value) ) @@ -2759,27 +2758,25 @@ defmodule Explorer.Chain do data _ -> - nil + "" end - if data == nil do - nil - else - revert_reason_parts = String.split(data, "revert: ") + revert_reason_parts = String.split(data, "revert: ") - formatted_revert_reason = - if Enum.count(revert_reason_parts) > 1 do - Enum.at(revert_reason_parts, 1) - else - data - end + formatted_revert_reason = + if Enum.count(revert_reason_parts) > 1 do + Enum.at(revert_reason_parts, 1) + else + data + end + if byte_size(formatted_revert_reason) > 0 do transaction |> Changeset.change(%{revert_reason: formatted_revert_reason}) |> Repo.update() - - formatted_revert_reason end + + formatted_revert_reason end @doc """ diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs index 76774460c1..ed6d37aaa4 100644 --- a/apps/explorer/test/explorer/chain_test.exs +++ b/apps/explorer/test/explorer/chain_test.exs @@ -5165,4 +5165,32 @@ defmodule Explorer.ChainTest do } end end + + describe "transaction_to_revert_reason/1" do + test "returns correct revert_reason from DB" do + transaction = insert(:transaction, revert_reason: "No credit of that type") + assert Chain.transaction_to_revert_reason(transaction) == "No credit of that type" + end + + test "returns correct revert_reason from the archive node" do + transaction = + insert(:transaction, + gas: 27319, + gas_price: "0x1b31d2900", + value: "0x86b3", + input: %Explorer.Chain.Data{bytes: <<1>>} + ) + |> with_block(insert(:block, number: 1)) + + expect( + EthereumJSONRPC.Mox, + :json_rpc, + fn _json, [] -> + {:error, %{code: -32015, message: "VM execution error.", data: "revert: No credit of that type"}} + end + ) + + assert Chain.transaction_to_revert_reason(transaction) == "No credit of that type" + end + end end diff --git a/apps/indexer/test/indexer/fetcher/coin_balance_test.exs b/apps/indexer/test/indexer/fetcher/coin_balance_test.exs index e61d54d6f8..12acf46a09 100644 --- a/apps/indexer/test/indexer/fetcher/coin_balance_test.exs +++ b/apps/indexer/test/indexer/fetcher/coin_balance_test.exs @@ -440,8 +440,8 @@ defmodule Indexer.Fetcher.CoinBalanceTest do {:ok, responses} end) - bad_block_quantity = integer_to_quantity(bad_block_number) - res_bad = eth_block_number_fake_response(bad_block_quantity) + good_block_quantity = integer_to_quantity(good_block_number) + res_good = eth_block_number_fake_response(good_block_quantity) EthereumJSONRPC.Mox |> expect(:json_rpc, fn [ @@ -449,11 +449,11 @@ defmodule Indexer.Fetcher.CoinBalanceTest do id: 0, jsonrpc: "2.0", method: "eth_getBlockByNumber", - params: [bad_block_quantity, true] + params: [^good_block_quantity, true] } ], [] -> - {:ok, [res_bad]} + {:ok, [res_good]} end) assert {:retry, [{^address_hash_bytes, ^bad_block_number}]} = From 7b079ee10e1279390afcc501ceb2bbcf520ef58d Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Fri, 12 Jun 2020 10:51:39 +0300 Subject: [PATCH 50/51] Fix contract compilation tests for old versions of compiler: change compiler version since nightly versions of some older versions were removed --- .../explorer/smart_contract/solidity/code_compiler_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs b/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs index 270684c8d1..8d092d66bf 100644 --- a/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs +++ b/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs @@ -166,7 +166,7 @@ defmodule Explorer.SmartContract.Solidity.CodeCompilerTest do } """ - version = "v0.1.3-nightly.2015.9.25+commit.4457170" + version = "v0.1.3+commit.028f561d" response = CodeCompiler.run(name: name, compiler_version: version, code: code, optimize: optimize) From 8e262ddd27da795be8d088aa8b8ebaad798c017b Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Fri, 12 Jun 2020 10:54:12 +0300 Subject: [PATCH 51/51] Add CHANGELOG entry --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e35153b36..5371ca264f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ ### Chore +- [#3152](https://github.com/poanetwork/blockscout/pull/3152) - Fix contract compilation tests for old versions of compiler + ## 3.1.3-beta