Merge branch 'master' into ab-fix-reorg-uncles-pagination

pull/1905/head
Ayrat Badykov 6 years ago committed by GitHub
commit b036c23d63
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      CHANGELOG.md
  2. 3
      apps/explorer/config/config.exs
  3. 4
      apps/explorer/lib/explorer/chain/block_count_cache.ex
  4. 24
      apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex
  5. 37
      apps/explorer/lib/explorer/smart_contract/verifier.ex
  6. 25
      apps/explorer/lib/explorer/smart_contract/verifier/constructor_arguments.ex
  7. 4
      apps/explorer/priv/compile_solc.js
  8. 95
      apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs
  9. 14
      apps/explorer/test/explorer/smart_contract/verifier/constructor_arguments_test.exs
  10. 43
      apps/explorer/test/explorer/smart_contract/verifier_test.exs
  11. 3
      apps/indexer/README.md
  12. 10
      apps/indexer/lib/indexer/supervisor.ex
  13. 147
      apps/indexer/lib/indexer/temporary/addresses_without_code.ex
  14. 132
      apps/indexer/lib/indexer/temporary/failed_created_addresses.ex
  15. 389
      apps/indexer/test/indexer/temporary/addresses_without_code_test.exs
  16. 78
      apps/indexer/test/indexer/temporary/failed_created_addresses_test.exs

@ -26,11 +26,15 @@
- [#1896](https://github.com/poanetwork/blockscout/pull/1896) - re-query tokens in top nav automplete - [#1896](https://github.com/poanetwork/blockscout/pull/1896) - re-query tokens in top nav automplete
- [#1881](https://github.com/poanetwork/blockscout/pull/1881) - fix: store solc versions locally for performance - [#1881](https://github.com/poanetwork/blockscout/pull/1881) - fix: store solc versions locally for performance
- [#1905](https://github.com/poanetwork/blockscout/pull/1905) - fix reorgs, uncles pagination - [#1905](https://github.com/poanetwork/blockscout/pull/1905) - fix reorgs, uncles pagination
- [#1875](https://github.com/poanetwork/blockscout/pull/1875) - fix: resolve false positive constructor arguments
- [#1904](https://github.com/poanetwork/blockscout/pull/1904) - fix `BLOCK_COUNT_CACHE_TTL` env var type
- [#1898](https://github.com/poanetwork/blockscout/pull/1898) - check if the constructor has arguments before verifying constructor arguments
### Chore ### Chore
- [#1814](https://github.com/poanetwork/blockscout/pull/1814) - Clear build artefacts script - [#1814](https://github.com/poanetwork/blockscout/pull/1814) - Clear build artefacts script
- [#1837](https://github.com/poanetwork/blockscout/pull/1837) - Add -f flag to clear_build.sh script delete static folder - [#1837](https://github.com/poanetwork/blockscout/pull/1837) - Add -f flag to clear_build.sh script delete static folder
- [#1892](https://github.com/poanetwork/blockscout/pull/1892) - Remove temporary worker modules
## 1.3.10-beta ## 1.3.10-beta

@ -82,7 +82,8 @@ config :spandex_ecto, SpandexEcto.EctoLogger,
tracer: Explorer.Tracer, tracer: Explorer.Tracer,
otp_app: :explorer otp_app: :explorer
config :explorer, Explorer.Chain.BlockCountCache, ttl: System.get_env("BLOCK_COUNT_CACHE_TTL") config :explorer, Explorer.Chain.BlockCountCache,
ttl: System.get_env("BLOCK_COUNT_CACHE_TTL") && String.to_integer(System.get_env("BLOCK_COUNT_CACHE_TTL"))
# Import environment specific config. This must remain at the bottom # Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above. # of this file so it overrides the configuration defined above.

@ -6,8 +6,8 @@ defmodule Explorer.Chain.BlockCountCache do
alias Explorer.Chain alias Explorer.Chain
@tab :block_count_cache @tab :block_count_cache
# 1 minutes # 10 minutes
@cache_period 1_000 * 60 @cache_period 1_000 * 60 * 10
@key "count" @key "count"
@opts_key "opts" @opts_key "opts"

@ -5,6 +5,8 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do
alias Explorer.SmartContract.SolcDownloader alias Explorer.SmartContract.SolcDownloader
require Logger
@new_contract_name "New.sol" @new_contract_name "New.sol"
@allowed_evm_versions ["homestead", "tangerineWhistle", "spuriousDragon", "byzantium", "constantinople", "petersburg"] @allowed_evm_versions ["homestead", "tangerineWhistle", "spuriousDragon", "byzantium", "constantinople", "petersburg"]
@ -63,6 +65,7 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do
} }
} }
""" """
@spec run(Keyword.t()) :: {:ok, map} | {:error, :compilation | :name}
def run(params) do def run(params) do
name = Keyword.fetch!(params, :name) name = Keyword.fetch!(params, :name)
compiler_version = Keyword.fetch!(params, :compiler_version) compiler_version = Keyword.fetch!(params, :compiler_version)
@ -100,7 +103,8 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do
] ]
) )
with {:ok, contracts} <- Jason.decode(response), with {:ok, decoded} <- Jason.decode(response),
{:ok, contracts} <- get_contracts(decoded),
%{"abi" => abi, "evm" => %{"deployedBytecode" => %{"object" => bytecode}}} <- %{"abi" => abi, "evm" => %{"deployedBytecode" => %{"object" => bytecode}}} <-
get_contract_info(contracts, name) do get_contract_info(contracts, name) do
{:ok, %{"abi" => abi, "bytecode" => bytecode, "name" => name}} {:ok, %{"abi" => abi, "bytecode" => bytecode, "name" => name}}
@ -108,9 +112,16 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do
{:error, %Jason.DecodeError{}} -> {:error, %Jason.DecodeError{}} ->
{:error, :compilation} {:error, :compilation}
{:error, reason} when reason in [:name, :compilation] ->
{:error, reason}
error -> error ->
parse_error(error) error = parse_error(error)
Logger.warn(["There was an error compiling a provided contract: ", inspect(error)])
{:error, :compilation}
end end
else
{:error, :compilation}
end end
end end
@ -133,10 +144,15 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do
end end
end end
def parse_error(%{"error" => error}), do: {:error, [error]} def parse_error({:error, %{"error" => error}}), do: {:error, [error]}
def parse_error(%{"errors" => errors}), do: {:error, errors} def parse_error({:error, %{"errors" => errors}}), do: {:error, errors}
def parse_error({:error, _} = error), do: error def parse_error({:error, _} = error), do: error
# Older solc-bin versions don't use filename as contract key
defp get_contracts(%{"contracts" => %{"New.sol" => contracts}}), do: {:ok, contracts}
defp get_contracts(%{"contracts" => %{"" => contracts}}), do: {:ok, contracts}
defp get_contracts(response), do: {:error, response}
defp optimize_value(false), do: "0" defp optimize_value(false), do: "0"
defp optimize_value("false"), do: "0" defp optimize_value("false"), do: "0"

@ -74,7 +74,7 @@ defmodule Explorer.SmartContract.Verifier do
generated_bytecode != blockchain_bytecode_without_whisper -> generated_bytecode != blockchain_bytecode_without_whisper ->
{:error, :generated_bytecode} {:error, :generated_bytecode}
!ConstructorArguments.verify(address_hash, arguments_data) -> has_constructor_with_params?(abi) && !ConstructorArguments.verify(address_hash, arguments_data) ->
{:error, :constructor_arguments} {:error, :constructor_arguments}
true -> true ->
@ -86,17 +86,32 @@ defmodule Explorer.SmartContract.Verifier do
In order to discover the bytecode we need to remove the `swarm source` from In order to discover the bytecode we need to remove the `swarm source` from
the hash. the hash.
`64` characters to the left of `0029` are the `swarm source`. The rest on For more information on the swarm hash, check out:
the left is the `bytecode` to be validated. 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)
end
def extract_bytecode(code) do def extract_bytecode(code) do
{bytecode, _swarm_source} = do_extract_bytecode([], String.downcase(code))
code end
|> String.split("0029")
|> List.first() defp do_extract_bytecode(extracted, remaining) do
|> String.split_at(-64) case remaining do
<<>> ->
extracted
|> Enum.reverse()
|> :binary.list_to_bin()
bytecode "a165627a7a72305820" <> <<_::binary-size(64)>> <> "0029" <> _constructor_arguments ->
extracted
|> Enum.reverse()
|> :binary.list_to_bin()
<<next::binary-size(2)>> <> rest ->
do_extract_bytecode([next | extracted], rest)
end
end end
def next_evm_version(current_evm_version) do def next_evm_version(current_evm_version) do
@ -111,4 +126,8 @@ defmodule Explorer.SmartContract.Verifier do
prev_version prev_version
end end
end end
defp has_constructor_with_params?(abi) do
Enum.any?(abi, fn el -> el["type"] == "constructor" && el["inputs"] != [] end)
end
end end

@ -7,20 +7,21 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do
def verify(address_hash, arguments_data) do def verify(address_hash, arguments_data) do
arguments_data = String.replace(arguments_data, "0x", "") arguments_data = String.replace(arguments_data, "0x", "")
creation_input_data = Chain.contract_creation_input_data(address_hash)
data_with_swarm = address_hash
creation_input_data |> Chain.contract_creation_input_data()
|> String.split("0029")
|> List.first()
|> Kernel.<>("0029")
expected_arguments_data =
creation_input_data
|> String.split(data_with_swarm)
|> List.last()
|> String.replace("0x", "") |> String.replace("0x", "")
|> extract_constructor_arguments()
|> Kernel.==(arguments_data)
end
defp extract_constructor_arguments(<<>>), do: ""
defp extract_constructor_arguments("a165627a7a72305820" <> <<_::binary-size(64)>> <> "0029" <> constructor_arguments) do
constructor_arguments
end
expected_arguments_data == arguments_data defp extract_constructor_arguments(<<_::binary-size(2)>> <> rest) do
extract_constructor_arguments(rest)
end end
end end

@ -39,6 +39,4 @@ const input = {
const output = JSON.parse(solc.compile(JSON.stringify(input))) const output = JSON.parse(solc.compile(JSON.stringify(input)))
/** Older solc-bin versions don't use filename as contract key */ console.log(JSON.stringify(output));
const response = output.contracts[newContractName] || output.contracts['']
console.log(JSON.stringify(response));

@ -173,6 +173,101 @@ defmodule Explorer.SmartContract.Solidity.CodeCompilerTest do
assert {:error, :compilation} = response assert {:error, :compilation} = response
end end
test "returns constructor in abi" do
code = """
pragma solidity ^0.4.22;
contract OwnedToken {
// TokenCreator is a contract type that is defined below.
// It is fine to reference it as long as it is not used
// to create a new contract.
TokenCreator creator;
address owner;
bytes32 name;
// This is the constructor which registers the
// creator and the assigned name.
constructor(bytes32 _name) public {
// State variables are accessed via their name
// and not via e.g. this.owner. This also applies
// to functions and especially in the constructors,
// you can only call them like that ("internally"),
// because the contract itself does not exist yet.
owner = msg.sender;
// We do an explicit type conversion from `address`
// to `TokenCreator` and assume that the type of
// the calling contract is TokenCreator, there is
// no real way to check that.
creator = TokenCreator(msg.sender);
name = _name;
}
function changeName(bytes32 newName) public {
// Only the creator can alter the name --
// the comparison is possible since contracts
// are implicitly convertible to addresses.
if (msg.sender == address(creator))
name = newName;
}
function transfer(address newOwner) public {
// Only the current owner can transfer the token.
if (msg.sender != owner) return;
// We also want to ask the creator if the transfer
// is fine. Note that this calls a function of the
// contract defined below. If the call fails (e.g.
// due to out-of-gas), the execution here stops
// immediately.
if (creator.isTokenTransferOK(owner, newOwner))
owner = newOwner;
}
}
contract TokenCreator {
function createToken(bytes32 name)
public
returns (OwnedToken tokenAddress)
{
// Create a new Token contract and return its address.
// From the JavaScript side, the return type is simply
// `address`, as this is the closest type available in
// the ABI.
return new OwnedToken(name);
}
function changeName(OwnedToken tokenAddress, bytes32 name) public {
// Again, the external type of `tokenAddress` is
// simply `address`.
tokenAddress.changeName(name);
}
function isTokenTransferOK(address currentOwner, address newOwner)
public
view
returns (bool ok)
{
// Check some arbitrary condition.
address tokenAddress = msg.sender;
return (keccak256(newOwner) & 0xff) == (bytes20(tokenAddress) & 0xff);
}
}
"""
name = "OwnedToken"
compiler_version = "v0.4.22+commit.4cb486ee"
{:ok, %{"abi" => abi}} =
CodeCompiler.run(
name: name,
compiler_version: compiler_version,
code: code,
evm_version: "byzantium",
optimize: true
)
assert Enum.any?(abi, fn el -> el["type"] == "constructor" end)
end
end end
describe "get_contract_info/1" do describe "get_contract_info/1" do

@ -7,17 +7,19 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArgumentsTest do
alias Explorer.SmartContract.Verifier.ConstructorArguments alias Explorer.SmartContract.Verifier.ConstructorArguments
test "veriies constructor constructor arguments with whisper data" do test "veriies constructor constructor arguments with whisper data" do
constructor_arguments = "0x0405" constructor_arguments = Base.encode16(:crypto.strong_rand_bytes(64), case: :lower)
address = insert(:address) address = insert(:address)
input = %Data{ input =
bytes: "a165627a7a72305820" <>
<<1, 2, 3, 93, 148, 60, 87, 91, 232, 162, 174, 226, 187, 119, 55, 167, 101, 253, 210, 198, 228, 155, 116, 205, Base.encode16(:crypto.strong_rand_bytes(32), case: :lower) <> "0029" <> constructor_arguments
44, 146, 171, 15, 168, 228, 40, 45, 26, 117, 174, 0, 41, 4, 5>>
input_data = %Data{
bytes: Base.decode16!(input, case: :lower)
} }
:transaction :transaction
|> insert(created_contract_address_hash: address.hash, input: input) |> insert(created_contract_address_hash: address.hash, input: input_data)
|> with_block() |> with_block()
assert ConstructorArguments.verify(address.hash, constructor_arguments) assert ConstructorArguments.verify(address.hash, constructor_arguments)

@ -117,31 +117,6 @@ defmodule Explorer.SmartContract.VerifierTest do
assert abi != nil assert abi != nil
end end
test "returns error when constructor arguments do not match", %{
contract_code_info: contract_code_info
} do
contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode)
constructor_arguments = "0102030405"
params = %{
"contract_source_code" => contract_code_info.source_code,
"compiler_version" => contract_code_info.version,
"name" => contract_code_info.name,
"optimization" => contract_code_info.optimized,
"constructor_arguments" => constructor_arguments
}
:transaction
|> insert(
created_contract_address_hash: contract_address.hash,
input: Verifier.extract_bytecode(contract_code_info.bytecode) <> "010203"
)
|> with_block()
assert {:error, :constructor_arguments} = Verifier.evaluate_authenticity(contract_address.hash, params)
end
test "returns error when bytecode doesn't match", %{contract_code_info: contract_code_info} 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) contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode)
@ -181,7 +156,7 @@ defmodule Explorer.SmartContract.VerifierTest do
swarm_source = "3c381c1b48b38d050c54d7ef296ecd411040e19420dfec94772b9c49ae106a0b" swarm_source = "3c381c1b48b38d050c54d7ef296ecd411040e19420dfec94772b9c49ae106a0b"
bytecode = bytecode =
"0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a72305820" "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600"
assert bytecode == Verifier.extract_bytecode(code) assert bytecode == Verifier.extract_bytecode(code)
assert bytecode != code assert bytecode != code
@ -189,5 +164,21 @@ defmodule Explorer.SmartContract.VerifierTest do
assert String.contains?(bytecode, "0029") == false assert String.contains?(bytecode, "0029") == false
assert String.contains?(bytecode, swarm_source) == false assert String.contains?(bytecode, swarm_source) == false
end end
test "extracts everything to the left of the swarm hash" do
code =
"0x608060405234801561001057600080fd5b5060df80610010029f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a723058203c381c1b48b38d050c54d7ef296ecd411040e19420dfec94772b9c49ae106a0b0029"
swarm_source = "3c381c1b48b38d050c54d7ef296ecd411040e19420dfec94772b9c49ae106a0b"
bytecode =
"0x608060405234801561001057600080fd5b5060df80610010029f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600"
assert bytecode == Verifier.extract_bytecode(code)
assert bytecode != code
assert String.contains?(code, bytecode) == true
assert String.contains?(bytecode, "0029") == true
assert String.contains?(bytecode, swarm_source) == false
end
end end
end end

@ -91,8 +91,6 @@ These workers are created for fetching information, which previously wasn't fetc
After all deployed instances get all needed data, these fetchers should be deprecated and removed. After all deployed instances get all needed data, these fetchers should be deprecated and removed.
- `uncataloged_token_transfers`: extracts token transfers from logs, which previously weren't parsed due to unknown format - `uncataloged_token_transfers`: extracts token transfers from logs, which previously weren't parsed due to unknown format
- `addresses_without_codes`: forces complete refetch of blocks, which have created contract addresses without contract code
- `failed_created_addresses`: forces refetch of contract code for failed transactions, which previously got incorrectly overwritten
- `uncles_without_index`: adds previously unfetched `index` field for unfetched blocks in `block_second_degree_relations` - `uncles_without_index`: adds previously unfetched `index` field for unfetched blocks in `block_second_degree_relations`
## Memory Usage ## Memory Usage
@ -156,4 +154,3 @@ mix test --exclude no_geth
|:----------|:--------------------------------------------------| |:----------|:--------------------------------------------------|
| HTTP | `https://mainnet.infura.io/8lTvJTKmHPCHazkneJsY` | | HTTP | `https://mainnet.infura.io/8lTvJTKmHPCHazkneJsY` |
| WebSocket | `wss://mainnet.infura.io/ws/8lTvJTKmHPCHazkneJsY` | | WebSocket | `wss://mainnet.infura.io/ws/8lTvJTKmHPCHazkneJsY` |

@ -24,8 +24,6 @@ defmodule Indexer.Supervisor do
} }
alias Indexer.Temporary.{ alias Indexer.Temporary.{
AddressesWithoutCode,
FailedCreatedAddresses,
UncatalogedTokenTransfers, UncatalogedTokenTransfers,
UnclesWithoutIndex UnclesWithoutIndex
} }
@ -80,12 +78,6 @@ defmodule Indexer.Supervisor do
|> Map.drop(~w(block_interval blocks_concurrency memory_monitor subscribe_named_arguments realtime_overrides)a) |> Map.drop(~w(block_interval blocks_concurrency memory_monitor subscribe_named_arguments realtime_overrides)a)
|> Block.Fetcher.new() |> Block.Fetcher.new()
fixing_realtime_fetcher = %Block.Fetcher{
broadcast: false,
callback_module: Realtime.Fetcher,
json_rpc_named_arguments: json_rpc_named_arguments
}
realtime_block_fetcher = realtime_block_fetcher =
named_arguments named_arguments
|> Map.drop(~w(block_interval blocks_concurrency memory_monitor subscribe_named_arguments realtime_overrides)a) |> Map.drop(~w(block_interval blocks_concurrency memory_monitor subscribe_named_arguments realtime_overrides)a)
@ -130,8 +122,6 @@ defmodule Indexer.Supervisor do
{TokenUpdater.Supervisor, [%{update_interval: metadata_updater_inverval}]}, {TokenUpdater.Supervisor, [%{update_interval: metadata_updater_inverval}]},
# Temporary workers # Temporary workers
{AddressesWithoutCode.Supervisor, [fixing_realtime_fetcher]},
{FailedCreatedAddresses.Supervisor, [json_rpc_named_arguments]},
{UncatalogedTokenTransfers.Supervisor, [[]]}, {UncatalogedTokenTransfers.Supervisor, [[]]},
{UnclesWithoutIndex.Supervisor, {UnclesWithoutIndex.Supervisor,
[[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]]} [[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]]}

@ -1,147 +0,0 @@
defmodule Indexer.Temporary.AddressesWithoutCode do
@moduledoc """
Temporary module to fetch contract code for addresses without it.
"""
use GenServer
use Indexer.Fetcher
require Logger
import Ecto.Query
alias Explorer.Chain.{Address, Block, Transaction}
alias Explorer.Repo
alias Indexer.Block.Realtime.Fetcher
alias Indexer.Temporary.AddressesWithoutCode.TaskSupervisor
@task_options [max_concurrency: 3, timeout: :infinity]
@batch_size 500
@query_timeout :infinity
def start_link([fetcher, gen_server_options]) do
GenServer.start_link(__MODULE__, fetcher, gen_server_options)
end
@impl GenServer
def init(fetcher) do
schedule_work()
{:ok, fetcher}
end
def schedule_work do
Process.send_after(self(), :run, 1_000)
end
@impl GenServer
def handle_info(:run, fetcher) do
run(fetcher)
{:noreply, fetcher}
end
def run(fetcher) do
fix_transaction_without_to_address_and_created_contract_address(fetcher)
fix_addresses_with_creation_transaction_but_without_code(fetcher)
end
def fix_transaction_without_to_address_and_created_contract_address(fetcher) do
Logger.debug(
[
"Started fix_transaction_without_to_address_and_created_contract_address"
],
fetcher: :addresses_without_code
)
query =
from(block in Block,
left_join: transaction in Transaction,
on: block.hash == transaction.block_hash,
where:
is_nil(transaction.to_address_hash) and is_nil(transaction.created_contract_address_hash) and
block.consensus == true and is_nil(transaction.error) and not is_nil(transaction.hash),
distinct: block.hash
)
process_query(query, fetcher)
Logger.debug(
[
"Started fix_transaction_without_to_address_and_created_contract_address"
],
fetcher: :addresses_without_code
)
end
def fix_addresses_with_creation_transaction_but_without_code(fetcher) do
Logger.debug(
[
"Started fix_addresses_with_creation_transaction_but_without_code"
],
fetcher: :addresses_without_code
)
second_query =
from(block in Block,
left_join: transaction in Transaction,
on: transaction.block_hash == block.hash,
left_join: address in Address,
on: address.hash == transaction.created_contract_address_hash,
where:
not is_nil(transaction.block_hash) and not is_nil(transaction.created_contract_address_hash) and
is_nil(address.contract_code) and
block.consensus == true and is_nil(transaction.error) and not is_nil(transaction.hash),
distinct: block.hash
)
process_query(second_query, fetcher)
Logger.debug(
[
"Finished fix_addresses_with_creation_transaction_but_without_code"
],
fetcher: :addresses_without_code
)
end
defp process_query(query, fetcher) do
query_stream = Repo.stream(query, max_rows: @batch_size, timeout: @query_timeout)
stream =
TaskSupervisor
|> Task.Supervisor.async_stream_nolink(
query_stream,
fn block -> refetch_block(block, fetcher) end,
@task_options
)
Repo.transaction(fn -> Stream.run(stream) end, timeout: @query_timeout)
end
def refetch_block(block, fetcher) do
Logger.debug(
[
"Processing block #{to_string(block.hash)} #{block.number}"
],
fetcher: :addresses_without_code
)
Fetcher.fetch_and_import_block(block.number, fetcher, false)
Logger.debug(
[
"Finished processing block #{to_string(block.hash)} #{block.number}"
],
fetcher: :addresses_without_code
)
rescue
e ->
Logger.debug(
[
"Failed to fetch block #{to_string(block.hash)} #{block.number} because of #{inspect(e)}"
],
fetcher: :addresses_without_code
)
end
end

@ -1,132 +0,0 @@
defmodule Indexer.Temporary.FailedCreatedAddresses do
@moduledoc """
Temporary module to fix internal transactions and their created transactions if a parent transaction has failed.
"""
use GenServer
use Indexer.Fetcher
require Logger
import Ecto.Query
alias Explorer.Chain.{Address, Data, InternalTransaction, Transaction}
alias Explorer.Repo
alias Indexer.Fetcher.ContractCode
alias Indexer.Temporary.FailedCreatedAddresses.TaskSupervisor
@task_options [max_concurrency: 3, timeout: :infinity]
@query_timeout :infinity
def start_link([json_rpc_named_arguments, gen_server_options]) do
GenServer.start_link(__MODULE__, json_rpc_named_arguments, gen_server_options)
end
@impl GenServer
def init(json_rpc_named_arguments) do
schedule_work()
{:ok, json_rpc_named_arguments}
end
def schedule_work do
Process.send_after(self(), :run, 1_000)
end
@impl GenServer
def handle_info(:run, json_rpc_named_arguments) do
run(json_rpc_named_arguments)
{:noreply, json_rpc_named_arguments}
end
def run(json_rpc_named_arguments) do
Logger.debug(
[
"Started query to fetch internal transactions that need to be fixed"
],
fetcher: :failed_created_addresses
)
data = %Data{bytes: ""}
query =
from(t in Transaction,
left_join: it in InternalTransaction,
on: it.transaction_hash == t.hash,
left_join: address in Address,
on: address.hash == it.created_contract_address_hash,
where: t.status == ^0 and not is_nil(it.created_contract_address_hash) and address.contract_code != ^data,
distinct: t.hash
)
found_transactions = Repo.all(query, timeout: @query_timeout)
Logger.debug(
[
"Finished query to fetch internal transactions that need to be fixed. Number of records is #{
Enum.count(found_transactions)
}"
],
fetcher: :failed_created_addresses
)
TaskSupervisor
|> Task.Supervisor.async_stream_nolink(
found_transactions,
fn transaction -> fix_internal_transaction(transaction, json_rpc_named_arguments) end,
@task_options
)
|> Enum.to_list()
end
def fix_internal_transaction(transaction, json_rpc_named_arguments) do
# credo:disable-for-next-line
try do
Logger.debug(
[
"Started fixing transaction #{to_string(transaction.hash)}"
],
fetcher: :failed_created_addresses
)
transaction_with_internal_transactions = Repo.preload(transaction, [:internal_transactions])
transaction_with_internal_transactions.internal_transactions
|> Enum.filter(fn internal_transaction ->
internal_transaction.created_contract_address_hash
end)
|> Enum.each(fn internal_transaction ->
:ok =
internal_transaction
|> code_entry()
|> ContractCode.run(json_rpc_named_arguments)
end)
Logger.debug(
[
"Finished fixing transaction #{to_string(transaction.hash)}"
],
fetcher: :failed_created_addresses
)
rescue
e ->
Logger.debug(
[
"Failed fixing transaction #{to_string(transaction.hash)} because of #{inspect(e)}"
],
fetcher: :failed_created_addresses
)
end
end
def code_entry(%InternalTransaction{
block_number: block_number,
created_contract_address_hash: %{bytes: created_contract_bytes}
}) do
[{block_number, created_contract_bytes, <<>>}]
end
def transaction_entry(%Transaction{hash: %{bytes: bytes}, index: index, block_number: block_number}) do
[{block_number, bytes, index}]
end
end

@ -1,389 +0,0 @@
defmodule Indexer.Temporary.AddressesWithoutCodeTest do
use Explorer.DataCase, async: false
use EthereumJSONRPC.Case, async: false
import Mox
import Ecto.Query
alias Explorer.Repo
alias Explorer.Chain.{Address, Transaction}
alias Indexer.Block.Fetcher
alias Indexer.Block.Realtime.Fetcher, as: RealtimeFetcher
alias Indexer.Fetcher.{CoinBalance, ContractCode, InternalTransaction, ReplacedTransaction, Token, TokenBalance}
alias Indexer.Temporary.AddressesWithoutCode.Supervisor
@moduletag capture_log: true
setup :set_mox_global
setup :verify_on_exit!
describe "run/1" do
setup %{json_rpc_named_arguments: json_rpc_named_arguments} do
CoinBalance.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
ContractCode.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
InternalTransaction.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
Token.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
TokenBalance.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
ReplacedTransaction.Supervisor.Case.start_supervised!()
[name: Indexer.Block.Realtime.TaskSupervisor]
|> Task.Supervisor.child_spec()
|> ExUnit.Callbacks.start_supervised!()
fetcher = %Fetcher{
broadcast: false,
callback_module: RealtimeFetcher,
json_rpc_named_arguments: json_rpc_named_arguments
}
{:ok, %{fetcher: fetcher}}
end
@tag :no_parity
@tag :no_geth
test "refetches blocks setting created address and code", %{
fetcher: %{json_rpc_named_arguments: json_rpc_named_arguments} = fetcher
} do
block = insert(:block, consensus: true)
transaction =
:transaction
|> insert(
status: 0,
to_address: nil,
created_contract_address_hash: nil,
block: block,
block_number: block.number,
block_hash: block.hash,
cumulative_gas_used: 200,
gas_used: 100,
index: 0
)
address = insert(:address, contract_code: nil)
if json_rpc_named_arguments[:transport] == EthereumJSONRPC.Mox do
EthereumJSONRPC.Mox
|> expect(:json_rpc, fn [%{id: id, method: "eth_getBlockByNumber", params: [_block_quantity, true]}],
_options ->
{:ok,
[
%{
id: id,
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" => "0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca",
"creates" => to_string(address.hash),
"from" => "0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca",
"to" => nil,
"gas" => "0x47b760",
"gasPrice" => "0x174876e800",
"hash" => to_string(transaction.hash),
"input" => "0x10855269000000000000000000000000862d67cb0773ee3f8ce7ea89b328ffea861ab3ef",
"nonce" => "0x4",
"publicKey" =>
"0xe5d196ad4ceada719d9e592f7166d0c75700f6eab2e3c3de34ba751ea786527cb3f6eb96ad9fdfdb9989ff572df50f1c42ef800af9c5207a38b929aff969b5c9",
"r" => "0xa7f8f45cce375bb7af8750416e1b03e0473f93c256da2285d1134fc97a700e01",
"raw" =>
"0xf88a0485174876e8008347b760948bf38d4764929064f2d4d3a56520a76ab3df415b80a410855269000000000000000000000000862d67cb0773ee3f8ce7ea89b328ffea861ab3ef81bea0a7f8f45cce375bb7af8750416e1b03e0473f93c256da2285d1134fc97a700e01a01f87a076f13824f4be8963e3dffd7300dae64d5f23c9a062af0c6ead347c135f",
"s" => "0x1f87a076f13824f4be8963e3dffd7300dae64d5f23c9a062af0c6ead347c135f",
"standardV" => "0x1",
"transactionIndex" => "0x0",
"v" => "0xbe",
"value" => "0x0"
}
],
"transactionsRoot" => "0x68e314a05495f390f9cd0c36267159522e5450d2adf254a74567b452e767bf34",
"uncles" => []
}
}
]}
end)
|> expect(:json_rpc, fn [
%{
id: id,
method: "eth_getTransactionReceipt",
params: _
}
],
_options ->
{:ok,
[
%{
id: id,
jsonrpc: "2.0",
result: %{
"blockHash" => "0xf6b4b8c88df3ebd252ec476328334dc026cf66606a84fb769b3d3cbccc8471bd",
"blockNumber" => "0x25",
"contractAddress" => to_string(address.hash),
"cumulativeGasUsed" => "0xc512",
"gasUsed" => "0xc512",
"logs" => [
%{
"address" => "0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca",
"blockHash" => "0xf6b4b8c88df3ebd252ec476328334dc026cf66606a84fb769b3d3cbccc8471bd",
"blockNumber" => "0x25",
"data" => "0x000000000000000000000000862d67cb0773ee3f8ce7ea89b328ffea861ab3ef",
"logIndex" => "0x0",
"topics" => ["0x600bcf04a13e752d1e3670a5a9f1c21177ca2a93c6f5391d4f1298d098097c22"],
"transactionHash" => to_string(transaction.hash),
"transactionIndex" => "0x0",
"transactionLogIndex" => "0x0",
"type" => "mined"
}
],
"logsBloom" =>
"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000200000000000000000000020000000000000000200000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"root" => nil,
"status" => "0x1",
"transactionHash" => to_string(transaction.hash),
"transactionIndex" => "0x0"
}
}
]}
end)
|> expect(:json_rpc, fn [%{id: id, method: "trace_block", params: _}], _options ->
{:ok, [%{id: id, result: []}]}
end)
|> expect(:json_rpc, fn [%{id: id, method: "trace_replayBlockTransactions", params: _}], _options ->
{:ok, [%{id: id, result: []}]}
end)
|> expect(:json_rpc, fn [
%{
id: 0,
jsonrpc: "2.0",
method: "eth_getBalance",
params: ["0x0000000000000000000000000000000000000003", "0x25"]
},
%{
id: 1,
jsonrpc: "2.0",
method: "eth_getBalance",
params: ["0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca", "0x25"]
}
],
_options ->
{:ok, [%{id: 0, jsonrpc: "2.0", result: "0x0"}, %{id: 1, jsonrpc: "2.0", result: "0x0"}]}
end)
end
[fetcher, [name: AddressesWithoutCodeTest]]
|> Supervisor.child_spec()
|> ExUnit.Callbacks.start_supervised!()
Process.sleep(5_000)
updated_address =
from(a in Address, where: a.hash == ^address.hash, preload: :contracts_creation_transaction) |> Repo.one()
assert updated_address.contracts_creation_transaction.hash == transaction.hash
updated_transaction =
from(t in Transaction, where: t.hash == ^transaction.hash, preload: :created_contract_address) |> Repo.one()
assert updated_transaction.created_contract_address.hash == address.hash
assert updated_address.contract_code == updated_transaction.input
end
@tag :no_parity
@tag :no_geth
test "doesn't set contract code if contract wasn't create", %{
fetcher: %{json_rpc_named_arguments: json_rpc_named_arguments} = fetcher
} do
block = insert(:block, consensus: true)
transaction =
:transaction
|> insert(
status: 0,
to_address: nil,
created_contract_address_hash: nil,
block: block,
block_number: block.number,
block_hash: block.hash,
cumulative_gas_used: 200,
gas_used: 100,
index: 0
)
address = insert(:address, contract_code: nil)
if json_rpc_named_arguments[:transport] == EthereumJSONRPC.Mox do
EthereumJSONRPC.Mox
|> expect(:json_rpc, fn [%{id: id, method: "eth_getBlockByNumber", params: [_block_quantity, true]}],
_options ->
{:ok,
[
%{
id: id,
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" => "0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca",
"from" => "0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca",
"to" => nil,
"gas" => "0x47b760",
"gasPrice" => "0x174876e800",
"hash" => to_string(transaction.hash),
"input" => "0x10855269000000000000000000000000862d67cb0773ee3f8ce7ea89b328ffea861ab3ef",
"nonce" => "0x4",
"publicKey" =>
"0xe5d196ad4ceada719d9e592f7166d0c75700f6eab2e3c3de34ba751ea786527cb3f6eb96ad9fdfdb9989ff572df50f1c42ef800af9c5207a38b929aff969b5c9",
"r" => "0xa7f8f45cce375bb7af8750416e1b03e0473f93c256da2285d1134fc97a700e01",
"raw" =>
"0xf88a0485174876e8008347b760948bf38d4764929064f2d4d3a56520a76ab3df415b80a410855269000000000000000000000000862d67cb0773ee3f8ce7ea89b328ffea861ab3ef81bea0a7f8f45cce375bb7af8750416e1b03e0473f93c256da2285d1134fc97a700e01a01f87a076f13824f4be8963e3dffd7300dae64d5f23c9a062af0c6ead347c135f",
"s" => "0x1f87a076f13824f4be8963e3dffd7300dae64d5f23c9a062af0c6ead347c135f",
"standardV" => "0x1",
"transactionIndex" => "0x0",
"v" => "0xbe",
"value" => "0x0"
}
],
"transactionsRoot" => "0x68e314a05495f390f9cd0c36267159522e5450d2adf254a74567b452e767bf34",
"uncles" => []
}
}
]}
end)
|> expect(:json_rpc, fn [
%{
id: id,
method: "eth_getTransactionReceipt",
params: _
}
],
_options ->
{:ok,
[
%{
id: id,
jsonrpc: "2.0",
result: %{
"blockHash" => "0xf6b4b8c88df3ebd252ec476328334dc026cf66606a84fb769b3d3cbccc8471bd",
"blockNumber" => "0x25",
"contractAddress" => nil,
"cumulativeGasUsed" => "0xc512",
"gasUsed" => "0xc512",
"logs" => [
%{
"address" => "0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca",
"blockHash" => "0xf6b4b8c88df3ebd252ec476328334dc026cf66606a84fb769b3d3cbccc8471bd",
"blockNumber" => "0x25",
"data" => "0x000000000000000000000000862d67cb0773ee3f8ce7ea89b328ffea861ab3ef",
"logIndex" => "0x0",
"topics" => ["0x600bcf04a13e752d1e3670a5a9f1c21177ca2a93c6f5391d4f1298d098097c22"],
"transactionHash" => to_string(transaction.hash),
"transactionIndex" => "0x0",
"transactionLogIndex" => "0x0",
"type" => "mined"
}
],
"logsBloom" =>
"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000200000000000000000000020000000000000000200000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"root" => nil,
"status" => "0x1",
"transactionHash" => to_string(transaction.hash),
"transactionIndex" => "0x0"
}
}
]}
end)
|> expect(:json_rpc, fn [%{id: id, method: "trace_block", params: _}], _options ->
{:ok, [%{id: id, result: []}]}
end)
|> expect(:json_rpc, fn [%{id: id, method: "trace_replayBlockTransactions", params: _}], _options ->
{:ok, [%{id: id, result: []}]}
end)
|> expect(:json_rpc, fn [
%{
id: 1,
jsonrpc: "2.0",
method: "eth_getBalance",
params: ["0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca", "0x25"]
}
],
_options ->
{:ok, [%{id: 1, jsonrpc: "2.0", result: "0x0"}]}
end)
end
[fetcher, [name: AddressesWithoutCodeTest1]]
|> Supervisor.child_spec()
|> ExUnit.Callbacks.start_supervised!()
Process.sleep(2_000)
updated_address =
from(a in Address, where: a.hash == ^address.hash, preload: :contracts_creation_transaction) |> Repo.one()
assert is_nil(updated_address.contracts_creation_transaction)
updated_transaction =
from(t in Transaction, where: t.hash == ^transaction.hash, preload: :created_contract_address) |> Repo.one()
assert is_nil(updated_transaction.created_contract_address)
assert is_nil(updated_address.contract_code)
end
end
end

@ -1,78 +0,0 @@
defmodule Indexer.Temporary.FailedCreatedAddressesTest do
use Explorer.DataCase, async: false
use EthereumJSONRPC.Case, async: false
import Mox
import Ecto.Query
alias Explorer.Repo
alias Explorer.Chain.Address
alias Indexer.Fetcher.CoinBalance
alias Indexer.Temporary.FailedCreatedAddresses.Supervisor
@moduletag capture_log: true
setup :set_mox_global
setup :verify_on_exit!
describe "run/1" do
@tag :no_parity
@tag :no_geth
test "updates failed replaced transactions", %{json_rpc_named_arguments: json_rpc_named_arguments} do
CoinBalance.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
block = insert(:block)
transaction =
:transaction
|> insert(
status: 0,
error: "Reverted",
internal_transactions_indexed_at: DateTime.utc_now(),
block: block,
block_number: block.number,
cumulative_gas_used: 200,
gas_used: 100,
index: 0
)
address = insert(:address, contract_code: "0x0102030405")
insert(:internal_transaction,
block_number: transaction.block_number,
transaction: transaction,
index: 0,
created_contract_address_hash: address.hash
)
if json_rpc_named_arguments[:transport] == EthereumJSONRPC.Mox do
EthereumJSONRPC.Mox
|> expect(:json_rpc, fn _json, _options ->
{:ok, [%{id: 0, jsonrpc: "2.0", result: "0x"}]}
end)
|> expect(:json_rpc, fn [%{id: id, method: "eth_getBalance", params: [_address, _block_quantity]}], _options ->
{:ok, [%{id: id, result: "0x0"}]}
end)
end
params = [json_rpc_named_arguments, [name: TestFailedCreatedAddresses]]
params
|> Supervisor.child_spec()
|> ExUnit.Callbacks.start_supervised!()
Process.sleep(3_000)
fetched_address =
Repo.one(
from(a in Address,
where: a.hash == ^address.hash
)
)
assert fetched_address.contract_code == %Explorer.Chain.Data{bytes: ""}
end
end
end
Loading…
Cancel
Save