- <%= gettext "Showing 250 addresses of" %> + <%= gettext "Showing " %> + <%= Cldr.Number.to_string!(@page_address_count, format: "#,###") %> + <%= gettext " addresses of" %> <%= Cldr.Number.to_string!(@address_count, format: "#,###") %> <%= gettext "total addresses with a balance" %> + <%= gettext " (page" %> + <%= Cldr.Number.to_string!(@cur_page_number, format: "#,###)") %>
+ <%= for {{address, tx_count}, index} <- Enum.with_index(@address_tx_count_pairs, 1) do %> <%= render "_tile.html", @@ -14,6 +30,17 @@ total_supply: @total_supply, tx_count: tx_count, validation_count: validation_count(address) %> <% end %> + #
\n # eveem.org 6 Feb 2019
\n # Decompiled source of 0x00Bd9e214FAb74d6fC21bf1aF34261765f57e875
\n #
\n # Let's make the world open source
\n #
\n #
\n # I failed with these:
\n # - unknowne77c646d(?)
\n # - transferFromWithData(address _from, address _to, uint256 _value, bytes _data)
\n # All the rest is below.
\n #
\n
\n
\n # Storage definitions and getters
\n
\n def storage:
\n allowance is uint256 => uint256 # mask(256, 0) at storage #2
\n stor4 is uint256 => uint8 # mask(8, 0) at storage #4
\n
\n def allowance(address _owner, address _spender) payable: 64
\n return allowance[sha3(((320 - 1) and (320 - 1) and _owner), 1), ((320 - 1) and _spender and (320 - 1))]
\n
\n
\n #
\n # Regular functions - see Tutorial for understanding quirks of the code
\n #
\n
\n
\n # folder failed in this function - may be terribly long, sorry
\n def unknownc47d033b(?) payable: not cd[4]:
\n revert
\n else:
\n mem[0]cd[4]
\n mem[32] = 4
\n mem[96] = bool(stor4[((320 - 1) and (320 - 1) and cd[4])])
\n return bool(stor4[((320 - 1) and (320 - 1) and cd[4])])
\n
\n def _fallback() payable: # default function
\n revert
\n
\n"
+ " #
\n # eveem.org 6 Feb 2019
\n # Decompiled source of 0x00Bd9e214FAb74d6fC21bf1aF34261765f57e875
\n #
\n # Let's make the world open source
\n #
\n #
\n # I failed with these:
\n # - unknowne77c646d(?)
\n # - transferFromWithData(address _from, address _to, uint256 _value, bytes _data)
\n # All the rest is below.
\n #
\n
\n
\n # Storage definitions and getters
\n
\n def storage:
\n allowance is uint256 => uint256 # mask(256, 0) at storage #2
\n stor4 is uint256 => uint8 # mask(8, 0) at storage #4
\n
\n def allowance(address _owner, address _spender) payable: 64
\n return allowance[_owner_spender(320 - 1))]
\n
\n
\n #
\n # Regular functions - see Tutorial for understanding quirks of the code
\n #
\n
\n
\n # folder failed in this function - may be terribly long, sorry
\n def unknownc47d033b(?) payable: not cd[4]:
\n revert
\n else:
\n mem[0]cd[4]
\n mem[32] = 4
\n mem[96] = bool(stor4[cd[4])])
\n return bool(stor4[cd[4])])
\n
\n def _fallback() payable: # default function
\n revert
\n
\n
\n"
end
test "adds style span to every line" do
@@ -70,7 +70,7 @@ defmodule BlockScoutWeb.AddressDecompiledContractViewTest do
"""
assert AddressDecompiledContractView.highlight_decompiled_code(code) ==
- " #
\n # eveem.org 6 Feb 2019
\n # Decompiled source of 0x00Bd9e214FAb74d6fC21bf1aF34261765f57e875
\n #
\n # Let's make the world open source
\n #
\n
\n"
+ " #
\n # eveem.org 6 Feb 2019
\n # Decompiled source of 0x00Bd9e214FAb74d6fC21bf1aF34261765f57e875
\n #
\n # Let's make the world open source
\n #
\n
\n
\n"
end
end
diff --git a/apps/explorer/.gitignore b/apps/explorer/.gitignore
index f4cd5728cc..bf75b19355 100644
--- a/apps/explorer/.gitignore
+++ b/apps/explorer/.gitignore
@@ -1 +1,2 @@
-priv/.recovery
\ No newline at end of file
+priv/.recovery
+priv/solc_compilers/
diff --git a/apps/explorer/lib/explorer/application.ex b/apps/explorer/lib/explorer/application.ex
index 47b30cfa2b..b4f8589a57 100644
--- a/apps/explorer/lib/explorer/application.ex
+++ b/apps/explorer/lib/explorer/application.ex
@@ -26,6 +26,7 @@ defmodule Explorer.Application do
Supervisor.Spec.worker(SpandexDatadog.ApiServer, [datadog_opts()]),
Supervisor.child_spec({Task.Supervisor, name: Explorer.MarketTaskSupervisor}, id: Explorer.MarketTaskSupervisor),
Supervisor.child_spec({Task.Supervisor, name: Explorer.TaskSupervisor}, id: Explorer.TaskSupervisor),
+ Explorer.SmartContract.SolcDownloader,
{Registry, keys: :duplicate, name: Registry.ChainEvents, id: Registry.ChainEvents},
{Admin.Recovery, [[], [name: Admin.Recovery]]},
{TransactionCountCache, [[], []]}
diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex
index beee541ebf..e6fb9c011e 100644
--- a/apps/explorer/lib/explorer/chain.ex
+++ b/apps/explorer/lib/explorer/chain.ex
@@ -1143,21 +1143,25 @@ defmodule Explorer.Chain do
end
@doc """
- Lists the top 250 `t:Explorer.Chain.Address.t/0`'s' in descending order based on coin balance.
+ Lists the top `t:Explorer.Chain.Address.t/0`'s' in descending order based on coin balance and address hash.
"""
@spec list_top_addresses :: [{Address.t(), non_neg_integer()}]
- def list_top_addresses do
- query =
+ def list_top_addresses(options \\ []) do
+ paging_options = Keyword.get(options, :paging_options, @default_paging_options)
+
+ base_query =
from(a in Address,
where: a.fetched_coin_balance > ^0,
order_by: [desc: a.fetched_coin_balance, asc: a.hash],
preload: [:names],
- select: {a, fragment("coalesce(1 + ?, 0)", a.nonce)},
- limit: 250
+ select: {a, fragment("coalesce(1 + ?, 0)", a.nonce)}
)
- Repo.all(query)
+ base_query
+ |> page_addresses(paging_options)
+ |> limit(^paging_options.page_size)
+ |> Repo.all()
end
@doc """
@@ -2293,6 +2297,12 @@ defmodule Explorer.Chain do
end)
end
+ defp page_addresses(query, %PagingOptions{key: nil}), do: query
+
+ defp page_addresses(query, %PagingOptions{key: {coin_balance, hash}}) do
+ where(query, [address], address.fetched_coin_balance <= ^coin_balance and address.hash > ^hash)
+ end
+
defp page_blocks(query, %PagingOptions{key: nil}), do: query
defp page_blocks(query, %PagingOptions{key: {block_number}}) do
diff --git a/apps/explorer/lib/explorer/smart_contract/solc_downloader.ex b/apps/explorer/lib/explorer/smart_contract/solc_downloader.ex
new file mode 100644
index 0000000000..1aa81d2488
--- /dev/null
+++ b/apps/explorer/lib/explorer/smart_contract/solc_downloader.ex
@@ -0,0 +1,92 @@
+defmodule Explorer.SmartContract.SolcDownloader do
+ @moduledoc """
+ Checks to see if the requested solc compiler version exists, and if not it
+ downloads and stores the file.
+ """
+ use GenServer
+
+ alias Explorer.SmartContract.Solidity.CompilerVersion
+
+ @latest_compiler_refetch_time :timer.minutes(30)
+
+ def ensure_exists(version) do
+ path = file_path(version)
+
+ if File.exists?(path) do
+ path
+ else
+ {:ok, compiler_versions} = CompilerVersion.fetch_versions()
+
+ if version in compiler_versions do
+ GenServer.call(__MODULE__, {:ensure_exists, version}, 60_000)
+ else
+ false
+ end
+ end
+ end
+
+ def start_link(_) do
+ GenServer.start_link(__MODULE__, [], name: __MODULE__)
+ end
+
+ # sobelow_skip ["Traversal"]
+ @impl true
+ def init([]) do
+ File.mkdir(compiler_dir())
+
+ {:ok, []}
+ end
+
+ # sobelow_skip ["Traversal"]
+ @impl true
+ def handle_call({:ensure_exists, version}, _from, state) do
+ path = file_path(version)
+
+ if fetch?(version, path) do
+ temp_path = file_path("#{version}-tmp")
+
+ contents = download(version)
+
+ file = File.open!(temp_path, [:write, :exclusive])
+
+ IO.binwrite(file, contents)
+
+ File.rename(temp_path, path)
+ end
+
+ {:reply, path, state}
+ end
+
+ defp fetch?("latest", path) do
+ case File.stat(path) do
+ {:error, :enoent} ->
+ true
+
+ {:ok, %{mtime: mtime}} ->
+ last_modified = NaiveDateTime.from_erl!(mtime)
+ diff = Timex.diff(NaiveDateTime.utc_now(), last_modified, :milliseconds)
+
+ diff > @latest_compiler_refetch_time
+ end
+ end
+
+ defp fetch?(_, path) do
+ not File.exists?(path)
+ end
+
+ defp file_path(version) do
+ Path.join(compiler_dir(), "#{version}.js")
+ end
+
+ defp compiler_dir do
+ Application.app_dir(:explorer, "priv/solc_compilers/")
+ end
+
+ defp download(version) do
+ download_path = "https://ethereum.github.io/solc-bin/bin/soljson-#{version}.js"
+
+ download_path
+ |> HTTPoison.get!([], timeout: 60_000)
+ |> Map.get(:body)
+ 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 e7f6090707..72c5bae186 100644
--- a/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex
+++ b/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex
@@ -3,6 +3,8 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do
Module responsible to compile the Solidity code of a given Smart Contract.
"""
+ alias Explorer.SmartContract.SolcDownloader
+
@new_contract_name "New.sol"
@allowed_evm_versions ["homestead", "tangerineWhistle", "spuriousDragon", "byzantium", "constantinople", "petersburg"]
@@ -79,31 +81,36 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do
"byzantium"
end
- {response, _status} =
- System.cmd(
- "node",
- [
- Application.app_dir(:explorer, "priv/compile_solc.js"),
- code,
- compiler_version,
- optimize_value(optimize),
- optimization_runs,
- @new_contract_name,
- external_libs_string,
- checked_evm_version
- ]
- )
-
- with {:ok, contracts} <- Jason.decode(response),
- %{"abi" => abi, "evm" => %{"deployedBytecode" => %{"object" => bytecode}}} <-
- get_contract_info(contracts, name) do
- {:ok, %{"abi" => abi, "bytecode" => bytecode, "name" => name}}
- else
- {:error, %Jason.DecodeError{}} ->
- {:error, :compilation}
-
- error ->
- parse_error(error)
+ path = SolcDownloader.ensure_exists(compiler_version)
+
+ if path do
+ {response, _status} =
+ System.cmd(
+ "node",
+ [
+ Application.app_dir(:explorer, "priv/compile_solc.js"),
+ code,
+ compiler_version,
+ optimize_value(optimize),
+ optimization_runs,
+ @new_contract_name,
+ external_libs_string,
+ checked_evm_version,
+ path
+ ]
+ )
+
+ with {:ok, contracts} <- Jason.decode(response),
+ %{"abi" => abi, "evm" => %{"deployedBytecode" => %{"object" => bytecode}}} <-
+ get_contract_info(contracts, name) do
+ {:ok, %{"abi" => abi, "bytecode" => bytecode, "name" => name}}
+ else
+ {:error, %Jason.DecodeError{}} ->
+ {:error, :compilation}
+
+ error ->
+ parse_error(error)
+ end
end
end
diff --git a/apps/explorer/priv/compile_solc.js b/apps/explorer/priv/compile_solc.js
index 7179ebda56..5aaf3f54d7 100755
--- a/apps/explorer/priv/compile_solc.js
+++ b/apps/explorer/priv/compile_solc.js
@@ -1,7 +1,5 @@
#!/usr/bin/env node
-const solc = require('solc');
-
var sourceCode = process.argv[2];
var version = process.argv[3];
var optimize = process.argv[4];
@@ -9,38 +7,38 @@ 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);
+var solc = solc.setupMethods(compilerSnapshot);
-var compiled_code = solc.loadRemoteVersion(version, function (err, solcSnapshot) {
- if (err) {
- console.log(JSON.stringify(err.message));
- } else {
- const input = {
- language: 'Solidity',
- sources: {
- [newContractName]: {
- content: sourceCode
- }
- },
- settings: {
- evmVersion: evmVersion,
- optimizer: {
- enabled: optimize == '1',
- runs: optimizationRuns
- },
- libraries: {
- [newContractName]: externalLibraries
- },
- outputSelection: {
- '*': {
- '*': ['*']
- }
- }
+const input = {
+ language: 'Solidity',
+ sources: {
+ [newContractName]: {
+ content: sourceCode
+ }
+ },
+ settings: {
+ evmVersion: evmVersion,
+ optimizer: {
+ enabled: optimize == '1',
+ runs: optimizationRuns
+ },
+ libraries: {
+ [newContractName]: externalLibraries
+ },
+ outputSelection: {
+ '*': {
+ '*': ['*']
}
}
-
- const output = JSON.parse(solcSnapshot.compile(JSON.stringify(input)))
- /** Older solc-bin versions don't use filename as contract key */
- const response = output.contracts[newContractName] || output.contracts['']
- console.log(JSON.stringify(response));
}
-});
+}
+
+
+const output = JSON.parse(solc.compile(JSON.stringify(input)))
+/** Older solc-bin versions don't use filename as contract key */
+const response = output.contracts[newContractName] || output.contracts['']
+console.log(JSON.stringify(response));
diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs
index 36edace635..a9b479c3bd 100644
--- a/apps/explorer/test/explorer/chain_test.exs
+++ b/apps/explorer/test/explorer/chain_test.exs
@@ -1415,6 +1415,33 @@ defmodule Explorer.ChainTest do
|> Enum.map(fn {address, _transaction_count} -> address end)
|> Enum.map(& &1.hash)
end
+
+ test "paginates addresses" do
+ test_hashes =
+ 4..0
+ |> Enum.map(&Explorer.Chain.Hash.cast(Explorer.Chain.Hash.Address, &1))
+ |> Enum.map(&elem(&1, 1))
+
+ result =
+ 4..1
+ |> Enum.map(&insert(:address, fetched_coin_balance: &1, hash: Enum.fetch!(test_hashes, &1 - 1)))
+ |> Enum.map(& &1.hash)
+
+ options = [paging_options: %PagingOptions{page_size: 1}]
+
+ [{top_address, _}] = Chain.list_top_addresses(options)
+ assert top_address.hash == List.first(result)
+
+ tail_options = [
+ paging_options: %PagingOptions{key: {top_address.fetched_coin_balance.value, top_address.hash}, page_size: 3}
+ ]
+
+ tail_result = tail_options |> Chain.list_top_addresses() |> Enum.map(fn {address, _} -> address.hash end)
+
+ [_ | expected_tail] = result
+
+ assert tail_result == expected_tail
+ end
end
describe "stream_blocks_without_rewards/2" do