FORMAT ALL TEH THINGS

pull/42/head
CJ Bryan and Desmond Bowe 7 years ago
parent 99615e86c6
commit 5f937d84ed
  1. 5
      .formatter.exs
  2. 1
      circle.yml
  3. 25
      config/config.exs
  4. 30
      config/dev.exs
  5. 37
      config/prod.exs
  6. 6
      config/test.exs
  7. 22
      lib/explorer/address.ex
  8. 16
      lib/explorer/application.ex
  9. 34
      lib/explorer/block.ex
  10. 13
      lib/explorer/block_transaction.ex
  11. 54
      lib/explorer/chain.ex
  12. 13
      lib/explorer/credit.ex
  13. 13
      lib/explorer/debit.ex
  14. 4
      lib/explorer/forms/address_form.ex
  15. 19
      lib/explorer/forms/block_form.ex
  16. 8
      lib/explorer/forms/pending_transaction_form.ex
  17. 34
      lib/explorer/forms/transaction_form.ex
  18. 13
      lib/explorer/from_address.ex
  19. 23
      lib/explorer/importers/block_importer.ex
  20. 19
      lib/explorer/importers/internal_transaction_importer.ex
  21. 22
      lib/explorer/importers/receipt_importer.ex
  22. 46
      lib/explorer/importers/transaction_importer.ex
  23. 28
      lib/explorer/internal_transaction.ex
  24. 26
      lib/explorer/log.ex
  25. 18
      lib/explorer/receipt.ex
  26. 32
      lib/explorer/resource.ex
  27. 5
      lib/explorer/servers/chain_statistics.ex
  28. 9
      lib/explorer/services/transaction.ex
  29. 19
      lib/explorer/skipped_blocks.ex
  30. 15
      lib/explorer/skipped_receipts.ex
  31. 13
      lib/explorer/to_address.ex
  32. 46
      lib/explorer/transaction.ex
  33. 4
      lib/explorer/workers/import_skipped_blocks.ex
  34. 13
      lib/explorer/workers/refresh_balance.ex
  35. 5
      lib/explorer_web.ex
  36. 2
      lib/explorer_web/channels/user_socket.ex
  37. 12
      lib/explorer_web/controllers/address_controller.ex
  38. 34
      lib/explorer_web/controllers/address_transaction_from_controller.ex
  39. 34
      lib/explorer_web/controllers/address_transaction_to_controller.ex
  40. 18
      lib/explorer_web/controllers/block_controller.ex
  41. 20
      lib/explorer_web/controllers/block_transaction_controller.ex
  42. 16
      lib/explorer_web/controllers/chain_controller.ex
  43. 8
      lib/explorer_web/controllers/internal_transaction_controller.ex
  44. 67
      lib/explorer_web/controllers/pending_transaction_controller.ex
  45. 98
      lib/explorer_web/controllers/transaction_controller.ex
  46. 13
      lib/explorer_web/controllers/transaction_log_controller.ex
  47. 36
      lib/explorer_web/endpoint.ex
  48. 11
      lib/explorer_web/gettext.ex
  49. 86
      lib/explorer_web/router.ex
  50. 4
      lib/explorer_web/views/error_helpers.ex
  51. 2
      lib/explorer_web/views/error_view.ex
  52. 2
      lib/explorer_web/views/transaction_view.ex
  53. 2
      lib/mix/tasks/exq.start.ex
  54. 2
      lib/mix/tasks/scrape.blocks.ex
  55. 2
      lib/mix/tasks/scrape.receipts.ex
  56. 44
      mix.exs
  57. 4
      test/explorer/address_test.exs
  58. 16
      test/explorer/block_test.exs
  59. 13
      test/explorer/chain_test.exs
  60. 16
      test/explorer/credit_test.exs
  61. 16
      test/explorer/debit_test.exs
  62. 7
      test/explorer/forms/address_form_test.exs
  63. 2
      test/explorer/forms/block_form_test.exs
  64. 24
      test/explorer/forms/pending_transaction_form_test.exs
  65. 337
      test/explorer/forms/transaction_form_test.exs
  66. 93
      test/explorer/importers/block_importer_test.exs
  67. 71
      test/explorer/importers/internal_transaction_importer_test.exs
  68. 94
      test/explorer/importers/receipt_importer_test.exs
  69. 185
      test/explorer/importers/transaction_importer_test.exs
  70. 32
      test/explorer/internal_transaction_test.exs
  71. 2
      test/explorer/receipt_test.exs
  72. 22
      test/explorer/services/transaction_test.exs
  73. 17
      test/explorer/transaction_test.exs
  74. 16
      test/explorer/workers/import_block_test.exs
  75. 18
      test/explorer/workers/import_receipt_test.exs
  76. 5
      test/explorer/workers/import_skipped_blocks_test.exs
  77. 71
      test/explorer/workers/import_transaction_test.exs
  78. 8
      test/explorer/workers/refresh_balance_test.exs
  79. 4
      test/explorer_web/controllers/address_controller_test.exs
  80. 25
      test/explorer_web/controllers/address_transaction_from_controller_test.exs
  81. 25
      test/explorer_web/controllers/address_transaction_to_controller_test.exs
  82. 8
      test/explorer_web/controllers/block_controller_test.exs
  83. 39
      test/explorer_web/controllers/chain_controller_test.exs
  84. 11
      test/explorer_web/controllers/pending_transaction_controller_test.exs
  85. 6
      test/explorer_web/controllers/transaction_controller_test.exs
  86. 98
      test/explorer_web/features/contributor_browsing_test.exs
  87. 9
      test/explorer_web/views/error_view_test.exs
  88. 4
      test/support/channel_case.ex
  89. 2
      test/support/conn_case.ex
  90. 2
      test/support/factories/block_factory.ex
  91. 6
      test/support/factories/internal_transaction_factory.ex
  92. 2
      test/support/factories/log_factory.ex
  93. 2
      test/support/factories/receipt_factory.ex
  94. 16
      test/support/factories/transaction_factory.ex
  95. 4
      test/test_helper.exs

@ -0,0 +1,5 @@
[
inputs: [
"mix.exs", "{config,lib,test}/**/*.{ex,exs}"
]
]

@ -55,6 +55,7 @@ dependencies:
test: test:
pre: pre:
- mix format --check-formatted
- mix credo - mix credo
- mix sobelow --config - mix sobelow --config
- mix dialyzer --halt-exit-status - mix dialyzer --halt-exit-status

@ -6,8 +6,7 @@
use Mix.Config use Mix.Config
# General application configuration # General application configuration
config :explorer, config :explorer, ecto_repos: [Explorer.Repo]
ecto_repos: [Explorer.Repo]
# Configures gettext # Configures gettext
config :explorer, ExplorerWeb.Gettext, locales: ~w(en), default_locale: "en" config :explorer, ExplorerWeb.Gettext, locales: ~w(en), default_locale: "en"
@ -18,24 +17,21 @@ config :explorer, ExplorerWeb.Endpoint,
render_errors: [view: ExplorerWeb.ErrorView, accepts: ~w(html json)], render_errors: [view: ExplorerWeb.ErrorView, accepts: ~w(html json)],
pubsub: [name: Explorer.PubSub, adapter: Phoenix.PubSub.PG2] pubsub: [name: Explorer.PubSub, adapter: Phoenix.PubSub.PG2]
config :explorer, Explorer.Integrations.EctoLogger, config :explorer, Explorer.Integrations.EctoLogger, query_time_ms_threshold: 2_000
query_time_ms_threshold: 2_000
# Configures Elixir's Logger # Configures Elixir's Logger
config :logger, :console, config :logger, :console,
format: "$time $metadata[$level] $message\n", format: "$time $metadata[$level] $message\n",
metadata: [:request_id] metadata: [:request_id]
config :ethereumex, config :ethereumex, url: "http://localhost:8545"
url: "http://localhost:8545"
config :new_relixir, config :new_relixir, active: false
active: false
config :ex_cldr, config :ex_cldr,
default_locale: "en", default_locale: "en",
locales: ["en"], locales: ["en"],
gettext: ExplorerWeb.Gettext gettext: ExplorerWeb.Gettext
config :exq, config :exq,
host: "localhost", host: "localhost",
@ -47,9 +43,8 @@ config :exq,
max_retries: 10, max_retries: 10,
queues: [{"default", 1}, {"blocks", 1}, {"transactions", 1}, {"receipts", 1}] queues: [{"default", 1}, {"blocks", 1}, {"transactions", 1}, {"receipts", 1}]
config :exq_ui, config :exq_ui, server: false
server: false
# 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.
import_config "#{Mix.env}.exs" import_config "#{Mix.env()}.exs"

@ -11,8 +11,14 @@ config :explorer, ExplorerWeb.Endpoint,
debug_errors: true, debug_errors: true,
code_reloader: true, code_reloader: true,
check_origin: false, check_origin: false,
watchers: [node: ["node_modules/brunch/bin/brunch", "watch", "--stdin", watchers: [
cd: Path.expand("../assets", __DIR__)]] node: [
"node_modules/brunch/bin/brunch",
"watch",
"--stdin",
cd: Path.expand("../assets", __DIR__)
]
]
# ## SSL Support # ## SSL Support
# #
@ -58,10 +64,22 @@ config :explorer, Explorer.Repo,
# Configure Quantum # Configure Quantum
config :explorer, Explorer.Scheduler, config :explorer, Explorer.Scheduler,
jobs: [ jobs: [
[schedule: {:extended, "*/15 * * * * *"}, task: {Explorer.Workers.RefreshBalance, :perform_later, []}], [
[schedule: {:extended, "*/5 * * * * *"}, task: {Explorer.Workers.ImportBlock, :perform_later, ["latest"]}], schedule: {:extended, "*/15 * * * * *"},
[schedule: {:extended, "*/5 * * * * *"}, task: {Explorer.Workers.ImportBlock, :perform_later, ["pending"]}], task: {Explorer.Workers.RefreshBalance, :perform_later, []}
[schedule: {:extended, "*/15 * * * * *"}, task: {Explorer.Workers.ImportSkippedBlocks, :perform_later, [1]}], ],
[
schedule: {:extended, "*/5 * * * * *"},
task: {Explorer.Workers.ImportBlock, :perform_later, ["latest"]}
],
[
schedule: {:extended, "*/5 * * * * *"},
task: {Explorer.Workers.ImportBlock, :perform_later, ["pending"]}
],
[
schedule: {:extended, "*/15 * * * * *"},
task: {Explorer.Workers.ImportSkippedBlocks, :perform_later, [1]}
]
] ]
import_config "dev.secret.exs" import_config "dev.secret.exs"

@ -18,9 +18,17 @@ config :explorer, ExplorerWeb.Endpoint,
force_ssl: [rewrite_on: [:x_forwarded_proto]], force_ssl: [rewrite_on: [:x_forwarded_proto]],
instrumenters: [NewRelixir.Instrumenters.Phoenix], instrumenters: [NewRelixir.Instrumenters.Phoenix],
load_from_system_env: true, load_from_system_env: true,
pubsub: [adapter: Phoenix.PubSub.Redis, url: System.get_env("REDIS_URL"), node_name: System.get_env("DYNO")], pubsub: [
adapter: Phoenix.PubSub.Redis,
url: System.get_env("REDIS_URL"),
node_name: System.get_env("DYNO")
],
secret_key_base: System.get_env("SECRET_KEY_BASE"), secret_key_base: System.get_env("SECRET_KEY_BASE"),
url: [scheme: "https", host: Map.fetch!(System.get_env(), "HEROKU_APP_NAME") <> ".herokuapp.com", port: 443] url: [
scheme: "https",
host: Map.fetch!(System.get_env(), "HEROKU_APP_NAME") <> ".herokuapp.com",
port: 443
]
# Do not print debug messages in production # Do not print debug messages in production
config :logger, level: :info config :logger, level: :info
@ -42,16 +50,29 @@ config :new_relixir,
active: true active: true
# Configure Web3 # Configure Web3
config :ethereumex, config :ethereumex, url: System.get_env("ETHEREUM_URL")
url: System.get_env("ETHEREUM_URL")
# Configure Quantum # Configure Quantum
config :explorer, Explorer.Scheduler, config :explorer, Explorer.Scheduler,
jobs: [ jobs: [
[schedule: {:extended, System.get_env("EXQ_BALANCE_SCHEDULE") || "0 * * * * *"}, task: {Explorer.Workers.RefreshBalance, :perform_later, []}], [
[schedule: {:extended, System.get_env("EXQ_LATEST_BLOCK_SCHEDULE") || "* * * * * *"}, task: {Explorer.Workers.ImportBlock, :perform_later, ["latest"]}], schedule: {:extended, System.get_env("EXQ_BALANCE_SCHEDULE") || "0 * * * * *"},
[schedule: {:extended, System.get_env("EXQ_PENDING_BLOCK_SCHEDULE") || "* * * * * *"}, task: {Explorer.Workers.ImportBlock, :perform_later, ["pending"]}], task: {Explorer.Workers.RefreshBalance, :perform_later, []}
[schedule: {:extended, System.get_env("EXQ_BACKFILL_SCHEDULE") || "* * * * * *"}, task: {Explorer.Workers.ImportSkippedBlocks, :perform_later, [String.to_integer(System.get_env("EXQ_BACKFILL_BATCH_SIZE") || "1")]}], ],
[
schedule: {:extended, System.get_env("EXQ_LATEST_BLOCK_SCHEDULE") || "* * * * * *"},
task: {Explorer.Workers.ImportBlock, :perform_later, ["latest"]}
],
[
schedule: {:extended, System.get_env("EXQ_PENDING_BLOCK_SCHEDULE") || "* * * * * *"},
task: {Explorer.Workers.ImportBlock, :perform_later, ["pending"]}
],
[
schedule: {:extended, System.get_env("EXQ_BACKFILL_SCHEDULE") || "* * * * * *"},
task:
{Explorer.Workers.ImportSkippedBlocks, :perform_later,
[String.to_integer(System.get_env("EXQ_BACKFILL_BATCH_SIZE") || "1")]}
]
] ]
# Configure Exq # Configure Exq

@ -21,9 +21,7 @@ config :explorer, Explorer.Repo,
ownership_timeout: 60_000 ownership_timeout: 60_000
# Configure wallaby # Configure wallaby
config :wallaby, config :wallaby, screenshot_on_failure: true
screenshot_on_failure: true
# Configure ethereumex # Configure ethereumex
config :ethereumex, config :ethereumex, url: "https://sokol-trace.poa.network"
url: "https://sokol-trace.poa.network"

@ -13,13 +13,15 @@ defmodule Explorer.Address do
alias Explorer.Debit alias Explorer.Debit
alias Explorer.Repo.NewRelic, as: Repo alias Explorer.Repo.NewRelic, as: Repo
@timestamps_opts [type: Timex.Ecto.DateTime, @timestamps_opts [
autogenerate: {Timex.Ecto.DateTime, :autogenerate, []}] type: Timex.Ecto.DateTime,
autogenerate: {Timex.Ecto.DateTime, :autogenerate, []}
]
schema "addresses" do schema "addresses" do
has_one :credit, Credit has_one(:credit, Credit)
has_one :debit, Debit has_one(:debit, Debit)
field :hash, :string field(:hash, :string)
timestamps() timestamps()
end end
@ -27,9 +29,13 @@ defmodule Explorer.Address do
@optional_attrs ~w()a @optional_attrs ~w()a
def find_or_create_by_hash(hash) do def find_or_create_by_hash(hash) do
query = from a in Address, query =
where: fragment("lower(?)", a.hash) == ^String.downcase(hash), from(
limit: 1 a in Address,
where: fragment("lower(?)", a.hash) == ^String.downcase(hash),
limit: 1
)
case query |> Repo.one() do case query |> Repo.one() do
nil -> Repo.insert!(Address.changeset(%Address{}, %{hash: hash})) nil -> Repo.insert!(Address.changeset(%Address{}, %{hash: hash}))
address -> address address -> address

@ -11,7 +11,7 @@ defmodule Explorer.Application do
# See https://hexdocs.pm/elixir/Supervisor.html # See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options # for other strategies and supported options
opts = [strategy: :one_for_one, name: Explorer.Supervisor] opts = [strategy: :one_for_one, name: Explorer.Supervisor]
Supervisor.start_link(children(Mix.env), opts) Supervisor.start_link(children(Mix.env()), opts)
end end
# Tell Phoenix to update the endpoint configuration # Tell Phoenix to update the endpoint configuration
@ -23,20 +23,24 @@ defmodule Explorer.Application do
end end
defp children(:test), do: children() defp children(:test), do: children()
defp children(_) do defp children(_) do
import Supervisor.Spec import Supervisor.Spec
exq_options = [] |> Keyword.put(:mode, :enqueuer) exq_options = [] |> Keyword.put(:mode, :enqueuer)
children() ++ [
supervisor(Exq, [exq_options]), children() ++
worker(Explorer.Servers.ChainStatistics, []) [
] supervisor(Exq, [exq_options]),
worker(Explorer.Servers.ChainStatistics, [])
]
end end
defp children do defp children do
import Supervisor.Spec import Supervisor.Spec
[ [
supervisor(Explorer.Repo, []), supervisor(Explorer.Repo, []),
supervisor(ExplorerWeb.Endpoint, []), supervisor(ExplorerWeb.Endpoint, [])
] ]
end end
end end

@ -12,24 +12,26 @@ defmodule Explorer.Block do
alias Explorer.BlockTransaction alias Explorer.BlockTransaction
alias Explorer.Transaction alias Explorer.Transaction
@timestamps_opts [type: Timex.Ecto.DateTime, @timestamps_opts [
autogenerate: {Timex.Ecto.DateTime, :autogenerate, []}] type: Timex.Ecto.DateTime,
autogenerate: {Timex.Ecto.DateTime, :autogenerate, []}
]
schema "blocks" do schema "blocks" do
has_many :block_transactions, BlockTransaction has_many(:block_transactions, BlockTransaction)
many_to_many :transactions, Transaction, join_through: "block_transactions" many_to_many(:transactions, Transaction, join_through: "block_transactions")
field :number, :integer field(:number, :integer)
field :hash, :string field(:hash, :string)
field :parent_hash, :string field(:parent_hash, :string)
field :nonce, :string field(:nonce, :string)
field :miner, :string field(:miner, :string)
field :difficulty, :decimal field(:difficulty, :decimal)
field :total_difficulty, :decimal field(:total_difficulty, :decimal)
field :size, :integer field(:size, :integer)
field :gas_limit, :integer field(:gas_limit, :integer)
field :gas_used, :integer field(:gas_used, :integer)
field :timestamp, Timex.Ecto.DateTime field(:timestamp, Timex.Ecto.DateTime)
timestamps() timestamps()
end end

@ -4,13 +4,15 @@ defmodule Explorer.BlockTransaction do
import Ecto.Changeset import Ecto.Changeset
use Ecto.Schema use Ecto.Schema
@timestamps_opts [type: Timex.Ecto.DateTime, @timestamps_opts [
autogenerate: {Timex.Ecto.DateTime, :autogenerate, []}] type: Timex.Ecto.DateTime,
autogenerate: {Timex.Ecto.DateTime, :autogenerate, []}
]
@primary_key false @primary_key false
schema "block_transactions" do schema "block_transactions" do
belongs_to :block, Explorer.Block belongs_to(:block, Explorer.Block)
belongs_to :transaction, Explorer.Transaction, primary_key: true belongs_to(:transaction, Explorer.Transaction, primary_key: true)
timestamps() timestamps()
end end
@ -22,7 +24,6 @@ defmodule Explorer.BlockTransaction do
|> validate_required(@required_attrs) |> validate_required(@required_attrs)
|> cast_assoc(:block) |> cast_assoc(:block)
|> cast_assoc(:transaction) |> cast_assoc(:transaction)
|> unique_constraint(:transaction_id, |> unique_constraint(:transaction_id, name: :block_transactions_transaction_id_index)
name: :block_transactions_transaction_id_index)
end end
end end

@ -11,18 +11,16 @@ defmodule Explorer.Chain do
alias Explorer.Repo, as: Repo alias Explorer.Repo, as: Repo
alias Timex.Duration alias Timex.Duration
defstruct [ defstruct number: -1,
number: -1, timestamp: :calendar.universal_time(),
timestamp: :calendar.universal_time(), average_time: %Duration{seconds: 0, megaseconds: 0, microseconds: 0},
average_time: %Duration{seconds: 0, megaseconds: 0, microseconds: 0}, lag: %Duration{seconds: 0, megaseconds: 0, microseconds: 0},
lag: %Duration{seconds: 0, megaseconds: 0, microseconds: 0}, transaction_count: 0,
transaction_count: 0, skipped_blocks: 0,
skipped_blocks: 0, block_velocity: 0,
block_velocity: 0, transaction_velocity: 0,
transaction_velocity: 0, blocks: [],
blocks: [], transactions: []
transactions: []
]
@average_time_query """ @average_time_query """
SELECT coalesce(avg(difference), interval '0 seconds') SELECT coalesce(avg(difference), interval '0 seconds')
@ -71,19 +69,26 @@ defmodule Explorer.Chain do
""" """
def fetch do def fetch do
blocks = from block in Block, blocks =
order_by: [desc: block.number], from(
preload: :transactions, block in Block,
limit: 5 order_by: [desc: block.number],
preload: :transactions,
limit: 5
)
transactions = from transaction in Transaction, transactions =
join: block in assoc(transaction, :block), from(
order_by: [desc: block.number], transaction in Transaction,
preload: [block: block], join: block in assoc(transaction, :block),
limit: 5 order_by: [desc: block.number],
preload: [block: block],
limit: 5
)
last_block = Block |> Block.latest() |> limit(1) |> Repo.one() last_block = Block |> Block.latest() |> limit(1) |> Repo.one()
latest_block = last_block || Block.null latest_block = last_block || Block.null()
%Explorer.Chain{ %Explorer.Chain{
number: latest_block.number, number: latest_block.number,
timestamp: latest_block.timestamp, timestamp: latest_block.timestamp,
@ -105,10 +110,13 @@ defmodule Explorer.Chain do
defp query_duration(query) do defp query_duration(query) do
results = SQL.query!(Repo, query, []) results = SQL.query!(Repo, query, [])
{:ok, value} = results.rows
{:ok, value} =
results.rows
|> List.first() |> List.first()
|> List.first() |> List.first()
|> Timex.Ecto.Time.load() |> Timex.Ecto.Time.load()
value value
end end
end end

@ -9,19 +9,22 @@ defmodule Explorer.Credit do
alias Explorer.Address alias Explorer.Address
alias Explorer.Repo alias Explorer.Repo
@timestamps_opts [type: Timex.Ecto.DateTime, @timestamps_opts [
autogenerate: {Timex.Ecto.DateTime, :autogenerate, []}] type: Timex.Ecto.DateTime,
autogenerate: {Timex.Ecto.DateTime, :autogenerate, []}
]
@primary_key false @primary_key false
schema "credits" do schema "credits" do
belongs_to :address, Address, primary_key: true belongs_to(:address, Address, primary_key: true)
field :value, :decimal field(:value, :decimal)
field :count, :integer field(:count, :integer)
timestamps() timestamps()
end end
def refresh do def refresh do
SQL.query!(Repo, "REFRESH MATERIALIZED VIEW CONCURRENTLY credits;", [], timeout: 120_000) SQL.query!(Repo, "REFRESH MATERIALIZED VIEW CONCURRENTLY credits;", [], timeout: 120_000)
end end
def null, do: %__MODULE__{value: Decimal.new(0), count: 0} def null, do: %__MODULE__{value: Decimal.new(0), count: 0}
end end

@ -9,19 +9,22 @@ defmodule Explorer.Debit do
alias Explorer.Address alias Explorer.Address
alias Explorer.Repo alias Explorer.Repo
@timestamps_opts [type: Timex.Ecto.DateTime, @timestamps_opts [
autogenerate: {Timex.Ecto.DateTime, :autogenerate, []}] type: Timex.Ecto.DateTime,
autogenerate: {Timex.Ecto.DateTime, :autogenerate, []}
]
@primary_key false @primary_key false
schema "debits" do schema "debits" do
belongs_to :address, Address, primary_key: true belongs_to(:address, Address, primary_key: true)
field :value, :decimal field(:value, :decimal)
field :count, :integer field(:count, :integer)
timestamps() timestamps()
end end
def refresh do def refresh do
SQL.query!(Repo, "REFRESH MATERIALIZED VIEW CONCURRENTLY debits;", [], timeout: 120_000) SQL.query!(Repo, "REFRESH MATERIALIZED VIEW CONCURRENTLY debits;", [], timeout: 120_000)
end end
def null, do: %__MODULE__{value: Decimal.new(0), count: 0} def null, do: %__MODULE__{value: Decimal.new(0), count: 0}
end end

@ -5,8 +5,8 @@ defmodule Explorer.AddressForm do
alias Explorer.Debit alias Explorer.Debit
def build(address) do def build(address) do
credit = address.credit || Credit.null credit = address.credit || Credit.null()
debit = address.debit || Debit.null debit = address.debit || Debit.null()
balance = Decimal.sub(credit.value, debit.value) balance = Decimal.sub(credit.value, debit.value)
Map.put(address, :balance, balance) Map.put(address, :balance, balance)
end end

@ -6,24 +6,29 @@ defmodule Explorer.BlockForm do
import Ecto.Query import Ecto.Query
def build(block) do def build(block) do
block |> Map.merge(%{ block
|> Map.merge(%{
transactions_count: block |> get_transactions_count, transactions_count: block |> get_transactions_count,
age: block |> calculate_age, age: block |> calculate_age,
formatted_timestamp: block |> format_timestamp, formatted_timestamp: block |> format_timestamp
}) })
end end
def get_transactions_count(block) do def get_transactions_count(block) do
query = from block_transaction in BlockTransaction, query =
join: block in Block, from(
block_transaction in BlockTransaction,
join: block in Block,
where: block.id == block_transaction.block_id, where: block.id == block_transaction.block_id,
where: block.id == ^block.id, where: block.id == ^block.id,
select: count(block_transaction.block_id) select: count(block_transaction.block_id)
)
Repo.one(query) Repo.one(query)
end end
def calculate_age(block) do def calculate_age(block) do
block.timestamp |> Timex.from_now block.timestamp |> Timex.from_now()
end end
def format_timestamp(block) do def format_timestamp(block) do

@ -15,18 +15,18 @@ defmodule Explorer.PendingTransactionForm do
end end
def to_address_hash(transaction) do def to_address_hash(transaction) do
transaction.to_address && transaction.to_address.hash || nil (transaction.to_address && transaction.to_address.hash) || nil
end end
def from_address_hash(transaction) do def from_address_hash(transaction) do
transaction.to_address && transaction.from_address.hash || nil (transaction.to_address && transaction.from_address.hash) || nil
end end
def first_seen(transaction) do def first_seen(transaction) do
transaction.inserted_at |> Timex.from_now transaction.inserted_at |> Timex.from_now()
end end
def last_seen(transaction) do def last_seen(transaction) do
transaction.updated_at |> Timex.from_now transaction.updated_at |> Timex.from_now()
end end
end end

@ -10,9 +10,9 @@ defmodule Explorer.TransactionForm do
alias Explorer.Repo alias Explorer.Repo
def build(transaction) do def build(transaction) do
block = Ecto.assoc_loaded?(transaction.block) && transaction.block || nil block = (Ecto.assoc_loaded?(transaction.block) && transaction.block) || nil
receipt = Ecto.assoc_loaded?(transaction.receipt) && transaction.receipt receipt = Ecto.assoc_loaded?(transaction.receipt) && transaction.receipt
status = status(transaction, receipt || Receipt.null) status = status(transaction, receipt || Receipt.null())
%{ %{
block_number: block |> block_number, block_number: block |> block_number,
@ -26,7 +26,7 @@ defmodule Explorer.TransactionForm do
status: status, status: status,
formatted_status: status |> format_status, formatted_status: status |> format_status,
first_seen: transaction |> first_seen, first_seen: transaction |> first_seen,
last_seen: transaction |> last_seen, last_seen: transaction |> last_seen
} }
end end
@ -35,36 +35,37 @@ defmodule Explorer.TransactionForm do
end end
def block_number(block) do def block_number(block) do
block && block.number || "" (block && block.number) || ""
end end
def block_age(block) do def block_age(block) do
block && block.timestamp |> Timex.from_now || gettext("Pending") (block && block.timestamp |> Timex.from_now()) || gettext("Pending")
end end
def format_age(block) do def format_age(block) do
block && "#{block_age(block)} (#{format_timestamp(block)})" || gettext("Pending") (block && "#{block_age(block)} (#{format_timestamp(block)})") || gettext("Pending")
end end
def format_timestamp(block) do def format_timestamp(block) do
block && block.timestamp |> Timex.format!("%b-%d-%Y %H:%M:%S %p %Z", :strftime) || gettext("Pending") (block && block.timestamp |> Timex.format!("%b-%d-%Y %H:%M:%S %p %Z", :strftime)) ||
gettext("Pending")
end end
def cumulative_gas_used(block) do def cumulative_gas_used(block) do
block && block.gas_used |> Number.to_string! || gettext("Pending") (block && block.gas_used |> Number.to_string!()) || gettext("Pending")
end end
def to_address_hash(transaction) do def to_address_hash(transaction) do
transaction.to_address && transaction.to_address.hash || nil (transaction.to_address && transaction.to_address.hash) || nil
end end
def from_address_hash(transaction) do def from_address_hash(transaction) do
transaction.to_address && transaction.from_address.hash || nil (transaction.to_address && transaction.from_address.hash) || nil
end end
def confirmations(block) do def confirmations(block) do
query = from block in Block, select: max(block.number) query = from(block in Block, select: max(block.number))
block && Repo.one(query) - block.number || 0 (block && Repo.one(query) - block.number) || 0
end end
def status(transaction, receipt) do def status(transaction, receipt) do
@ -81,15 +82,16 @@ defmodule Explorer.TransactionForm do
out_of_gas: gettext("Out of Gas"), out_of_gas: gettext("Out of Gas"),
failed: gettext("Failed"), failed: gettext("Failed"),
success: gettext("Success"), success: gettext("Success"),
pending: gettext("Pending"), pending: gettext("Pending")
} |> Map.fetch!(status) }
|> Map.fetch!(status)
end end
def first_seen(transaction) do def first_seen(transaction) do
transaction.inserted_at |> Timex.from_now transaction.inserted_at |> Timex.from_now()
end end
def last_seen(transaction) do def last_seen(transaction) do
transaction.updated_at |> Timex.from_now transaction.updated_at |> Timex.from_now()
end end
end end

@ -4,20 +4,21 @@ defmodule Explorer.FromAddress do
import Ecto.Changeset import Ecto.Changeset
use Ecto.Schema use Ecto.Schema
@timestamps_opts [type: Timex.Ecto.DateTime, @timestamps_opts [
autogenerate: {Timex.Ecto.DateTime, :autogenerate, []}] type: Timex.Ecto.DateTime,
autogenerate: {Timex.Ecto.DateTime, :autogenerate, []}
]
@primary_key false @primary_key false
schema "from_addresses" do schema "from_addresses" do
belongs_to :transaction, Explorer.Transaction, primary_key: true belongs_to(:transaction, Explorer.Transaction, primary_key: true)
belongs_to :address, Explorer.Address belongs_to(:address, Explorer.Address)
timestamps() timestamps()
end end
def changeset(%FromAddress{} = to_address, attrs \\ %{}) do def changeset(%FromAddress{} = to_address, attrs \\ %{}) do
to_address to_address
|> cast(attrs, [:transaction_id, :address_id]) |> cast(attrs, [:transaction_id, :address_id])
|> unique_constraint(:transaction_id, |> unique_constraint(:transaction_id, name: :from_addresses_transaction_id_index)
name: :from_addresses_transaction_id_index)
end end
end end

@ -12,7 +12,7 @@ defmodule Explorer.BlockImporter do
changes = extract_block(raw_block) changes = extract_block(raw_block)
block = changes.hash |> find() block = changes.hash |> find()
if is_nil(block.id), do: block |> Block.changeset(changes) |> Repo.insert if is_nil(block.id), do: block |> Block.changeset(changes) |> Repo.insert()
Enum.map(raw_block["transactions"], &ImportTransaction.perform/1) Enum.map(raw_block["transactions"], &ImportTransaction.perform/1)
end end
@ -30,17 +30,23 @@ defmodule Explorer.BlockImporter do
end end
def find(hash) do def find(hash) do
query = from b in Block, query =
where: fragment("lower(?)", b.hash) == ^String.downcase(hash), from(
limit: 1 b in Block,
(query |> Repo.one()) || %Block{} where: fragment("lower(?)", b.hash) == ^String.downcase(hash),
limit: 1
)
query |> Repo.one() || %Block{}
end end
@dialyzer {:nowarn_function, download_block: 1} @dialyzer {:nowarn_function, download_block: 1}
def download_block(block_number) do def download_block(block_number) do
{:ok, block} = block_number {:ok, block} =
block_number
|> encode_number() |> encode_number()
|> eth_get_block_by_number(true) |> eth_get_block_by_number(true)
block block
end end
@ -56,7 +62,7 @@ defmodule Explorer.BlockImporter do
total_difficulty: raw_block["totalDifficulty"] |> decode_integer_field, total_difficulty: raw_block["totalDifficulty"] |> decode_integer_field,
size: raw_block["size"] |> decode_integer_field, size: raw_block["size"] |> decode_integer_field,
gas_limit: raw_block["gasLimit"] |> decode_integer_field, gas_limit: raw_block["gasLimit"] |> decode_integer_field,
nonce: raw_block["nonce"] || "0", nonce: raw_block["nonce"] || "0"
} }
end end
@ -64,6 +70,7 @@ defmodule Explorer.BlockImporter do
defp encode_number("earliest"), do: "earliest" defp encode_number("earliest"), do: "earliest"
defp encode_number("pending"), do: "pending" defp encode_number("pending"), do: "pending"
defp encode_number("0x" <> number) when is_binary(number), do: number defp encode_number("0x" <> number) when is_binary(number), do: number
defp encode_number(number) when is_binary(number) do defp encode_number(number) when is_binary(number) do
number number
|> String.to_integer() |> String.to_integer()
@ -78,6 +85,6 @@ defmodule Explorer.BlockImporter do
end end
def decode_time_field(field) do def decode_time_field(field) do
field |> decode_integer_field |> Timex.from_unix field |> decode_integer_field |> Timex.from_unix()
end end
end end

@ -12,6 +12,7 @@ defmodule Explorer.InternalTransactionImporter do
@dialyzer {:nowarn_function, import: 1} @dialyzer {:nowarn_function, import: 1}
def import(hash) do def import(hash) do
transaction = find_transaction(hash) transaction = find_transaction(hash)
hash hash
|> download_trace |> download_trace
|> extract_attrs |> extract_attrs
@ -24,9 +25,13 @@ defmodule Explorer.InternalTransactionImporter do
end end
defp find_transaction(hash) do defp find_transaction(hash) do
query = from t in Transaction, query =
where: fragment("lower(?)", t.hash) == ^String.downcase(hash), from(
limit: 1 t in Transaction,
where: fragment("lower(?)", t.hash) == ^String.downcase(hash),
limit: 1
)
Repo.one!(query) Repo.one!(query)
end end
@ -47,12 +52,13 @@ defmodule Explorer.InternalTransactionImporter do
gas: trace["action"]["gas"] |> decode_integer_field, gas: trace["action"]["gas"] |> decode_integer_field,
gas_used: trace["result"]["gasUsed"] |> decode_integer_field, gas_used: trace["result"]["gasUsed"] |> decode_integer_field,
input: trace["action"]["input"], input: trace["action"]["input"],
output: trace["result"]["output"], output: trace["result"]["output"]
} }
end end
defp to_address(%{"action" => %{"to" => address}}) defp to_address(%{"action" => %{"to" => address}})
when not is_nil(address), do: address when not is_nil(address),
do: address
defp to_address(%{"result" => %{"address" => address}}), do: address defp to_address(%{"result" => %{"address" => address}}), do: address
@ -60,8 +66,9 @@ defmodule Explorer.InternalTransactionImporter do
@dialyzer {:nowarn_function, persist_internal_transactions: 2} @dialyzer {:nowarn_function, persist_internal_transactions: 2}
defp persist_internal_transactions(traces, transaction) do defp persist_internal_transactions(traces, transaction) do
Enum.map(traces, fn(trace) -> Enum.map(traces, fn trace ->
trace = Map.merge(trace, %{transaction_id: transaction.id}) trace = Map.merge(trace, %{transaction_id: transaction.id})
%InternalTransaction{} %InternalTransaction{}
|> InternalTransaction.changeset(trace) |> InternalTransaction.changeset(trace)
|> Repo.insert() |> Repo.insert()

@ -11,6 +11,7 @@ defmodule Explorer.ReceiptImporter do
def import(hash) do def import(hash) do
transaction = hash |> find_transaction() transaction = hash |> find_transaction()
hash hash
|> download_receipt() |> download_receipt()
|> extract_receipt() |> extract_receipt()
@ -25,12 +26,16 @@ defmodule Explorer.ReceiptImporter do
end end
defp find_transaction(hash) do defp find_transaction(hash) do
query = from transaction in Transaction, query =
left_join: receipt in assoc(transaction, :receipt), from(
where: fragment("lower(?)", transaction.hash) == ^hash, transaction in Transaction,
where: is_nil(receipt.id), left_join: receipt in assoc(transaction, :receipt),
limit: 1 where: fragment("lower(?)", transaction.hash) == ^hash,
Repo.one(query) || Transaction.null where: is_nil(receipt.id),
limit: 1
)
Repo.one(query) || Transaction.null()
end end
defp save_receipt(receipt) do defp save_receipt(receipt) do
@ -43,6 +48,7 @@ defmodule Explorer.ReceiptImporter do
defp extract_receipt(receipt) do defp extract_receipt(receipt) do
logs = receipt["logs"] || [] logs = receipt["logs"] || []
%{ %{
index: receipt["transactionIndex"] |> decode_integer_field(), index: receipt["transactionIndex"] |> decode_integer_field(),
cumulative_gas_used: receipt["cumulativeGasUsed"] |> decode_integer_field(), cumulative_gas_used: receipt["cumulativeGasUsed"] |> decode_integer_field(),
@ -54,6 +60,7 @@ defmodule Explorer.ReceiptImporter do
defp extract_log(log) do defp extract_log(log) do
address = Address.find_or_create_by_hash(log["address"]) address = Address.find_or_create_by_hash(log["address"])
%{ %{
address_id: address.id, address_id: address.id,
index: log["logIndex"] |> decode_integer_field(), index: log["logIndex"] |> decode_integer_field(),
@ -62,12 +69,13 @@ defmodule Explorer.ReceiptImporter do
first_topic: log["topics"] |> Enum.at(0), first_topic: log["topics"] |> Enum.at(0),
second_topic: log["topics"] |> Enum.at(1), second_topic: log["topics"] |> Enum.at(1),
third_topic: log["topics"] |> Enum.at(2), third_topic: log["topics"] |> Enum.at(2),
fourth_topic: log["topics"] |> Enum.at(3), fourth_topic: log["topics"] |> Enum.at(3)
} }
end end
defp decode_integer_field("0x" <> hex) when is_binary(hex) do defp decode_integer_field("0x" <> hex) when is_binary(hex) do
String.to_integer(hex, 16) String.to_integer(hex, 16)
end end
defp decode_integer_field(field), do: field defp decode_integer_field(field), do: field
end end

@ -23,14 +23,18 @@ defmodule Explorer.TransactionImporter do
def persist_transaction(raw_transaction) do def persist_transaction(raw_transaction) do
found_transaction = raw_transaction["hash"] |> find() found_transaction = raw_transaction["hash"] |> find()
transaction = case is_nil(found_transaction.id) do transaction =
false -> found_transaction case is_nil(found_transaction.id) do
true -> false ->
changes = extract_attrs(raw_transaction) found_transaction
found_transaction |> Transaction.changeset(changes) |> Repo.insert!
end true ->
changes = extract_attrs(raw_transaction)
found_transaction |> Transaction.changeset(changes) |> Repo.insert!()
end
to_address = raw_transaction["to"] || raw_transaction["creates"] to_address = raw_transaction["to"] || raw_transaction["creates"]
transaction transaction
|> create_from_address(raw_transaction["from"]) |> create_from_address(raw_transaction["from"])
|> create_to_address(to_address) |> create_to_address(to_address)
@ -38,10 +42,14 @@ defmodule Explorer.TransactionImporter do
end end
def find(hash) do def find(hash) do
query = from t in Transaction, query =
where: fragment("lower(?)", t.hash) == ^String.downcase(hash), from(
limit: 1 t in Transaction,
(query |> Repo.one()) || %Transaction{} where: fragment("lower(?)", t.hash) == ^String.downcase(hash),
limit: 1
)
query |> Repo.one() || %Transaction{}
end end
def download_transaction(hash) do def download_transaction(hash) do
@ -62,27 +70,33 @@ defmodule Explorer.TransactionImporter do
s: raw_transaction["s"], s: raw_transaction["s"],
standard_v: raw_transaction["standardV"], standard_v: raw_transaction["standardV"],
transaction_index: raw_transaction["transactionIndex"], transaction_index: raw_transaction["transactionIndex"],
v: raw_transaction["v"], v: raw_transaction["v"]
} }
end end
def create_block_transaction(transaction, hash) do def create_block_transaction(transaction, hash) do
query = from t in Block, query =
where: fragment("lower(?)", t.hash) == ^String.downcase(hash), from(
limit: 1 t in Block,
where: fragment("lower(?)", t.hash) == ^String.downcase(hash),
limit: 1
)
block = query |> Repo.one() block = query |> Repo.one()
if block do if block do
changes = %{block_id: block.id, transaction_id: transaction.id} changes = %{block_id: block.id, transaction_id: transaction.id}
case Repo.get_by(BlockTransaction, transaction_id: transaction.id) do case Repo.get_by(BlockTransaction, transaction_id: transaction.id) do
nil -> nil ->
%BlockTransaction{} %BlockTransaction{}
|> BlockTransaction.changeset(changes) |> BlockTransaction.changeset(changes)
|> Repo.insert |> Repo.insert()
block_transaction -> block_transaction ->
block_transaction block_transaction
|> BlockTransaction.changeset(%{block_id: block.id}) |> BlockTransaction.changeset(%{block_id: block.id})
|> Repo.update |> Repo.update()
end end
end end

@ -9,21 +9,23 @@ defmodule Explorer.InternalTransaction do
alias Explorer.Transaction alias Explorer.Transaction
alias Explorer.Address alias Explorer.Address
@timestamps_opts [type: Timex.Ecto.DateTime, @timestamps_opts [
autogenerate: {Timex.Ecto.DateTime, :autogenerate, []}] type: Timex.Ecto.DateTime,
autogenerate: {Timex.Ecto.DateTime, :autogenerate, []}
]
schema "internal_transactions" do schema "internal_transactions" do
belongs_to :transaction, Transaction belongs_to(:transaction, Transaction)
belongs_to :from_address, Address belongs_to(:from_address, Address)
belongs_to :to_address, Address belongs_to(:to_address, Address)
field :index, :integer field(:index, :integer)
field :call_type, :string field(:call_type, :string)
field :trace_address, {:array, :integer} field(:trace_address, {:array, :integer})
field :value, :decimal field(:value, :decimal)
field :gas, :decimal field(:gas, :decimal)
field :gas_used, :decimal field(:gas_used, :decimal)
field :input, :string field(:input, :string)
field :output, :string field(:output, :string)
timestamps() timestamps()
end end

@ -9,8 +9,10 @@ defmodule Explorer.Log do
alias Explorer.Log alias Explorer.Log
alias Explorer.Receipt alias Explorer.Receipt
@timestamps_opts [type: Timex.Ecto.DateTime, @timestamps_opts [
autogenerate: {Timex.Ecto.DateTime, :autogenerate, []}] type: Timex.Ecto.DateTime,
autogenerate: {Timex.Ecto.DateTime, :autogenerate, []}
]
@required_attrs ~w(index data type)a @required_attrs ~w(index data type)a
@optional_attrs ~w( @optional_attrs ~w(
@ -18,16 +20,16 @@ defmodule Explorer.Log do
)a )a
schema "logs" do schema "logs" do
belongs_to :receipt, Receipt belongs_to(:receipt, Receipt)
belongs_to :address, Address belongs_to(:address, Address)
has_one :transaction, through: [:receipt, :transaction] has_one(:transaction, through: [:receipt, :transaction])
field :index, :integer field(:index, :integer)
field :data, :string field(:data, :string)
field :type, :string field(:type, :string)
field :first_topic, :string field(:first_topic, :string)
field :second_topic, :string field(:second_topic, :string)
field :third_topic, :string field(:third_topic, :string)
field :fourth_topic, :string field(:fourth_topic, :string)
timestamps() timestamps()
end end

@ -9,19 +9,21 @@ defmodule Explorer.Receipt do
alias Explorer.Log alias Explorer.Log
alias Explorer.Receipt alias Explorer.Receipt
@timestamps_opts [type: Timex.Ecto.DateTime, @timestamps_opts [
autogenerate: {Timex.Ecto.DateTime, :autogenerate, []}] type: Timex.Ecto.DateTime,
autogenerate: {Timex.Ecto.DateTime, :autogenerate, []}
]
@required_attrs ~w(cumulative_gas_used gas_used status index)a @required_attrs ~w(cumulative_gas_used gas_used status index)a
@optional_attrs ~w(transaction_id)a @optional_attrs ~w(transaction_id)a
schema "receipts" do schema "receipts" do
belongs_to :transaction, Transaction belongs_to(:transaction, Transaction)
has_many :logs, Log has_many(:logs, Log)
field :cumulative_gas_used, :decimal field(:cumulative_gas_used, :decimal)
field :gas_used, :decimal field(:gas_used, :decimal)
field :status, :integer field(:status, :integer)
field :index, :integer field(:index, :integer)
timestamps() timestamps()
end end

@ -1,5 +1,4 @@
defmodule Explorer.Resource do defmodule Explorer.Resource do
@moduledoc "Looks up and fetches resource based on its handle (either an id or hash)" @moduledoc "Looks up and fetches resource based on its handle (either an id or hash)"
import Ecto.Query, only: [from: 2] import Ecto.Query, only: [from: 2]
@ -16,32 +15,41 @@ defmodule Explorer.Resource do
def lookup(number), do: fetch_block(number) def lookup(number), do: fetch_block(number)
def fetch_address(hash) do def fetch_address(hash) do
query = from address in Address, query =
where: fragment("lower(?)", address.hash) == ^String.downcase(hash), from(
limit: 1 address in Address,
where: fragment("lower(?)", address.hash) == ^String.downcase(hash),
limit: 1
)
Repo.one(query) Repo.one(query)
end end
def fetch_transaction(hash) do def fetch_transaction(hash) do
query = from transaction in Transaction, query =
where: fragment("lower(?)", transaction.hash) == ^String.downcase(hash), from(
limit: 1 transaction in Transaction,
where: fragment("lower(?)", transaction.hash) == ^String.downcase(hash),
limit: 1
)
Repo.one(query) Repo.one(query)
end end
def fetch_block(block_number) when is_bitstring(block_number) do def fetch_block(block_number) when is_bitstring(block_number) do
case Integer.parse(block_number) do case Integer.parse(block_number) do
{number, ""} -> fetch_block(number) {number, ""} -> fetch_block(number)
_ -> nil _ -> nil
end end
end end
def fetch_block(number) when is_integer(number) do def fetch_block(number) when is_integer(number) do
query = from b in Block, query =
where: b.number == ^number, from(
limit: 1 b in Block,
where: b.number == ^number,
limit: 1
)
Repo.one(query) Repo.one(query)
end end

@ -15,6 +15,7 @@ defmodule Explorer.Servers.ChainStatistics do
end end
def start_link, do: start_link(true) def start_link, do: start_link(true)
def start_link(refresh) do def start_link(refresh) do
GenServer.start_link(__MODULE__, refresh, name: __MODULE__) GenServer.start_link(__MODULE__, refresh, name: __MODULE__)
end end
@ -30,14 +31,18 @@ defmodule Explorer.Servers.ChainStatistics do
Task.start_link(fn -> Task.start_link(fn ->
GenServer.cast(__MODULE__, {:update, Chain.fetch()}) GenServer.cast(__MODULE__, {:update, Chain.fetch()})
end) end)
{:noreply, chain} {:noreply, chain}
end end
def handle_info(_, %Chain{} = chain), do: {:noreply, chain} def handle_info(_, %Chain{} = chain), do: {:noreply, chain}
def handle_call(:fetch, _, %Chain{} = chain), do: {:reply, chain, chain} def handle_call(:fetch, _, %Chain{} = chain), do: {:reply, chain, chain}
def handle_call(_, _, %Chain{} = chain), do: {:noreply, chain} def handle_call(_, _, %Chain{} = chain), do: {:noreply, chain}
def handle_cast({:update, %Chain{} = chain}, %Chain{} = _) do def handle_cast({:update, %Chain{} = chain}, %Chain{} = _) do
Process.send_after(self(), :refresh, @interval) Process.send_after(self(), :refresh, @interval)
{:noreply, chain} {:noreply, chain}
end end
def handle_cast(_, %Chain{} = chain), do: {:noreply, chain} def handle_cast(_, %Chain{} = chain), do: {:noreply, chain}
end end

@ -18,17 +18,20 @@ defmodule Explorer.Transaction.Service do
import Ecto.Query, only: [from: 2] import Ecto.Query, only: [from: 2]
def for_transaction(query, hash) do def for_transaction(query, hash) do
from(child in query, from(
child in query,
inner_join: transaction in assoc(child, :transaction), inner_join: transaction in assoc(child, :transaction),
where: fragment("lower(?)", transaction.hash) == ^String.downcase(hash) where: fragment("lower(?)", transaction.hash) == ^String.downcase(hash)
) )
end end
def join_from_and_to_addresses(query) do def join_from_and_to_addresses(query) do
from(q in query, from(
q in query,
inner_join: to_address in assoc(q, :to_address), inner_join: to_address in assoc(q, :to_address),
inner_join: from_address in assoc(q, :from_address), inner_join: from_address in assoc(q, :from_address),
preload: [:to_address, :from_address]) preload: [:to_address, :from_address]
)
end end
end end
end end

@ -9,18 +9,23 @@ defmodule Explorer.SkippedBlocks do
@missing_number_query "SELECT generate_series(?, 0, -1) AS missing_number" @missing_number_query "SELECT generate_series(?, 0, -1) AS missing_number"
def first, do: first(1) def first, do: first(1)
def first(count) do def first(count) do
blocks = from b in Block, blocks =
right_join: fragment(@missing_number_query, ^latest_block_number()), from(
on: b.number == fragment("missing_number"), b in Block,
select: fragment("missing_number::text"), right_join: fragment(@missing_number_query, ^latest_block_number()),
where: is_nil(b.id), on: b.number == fragment("missing_number"),
limit: ^count select: fragment("missing_number::text"),
where: is_nil(b.id),
limit: ^count
)
Repo.all(blocks) Repo.all(blocks)
end end
def latest_block_number do def latest_block_number do
block = Repo.one(Block |> Block.latest |> limit(1)) || Block.null block = Repo.one(Block |> Block.latest() |> limit(1)) || Block.null()
block.number block.number
end end
end end

@ -8,12 +8,17 @@ defmodule Explorer.SkippedReceipts do
alias Explorer.Repo.NewRelic, as: Repo alias Explorer.Repo.NewRelic, as: Repo
def first, do: first(1) def first, do: first(1)
def first(count) do def first(count) do
transactions = from transaction in Transaction, transactions =
left_join: receipt in assoc(transaction, :receipt), from(
select: fragment("hash"), transaction in Transaction,
where: is_nil(receipt.id), left_join: receipt in assoc(transaction, :receipt),
limit: ^count select: fragment("hash"),
where: is_nil(receipt.id),
limit: ^count
)
Repo.all(transactions) Repo.all(transactions)
end end
end end

@ -4,20 +4,21 @@ defmodule Explorer.ToAddress do
import Ecto.Changeset import Ecto.Changeset
use Ecto.Schema use Ecto.Schema
@timestamps_opts [type: Timex.Ecto.DateTime, @timestamps_opts [
autogenerate: {Timex.Ecto.DateTime, :autogenerate, []}] type: Timex.Ecto.DateTime,
autogenerate: {Timex.Ecto.DateTime, :autogenerate, []}
]
@primary_key false @primary_key false
schema "to_addresses" do schema "to_addresses" do
belongs_to :transaction, Explorer.Transaction, primary_key: true belongs_to(:transaction, Explorer.Transaction, primary_key: true)
belongs_to :address, Explorer.Address belongs_to(:address, Explorer.Address)
timestamps() timestamps()
end end
def changeset(%ToAddress{} = to_address, attrs \\ %{}) do def changeset(%ToAddress{} = to_address, attrs \\ %{}) do
to_address to_address
|> cast(attrs, [:transaction_id, :address_id]) |> cast(attrs, [:transaction_id, :address_id])
|> unique_constraint(:transaction_id, |> unique_constraint(:transaction_id, name: :to_addresses_transaction_id_index)
name: :to_addresses_transaction_id_index)
end end
end end

@ -12,30 +12,32 @@ defmodule Explorer.Transaction do
alias Explorer.ToAddress alias Explorer.ToAddress
alias Explorer.Transaction alias Explorer.Transaction
@timestamps_opts [type: Timex.Ecto.DateTime, @timestamps_opts [
autogenerate: {Timex.Ecto.DateTime, :autogenerate, []}] type: Timex.Ecto.DateTime,
autogenerate: {Timex.Ecto.DateTime, :autogenerate, []}
]
schema "transactions" do schema "transactions" do
has_one :receipt, Receipt has_one(:receipt, Receipt)
has_one :block_transaction, BlockTransaction has_one(:block_transaction, BlockTransaction)
has_one :block, through: [:block_transaction, :block] has_one(:block, through: [:block_transaction, :block])
has_one :to_address_join, ToAddress has_one(:to_address_join, ToAddress)
has_one :to_address, through: [:to_address_join, :address] has_one(:to_address, through: [:to_address_join, :address])
has_one :from_address_join, FromAddress has_one(:from_address_join, FromAddress)
has_one :from_address, through: [:from_address_join, :address] has_one(:from_address, through: [:from_address_join, :address])
has_many :internal_transactions, InternalTransaction has_many(:internal_transactions, InternalTransaction)
field :hash, :string field(:hash, :string)
field :value, :decimal field(:value, :decimal)
field :gas, :decimal field(:gas, :decimal)
field :gas_price, :decimal field(:gas_price, :decimal)
field :input, :string field(:input, :string)
field :nonce, :integer field(:nonce, :integer)
field :public_key, :string field(:public_key, :string)
field :r, :string field(:r, :string)
field :s, :string field(:s, :string)
field :standard_v, :string field(:standard_v, :string)
field :transaction_index, :string field(:transaction_index, :string)
field :v, :string field(:v, :string)
timestamps() timestamps()
end end

@ -5,11 +5,13 @@ defmodule Explorer.Workers.ImportSkippedBlocks do
@moduledoc "Imports skipped blocks." @moduledoc "Imports skipped blocks."
def perform, do: perform(1) def perform, do: perform(1)
def perform(count) do def perform(count) do
count |> SkippedBlocks.first |> Enum.map(&ImportBlock.perform_later/1) count |> SkippedBlocks.first() |> Enum.map(&ImportBlock.perform_later/1)
end end
def perform_later, do: perform_later(1) def perform_later, do: perform_later(1)
def perform_later(count) do def perform_later(count) do
Exq.enqueue(Exq.Enqueuer, "default", __MODULE__, [count]) Exq.enqueue(Exq.Enqueuer, "default", __MODULE__, [count])
end end

@ -8,8 +8,8 @@ defmodule Explorer.Workers.RefreshBalance do
alias Explorer.Debit alias Explorer.Debit
alias Explorer.Repo alias Explorer.Repo
def perform("credit"), do: unless refreshing("credits"), do: Credit.refresh() def perform("credit"), do: unless(refreshing("credits"), do: Credit.refresh())
def perform("debit"), do: unless refreshing("debits"), do: Debit.refresh() def perform("debit"), do: unless(refreshing("debits"), do: Debit.refresh())
def perform do def perform do
perform_later(["credit"]) perform_later(["credit"])
@ -22,11 +22,10 @@ defmodule Explorer.Workers.RefreshBalance do
def refreshing(table) do def refreshing(table) do
query = "REFRESH MATERIALIZED VIEW CONCURRENTLY #{table}%" query = "REFRESH MATERIALIZED VIEW CONCURRENTLY #{table}%"
result = SQL.query!(
Repo, result =
"SELECT TRUE FROM pg_stat_activity WHERE query ILIKE '$#{query}'", SQL.query!(Repo, "SELECT TRUE FROM pg_stat_activity WHERE query ILIKE '$#{query}'", [])
[]
)
Enum.count(result.rows) > 0 Enum.count(result.rows) > 0
end end
end end

@ -28,8 +28,9 @@ defmodule ExplorerWeb do
def view do def view do
quote do quote do
use Phoenix.View, root: "lib/explorer_web/templates", use Phoenix.View,
namespace: ExplorerWeb root: "lib/explorer_web/templates",
namespace: ExplorerWeb
# Import convenience functions from controllers # Import convenience functions from controllers
import Phoenix.Controller, only: [get_flash: 2, view_module: 1] import Phoenix.Controller, only: [get_flash: 2, view_module: 1]

@ -5,7 +5,7 @@ defmodule ExplorerWeb.UserSocket do
# channel "room:*", ExplorerWeb.RoomChannel # channel "room:*", ExplorerWeb.RoomChannel
## Transports ## Transports
transport :websocket, Phoenix.Transports.WebSocket, timeout: 45_000 transport(:websocket, Phoenix.Transports.WebSocket, timeout: 45_000)
# transport :longpoll, Phoenix.Transports.LongPoll # transport :longpoll, Phoenix.Transports.LongPoll
# Socket params are passed from the client and can # Socket params are passed from the client and can

@ -9,10 +9,14 @@ defmodule ExplorerWeb.AddressController do
def show(conn, %{"id" => id}) do def show(conn, %{"id" => id}) do
hash = String.downcase(id) hash = String.downcase(id)
query = from address in Address,
where: fragment("lower(?)", address.hash) == ^hash, query =
preload: [:credit, :debit], from(
limit: 1 address in Address,
where: fragment("lower(?)", address.hash) == ^hash,
preload: [:credit, :debit],
limit: 1
)
address = Repo.one(query) address = Repo.one(query)
render(conn, "show.html", address: AddressForm.build(address)) render(conn, "show.html", address: AddressForm.build(address))

@ -14,18 +14,30 @@ defmodule ExplorerWeb.AddressTransactionFromController do
def index(conn, %{"address_id" => address_id} = params) do def index(conn, %{"address_id" => address_id} = params) do
hash = String.downcase(address_id) hash = String.downcase(address_id)
address = Repo.one(from address in Address,
where: fragment("lower(?)", address.hash) == ^hash, address =
limit: 1) Repo.one(
from(
address in Address,
where: fragment("lower(?)", address.hash) == ^hash,
limit: 1
)
)
address_id = address.id address_id = address.id
query = from transaction in Transaction,
join: block in assoc(transaction, :block), query =
join: receipt in assoc(transaction, :receipt), from(
join: from_address in assoc(transaction, :from_address), transaction in Transaction,
join: to_address in assoc(transaction, :to_address), join: block in assoc(transaction, :block),
preload: [:block, :receipt, :to_address, :from_address], join: receipt in assoc(transaction, :receipt),
order_by: [desc: transaction.inserted_at], join: from_address in assoc(transaction, :from_address),
where: from_address.id == ^address_id join: to_address in assoc(transaction, :to_address),
preload: [:block, :receipt, :to_address, :from_address],
order_by: [desc: transaction.inserted_at],
where: from_address.id == ^address_id
)
page = Repo.paginate(query, params) page = Repo.paginate(query, params)
entries = Enum.map(page.entries, &TransactionForm.build_and_merge/1) entries = Enum.map(page.entries, &TransactionForm.build_and_merge/1)
render(conn, "index.html", transactions: Map.put(page, :entries, entries)) render(conn, "index.html", transactions: Map.put(page, :entries, entries))

@ -14,18 +14,30 @@ defmodule ExplorerWeb.AddressTransactionToController do
def index(conn, %{"address_id" => address_id} = params) do def index(conn, %{"address_id" => address_id} = params) do
hash = String.downcase(address_id) hash = String.downcase(address_id)
address = Repo.one(from address in Address,
where: fragment("lower(?)", address.hash) == ^hash, address =
limit: 1) Repo.one(
from(
address in Address,
where: fragment("lower(?)", address.hash) == ^hash,
limit: 1
)
)
address_id = address.id address_id = address.id
query = from transaction in Transaction,
join: block in assoc(transaction, :block), query =
join: receipt in assoc(transaction, :receipt), from(
join: from_address in assoc(transaction, :from_address), transaction in Transaction,
join: to_address in assoc(transaction, :to_address), join: block in assoc(transaction, :block),
preload: [:block, :receipt, :to_address, :from_address], join: receipt in assoc(transaction, :receipt),
order_by: [desc: transaction.inserted_at], join: from_address in assoc(transaction, :from_address),
where: to_address.id == ^address_id join: to_address in assoc(transaction, :to_address),
preload: [:block, :receipt, :to_address, :from_address],
order_by: [desc: transaction.inserted_at],
where: to_address.id == ^address_id
)
page = Repo.paginate(query, params) page = Repo.paginate(query, params)
entries = Enum.map(page.entries, &TransactionForm.build_and_merge/1) entries = Enum.map(page.entries, &TransactionForm.build_and_merge/1)
render(conn, "index.html", transactions: Map.put(page, :entries, entries)) render(conn, "index.html", transactions: Map.put(page, :entries, entries))

@ -8,18 +8,24 @@ defmodule ExplorerWeb.BlockController do
alias Explorer.Repo.NewRelic, as: Repo alias Explorer.Repo.NewRelic, as: Repo
def index(conn, params) do def index(conn, params) do
blocks = from block in Block, blocks =
order_by: [desc: block.number], from(
preload: :transactions block in Block,
order_by: [desc: block.number],
preload: :transactions
)
render(conn, "index.html", blocks: Repo.paginate(blocks, params)) render(conn, "index.html", blocks: Repo.paginate(blocks, params))
end end
def show(conn, %{"id" => number}) do def show(conn, %{"id" => number}) do
block = Block block =
Block
|> where(number: ^number) |> where(number: ^number)
|> first |> Repo.one |> first
|> BlockForm.build |> Repo.one()
|> BlockForm.build()
render(conn, "show.html", block: block) render(conn, "show.html", block: block)
end end
end end

@ -8,14 +8,18 @@ defmodule ExplorerWeb.BlockTransactionController do
alias Explorer.TransactionForm alias Explorer.TransactionForm
def index(conn, %{"block_id" => block_number} = params) do def index(conn, %{"block_id" => block_number} = params) do
query = from transaction in Transaction, query =
join: block in assoc(transaction, :block), from(
join: receipt in assoc(transaction, :receipt), transaction in Transaction,
join: from_address in assoc(transaction, :from_address), join: block in assoc(transaction, :block),
join: to_address in assoc(transaction, :to_address), join: receipt in assoc(transaction, :receipt),
preload: [:block, :receipt, :to_address, :from_address], join: from_address in assoc(transaction, :from_address),
order_by: [desc: transaction.inserted_at], join: to_address in assoc(transaction, :to_address),
where: block.number == ^block_number preload: [:block, :receipt, :to_address, :from_address],
order_by: [desc: transaction.inserted_at],
where: block.number == ^block_number
)
page = Repo.paginate(query, params) page = Repo.paginate(query, params)
entries = Enum.map(page.entries, &TransactionForm.build_and_merge/1) entries = Enum.map(page.entries, &TransactionForm.build_and_merge/1)
render(conn, "index.html", transactions: Map.put(page, :entries, entries)) render(conn, "index.html", transactions: Map.put(page, :entries, entries))

@ -15,22 +15,30 @@ defmodule ExplorerWeb.ChainController do
|> put_status(:not_found) |> put_status(:not_found)
|> put_view(ExplorerWeb.ErrorView) |> put_view(ExplorerWeb.ErrorView)
|> render("404.html") |> render("404.html")
item -> item ->
redirect_search_results(conn, item) redirect_search_results(conn, item)
end end
end end
defp redirect_search_results(conn, %Explorer.Block{} = item) do defp redirect_search_results(conn, %Explorer.Block{} = item) do
redirect conn, to: block_path(conn, :show, Gettext.get_locale, item.number) redirect(conn, to: block_path(conn, :show, Gettext.get_locale(), item.number))
end end
defp redirect_search_results(conn, %Explorer.Transaction{} = item) do defp redirect_search_results(conn, %Explorer.Transaction{} = item) do
redirect conn, to: transaction_path( redirect(
conn, :show, Gettext.get_locale, item.hash conn,
to:
transaction_path(
conn,
:show,
Gettext.get_locale(),
item.hash
)
) )
end end
defp redirect_search_results(conn, %Explorer.Address{} = item) do defp redirect_search_results(conn, %Explorer.Address{} = item) do
redirect conn, to: address_path(conn, :show, Gettext.get_locale, item.hash) redirect(conn, to: address_path(conn, :show, Gettext.get_locale(), item.hash))
end end
end end

@ -8,8 +8,10 @@ defmodule ExplorerWeb.InternalTransactionController do
internal_transactions = Transaction.internal_transactions(hash) internal_transactions = Transaction.internal_transactions(hash)
render(conn, render(
internal_transactions: internal_transactions, conn,
transaction_hash: hash) internal_transactions: internal_transactions,
transaction_hash: hash
)
end end
end end

@ -8,35 +8,52 @@ defmodule ExplorerWeb.PendingTransactionController do
alias Explorer.PendingTransactionForm alias Explorer.PendingTransactionForm
def index(conn, %{"last_seen" => last_seen} = _) do def index(conn, %{"last_seen" => last_seen} = _) do
query = from transaction in Transaction, query =
left_join: receipt in assoc(transaction, :receipt), from(
inner_join: to_address in assoc(transaction, :to_address), transaction in Transaction,
inner_join: from_address in assoc(transaction, :from_address), left_join: receipt in assoc(transaction, :receipt),
preload: [to_address: to_address, from_address: from_address], inner_join: to_address in assoc(transaction, :to_address),
where: is_nil(receipt.transaction_id), inner_join: from_address in assoc(transaction, :from_address),
where: transaction.id < ^last_seen, preload: [to_address: to_address, from_address: from_address],
order_by: [desc: transaction.id], where: is_nil(receipt.transaction_id),
limit: 10 where: transaction.id < ^last_seen,
total_query = from transaction in Transaction, order_by: [desc: transaction.id],
select: fragment("count(?)", transaction.id), limit: 10
left_join: receipt in assoc(transaction, :receipt), )
where: is_nil(receipt.transaction_id)
total_query =
from(
transaction in Transaction,
select: fragment("count(?)", transaction.id),
left_join: receipt in assoc(transaction, :receipt),
where: is_nil(receipt.transaction_id)
)
entries = Repo.all(query) entries = Repo.all(query)
last = List.last(entries) || Transaction.null last = List.last(entries) || Transaction.null()
render(conn, "index.html", transactions: %{
entries: entries |> Enum.map(&PendingTransactionForm.build/1), render(
total_entries: Repo.one(total_query), conn,
last_seen: last.id "index.html",
}) transactions: %{
entries: entries |> Enum.map(&PendingTransactionForm.build/1),
total_entries: Repo.one(total_query),
last_seen: last.id
}
)
end end
def index(conn, params) do def index(conn, params) do
query = from transaction in Transaction, query =
select: transaction.id, from(
left_join: receipt in assoc(transaction, :receipt), transaction in Transaction,
where: is_nil(receipt.transaction_id), select: transaction.id,
order_by: [desc: transaction.id], left_join: receipt in assoc(transaction, :receipt),
limit: 1 where: is_nil(receipt.transaction_id),
order_by: [desc: transaction.id],
limit: 1
)
first_id = Repo.one(query) || 0 first_id = Repo.one(query) || 0
last_seen = Integer.to_string(first_id + 1) last_seen = Integer.to_string(first_id + 1)
index(conn, Map.put(params, "last_seen", last_seen)) index(conn, Map.put(params, "last_seen", last_seen))

@ -8,38 +8,59 @@ defmodule ExplorerWeb.TransactionController do
alias Explorer.TransactionForm alias Explorer.TransactionForm
def index(conn, %{"last_seen" => last_seen} = _) do def index(conn, %{"last_seen" => last_seen} = _) do
query = from transaction in Transaction, query =
where: transaction.id < ^last_seen, from(
inner_join: receipt in assoc(transaction, :receipt), transaction in Transaction,
inner_join: block in assoc(transaction, :block), where: transaction.id < ^last_seen,
inner_join: to_address in assoc(transaction, :to_address), inner_join: receipt in assoc(transaction, :receipt),
inner_join: from_address in assoc(transaction, :from_address), inner_join: block in assoc(transaction, :block),
preload: [ inner_join: to_address in assoc(transaction, :to_address),
block: block, receipt: receipt, inner_join: from_address in assoc(transaction, :from_address),
to_address: to_address, from_address: from_address], preload: [
order_by: [desc: transaction.id], block: block,
limit: 10 receipt: receipt,
total_query = from transaction in Transaction, to_address: to_address,
select: fragment("count(?)", transaction.id), from_address: from_address
inner_join: receipt in assoc(transaction, :receipt), ],
inner_join: block in assoc(transaction, :block) order_by: [desc: transaction.id],
limit: 10
)
total_query =
from(
transaction in Transaction,
select: fragment("count(?)", transaction.id),
inner_join: receipt in assoc(transaction, :receipt),
inner_join: block in assoc(transaction, :block)
)
entries = entries =
query query
|> Repo.all() |> Repo.all()
|> Enum.map(&TransactionForm.build_and_merge/1) |> Enum.map(&TransactionForm.build_and_merge/1)
last = List.last(entries) || Transaction.null
render(conn, "index.html", transactions: %{ last = List.last(entries) || Transaction.null()
entries: entries,
total_entries: Repo.one(total_query), render(
last_seen: last.id conn,
}) "index.html",
transactions: %{
entries: entries,
total_entries: Repo.one(total_query),
last_seen: last.id
}
)
end end
def index(conn, params) do def index(conn, params) do
query = from t in Transaction, query =
select: t.id, from(
order_by: [desc: t.id], t in Transaction,
limit: 1 select: t.id,
order_by: [desc: t.id],
limit: 1
)
first_id = Repo.one(query) || 0 first_id = Repo.one(query) || 0
last_seen = Integer.to_string(first_id + 1) last_seen = Integer.to_string(first_id + 1)
index(conn, Map.put(params, "last_seen", last_seen)) index(conn, Map.put(params, "last_seen", last_seen))
@ -47,16 +68,23 @@ defmodule ExplorerWeb.TransactionController do
def show(conn, params) do def show(conn, params) do
hash = String.downcase(params["id"]) hash = String.downcase(params["id"])
query = from transaction in Transaction,
left_join: receipt in assoc(transaction, :receipt), query =
left_join: block in assoc(transaction, :block), from(
inner_join: to_address in assoc(transaction, :to_address), transaction in Transaction,
inner_join: from_address in assoc(transaction, :from_address), left_join: receipt in assoc(transaction, :receipt),
preload: [ left_join: block in assoc(transaction, :block),
block: block, receipt: receipt, inner_join: to_address in assoc(transaction, :to_address),
to_address: to_address, from_address: from_address], inner_join: from_address in assoc(transaction, :from_address),
where: fragment("lower(?)", transaction.hash) == ^hash, preload: [
limit: 1 block: block,
receipt: receipt,
to_address: to_address,
from_address: from_address
],
where: fragment("lower(?)", transaction.hash) == ^hash,
limit: 1
)
transaction = query |> Repo.one() |> TransactionForm.build_and_merge() transaction = query |> Repo.one() |> TransactionForm.build_and_merge()

@ -8,10 +8,15 @@ defmodule ExplorerWeb.TransactionLogController do
def index(conn, %{"transaction_id" => transaction_id}) do def index(conn, %{"transaction_id" => transaction_id}) do
hash = String.downcase(transaction_id) hash = String.downcase(transaction_id)
logs = from log in Log,
join: transaction in assoc(log, :transaction), logs =
preload: [:address], from(
where: fragment("lower(?)", transaction.hash) == ^hash log in Log,
join: transaction in assoc(log, :transaction),
preload: [:address],
where: fragment("lower(?)", transaction.hash) == ^hash
)
render(conn, "index.html", logs: Repo.paginate(logs), transaction_hash: hash) render(conn, "index.html", logs: Repo.paginate(logs), transaction_hash: hash)
end end
end end

@ -2,47 +2,55 @@ defmodule ExplorerWeb.Endpoint do
use Phoenix.Endpoint, otp_app: :explorer use Phoenix.Endpoint, otp_app: :explorer
if Application.get_env(:explorer, :sql_sandbox) do if Application.get_env(:explorer, :sql_sandbox) do
plug Phoenix.Ecto.SQL.Sandbox plug(Phoenix.Ecto.SQL.Sandbox)
end end
socket "/socket", ExplorerWeb.UserSocket socket("/socket", ExplorerWeb.UserSocket)
# Serve at "/" the static files from "priv/static" directory. # Serve at "/" the static files from "priv/static" directory.
# #
# You should set gzip to true if you are running phoenix.digest # You should set gzip to true if you are running phoenix.digest
# when deploying your static files in production. # when deploying your static files in production.
plug Plug.Static, plug(
at: "/", from: :explorer, gzip: false, Plug.Static,
at: "/",
from: :explorer,
gzip: false,
only: ~w(css fonts images js favicon.ico robots.txt) only: ~w(css fonts images js favicon.ico robots.txt)
)
# Code reloading can be explicitly enabled under the # Code reloading can be explicitly enabled under the
# :code_reloader configuration of your endpoint. # :code_reloader configuration of your endpoint.
if code_reloading? do if code_reloading? do
socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket socket("/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket)
plug Phoenix.LiveReloader plug(Phoenix.LiveReloader)
plug Phoenix.CodeReloader plug(Phoenix.CodeReloader)
end end
plug Plug.RequestId plug(Plug.RequestId)
plug Plug.Logger plug(Plug.Logger)
plug Plug.Parsers, plug(
Plug.Parsers,
parsers: [:urlencoded, :multipart, :json], parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"], pass: ["*/*"],
json_decoder: Poison json_decoder: Poison
)
plug Plug.MethodOverride plug(Plug.MethodOverride)
plug Plug.Head plug(Plug.Head)
# The session will be stored in the cookie and signed, # The session will be stored in the cookie and signed,
# this means its contents can be read but not tampered with. # this means its contents can be read but not tampered with.
# Set :encryption_salt if you would also like to encrypt it. # Set :encryption_salt if you would also like to encrypt it.
plug Plug.Session, plug(
Plug.Session,
store: :cookie, store: :cookie,
key: "_explorer_key", key: "_explorer_key",
signing_salt: "iC2ksJHS" signing_salt: "iC2ksJHS"
)
plug ExplorerWeb.Router plug(ExplorerWeb.Router)
@doc """ @doc """
Callback invoked for dynamically configuring the endpoint. Callback invoked for dynamically configuring the endpoint.

@ -22,9 +22,10 @@ defmodule ExplorerWeb.Gettext do
""" """
use Gettext, otp_app: :explorer use Gettext, otp_app: :explorer
@dialyzer [{:nowarn_function, 'MACRO-dgettext': 3}, @dialyzer [
{:nowarn_function, 'MACRO-dgettext': 4}, {:nowarn_function, "MACRO-dgettext": 3},
{:nowarn_function, 'MACRO-dngettext': 5}, {:nowarn_function, "MACRO-dgettext": 4},
{:nowarn_function, 'MACRO-dngettext': 6}, {:nowarn_function, "MACRO-dngettext": 5},
] {:nowarn_function, "MACRO-dngettext": 6}
]
end end

@ -2,11 +2,12 @@ defmodule ExplorerWeb.Router do
use ExplorerWeb, :router use ExplorerWeb, :router
pipeline :browser do pipeline :browser do
plug :accepts, ["html"] plug(:accepts, ["html"])
plug :fetch_session plug(:fetch_session)
plug :fetch_flash plug(:fetch_flash)
plug :protect_from_forgery plug(:protect_from_forgery)
plug :put_secure_browser_headers, %{
plug(:put_secure_browser_headers, %{
"content-security-policy" => "\ "content-security-policy" => "\
default-src 'self';\ default-src 'self';\
script-src 'self' 'unsafe-inline' 'unsafe-eval';\ script-src 'self' 'unsafe-inline' 'unsafe-eval';\
@ -14,67 +15,84 @@ defmodule ExplorerWeb.Router do
img-src 'self' 'unsafe-inline' 'unsafe-eval' data:;\ img-src 'self' 'unsafe-inline' 'unsafe-eval' data:;\
font-src 'self' 'unsafe-inline' 'unsafe-eval' data:;\ font-src 'self' 'unsafe-inline' 'unsafe-eval' data:;\
" "
} })
end end
pipeline :set_locale do pipeline :set_locale do
plug SetLocale, gettext: ExplorerWeb.Gettext, default_locale: "en" plug(SetLocale, gettext: ExplorerWeb.Gettext, default_locale: "en")
end end
pipeline :exq do pipeline :exq do
plug :accepts, ["html"] plug(:accepts, ["html"])
plug :fetch_session plug(:fetch_session)
plug :fetch_flash plug(:fetch_flash)
plug :put_secure_browser_headers, %{
plug(:put_secure_browser_headers, %{
"content-security-policy" => "\ "content-security-policy" => "\
default-src 'self';\ default-src 'self';\
script-src 'self' 'unsafe-inline';\ script-src 'self' 'unsafe-inline';\
font-src 'self' fonts.gstatic.com;\ font-src 'self' fonts.gstatic.com;\
style-src 'self' 'unsafe-inline' fonts.googleapis.com;\ style-src 'self' 'unsafe-inline' fonts.googleapis.com;\
" "
} })
plug ExqUi.RouterPlug, namespace: "exq"
plug(ExqUi.RouterPlug, namespace: "exq")
end end
pipeline :jasmine do pipeline :jasmine do
if Mix.env != :prod, do: plug Jasmine, js_files: ["js/test.js"], css_files: ["css/test.css"] if Mix.env() != :prod,
do: plug(Jasmine, js_files: ["js/test.js"], css_files: ["css/test.css"])
end end
pipeline :api do pipeline :api do
plug :accepts, ["json"] plug(:accepts, ["json"])
end end
scope "/exq", ExqUi do scope "/exq", ExqUi do
pipe_through :exq pipe_through(:exq)
forward "/", RouterPlug.Router, :index forward("/", RouterPlug.Router, :index)
end end
scope "/", ExplorerWeb do scope "/", ExplorerWeb do
pipe_through :browser pipe_through(:browser)
pipe_through :jasmine pipe_through(:jasmine)
pipe_through :set_locale pipe_through(:set_locale)
resources "/", ChainController, only: [:show], singleton: true, as: :chain resources("/", ChainController, only: [:show], singleton: true, as: :chain)
end end
scope "/:locale", ExplorerWeb do scope "/:locale", ExplorerWeb do
pipe_through :browser pipe_through(:browser)
pipe_through :jasmine pipe_through(:jasmine)
pipe_through :set_locale pipe_through(:set_locale)
resources "/", ChainController, only: [:show], singleton: true, as: :chain resources("/", ChainController, only: [:show], singleton: true, as: :chain)
resources "/blocks", BlockController, only: [:index, :show] do resources "/blocks", BlockController, only: [:index, :show] do
resources "/transactions", BlockTransactionController, only: [:index], as: :transaction resources("/transactions", BlockTransactionController, only: [:index], as: :transaction)
end end
resources "/pending_transactions", PendingTransactionController, only: [:index]
resources("/pending_transactions", PendingTransactionController, only: [:index])
resources "/transactions", TransactionController, only: [:index, :show] do resources "/transactions", TransactionController, only: [:index, :show] do
resources "/logs", TransactionLogController, only: [:index], as: :log resources("/logs", TransactionLogController, only: [:index], as: :log)
resources "/internal", InternalTransactionController, only: [:index] resources("/internal", InternalTransactionController, only: [:index])
end end
resources "/addresses", AddressController, only: [:show] do resources "/addresses", AddressController, only: [:show] do
resources "/transactions", AddressTransactionToController, resources(
only: [:index], as: :transaction_to "/transactions",
resources "/transactions_from", AddressTransactionFromController, AddressTransactionToController,
only: [:index], as: :transaction_from only: [:index],
as: :transaction_to
)
resources(
"/transactions_from",
AddressTransactionFromController,
only: [:index],
as: :transaction_from
)
end end
get "/search", ChainController, :search
get("/search", ChainController, :search)
end end
end end

@ -9,8 +9,8 @@ defmodule ExplorerWeb.ErrorHelpers do
Generates tag for inlined form input errors. Generates tag for inlined form input errors.
""" """
def error_tag(form, field) do def error_tag(form, field) do
Enum.map(Keyword.get_values(form.errors, field), fn (error) -> Enum.map(Keyword.get_values(form.errors, field), fn error ->
content_tag :span, translate_error(error), class: "help-block" content_tag(:span, translate_error(error), class: "help-block")
end) end)
end end

@ -12,6 +12,6 @@ defmodule ExplorerWeb.ErrorView do
# In case no render clause matches or no # In case no render clause matches or no
# template is found, let's render it as 500 # template is found, let's render it as 500
def template_not_found(_template, assigns) do def template_not_found(_template, assigns) do
render "500.html", assigns render("500.html", assigns)
end end
end end

@ -6,6 +6,6 @@ defmodule ExplorerWeb.TransactionView do
def format_gas_limit(gas) do def format_gas_limit(gas) do
gas gas
|> Number.to_string! |> Number.to_string!()
end end
end end

@ -7,6 +7,7 @@ defmodule Mix.Tasks.Exq.Start do
def run(["scheduler"]) do def run(["scheduler"]) do
[:postgrex, :ecto, :ethereumex, :tzdata] [:postgrex, :ecto, :ethereumex, :tzdata]
|> Enum.each(&Application.ensure_all_started/1) |> Enum.each(&Application.ensure_all_started/1)
Repo.start_link() Repo.start_link()
Exq.start_link(mode: :enqueuer) Exq.start_link(mode: :enqueuer)
Scheduler.start_link() Scheduler.start_link()
@ -16,6 +17,7 @@ defmodule Mix.Tasks.Exq.Start do
def run(_) do def run(_) do
[:postgrex, :ecto, :ethereumex, :tzdata] [:postgrex, :ecto, :ethereumex, :tzdata]
|> Enum.each(&Application.ensure_all_started/1) |> Enum.each(&Application.ensure_all_started/1)
Repo.start_link() Repo.start_link()
Exq.start_link(mode: :default) Exq.start_link(mode: :default)
:timer.sleep(:infinity) :timer.sleep(:infinity)

@ -6,9 +6,11 @@ defmodule Mix.Tasks.Scrape.Blocks do
alias Explorer.BlockImporter alias Explorer.BlockImporter
def run([]), do: run(1) def run([]), do: run(1)
def run(count) do def run(count) do
[:postgrex, :ecto, :ethereumex, :tzdata] [:postgrex, :ecto, :ethereumex, :tzdata]
|> Enum.each(&Application.ensure_all_started/1) |> Enum.each(&Application.ensure_all_started/1)
Repo.start_link() Repo.start_link()
Exq.start_link(mode: :enqueuer) Exq.start_link(mode: :enqueuer)

@ -7,9 +7,11 @@ defmodule Mix.Tasks.Scrape.Receipts do
alias Explorer.ReceiptImporter alias Explorer.ReceiptImporter
def run([]), do: run(1) def run([]), do: run(1)
def run(count) do def run(count) do
[:postgrex, :ecto, :ethereumex, :tzdata] [:postgrex, :ecto, :ethereumex, :tzdata]
|> Enum.each(&Application.ensure_all_started/1) |> Enum.each(&Application.ensure_all_started/1)
Repo.start_link() Repo.start_link()
"#{count}" "#{count}"

@ -6,9 +6,9 @@ defmodule Explorer.Mixfile do
app: :explorer, app: :explorer,
version: "0.0.1", version: "0.0.1",
elixir: "~> 1.4", elixir: "~> 1.4",
elixirc_paths: elixirc_paths(Mix.env), elixirc_paths: elixirc_paths(Mix.env()),
compilers: [:phoenix, :gettext | Mix.compilers], compilers: [:phoenix, :gettext | Mix.compilers()],
start_permanent: Mix.env == :prod, start_permanent: Mix.env() == :prod,
package: package(), package: package(),
aliases: aliases(), aliases: aliases(),
deps: deps(), deps: deps(),
@ -26,24 +26,37 @@ defmodule Explorer.Mixfile do
def application do def application do
[ [
mod: {Explorer.Application, []}, mod: {Explorer.Application, []},
extra_applications: extra_applications(Mix.env) extra_applications: extra_applications(Mix.env())
] ]
end end
# Specifies which paths to compile per environment. # Specifies which paths to compile per environment.
defp elixirc_paths(:test), do: ["test/support" | elixirc_paths()] defp elixirc_paths(:test), do: ["test/support" | elixirc_paths()]
defp elixirc_paths(_), do: elixirc_paths() defp elixirc_paths(_), do: elixirc_paths()
defp elixirc_paths, do: ["lib"] defp elixirc_paths, do: ["lib"]
# Specifies extra applications to start per environment # Specifies extra applications to start per environment
defp extra_applications(:prod), do: [:phoenix_pubsub_redis, :exq, :exq_ui | extra_applications()] defp extra_applications(:prod),
do: [:phoenix_pubsub_redis, :exq, :exq_ui | extra_applications()]
defp extra_applications(:dev), do: [:exq, :exq_ui | extra_applications()] defp extra_applications(:dev), do: [:exq, :exq_ui | extra_applications()]
defp extra_applications(_), do: extra_applications() defp extra_applications(_), do: extra_applications()
defp extra_applications, do: [
:scrivener_ecto, :scrivener_html, :ex_cldr, :ex_jasmine, :ethereumex, defp extra_applications,
:timex, :timex_ecto, :crontab, :set_locale, :logger, :runtime_tools, do: [
:new_relixir :scrivener_ecto,
] :scrivener_html,
:ex_cldr,
:ex_jasmine,
:ethereumex,
:timex,
:timex_ecto,
:crontab,
:set_locale,
:logger,
:runtime_tools,
:new_relixir
]
# Specifies your project dependencies. # Specifies your project dependencies.
# #
@ -80,11 +93,12 @@ defmodule Explorer.Mixfile do
{:react_phoenix, "~> 0.5"}, {:react_phoenix, "~> 0.5"},
{:scrivener_ecto, "~> 1.0"}, {:scrivener_ecto, "~> 1.0"},
{:scrivener_html, "~> 1.7"}, {:scrivener_html, "~> 1.7"},
{:set_locale, github: "minifast/set_locale", branch: "master"}, # Waiting on https://github.com/smeevil/set_locale/pull/9 # Waiting on https://github.com/smeevil/set_locale/pull/9
{:set_locale, github: "minifast/set_locale", branch: "master"},
{:sobelow, ">= 0.0.0", only: [:dev, :test], runtime: false}, {:sobelow, ">= 0.0.0", only: [:dev, :test], runtime: false},
{:timex, "~> 3.1.24"}, {:timex, "~> 3.1.24"},
{:timex_ecto, "~> 3.2.1"}, {:timex_ecto, "~> 3.2.1"},
{:wallaby, "~> 0.19.2", only: [:test], runtime: false}, {:wallaby, "~> 0.19.2", only: [:test], runtime: false}
] ]
end end
@ -98,7 +112,7 @@ defmodule Explorer.Mixfile do
[ [
"ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"], "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
"ecto.reset": ["ecto.drop", "ecto.setup"], "ecto.reset": ["ecto.drop", "ecto.setup"],
"test": ["ecto.create --quiet", "ecto.migrate", "test"] test: ["ecto.create --quiet", "ecto.migrate", "test"]
] ]
end end

@ -18,14 +18,14 @@ defmodule Explorer.AddressTest do
describe "find_or_create_by_hash/1" do describe "find_or_create_by_hash/1" do
test "that it creates a new address when one does not exist" do test "that it creates a new address when one does not exist" do
Address.find_or_create_by_hash("0xFreshPrince") Address.find_or_create_by_hash("0xFreshPrince")
last_address = Address |> order_by(desc: :inserted_at) |> Repo.one last_address = Address |> order_by(desc: :inserted_at) |> Repo.one()
assert last_address.hash == "0xfreshprince" assert last_address.hash == "0xfreshprince"
end end
test "when the address already exists it doesn't insert a new address" do test "when the address already exists it doesn't insert a new address" do
insert(:address, %{hash: "bigmouthbillybass"}) insert(:address, %{hash: "bigmouthbillybass"})
Address.find_or_create_by_hash("bigmouthbillybass") Address.find_or_create_by_hash("bigmouthbillybass")
number_of_addresses = Address |> Repo.all |> length number_of_addresses = Address |> Repo.all() |> length
assert number_of_addresses == 1 assert number_of_addresses == 1
end end

@ -17,14 +17,20 @@ defmodule Explorer.BlockTest do
test "with duplicate information" do test "with duplicate information" do
insert(:block, hash: "0x0") insert(:block, hash: "0x0")
{:error, changeset} = %Block{} |> Block.changeset(params_for(:block, hash: "0x0")) |> Repo.insert
{:error, changeset} =
%Block{} |> Block.changeset(params_for(:block, hash: "0x0")) |> Repo.insert()
refute changeset.valid? refute changeset.valid?
assert changeset.errors == [hash: {"has already been taken", []}] assert changeset.errors == [hash: {"has already been taken", []}]
end end
test "rejects duplicate blocks with mixed case" do test "rejects duplicate blocks with mixed case" do
insert(:block, hash: "0xa") insert(:block, hash: "0xa")
{:error, changeset} = %Block{} |> Block.changeset(params_for(:block, hash: "0xA")) |> Repo.insert
{:error, changeset} =
%Block{} |> Block.changeset(params_for(:block, hash: "0xA")) |> Repo.insert()
refute changeset.valid? refute changeset.valid?
assert changeset.errors == [hash: {"has already been taken", []}] assert changeset.errors == [hash: {"has already been taken", []}]
end end
@ -32,7 +38,7 @@ defmodule Explorer.BlockTest do
describe "null/0" do describe "null/0" do
test "returns a block with a number of 0" do test "returns a block with a number of 0" do
assert Block.null.number === -1 assert Block.null().number === -1
end end
end end
@ -40,7 +46,9 @@ defmodule Explorer.BlockTest do
test "returns the blocks sorted by number" do test "returns the blocks sorted by number" do
insert(:block, number: 1) insert(:block, number: 1)
insert(:block, number: 5) insert(:block, number: 5)
assert Block |> Block.latest |> Repo.all == Block |> order_by(desc: :number) |> Repo.all
assert Block |> Block.latest() |> Repo.all() ==
Block |> order_by(desc: :number) |> Repo.all()
end end
end end
end end

@ -30,11 +30,12 @@ defmodule Explorer.ChainTest do
insert(:block, timestamp: time) insert(:block, timestamp: time)
insert(:block, timestamp: next_time) insert(:block, timestamp: next_time)
chain = Chain.fetch() chain = Chain.fetch()
assert chain.average_time == %Duration{ assert chain.average_time == %Duration{
seconds: 5, seconds: 5,
megaseconds: 0, megaseconds: 0,
microseconds: 0 microseconds: 0
} }
end end
test "returns the count of transactions from blocks in the last day" do test "returns the count of transactions from blocks in the last day" do
@ -89,10 +90,12 @@ defmodule Explorer.ChainTest do
test "returns the last five transactions with blocks" do test "returns the last five transactions with blocks" do
block = insert(:block) block = insert(:block)
insert_list(6, :transaction) insert_list(6, :transaction)
|> Enum.map(fn (transaction) -> |> Enum.map(fn transaction ->
insert(:block_transaction, block: block, transaction: transaction) insert(:block_transaction, block: block, transaction: transaction)
end) end)
chain = Chain.fetch() chain = Chain.fetch()
assert chain.transactions |> Enum.count() == 5 assert chain.transactions |> Enum.count() == 5
end end

@ -10,7 +10,7 @@ defmodule Explorer.CreditTest do
test "returns nothing when an address has no transactions" do test "returns nothing when an address has no transactions" do
insert(:address) insert(:address)
Credit.refresh Credit.refresh()
assert Repo.one(Credit) == nil assert Repo.one(Credit) == nil
end end
@ -21,9 +21,9 @@ defmodule Explorer.CreditTest do
insert(:receipt, transaction: transaction, status: 1) insert(:receipt, transaction: transaction, status: 1)
insert(:from_address, transaction: transaction, address: sender) insert(:from_address, transaction: transaction, address: sender)
insert(:to_address, transaction: transaction, address: receipient) insert(:to_address, transaction: transaction, address: receipient)
Credit.refresh Credit.refresh()
credits = Credit |> Repo.all credits = Credit |> Repo.all()
assert credits |> Enum.count == 1 assert credits |> Enum.count() == 1
end end
test "returns no credits to the sender" do test "returns no credits to the sender" do
@ -34,8 +34,8 @@ defmodule Explorer.CreditTest do
insert(:from_address, transaction: transaction, address: sender) insert(:from_address, transaction: transaction, address: sender)
insert(:to_address, transaction: transaction, address: receipient) insert(:to_address, transaction: transaction, address: receipient)
address_id = sender.id address_id = sender.id
Credit.refresh Credit.refresh()
credit = Credit |> where(address_id: ^address_id) |> Repo.one credit = Credit |> where(address_id: ^address_id) |> Repo.one()
assert credit == nil assert credit == nil
end end
@ -47,8 +47,8 @@ defmodule Explorer.CreditTest do
insert(:from_address, transaction: transaction, address: sender) insert(:from_address, transaction: transaction, address: sender)
insert(:to_address, transaction: transaction, address: receipient) insert(:to_address, transaction: transaction, address: receipient)
address_id = receipient.id address_id = receipient.id
Credit.refresh Credit.refresh()
credit = Credit |> where(address_id: ^address_id) |> Repo.one credit = Credit |> where(address_id: ^address_id) |> Repo.one()
assert credit.value == Decimal.new(21) assert credit.value == Decimal.new(21)
end end
end end

@ -10,7 +10,7 @@ defmodule Explorer.DebitTest do
test "returns nothing when an address has no transactions" do test "returns nothing when an address has no transactions" do
insert(:address) insert(:address)
Debit.refresh Debit.refresh()
assert Repo.one(Debit) == nil assert Repo.one(Debit) == nil
end end
@ -21,9 +21,9 @@ defmodule Explorer.DebitTest do
insert(:receipt, transaction: transaction, status: 1) insert(:receipt, transaction: transaction, status: 1)
insert(:from_address, transaction: transaction, address: sender) insert(:from_address, transaction: transaction, address: sender)
insert(:to_address, transaction: transaction, address: receipient) insert(:to_address, transaction: transaction, address: receipient)
Debit.refresh Debit.refresh()
debits = Debit |> Repo.all debits = Debit |> Repo.all()
assert debits |> Enum.count == 1 assert debits |> Enum.count() == 1
end end
test "returns a debit against the sender" do test "returns a debit against the sender" do
@ -34,8 +34,8 @@ defmodule Explorer.DebitTest do
insert(:from_address, transaction: transaction, address: sender) insert(:from_address, transaction: transaction, address: sender)
insert(:to_address, transaction: transaction, address: receipient) insert(:to_address, transaction: transaction, address: receipient)
address_id = sender.id address_id = sender.id
Debit.refresh Debit.refresh()
debit = Debit |> where(address_id: ^address_id) |> Repo.one debit = Debit |> where(address_id: ^address_id) |> Repo.one()
assert debit.value == Decimal.new(21) assert debit.value == Decimal.new(21)
end end
@ -47,8 +47,8 @@ defmodule Explorer.DebitTest do
insert(:from_address, transaction: transaction, address: sender) insert(:from_address, transaction: transaction, address: sender)
insert(:to_address, transaction: transaction, address: receipient) insert(:to_address, transaction: transaction, address: receipient)
address_id = receipient.id address_id = receipient.id
Debit.refresh Debit.refresh()
debit = Debit |> where(address_id: ^address_id) |> Repo.one debit = Debit |> where(address_id: ^address_id) |> Repo.one()
assert debit == nil assert debit == nil
end end
end end

@ -16,10 +16,11 @@ defmodule Explorer.AddressFormTest do
insert(:from_address, address: sender, transaction: transaction) insert(:from_address, address: sender, transaction: transaction)
insert(:to_address, address: recipient, transaction: transaction) insert(:to_address, address: recipient, transaction: transaction)
Credit.refresh Credit.refresh()
Debit.refresh Debit.refresh()
assert AddressForm.build(Repo.preload(recipient, [:debit, :credit])).balance == Decimal.new(10) assert AddressForm.build(Repo.preload(recipient, [:debit, :credit])).balance ==
Decimal.new(10)
end end
test "returns a zero balance when the address does not have balances" do test "returns a zero balance when the address does not have balances" do

@ -16,7 +16,7 @@ defmodule Explorer.BlockFormTest do
end end
test "that it returns a block's age" do test "that it returns a block's age" do
block = insert(:block, timestamp: Timex.now |> Timex.shift(hours: -1)) block = insert(:block, timestamp: Timex.now() |> Timex.shift(hours: -1))
assert BlockForm.build(block).age == "1 hour ago" assert BlockForm.build(block).age == "1 hour ago"
end end

@ -11,15 +11,21 @@ defmodule Explorer.PendingTransactionFormTest do
transaction = insert(:transaction, inserted_at: time, updated_at: time) transaction = insert(:transaction, inserted_at: time, updated_at: time)
insert(:to_address, address: to_address, transaction: transaction) insert(:to_address, address: to_address, transaction: transaction)
insert(:from_address, address: from_address, transaction: transaction) insert(:from_address, address: from_address, transaction: transaction)
form = PendingTransactionForm.build(transaction |> Repo.preload([:to_address, :from_address]))
assert(form == Map.merge(transaction |> Repo.preload([:to_address, :from_address]), %{ form =
to_address_hash: "0xcafe", PendingTransactionForm.build(transaction |> Repo.preload([:to_address, :from_address]))
from_address_hash: "0xbee5",
first_seen: time |> Timex.from_now(), assert(
last_seen: time |> Timex.from_now(), form ==
status: :pending, Map.merge(transaction |> Repo.preload([:to_address, :from_address]), %{
formatted_status: "Pending" to_address_hash: "0xcafe",
})) from_address_hash: "0xbee5",
first_seen: time |> Timex.from_now(),
last_seen: time |> Timex.from_now(),
status: :pending,
formatted_status: "Pending"
})
)
end end
end end
end end

@ -6,215 +6,274 @@ defmodule Explorer.TransactionFormTest do
describe "build/1" do describe "build/1" do
test "returns a successful transaction when there is a successful receipt" do test "returns a successful transaction when there is a successful receipt" do
insert(:block, number: 24) insert(:block, number: 24)
time = Timex.now |> Timex.shift(hours: -2) time = Timex.now() |> Timex.shift(hours: -2)
block = insert(:block, %{
number: 1, block =
gas_used: 99523, insert(:block, %{
timestamp: time, number: 1,
}) gas_used: 99523,
timestamp: time
})
transaction = transaction =
insert(:transaction, insert(
:transaction,
inserted_at: Timex.parse!("1970-01-01T00:00:18-00:00", "{ISO:Extended}"), inserted_at: Timex.parse!("1970-01-01T00:00:18-00:00", "{ISO:Extended}"),
updated_at: Timex.parse!("1980-01-01T00:00:18-00:00", "{ISO:Extended}")) updated_at: Timex.parse!("1980-01-01T00:00:18-00:00", "{ISO:Extended}")
)
|> with_block(block) |> with_block(block)
|> with_addresses(%{to: "0xsleepypuppy", from: "0xilovefrogs"}) |> with_addresses(%{to: "0xsleepypuppy", from: "0xilovefrogs"})
insert(:receipt, status: 1, transaction: transaction) insert(:receipt, status: 1, transaction: transaction)
form = transaction |> Repo.preload([:block, :to_address, :from_address, :receipt]) |> TransactionForm.build() form =
transaction |> Repo.preload([:block, :to_address, :from_address, :receipt])
|> TransactionForm.build()
formatted_timestamp = block.timestamp |> Timex.format!("%b-%d-%Y %H:%M:%S %p %Z", :strftime) formatted_timestamp = block.timestamp |> Timex.format!("%b-%d-%Y %H:%M:%S %p %Z", :strftime)
assert(form == %{ assert(
block_number: 1, form == %{
age: "2 hours ago", block_number: 1,
formatted_age: "2 hours ago (#{formatted_timestamp})", age: "2 hours ago",
formatted_timestamp: formatted_timestamp, formatted_age: "2 hours ago (#{formatted_timestamp})",
cumulative_gas_used: "99,523", formatted_timestamp: formatted_timestamp,
to_address_hash: "0xsleepypuppy", cumulative_gas_used: "99,523",
from_address_hash: "0xilovefrogs", to_address_hash: "0xsleepypuppy",
confirmations: 23, from_address_hash: "0xilovefrogs",
status: :success, confirmations: 23,
formatted_status: "Success", status: :success,
first_seen: "48 years ago", formatted_status: "Success",
last_seen: "38 years ago", first_seen: "48 years ago",
}) last_seen: "38 years ago"
}
)
end end
test "returns a failed transaction when there is a failed receipt" do test "returns a failed transaction when there is a failed receipt" do
insert(:block, number: 24) insert(:block, number: 24)
time = Timex.now |> Timex.shift(hours: -2) time = Timex.now() |> Timex.shift(hours: -2)
block = insert(:block, %{
number: 1, block =
gas_used: 99523, insert(:block, %{
timestamp: time, number: 1,
}) gas_used: 99523,
timestamp: time
})
transaction = transaction =
insert(:transaction, insert(
:transaction,
gas: 155, gas: 155,
inserted_at: Timex.parse!("1970-01-01T00:00:18-00:00", "{ISO:Extended}"), inserted_at: Timex.parse!("1970-01-01T00:00:18-00:00", "{ISO:Extended}"),
updated_at: Timex.parse!("1980-01-01T00:00:18-00:00", "{ISO:Extended}")) updated_at: Timex.parse!("1980-01-01T00:00:18-00:00", "{ISO:Extended}")
)
|> with_block(block) |> with_block(block)
|> with_addresses(%{to: "0xsleepypuppy", from: "0xilovefrogs"}) |> with_addresses(%{to: "0xsleepypuppy", from: "0xilovefrogs"})
insert(:receipt, status: 0, gas_used: 100, transaction: transaction) insert(:receipt, status: 0, gas_used: 100, transaction: transaction)
form = transaction |> Repo.preload([:block, :to_address, :from_address, :receipt]) |> TransactionForm.build() form =
transaction |> Repo.preload([:block, :to_address, :from_address, :receipt])
|> TransactionForm.build()
formatted_timestamp = block.timestamp |> Timex.format!("%b-%d-%Y %H:%M:%S %p %Z", :strftime) formatted_timestamp = block.timestamp |> Timex.format!("%b-%d-%Y %H:%M:%S %p %Z", :strftime)
assert(form == %{ assert(
block_number: 1, form == %{
age: "2 hours ago", block_number: 1,
formatted_age: "2 hours ago (#{formatted_timestamp})", age: "2 hours ago",
formatted_timestamp: formatted_timestamp, formatted_age: "2 hours ago (#{formatted_timestamp})",
cumulative_gas_used: "99,523", formatted_timestamp: formatted_timestamp,
to_address_hash: "0xsleepypuppy", cumulative_gas_used: "99,523",
from_address_hash: "0xilovefrogs", to_address_hash: "0xsleepypuppy",
confirmations: 23, from_address_hash: "0xilovefrogs",
status: :failed, confirmations: 23,
formatted_status: "Failed", status: :failed,
first_seen: "48 years ago", formatted_status: "Failed",
last_seen: "38 years ago", first_seen: "48 years ago",
}) last_seen: "38 years ago"
}
)
end end
test "returns a out of gas transaction when the gas matches the gas used" do test "returns a out of gas transaction when the gas matches the gas used" do
insert(:block, number: 24) insert(:block, number: 24)
time = Timex.now |> Timex.shift(hours: -2) time = Timex.now() |> Timex.shift(hours: -2)
block = insert(:block, %{
number: 1, block =
gas_used: 99523, insert(:block, %{
timestamp: time, number: 1,
}) gas_used: 99523,
timestamp: time
})
transaction = transaction =
insert(:transaction, insert(
:transaction,
gas: 555, gas: 555,
inserted_at: Timex.parse!("1970-01-01T00:00:18-00:00", "{ISO:Extended}"), inserted_at: Timex.parse!("1970-01-01T00:00:18-00:00", "{ISO:Extended}"),
updated_at: Timex.parse!("1980-01-01T00:00:18-00:00", "{ISO:Extended}")) updated_at: Timex.parse!("1980-01-01T00:00:18-00:00", "{ISO:Extended}")
)
|> with_block(block) |> with_block(block)
|> with_addresses(%{to: "0xsleepypuppy", from: "0xilovefrogs"}) |> with_addresses(%{to: "0xsleepypuppy", from: "0xilovefrogs"})
insert(:receipt, status: 0, gas_used: 555, transaction: transaction) insert(:receipt, status: 0, gas_used: 555, transaction: transaction)
form = transaction |> Repo.preload([:block, :to_address, :from_address, :receipt]) |> TransactionForm.build() form =
transaction |> Repo.preload([:block, :to_address, :from_address, :receipt])
|> TransactionForm.build()
formatted_timestamp = block.timestamp |> Timex.format!("%b-%d-%Y %H:%M:%S %p %Z", :strftime) formatted_timestamp = block.timestamp |> Timex.format!("%b-%d-%Y %H:%M:%S %p %Z", :strftime)
assert(form == %{ assert(
block_number: 1, form == %{
age: "2 hours ago", block_number: 1,
formatted_age: "2 hours ago (#{formatted_timestamp})", age: "2 hours ago",
formatted_timestamp: formatted_timestamp, formatted_age: "2 hours ago (#{formatted_timestamp})",
cumulative_gas_used: "99,523", formatted_timestamp: formatted_timestamp,
to_address_hash: "0xsleepypuppy", cumulative_gas_used: "99,523",
from_address_hash: "0xilovefrogs", to_address_hash: "0xsleepypuppy",
confirmations: 23, from_address_hash: "0xilovefrogs",
status: :out_of_gas, confirmations: 23,
formatted_status: "Out of Gas", status: :out_of_gas,
first_seen: "48 years ago", formatted_status: "Out of Gas",
last_seen: "38 years ago", first_seen: "48 years ago",
}) last_seen: "38 years ago"
}
)
end end
test "returns a pending transaction when there is no receipt" do test "returns a pending transaction when there is no receipt" do
insert(:block, number: 24) insert(:block, number: 24)
time = Timex.now |> Timex.shift(hours: -2) time = Timex.now() |> Timex.shift(hours: -2)
block = insert(:block, %{
number: 1, block =
gas_used: 99523, insert(:block, %{
timestamp: time, number: 1,
}) gas_used: 99523,
timestamp: time
})
transaction = transaction =
insert(:transaction, insert(
:transaction,
inserted_at: Timex.parse!("1970-01-01T00:00:18-00:00", "{ISO:Extended}"), inserted_at: Timex.parse!("1970-01-01T00:00:18-00:00", "{ISO:Extended}"),
updated_at: Timex.parse!("1980-01-01T00:00:18-00:00", "{ISO:Extended}")) updated_at: Timex.parse!("1980-01-01T00:00:18-00:00", "{ISO:Extended}")
)
|> with_block(block) |> with_block(block)
|> with_addresses(%{to: "0xsleepypuppy", from: "0xilovefrogs"}) |> with_addresses(%{to: "0xsleepypuppy", from: "0xilovefrogs"})
|> Repo.preload([:block, :to_address, :from_address, :receipt]) |> Repo.preload([:block, :to_address, :from_address, :receipt])
form = TransactionForm.build(transaction) form = TransactionForm.build(transaction)
formatted_timestamp = block.timestamp |> Timex.format!("%b-%d-%Y %H:%M:%S %p %Z", :strftime) formatted_timestamp = block.timestamp |> Timex.format!("%b-%d-%Y %H:%M:%S %p %Z", :strftime)
assert(form == %{ assert(
block_number: 1, form == %{
age: "2 hours ago", block_number: 1,
formatted_age: "2 hours ago (#{formatted_timestamp})", age: "2 hours ago",
formatted_timestamp: formatted_timestamp, formatted_age: "2 hours ago (#{formatted_timestamp})",
cumulative_gas_used: "99,523", formatted_timestamp: formatted_timestamp,
to_address_hash: "0xsleepypuppy", cumulative_gas_used: "99,523",
from_address_hash: "0xilovefrogs", to_address_hash: "0xsleepypuppy",
confirmations: 23, from_address_hash: "0xilovefrogs",
status: :pending, confirmations: 23,
formatted_status: "Pending", status: :pending,
first_seen: "48 years ago", formatted_status: "Pending",
last_seen: "38 years ago", first_seen: "48 years ago",
}) last_seen: "38 years ago"
}
)
end end
test "returns a pending transaction when there is no block" do test "returns a pending transaction when there is no block" do
transaction = insert( transaction =
:transaction, insert(
inserted_at: Timex.parse!("1970-01-01T00:00:18-00:00", "{ISO:Extended}"), :transaction,
updated_at: Timex.parse!("1980-01-01T00:00:18-00:00", "{ISO:Extended}")) inserted_at: Timex.parse!("1970-01-01T00:00:18-00:00", "{ISO:Extended}"),
|> with_addresses(%{to: "0xchadmuska", from: "0xtonyhawk"}) updated_at: Timex.parse!("1980-01-01T00:00:18-00:00", "{ISO:Extended}")
|> Repo.preload([:to_address, :from_address]) )
|> with_addresses(%{to: "0xchadmuska", from: "0xtonyhawk"})
|> Repo.preload([:to_address, :from_address])
form = TransactionForm.build(transaction) form = TransactionForm.build(transaction)
assert(form == %{ assert(
block_number: "", form == %{
age: "Pending", block_number: "",
formatted_age: "Pending", age: "Pending",
formatted_timestamp: "Pending", formatted_age: "Pending",
cumulative_gas_used: "Pending", formatted_timestamp: "Pending",
to_address_hash: "0xchadmuska", cumulative_gas_used: "Pending",
from_address_hash: "0xtonyhawk", to_address_hash: "0xchadmuska",
confirmations: 0, from_address_hash: "0xtonyhawk",
status: :pending, confirmations: 0,
formatted_status: "Pending", status: :pending,
first_seen: "48 years ago", formatted_status: "Pending",
last_seen: "38 years ago", first_seen: "48 years ago",
}) last_seen: "38 years ago"
}
)
end end
test "works when there are no addresses" do test "works when there are no addresses" do
transaction = insert( transaction =
:transaction, insert(
inserted_at: Timex.parse!("1970-01-01T00:00:18-00:00", "{ISO:Extended}"), :transaction,
updated_at: Timex.parse!("1980-01-01T00:00:18-00:00", "{ISO:Extended}")) inserted_at: Timex.parse!("1970-01-01T00:00:18-00:00", "{ISO:Extended}"),
|> Repo.preload([:block, :to_address, :from_address]) updated_at: Timex.parse!("1980-01-01T00:00:18-00:00", "{ISO:Extended}")
)
|> Repo.preload([:block, :to_address, :from_address])
form = TransactionForm.build(transaction) form = TransactionForm.build(transaction)
assert(form == %{ assert(
block_number: "", form == %{
age: "Pending", block_number: "",
formatted_age: "Pending", age: "Pending",
formatted_timestamp: "Pending", formatted_age: "Pending",
cumulative_gas_used: "Pending", formatted_timestamp: "Pending",
to_address_hash: nil, cumulative_gas_used: "Pending",
from_address_hash: nil, to_address_hash: nil,
confirmations: 0, from_address_hash: nil,
status: :pending, confirmations: 0,
formatted_status: "Pending", status: :pending,
first_seen: "48 years ago", formatted_status: "Pending",
last_seen: "38 years ago", first_seen: "48 years ago",
}) last_seen: "38 years ago"
}
)
end end
end end
describe "build_and_merge/1" do describe "build_and_merge/1" do
test "it returns a merged map of a transaction and its built data" do test "it returns a merged map of a transaction and its built data" do
insert(:block, number: 24) insert(:block, number: 24)
time = Timex.now |> Timex.shift(hours: -2) time = Timex.now() |> Timex.shift(hours: -2)
block = insert(:block, %{
number: 1, block =
gas_used: 99523, insert(:block, %{
timestamp: time, number: 1,
}) gas_used: 99523,
timestamp: time
})
transaction = transaction =
insert(:transaction, insert(
:transaction,
hash: "0xkittenpower", hash: "0xkittenpower",
inserted_at: Timex.parse!("1970-01-01T00:00:18-00:00", "{ISO:Extended}"), inserted_at: Timex.parse!("1970-01-01T00:00:18-00:00", "{ISO:Extended}"),
updated_at: Timex.parse!("1980-01-01T00:00:18-00:00", "{ISO:Extended}"), updated_at: Timex.parse!("1980-01-01T00:00:18-00:00", "{ISO:Extended}"),
gas: 555) gas: 555
)
|> with_block(block) |> with_block(block)
|> with_addresses(%{to: "0xsleepypuppy", from: "0xilovefrogs"}) |> with_addresses(%{to: "0xsleepypuppy", from: "0xilovefrogs"})
insert(:receipt, status: 0, gas_used: 555, transaction: transaction) insert(:receipt, status: 0, gas_used: 555, transaction: transaction)
form = transaction |> Repo.preload([:block, :to_address, :from_address, :receipt]) |> TransactionForm.build_and_merge() form =
transaction |> Repo.preload([:block, :to_address, :from_address, :receipt])
|> TransactionForm.build_and_merge()
assert form.hash == "0xkittenpower" assert form.hash == "0xkittenpower"
assert form.block_number == 1 assert form.block_number == 1

@ -11,23 +11,35 @@ defmodule Explorer.BlockImporterTest do
describe "import/1" do describe "import/1" do
test "imports and saves a block to the database" do test "imports and saves a block to the database" do
use_cassette "block_importer_import_1_saves_the_block" do use_cassette "block_importer_import_1_saves_the_block" do
with_mock ImportTransaction, [perform: fn(_) -> {:ok} end] do with_mock ImportTransaction, perform: fn _ -> {:ok} end do
BlockImporter.import("0xc4f0d") BlockImporter.import("0xc4f0d")
block = Block |> order_by(desc: :inserted_at) |> Repo.one() block = Block |> order_by(desc: :inserted_at) |> Repo.one()
assert block.hash == "0x16cb43ccfb7875c14eb3f03bdc098e4af053160544270594fa429d256cbca64e" assert block.hash ==
"0x16cb43ccfb7875c14eb3f03bdc098e4af053160544270594fa429d256cbca64e"
end end
end end
end end
test "when a block with the same hash is imported it does not update the block" do test "when a block with the same hash is imported it does not update the block" do
use_cassette "block_importer_import_1_duplicate_block" do use_cassette "block_importer_import_1_duplicate_block" do
with_mock ImportTransaction, [perform: fn(hash) -> insert(:transaction, hash: hash) end] do with_mock ImportTransaction, perform: fn hash -> insert(:transaction, hash: hash) end do
insert(:block, hash: "0x16cb43ccfb7875c14eb3f03bdc098e4af053160544270594fa429d256cbca64e", gas_limit: 5) insert(
:block,
hash: "0x16cb43ccfb7875c14eb3f03bdc098e4af053160544270594fa429d256cbca64e",
gas_limit: 5
)
BlockImporter.import("0xc4f0d") BlockImporter.import("0xc4f0d")
block = Repo.get_by(Block, hash: "0x16cb43ccfb7875c14eb3f03bdc098e4af053160544270594fa429d256cbca64e")
block =
Repo.get_by(
Block,
hash: "0x16cb43ccfb7875c14eb3f03bdc098e4af053160544270594fa429d256cbca64e"
)
assert block.gas_limit == 5 assert block.gas_limit == 5
assert Block |> Repo.all |> Enum.count == 1 assert Block |> Repo.all() |> Enum.count() == 1
end end
end end
end end
@ -36,18 +48,18 @@ defmodule Explorer.BlockImporterTest do
describe "import/1 pending" do describe "import/1 pending" do
test "does not create a block" do test "does not create a block" do
use_cassette "block_importer_import_1_pending" do use_cassette "block_importer_import_1_pending" do
with_mock ImportTransaction, [perform_later: fn(_) -> {:ok} end] do with_mock ImportTransaction, perform_later: fn _ -> {:ok} end do
BlockImporter.import("pending") BlockImporter.import("pending")
assert Block |> Repo.all |> Enum.count == 0 assert Block |> Repo.all() |> Enum.count() == 0
end end
end end
end end
test "when a block with the same hash is imported does not create a block" do test "when a block with the same hash is imported does not create a block" do
use_cassette "block_importer_import_1_pending" do use_cassette "block_importer_import_1_pending" do
with_mock ImportTransaction, [perform_later: fn(_) -> insert(:transaction) end] do with_mock ImportTransaction, perform_later: fn _ -> insert(:transaction) end do
BlockImporter.import("pending") BlockImporter.import("pending")
assert Transaction |> Repo.all |> Enum.count != 0 assert Transaction |> Repo.all() |> Enum.count() != 0
end end
end end
end end
@ -75,40 +87,43 @@ defmodule Explorer.BlockImporterTest do
describe "extract_block/1" do describe "extract_block/1" do
test "extracts the block attributes" do test "extracts the block attributes" do
extracted_block = BlockImporter.extract_block(%{ extracted_block =
"difficulty" => "0xfffffffffffffffffffffffffffffffe", BlockImporter.extract_block(%{
"gasLimit" => "0x02", "difficulty" => "0xfffffffffffffffffffffffffffffffe",
"gasUsed" => "0x19522", "gasLimit" => "0x02",
"hash" => "bananas", "gasUsed" => "0x19522",
"miner" => "0xdb1207770e0a4258d7a4ce49ab037f92564fea85", "hash" => "bananas",
"number" => "0x7f2fb", "miner" => "0xdb1207770e0a4258d7a4ce49ab037f92564fea85",
"parentHash" => "0x70029f66ea5a3b2b1ede95079d95a2ab74b649b5b17cdcf6f29b6317e7c7efa6", "number" => "0x7f2fb",
"size" => "0x10", "parentHash" => "0x70029f66ea5a3b2b1ede95079d95a2ab74b649b5b17cdcf6f29b6317e7c7efa6",
"timestamp" => "0x12", "size" => "0x10",
"totalDifficulty" => "0xff", "timestamp" => "0x12",
"nonce" => "0xfb6e1a62d119228b", "totalDifficulty" => "0xff",
"transactions" => [] "nonce" => "0xfb6e1a62d119228b",
}) "transactions" => []
})
assert(extracted_block == %{
difficulty: 340282366920938463463374607431768211454, assert(
gas_limit: 2, extracted_block == %{
gas_used: 103714, difficulty: 340_282_366_920_938_463_463_374_607_431_768_211_454,
hash: "bananas", gas_limit: 2,
nonce: "0xfb6e1a62d119228b", gas_used: 103_714,
miner: "0xdb1207770e0a4258d7a4ce49ab037f92564fea85", hash: "bananas",
number: 520955, nonce: "0xfb6e1a62d119228b",
parent_hash: "0x70029f66ea5a3b2b1ede95079d95a2ab74b649b5b17cdcf6f29b6317e7c7efa6", miner: "0xdb1207770e0a4258d7a4ce49ab037f92564fea85",
size: 16, number: 520_955,
timestamp: Timex.parse!("1970-01-01T00:00:18-00:00", "{ISO:Extended}"), parent_hash: "0x70029f66ea5a3b2b1ede95079d95a2ab74b649b5b17cdcf6f29b6317e7c7efa6",
total_difficulty: 255, size: 16,
}) timestamp: Timex.parse!("1970-01-01T00:00:18-00:00", "{ISO:Extended}"),
total_difficulty: 255
}
)
end end
end end
describe "decode_integer_field/1" do describe "decode_integer_field/1" do
test "returns the integer value of a hex value" do test "returns the integer value of a hex value" do
assert(BlockImporter.decode_integer_field("0x7f2fb") == 520955) assert(BlockImporter.decode_integer_field("0x7f2fb") == 520_955)
end end
end end

@ -7,7 +7,12 @@ defmodule Explorer.InternalTransactionImporterTest do
describe "import/1" do describe "import/1" do
test "imports and saves an internal transaction to the database" do test "imports and saves an internal transaction to the database" do
use_cassette "internal_transaction_importer_import_1" do use_cassette "internal_transaction_importer_import_1" do
transaction = insert(:transaction, hash: "0x051e031f05b3b3a5ff73e1189c36e3e2a41fd1c2d9772b2c75349e22ed4d3f68") transaction =
insert(
:transaction,
hash: "0x051e031f05b3b3a5ff73e1189c36e3e2a41fd1c2d9772b2c75349e22ed4d3f68"
)
InternalTransactionImporter.import(transaction.hash) InternalTransactionImporter.import(transaction.hash)
internal_transactions = InternalTransaction |> Repo.all() internal_transactions = InternalTransaction |> Repo.all()
assert length(internal_transactions) == 2 assert length(internal_transactions) == 2
@ -16,18 +21,34 @@ defmodule Explorer.InternalTransactionImporterTest do
test "imports internal transactions with ordered indexes" do test "imports internal transactions with ordered indexes" do
use_cassette "internal_transaction_importer_import_1" do use_cassette "internal_transaction_importer_import_1" do
transaction = insert(:transaction, hash: "0x051e031f05b3b3a5ff73e1189c36e3e2a41fd1c2d9772b2c75349e22ed4d3f68") transaction =
insert(
:transaction,
hash: "0x051e031f05b3b3a5ff73e1189c36e3e2a41fd1c2d9772b2c75349e22ed4d3f68"
)
InternalTransactionImporter.import(transaction.hash) InternalTransactionImporter.import(transaction.hash)
last_internal_transaction = InternalTransaction |> order_by(desc: :index) |> limit(1) |> Repo.one()
last_internal_transaction =
InternalTransaction |> order_by(desc: :index) |> limit(1) |> Repo.one()
assert last_internal_transaction.index == 1 assert last_internal_transaction.index == 1
end end
end end
test "imports an internal transaction that creates a contract" do test "imports an internal transaction that creates a contract" do
use_cassette "internal_transaction_importer_import_1_with_contract_creation" do use_cassette "internal_transaction_importer_import_1_with_contract_creation" do
transaction = insert(:transaction, hash: "0x27d64b8e8564d2852c88767e967b88405c99341509cd3a3504fd67a65277116d") transaction =
insert(
:transaction,
hash: "0x27d64b8e8564d2852c88767e967b88405c99341509cd3a3504fd67a65277116d"
)
InternalTransactionImporter.import(transaction.hash) InternalTransactionImporter.import(transaction.hash)
last_internal_transaction = InternalTransaction |> order_by(desc: :index) |> limit(1) |> Repo.one()
last_internal_transaction =
InternalTransaction |> order_by(desc: :index) |> limit(1) |> Repo.one()
assert last_internal_transaction.call_type == "create" assert last_internal_transaction.call_type == "create"
end end
end end
@ -47,32 +68,34 @@ defmodule Explorer.InternalTransactionImporterTest do
"gas" => "0x4821f", "gas" => "0x4821f",
"input" => "0xd1f276d3", "input" => "0xd1f276d3",
"to" => "0xe213402e637565bb9de0651827517e7554693f53", "to" => "0xe213402e637565bb9de0651827517e7554693f53",
"value" => "0x0", "value" => "0x0"
}, },
"result" => %{ "result" => %{
"gasUsed" => "0x4e4", "gasUsed" => "0x4e4",
"output" => "0x000000000000000000000000ba9f067abbc4315ece8eb33e7a3d01030bb368ef" "output" => "0x000000000000000000000000ba9f067abbc4315ece8eb33e7a3d01030bb368ef"
}, },
"subtraces" => 0, "subtraces" => 0,
"traceAddress" => [2, 0], "traceAddress" => [2, 0],
"type" => "call" "type" => "call"
} }
to_address = insert(:address, hash: "0xe213402e637565bb9de0651827517e7554693f53") to_address = insert(:address, hash: "0xe213402e637565bb9de0651827517e7554693f53")
from_address = insert(:address, hash: "0xba9f067abbc4315ece8eb33e7a3d01030bb368ef") from_address = insert(:address, hash: "0xba9f067abbc4315ece8eb33e7a3d01030bb368ef")
assert(InternalTransactionImporter.extract_trace({trace, 2}) == %{ assert(
index: 2, InternalTransactionImporter.extract_trace({trace, 2}) == %{
to_address_id: to_address.id, index: 2,
from_address_id: from_address.id, to_address_id: to_address.id,
call_type: "call", from_address_id: from_address.id,
trace_address: [2, 0], call_type: "call",
value: 0, trace_address: [2, 0],
gas: 295455, value: 0,
gas_used: 1252, gas: 295_455,
input: "0xd1f276d3", gas_used: 1252,
output: "0x000000000000000000000000ba9f067abbc4315ece8eb33e7a3d01030bb368ef", input: "0xd1f276d3",
}) output: "0x000000000000000000000000ba9f067abbc4315ece8eb33e7a3d01030bb368ef"
}
)
end end
end end
end end

@ -7,72 +7,128 @@ defmodule Explorer.ReceiptImporterTest do
describe "import/1" do describe "import/1" do
test "saves a receipt to the database" do test "saves a receipt to the database" do
transaction = insert(:transaction, hash: "0xdc3a0dfd0bbffd5eabbe40fb13afbe35ac5f5c030bff148f3e50afe32974b291") transaction =
insert(
:transaction,
hash: "0xdc3a0dfd0bbffd5eabbe40fb13afbe35ac5f5c030bff148f3e50afe32974b291"
)
use_cassette "transaction_importer_import_1_receipt" do use_cassette "transaction_importer_import_1_receipt" do
ReceiptImporter.import("0xdc3a0dfd0bbffd5eabbe40fb13afbe35ac5f5c030bff148f3e50afe32974b291") ReceiptImporter.import(
receipt = Receipt |> preload([:transaction]) |> Repo.one "0xdc3a0dfd0bbffd5eabbe40fb13afbe35ac5f5c030bff148f3e50afe32974b291"
)
receipt = Receipt |> preload([:transaction]) |> Repo.one()
assert receipt.transaction == transaction assert receipt.transaction == transaction
end end
end end
test "saves a receipt log" do test "saves a receipt log" do
insert(:transaction, hash: "0xdc3a0dfd0bbffd5eabbe40fb13afbe35ac5f5c030bff148f3e50afe32974b291") insert(
:transaction,
hash: "0xdc3a0dfd0bbffd5eabbe40fb13afbe35ac5f5c030bff148f3e50afe32974b291"
)
use_cassette "transaction_importer_import_1_receipt" do use_cassette "transaction_importer_import_1_receipt" do
ReceiptImporter.import("0xdc3a0dfd0bbffd5eabbe40fb13afbe35ac5f5c030bff148f3e50afe32974b291") ReceiptImporter.import(
receipt = Receipt |> preload([:transaction]) |> Repo.one "0xdc3a0dfd0bbffd5eabbe40fb13afbe35ac5f5c030bff148f3e50afe32974b291"
log = Log |> preload([receipt: :transaction]) |> Repo.one )
receipt = Receipt |> preload([:transaction]) |> Repo.one()
log = Log |> preload(receipt: :transaction) |> Repo.one()
assert log.receipt == receipt assert log.receipt == receipt
end end
end end
test "saves a receipt log for an address" do test "saves a receipt log for an address" do
insert(:transaction, hash: "0xdc3a0dfd0bbffd5eabbe40fb13afbe35ac5f5c030bff148f3e50afe32974b291") insert(
:transaction,
hash: "0xdc3a0dfd0bbffd5eabbe40fb13afbe35ac5f5c030bff148f3e50afe32974b291"
)
address = insert(:address, hash: "0x353fe3ffbf77edef7f9c352c47965a38c07e837c") address = insert(:address, hash: "0x353fe3ffbf77edef7f9c352c47965a38c07e837c")
use_cassette "transaction_importer_import_1_receipt" do use_cassette "transaction_importer_import_1_receipt" do
ReceiptImporter.import("0xdc3a0dfd0bbffd5eabbe40fb13afbe35ac5f5c030bff148f3e50afe32974b291") ReceiptImporter.import(
log = Log |> preload([:address]) |> Repo.one "0xdc3a0dfd0bbffd5eabbe40fb13afbe35ac5f5c030bff148f3e50afe32974b291"
)
log = Log |> preload([:address]) |> Repo.one()
assert log.address == address assert log.address == address
end end
end end
test "saves a receipt for a failed transaction" do test "saves a receipt for a failed transaction" do
insert(:transaction, hash: "0x2532864dc2e0d0bc2dfabf4685c0c03dbdbe9cf67ebc593fc82d41087ab71435") insert(
:transaction,
hash: "0x2532864dc2e0d0bc2dfabf4685c0c03dbdbe9cf67ebc593fc82d41087ab71435"
)
use_cassette "transaction_importer_import_1_failed" do use_cassette "transaction_importer_import_1_failed" do
ReceiptImporter.import("0x2532864dc2e0d0bc2dfabf4685c0c03dbdbe9cf67ebc593fc82d41087ab71435") ReceiptImporter.import(
"0x2532864dc2e0d0bc2dfabf4685c0c03dbdbe9cf67ebc593fc82d41087ab71435"
)
receipt = Repo.one(Receipt) receipt = Repo.one(Receipt)
assert receipt.status == 0 assert receipt.status == 0
end end
end end
test "saves a receipt for a transaction that ran out of gas" do test "saves a receipt for a transaction that ran out of gas" do
insert(:transaction, hash: "0x702e518267b0a57e4cb44b9db100afe4d7115f2d2650466a8c376f3dbb77eb35") insert(
:transaction,
hash: "0x702e518267b0a57e4cb44b9db100afe4d7115f2d2650466a8c376f3dbb77eb35"
)
use_cassette "transaction_importer_import_1_out_of_gas" do use_cassette "transaction_importer_import_1_out_of_gas" do
ReceiptImporter.import("0x702e518267b0a57e4cb44b9db100afe4d7115f2d2650466a8c376f3dbb77eb35") ReceiptImporter.import(
"0x702e518267b0a57e4cb44b9db100afe4d7115f2d2650466a8c376f3dbb77eb35"
)
receipt = Repo.one(Receipt) receipt = Repo.one(Receipt)
assert receipt.status == 0 assert receipt.status == 0
end end
end end
test "does not import a receipt for a transaction that already has one" do test "does not import a receipt for a transaction that already has one" do
transaction = insert(:transaction, hash: "0xdc3a0dfd0bbffd5eabbe40fb13afbe35ac5f5c030bff148f3e50afe32974b291") transaction =
insert(
:transaction,
hash: "0xdc3a0dfd0bbffd5eabbe40fb13afbe35ac5f5c030bff148f3e50afe32974b291"
)
insert(:receipt, transaction: transaction) insert(:receipt, transaction: transaction)
use_cassette "transaction_importer_import_1_receipt" do use_cassette "transaction_importer_import_1_receipt" do
ReceiptImporter.import("0xdc3a0dfd0bbffd5eabbe40fb13afbe35ac5f5c030bff148f3e50afe32974b291") ReceiptImporter.import(
"0xdc3a0dfd0bbffd5eabbe40fb13afbe35ac5f5c030bff148f3e50afe32974b291"
)
assert Repo.all(Receipt) |> Enum.count() == 1 assert Repo.all(Receipt) |> Enum.count() == 1
end end
end end
test "does not import a receipt for a nonexistent transaction" do test "does not import a receipt for a nonexistent transaction" do
use_cassette "transaction_importer_import_1_receipt" do use_cassette "transaction_importer_import_1_receipt" do
ReceiptImporter.import("0xdc3a0dfd0bbffd5eabbe40fb13afbe35ac5f5c030bff148f3e50afe32974b291") ReceiptImporter.import(
"0xdc3a0dfd0bbffd5eabbe40fb13afbe35ac5f5c030bff148f3e50afe32974b291"
)
assert Repo.all(Receipt) |> Enum.count() == 0 assert Repo.all(Receipt) |> Enum.count() == 0
end end
end end
test "does not process a forever-pending receipt" do test "does not process a forever-pending receipt" do
insert(:transaction, hash: "0xde791cfcde3900d4771e5fcf8c11dc305714118df7aa7e42f84576e64dbf6246") insert(
:transaction,
hash: "0xde791cfcde3900d4771e5fcf8c11dc305714118df7aa7e42f84576e64dbf6246"
)
use_cassette "transaction_importer_import_1_pending" do use_cassette "transaction_importer_import_1_pending" do
ReceiptImporter.import("0xde791cfcde3900d4771e5fcf8c11dc305714118df7aa7e42f84576e64dbf6246") ReceiptImporter.import(
"0xde791cfcde3900d4771e5fcf8c11dc305714118df7aa7e42f84576e64dbf6246"
)
assert Repo.all(Receipt) |> Enum.count() == 0 assert Repo.all(Receipt) |> Enum.count() == 0
end end
end end

@ -23,39 +23,51 @@ defmodule Explorer.TransactionImporterTest do
"to" => "0x7a33b7d", "to" => "0x7a33b7d",
"standardV" => "0x11", "standardV" => "0x11",
"transactionIndex" => "0x12", "transactionIndex" => "0x12",
"v" => "0x13", "v" => "0x13"
} }
@processed_transaction %{ @processed_transaction %{
hash: "pepino", hash: "pepino",
value: 1000000000000000000, value: 1_000_000_000_000_000_000,
gas: 135168, gas: 135_168,
gas_price: 65536, gas_price: 65536,
input: "0x5c8eff12", input: "0x5c8eff12",
nonce: 201527, nonce: 201_527,
public_key: "0xb39af9c", public_key: "0xb39af9c",
r: "0x9", r: "0x9",
s: "0x10", s: "0x10",
standard_v: "0x11", standard_v: "0x11",
transaction_index: "0x12", transaction_index: "0x12",
v: "0x13", v: "0x13"
} }
describe "import/1" do describe "import/1" do
test "imports and saves a transaction to the database" do test "imports and saves a transaction to the database" do
use_cassette "transaction_importer_import_saves_the_transaction" do use_cassette "transaction_importer_import_saves_the_transaction" do
TransactionImporter.import("0xdc3a0dfd0bbffd5eabbe40fb13afbe35ac5f5c030bff148f3e50afe32974b291") TransactionImporter.import(
transaction = Transaction |> order_by(desc: :inserted_at) |> Repo.one "0xdc3a0dfd0bbffd5eabbe40fb13afbe35ac5f5c030bff148f3e50afe32974b291"
)
assert transaction.hash == "0xdc3a0dfd0bbffd5eabbe40fb13afbe35ac5f5c030bff148f3e50afe32974b291" transaction = Transaction |> order_by(desc: :inserted_at) |> Repo.one()
assert transaction.hash ==
"0xdc3a0dfd0bbffd5eabbe40fb13afbe35ac5f5c030bff148f3e50afe32974b291"
end end
end end
test "when the transaction has previously been saved does not update it" do test "when the transaction has previously been saved does not update it" do
use_cassette "transaction_importer_updates_the_association" do use_cassette "transaction_importer_updates_the_association" do
insert(:transaction, hash: "0x170baac4eca26076953370dd603c68eab340c0135b19b585010d3158a5dbbf23", gas: 5) insert(
TransactionImporter.import("0x170baac4eca26076953370dd603c68eab340c0135b19b585010d3158a5dbbf23") :transaction,
transaction = Transaction |> order_by(desc: :inserted_at) |> Repo.one hash: "0x170baac4eca26076953370dd603c68eab340c0135b19b585010d3158a5dbbf23",
gas: 5
)
TransactionImporter.import(
"0x170baac4eca26076953370dd603c68eab340c0135b19b585010d3158a5dbbf23"
)
transaction = Transaction |> order_by(desc: :inserted_at) |> Repo.one()
assert transaction.gas == Decimal.new(5) assert transaction.gas == Decimal.new(5)
end end
@ -63,9 +75,22 @@ defmodule Explorer.TransactionImporterTest do
test "binds an association to an existing block" do test "binds an association to an existing block" do
use_cassette "transaction_importer_saves_the_association" do use_cassette "transaction_importer_saves_the_association" do
block = insert(:block, hash: "0xfce13392435a8e7dab44c07d482212efb9dc39a9bea1915a9ead308b55a617f9") block =
TransactionImporter.import("0x64d851139325479c3bb7ccc6e6ab4cde5bc927dce6810190fe5d770a4c1ac333") insert(
transaction = Transaction |> Repo.get_by(hash: "0x64d851139325479c3bb7ccc6e6ab4cde5bc927dce6810190fe5d770a4c1ac333") :block,
hash: "0xfce13392435a8e7dab44c07d482212efb9dc39a9bea1915a9ead308b55a617f9"
)
TransactionImporter.import(
"0x64d851139325479c3bb7ccc6e6ab4cde5bc927dce6810190fe5d770a4c1ac333"
)
transaction =
Transaction
|> Repo.get_by(
hash: "0x64d851139325479c3bb7ccc6e6ab4cde5bc927dce6810190fe5d770a4c1ac333"
)
block_transaction = BlockTransaction |> Repo.get_by(transaction_id: transaction.id) block_transaction = BlockTransaction |> Repo.get_by(transaction_id: transaction.id)
assert block_transaction.block_id == block.id assert block_transaction.block_id == block.id
@ -74,8 +99,16 @@ defmodule Explorer.TransactionImporterTest do
test "when there is no block it does not save a block transaction" do test "when there is no block it does not save a block transaction" do
use_cassette "transaction_importer_txn_without_block" do use_cassette "transaction_importer_txn_without_block" do
TransactionImporter.import("0xc6aa189827c14880f012a65292f7add7b5310094f8773a3d85b66303039b9dcf") TransactionImporter.import(
transaction = Transaction |> Repo.get_by(hash: "0xc6aa189827c14880f012a65292f7add7b5310094f8773a3d85b66303039b9dcf") "0xc6aa189827c14880f012a65292f7add7b5310094f8773a3d85b66303039b9dcf"
)
transaction =
Transaction
|> Repo.get_by(
hash: "0xc6aa189827c14880f012a65292f7add7b5310094f8773a3d85b66303039b9dcf"
)
block_transaction = BlockTransaction |> Repo.get_by(transaction_id: transaction.id) block_transaction = BlockTransaction |> Repo.get_by(transaction_id: transaction.id)
refute block_transaction refute block_transaction
@ -84,11 +117,20 @@ defmodule Explorer.TransactionImporterTest do
test "creates a from address" do test "creates a from address" do
use_cassette "transaction_importer_creates_a_from_address" do use_cassette "transaction_importer_creates_a_from_address" do
TransactionImporter.import("0xc445f5410912458c480d992dd93355ae3dad64d9f65db25a3cf43a9c609a2e0d") TransactionImporter.import(
"0xc445f5410912458c480d992dd93355ae3dad64d9f65db25a3cf43a9c609a2e0d"
)
transaction =
Transaction
|> Repo.get_by(
hash: "0xc445f5410912458c480d992dd93355ae3dad64d9f65db25a3cf43a9c609a2e0d"
)
transaction = Transaction |> Repo.get_by(hash: "0xc445f5410912458c480d992dd93355ae3dad64d9f65db25a3cf43a9c609a2e0d")
address = Address |> Repo.get_by(hash: "0xa5b4b372112ab8dbbb48c8d0edd89227e24ec785") address = Address |> Repo.get_by(hash: "0xa5b4b372112ab8dbbb48c8d0edd89227e24ec785")
from_address = FromAddress |> Repo.get_by(transaction_id: transaction.id, address_id: address.id)
from_address =
FromAddress |> Repo.get_by(transaction_id: transaction.id, address_id: address.id)
assert from_address assert from_address
end end
@ -96,12 +138,22 @@ defmodule Explorer.TransactionImporterTest do
test "binds an existing from address" do test "binds an existing from address" do
insert(:address, hash: "0xa5b4b372112ab8dbbb48c8d0edd89227e24ec785") insert(:address, hash: "0xa5b4b372112ab8dbbb48c8d0edd89227e24ec785")
use_cassette "transaction_importer_creates_a_from_address" do use_cassette "transaction_importer_creates_a_from_address" do
TransactionImporter.import("0xc445f5410912458c480d992dd93355ae3dad64d9f65db25a3cf43a9c609a2e0d") TransactionImporter.import(
"0xc445f5410912458c480d992dd93355ae3dad64d9f65db25a3cf43a9c609a2e0d"
)
transaction =
Transaction
|> Repo.get_by(
hash: "0xc445f5410912458c480d992dd93355ae3dad64d9f65db25a3cf43a9c609a2e0d"
)
transaction = Transaction |> Repo.get_by(hash: "0xc445f5410912458c480d992dd93355ae3dad64d9f65db25a3cf43a9c609a2e0d")
address = Address |> Repo.get_by(hash: "0xa5b4b372112ab8dbbb48c8d0edd89227e24ec785") address = Address |> Repo.get_by(hash: "0xa5b4b372112ab8dbbb48c8d0edd89227e24ec785")
from_address = FromAddress |> Repo.get_by(transaction_id: transaction.id, address_id: address.id)
from_address =
FromAddress |> Repo.get_by(transaction_id: transaction.id, address_id: address.id)
assert from_address assert from_address
end end
@ -109,11 +161,20 @@ defmodule Explorer.TransactionImporterTest do
test "creates a to address" do test "creates a to address" do
use_cassette "transaction_importer_creates_a_to_address" do use_cassette "transaction_importer_creates_a_to_address" do
TransactionImporter.import("0xdc533d4227734a7cacd75a069e8dc57ac571b865ed97bae5ea4cb74b54145f4c") TransactionImporter.import(
"0xdc533d4227734a7cacd75a069e8dc57ac571b865ed97bae5ea4cb74b54145f4c"
)
transaction =
Transaction
|> Repo.get_by(
hash: "0xdc533d4227734a7cacd75a069e8dc57ac571b865ed97bae5ea4cb74b54145f4c"
)
transaction = Transaction |> Repo.get_by(hash: "0xdc533d4227734a7cacd75a069e8dc57ac571b865ed97bae5ea4cb74b54145f4c")
address = Address |> Repo.get_by(hash: "0x24e5b8528fe83257d5fe3497ef616026713347f8") address = Address |> Repo.get_by(hash: "0x24e5b8528fe83257d5fe3497ef616026713347f8")
to_address = ToAddress |> Repo.get_by(transaction_id: transaction.id, address_id: address.id)
to_address =
ToAddress |> Repo.get_by(transaction_id: transaction.id, address_id: address.id)
assert(to_address) assert(to_address)
end end
@ -121,12 +182,22 @@ defmodule Explorer.TransactionImporterTest do
test "binds an existing to address" do test "binds an existing to address" do
insert(:address, hash: "0x24e5b8528fe83257d5fe3497ef616026713347f8") insert(:address, hash: "0x24e5b8528fe83257d5fe3497ef616026713347f8")
use_cassette "transaction_importer_creates_a_to_address" do use_cassette "transaction_importer_creates_a_to_address" do
TransactionImporter.import("0xdc533d4227734a7cacd75a069e8dc57ac571b865ed97bae5ea4cb74b54145f4c") TransactionImporter.import(
"0xdc533d4227734a7cacd75a069e8dc57ac571b865ed97bae5ea4cb74b54145f4c"
)
transaction =
Transaction
|> Repo.get_by(
hash: "0xdc533d4227734a7cacd75a069e8dc57ac571b865ed97bae5ea4cb74b54145f4c"
)
transaction = Transaction |> Repo.get_by(hash: "0xdc533d4227734a7cacd75a069e8dc57ac571b865ed97bae5ea4cb74b54145f4c")
address = Address |> Repo.get_by(hash: "0x24e5b8528fe83257d5fe3497ef616026713347f8") address = Address |> Repo.get_by(hash: "0x24e5b8528fe83257d5fe3497ef616026713347f8")
to_address = ToAddress |> Repo.get_by(transaction_id: transaction.id, address_id: address.id)
to_address =
ToAddress |> Repo.get_by(transaction_id: transaction.id, address_id: address.id)
assert(to_address) assert(to_address)
end end
@ -134,10 +205,20 @@ defmodule Explorer.TransactionImporterTest do
test "creates a to address using creates when to is nil" do test "creates a to address using creates when to is nil" do
use_cassette "transaction_importer_creates_a_to_address_from_creates" do use_cassette "transaction_importer_creates_a_to_address_from_creates" do
TransactionImporter.import("0xdc533d4227734a7cacd75a069e8dc57ac571b865ed97bae5ea4cb74b54145f4c") TransactionImporter.import(
transaction = Transaction |> Repo.get_by(hash: "0xdc533d4227734a7cacd75a069e8dc57ac571b865ed97bae5ea4cb74b54145f4c") "0xdc533d4227734a7cacd75a069e8dc57ac571b865ed97bae5ea4cb74b54145f4c"
)
transaction =
Transaction
|> Repo.get_by(
hash: "0xdc533d4227734a7cacd75a069e8dc57ac571b865ed97bae5ea4cb74b54145f4c"
)
address = Address |> Repo.get_by(hash: "0x24e5b8528fe83257d5fe3497ef616026713347f8") address = Address |> Repo.get_by(hash: "0x24e5b8528fe83257d5fe3497ef616026713347f8")
to_address = ToAddress |> Repo.get_by(transaction_id: transaction.id, address_id: address.id)
to_address =
ToAddress |> Repo.get_by(transaction_id: transaction.id, address_id: address.id)
assert(to_address) assert(to_address)
end end
@ -145,7 +226,11 @@ defmodule Explorer.TransactionImporterTest do
test "processes a map of transaction attributes" do test "processes a map of transaction attributes" do
insert(:block, hash: "0xtakis") insert(:block, hash: "0xtakis")
TransactionImporter.import(Map.merge(@raw_transaction, %{"hash" => "0xmunchos", "blockHash" => "0xtakis"}))
TransactionImporter.import(
Map.merge(@raw_transaction, %{"hash" => "0xmunchos", "blockHash" => "0xtakis"})
)
last_transaction = Transaction |> order_by(desc: :inserted_at) |> limit(1) |> Repo.one() last_transaction = Transaction |> order_by(desc: :inserted_at) |> limit(1) |> Repo.one()
assert last_transaction.hash == "0xmunchos" assert last_transaction.hash == "0xmunchos"
@ -166,7 +251,11 @@ defmodule Explorer.TransactionImporterTest do
describe "download_transaction/1" do describe "download_transaction/1" do
test "downloads a transaction" do test "downloads a transaction" do
use_cassette "transaction_importer_download_transaction" do use_cassette "transaction_importer_download_transaction" do
raw_transaction = TransactionImporter.download_transaction("0x170baac4eca26076953370dd603c68eab340c0135b19b585010d3158a5dbbf23") raw_transaction =
TransactionImporter.download_transaction(
"0x170baac4eca26076953370dd603c68eab340c0135b19b585010d3158a5dbbf23"
)
assert(raw_transaction["from"] == "0xbe96ef1d056c97323e210fd0dd818aa027e57143") assert(raw_transaction["from"] == "0xbe96ef1d056c97323e210fd0dd818aa027e57143")
end end
end end
@ -192,6 +281,7 @@ defmodule Explorer.TransactionImporterTest do
block = insert(:block) block = insert(:block)
transaction = insert(:transaction) transaction = insert(:transaction)
TransactionImporter.create_block_transaction(transaction, block.hash) TransactionImporter.create_block_transaction(transaction, block.hash)
block_transaction = block_transaction =
BlockTransaction BlockTransaction
|> Repo.get_by(transaction_id: transaction.id, block_id: block.id) |> Repo.get_by(transaction_id: transaction.id, block_id: block.id)
@ -203,9 +293,18 @@ defmodule Explorer.TransactionImporterTest do
block = insert(:block) block = insert(:block)
transaction = insert(:transaction) transaction = insert(:transaction)
the_seventies = Timex.parse!("1970-01-01T00:00:18-00:00", "{ISO:Extended}") the_seventies = Timex.parse!("1970-01-01T00:00:18-00:00", "{ISO:Extended}")
block_transaction = insert(:block_transaction, %{block_id: block.id, transaction_id: transaction.id, inserted_at: the_seventies, updated_at: the_seventies})
block_transaction =
insert(:block_transaction, %{
block_id: block.id,
transaction_id: transaction.id,
inserted_at: the_seventies,
updated_at: the_seventies
})
update_block = insert(:block) update_block = insert(:block)
TransactionImporter.create_block_transaction(transaction, update_block.hash) TransactionImporter.create_block_transaction(transaction, update_block.hash)
updated_block_transaction = updated_block_transaction =
BlockTransaction BlockTransaction
|> Repo.get_by(transaction_id: transaction.id) |> Repo.get_by(transaction_id: transaction.id)
@ -219,7 +318,7 @@ defmodule Explorer.TransactionImporterTest do
test "that it creates a new address when one does not exist" do test "that it creates a new address when one does not exist" do
transaction = insert(:transaction) transaction = insert(:transaction)
TransactionImporter.create_from_address(transaction, "0xbb8") TransactionImporter.create_from_address(transaction, "0xbb8")
last_address = Address |> order_by(desc: :inserted_at) |> Repo.one last_address = Address |> order_by(desc: :inserted_at) |> Repo.one()
assert last_address.hash == "0xbb8" assert last_address.hash == "0xbb8"
end end
@ -227,8 +326,8 @@ defmodule Explorer.TransactionImporterTest do
test "that it joins transaction and from address" do test "that it joins transaction and from address" do
transaction = insert(:transaction) transaction = insert(:transaction)
TransactionImporter.create_from_address(transaction, "0xFreshPrince") TransactionImporter.create_from_address(transaction, "0xFreshPrince")
address = Address |> order_by(desc: :inserted_at) |> Repo.one address = Address |> order_by(desc: :inserted_at) |> Repo.one()
from_address = FromAddress |> order_by(desc: :inserted_at) |> Repo.one from_address = FromAddress |> order_by(desc: :inserted_at) |> Repo.one()
assert from_address.transaction_id == transaction.id assert from_address.transaction_id == transaction.id
assert from_address.address_id == address.id assert from_address.address_id == address.id
@ -239,7 +338,7 @@ defmodule Explorer.TransactionImporterTest do
insert(:address, hash: "0xbb8") insert(:address, hash: "0xbb8")
TransactionImporter.create_from_address(transaction, "0xbb8") TransactionImporter.create_from_address(transaction, "0xbb8")
assert Address |> Repo.all |> length == 1 assert Address |> Repo.all() |> length == 1
end end
end end
@ -247,7 +346,7 @@ defmodule Explorer.TransactionImporterTest do
test "that it creates a new address when one does not exist" do test "that it creates a new address when one does not exist" do
transaction = insert(:transaction) transaction = insert(:transaction)
TransactionImporter.create_to_address(transaction, "0xFreshPrince") TransactionImporter.create_to_address(transaction, "0xFreshPrince")
last_address = Address |> order_by(desc: :inserted_at) |> Repo.one last_address = Address |> order_by(desc: :inserted_at) |> Repo.one()
assert last_address.hash == "0xfreshprince" assert last_address.hash == "0xfreshprince"
end end
@ -255,8 +354,8 @@ defmodule Explorer.TransactionImporterTest do
test "that it joins transaction and address" do test "that it joins transaction and address" do
transaction = insert(:transaction) transaction = insert(:transaction)
TransactionImporter.create_to_address(transaction, "0xFreshPrince") TransactionImporter.create_to_address(transaction, "0xFreshPrince")
address = Address |> order_by(desc: :inserted_at) |> Repo.one address = Address |> order_by(desc: :inserted_at) |> Repo.one()
to_address = ToAddress |> order_by(desc: :inserted_at) |> Repo.one to_address = ToAddress |> order_by(desc: :inserted_at) |> Repo.one()
assert to_address.transaction_id == transaction.id assert to_address.transaction_id == transaction.id
assert to_address.address_id == address.id assert to_address.address_id == address.id
@ -267,13 +366,13 @@ defmodule Explorer.TransactionImporterTest do
insert(:address, hash: "bigmouthbillybass") insert(:address, hash: "bigmouthbillybass")
TransactionImporter.create_to_address(transaction, "bigmouthbillybass") TransactionImporter.create_to_address(transaction, "bigmouthbillybass")
assert Address |> Repo.all |> length == 1 assert Address |> Repo.all() |> length == 1
end end
end end
describe "decode_integer_field/1" do describe "decode_integer_field/1" do
test "returns the integer value of a hex value" do test "returns the integer value of a hex value" do
assert(TransactionImporter.decode_integer_field("0x7f2fb") == 520955) assert(TransactionImporter.decode_integer_field("0x7f2fb") == 520_955)
end end
end end
end end

@ -6,7 +6,22 @@ defmodule Explorer.InternalTransactionTest do
describe "changeset/2" do describe "changeset/2" do
test "with valid attributes" do test "with valid attributes" do
transaction = insert(:transaction) transaction = insert(:transaction)
changeset = InternalTransaction.changeset(%InternalTransaction{}, %{transaction_id: transaction.id, index: 0, call_type: "call", trace_address: [0, 1], value: 100, gas: 100, gas_used: 100, input: "pintos", output: "refried", to_address_id: 1, from_address_id: 2})
changeset =
InternalTransaction.changeset(%InternalTransaction{}, %{
transaction_id: transaction.id,
index: 0,
call_type: "call",
trace_address: [0, 1],
value: 100,
gas: 100,
gas_used: 100,
input: "pintos",
output: "refried",
to_address_id: 1,
from_address_id: 2
})
assert changeset.valid? assert changeset.valid?
end end
@ -17,7 +32,20 @@ defmodule Explorer.InternalTransactionTest do
test "that a valid changeset is persistable" do test "that a valid changeset is persistable" do
transaction = insert(:transaction) transaction = insert(:transaction)
changeset = InternalTransaction.changeset(%InternalTransaction{}, %{transaction: transaction, index: 0, call_type: "call", trace_address: [0, 1], value: 100, gas: 100, gas_used: 100, input: "thin-mints", output: "munchos"})
changeset =
InternalTransaction.changeset(%InternalTransaction{}, %{
transaction: transaction,
index: 0,
call_type: "call",
trace_address: [0, 1],
value: 100,
gas: 100,
gas_used: 100,
input: "thin-mints",
output: "munchos"
})
assert Repo.insert(changeset) assert Repo.insert(changeset)
end end
end end

@ -33,7 +33,7 @@ defmodule Explorer.ReceiptTest do
log_params = params_for(:log, address: address) log_params = params_for(:log, address: address)
params = params_for(:receipt, transaction: transaction, logs: [log_params]) params = params_for(:receipt, transaction: transaction, logs: [log_params])
changeset = Receipt.changeset(%Receipt{}, params) changeset = Receipt.changeset(%Receipt{}, params)
receipt = Repo.insert!(changeset) |> Repo.preload([logs: :address]) receipt = Repo.insert!(changeset) |> Repo.preload(logs: :address)
assert List.first(receipt.logs).address == address assert List.first(receipt.logs).address == address
end end
end end

@ -1,16 +1,16 @@
defmodule Explorer.Transaction.ServiceTest do defmodule Explorer.Transaction.ServiceTest do
use Explorer.DataCase use Explorer.DataCase
alias Explorer.Transaction.Service alias Explorer.Transaction.Service
describe "internal_transactions/1" do describe "internal_transactions/1" do
test "it returns all internal transactions for a given hash" do test "it returns all internal transactions for a given hash" do
transaction = insert(:transaction) transaction = insert(:transaction)
internal_transaction = insert(:internal_transaction, transaction_id: transaction.id) internal_transaction = insert(:internal_transaction, transaction_id: transaction.id)
result = hd(Service.internal_transactions(transaction.hash)) result = hd(Service.internal_transactions(transaction.hash))
assert result.id == internal_transaction.id assert result.id == internal_transaction.id
end end
end end
end end

@ -5,7 +5,22 @@ defmodule Explorer.TransactionTest do
describe "changeset/2" do describe "changeset/2" do
test "with valid attributes" do test "with valid attributes" do
changeset = Transaction.changeset(%Transaction{}, %{hash: "0x0", value: 1, gas: 21000, gas_price: 10000, input: "0x5c8eff12", nonce: "31337", public_key: "0xb39af9c", r: "0x9", s: "0x10", standard_v: "0x11", transaction_index: "0x12", v: "0x13"}) changeset =
Transaction.changeset(%Transaction{}, %{
hash: "0x0",
value: 1,
gas: 21000,
gas_price: 10000,
input: "0x5c8eff12",
nonce: "31337",
public_key: "0xb39af9c",
r: "0x9",
s: "0x10",
standard_v: "0x11",
transaction_index: "0x12",
v: "0x13"
})
assert changeset.valid? assert changeset.valid?
end end

@ -11,7 +11,7 @@ defmodule Explorer.Workers.ImportBlockTest do
test "imports the requested block number as an integer" do test "imports the requested block number as an integer" do
use_cassette "import_block_perform_1_integer" do use_cassette "import_block_perform_1_integer" do
ImportBlock.perform(1) ImportBlock.perform(1)
last_block = Block |> order_by(asc: :number) |> Repo.one last_block = Block |> order_by(asc: :number) |> Repo.one()
assert last_block.number == 1 assert last_block.number == 1
end end
end end
@ -19,7 +19,7 @@ defmodule Explorer.Workers.ImportBlockTest do
test "imports the requested block number as a string" do test "imports the requested block number as a string" do
use_cassette "import_block_perform_1_string" do use_cassette "import_block_perform_1_string" do
ImportBlock.perform("1") ImportBlock.perform("1")
last_block = Block |> order_by(asc: :number) |> Repo.one last_block = Block |> order_by(asc: :number) |> Repo.one()
assert last_block.number == 1 assert last_block.number == 1
end end
end end
@ -27,16 +27,16 @@ defmodule Explorer.Workers.ImportBlockTest do
test "imports the earliest block" do test "imports the earliest block" do
use_cassette "import_block_perform_1_earliest" do use_cassette "import_block_perform_1_earliest" do
ImportBlock.perform("earliest") ImportBlock.perform("earliest")
last_block = Block |> order_by(asc: :number) |> Repo.one last_block = Block |> order_by(asc: :number) |> Repo.one()
assert last_block.number == 0 assert last_block.number == 0
end end
end end
test "imports the latest block" do test "imports the latest block" do
use_cassette "import_block_perform_1_latest" do use_cassette "import_block_perform_1_latest" do
with_mock Exq, [enqueue: fn (_, _, _, [number]) -> insert(:block, number: number) end] do with_mock Exq, enqueue: fn _, _, _, [number] -> insert(:block, number: number) end do
ImportBlock.perform("latest") ImportBlock.perform("latest")
last_block = Block |> order_by(asc: :number) |> Repo.one last_block = Block |> order_by(asc: :number) |> Repo.one()
assert last_block.number > 0 assert last_block.number > 0
end end
end end
@ -46,7 +46,7 @@ defmodule Explorer.Workers.ImportBlockTest do
use_cassette "import_block_perform_1_duplicate" do use_cassette "import_block_perform_1_duplicate" do
insert(:block, hash: "0x52c867bc0a91e573dc39300143c3bead7408d09d45bdb686749f02684ece72f3") insert(:block, hash: "0x52c867bc0a91e573dc39300143c3bead7408d09d45bdb686749f02684ece72f3")
ImportBlock.perform("1") ImportBlock.perform("1")
block_count = Block |> Repo.all |> Enum.count block_count = Block |> Repo.all() |> Enum.count()
assert block_count == 1 assert block_count == 1
end end
end end
@ -55,9 +55,9 @@ defmodule Explorer.Workers.ImportBlockTest do
describe "perform_later/1" do describe "perform_later/1" do
test "does not retry fetching the latest block" do test "does not retry fetching the latest block" do
use_cassette "import_block_perform_later_1_latest" do use_cassette "import_block_perform_later_1_latest" do
with_mock Exq, [enqueue: fn (_, _, _, _) -> insert(:block, number: 1) end] do with_mock Exq, enqueue: fn _, _, _, _ -> insert(:block, number: 1) end do
ImportBlock.perform_later("latest") ImportBlock.perform_later("latest")
last_block = Block |> order_by(asc: :number) |> limit(1) |> Repo.one last_block = Block |> order_by(asc: :number) |> limit(1) |> Repo.one()
assert last_block.number == 1 assert last_block.number == 1
end end
end end

@ -8,16 +8,26 @@ defmodule Explorer.Workers.ImportReceiptTest do
describe "perform/1" do describe "perform/1" do
test "does not import a receipt when no transaction with the hash exists" do test "does not import a receipt when no transaction with the hash exists" do
use_cassette "import_receipt_perform_1" do use_cassette "import_receipt_perform_1" do
ImportReceipt.perform("0xf9a0959d5ccde33ec5221ddba1c6d7eaf9580a8d3512c7a1a60301362a98f926") ImportReceipt.perform(
"0xf9a0959d5ccde33ec5221ddba1c6d7eaf9580a8d3512c7a1a60301362a98f926"
)
assert Repo.one(Receipt) == nil assert Repo.one(Receipt) == nil
end end
end end
test "imports a receipt when a transaction with the hash exists" do test "imports a receipt when a transaction with the hash exists" do
insert(:transaction, hash: "0xf9a0959d5ccde33ec5221ddba1c6d7eaf9580a8d3512c7a1a60301362a98f926") insert(
:transaction,
hash: "0xf9a0959d5ccde33ec5221ddba1c6d7eaf9580a8d3512c7a1a60301362a98f926"
)
use_cassette "import_receipt_perform_1" do use_cassette "import_receipt_perform_1" do
ImportReceipt.perform("0xf9a0959d5ccde33ec5221ddba1c6d7eaf9580a8d3512c7a1a60301362a98f926") ImportReceipt.perform(
receipt_count = Receipt |> Repo.all |> Enum.count "0xf9a0959d5ccde33ec5221ddba1c6d7eaf9580a8d3512c7a1a60301362a98f926"
)
receipt_count = Receipt |> Repo.all() |> Enum.count()
assert receipt_count == 1 assert receipt_count == 1
end end
end end

@ -11,10 +11,11 @@ defmodule Explorer.Workers.ImportSkippedBlocksTest do
describe "perform/1" do describe "perform/1" do
test "imports the requested number of skipped blocks" do test "imports the requested number of skipped blocks" do
insert(:block, %{number: 2}) insert(:block, %{number: 2})
use_cassette "import_skipped_blocks_perform_1" do use_cassette "import_skipped_blocks_perform_1" do
with_mock ImportBlock, [perform_later: fn (number) -> insert(:block, number: number) end] do with_mock ImportBlock, perform_later: fn number -> insert(:block, number: number) end do
ImportSkippedBlocks.perform(1) ImportSkippedBlocks.perform(1)
last_block = Block |> order_by(asc: :number) |> limit(1) |> Repo.one last_block = Block |> order_by(asc: :number) |> limit(1) |> Repo.one()
assert last_block.number == 1 assert last_block.number == 1
end end
end end

@ -11,30 +11,50 @@ defmodule Explorer.Workers.ImportTransactionTest do
describe "perform/1" do describe "perform/1" do
test "imports the requested transaction hash" do test "imports the requested transaction hash" do
use_cassette "import_transaction_perform_1" do use_cassette "import_transaction_perform_1" do
with_mock Exq, [enqueue: fn (_, _, _, _) -> :ok end] do with_mock Exq, enqueue: fn _, _, _, _ -> :ok end do
ImportTransaction.perform("0xf9a0959d5ccde33ec5221ddba1c6d7eaf9580a8d3512c7a1a60301362a98f926") ImportTransaction.perform(
"0xf9a0959d5ccde33ec5221ddba1c6d7eaf9580a8d3512c7a1a60301362a98f926"
)
end end
transaction = Transaction |> Repo.one
assert transaction.hash == "0xf9a0959d5ccde33ec5221ddba1c6d7eaf9580a8d3512c7a1a60301362a98f926" transaction = Transaction |> Repo.one()
assert transaction.hash ==
"0xf9a0959d5ccde33ec5221ddba1c6d7eaf9580a8d3512c7a1a60301362a98f926"
end end
end end
test "when there is already a transaction with the requested hash" do test "when there is already a transaction with the requested hash" do
insert(:transaction, hash: "0xf9a0959d5ccde33ec5221ddba1c6d7eaf9580a8d3512c7a1a60301362a98f926") insert(
:transaction,
hash: "0xf9a0959d5ccde33ec5221ddba1c6d7eaf9580a8d3512c7a1a60301362a98f926"
)
use_cassette "import_transaction_perform_1" do use_cassette "import_transaction_perform_1" do
with_mock Exq, [enqueue: fn (_, _, _, _) -> :ok end] do with_mock Exq, enqueue: fn _, _, _, _ -> :ok end do
ImportTransaction.perform("0xf9a0959d5ccde33ec5221ddba1c6d7eaf9580a8d3512c7a1a60301362a98f926") ImportTransaction.perform(
"0xf9a0959d5ccde33ec5221ddba1c6d7eaf9580a8d3512c7a1a60301362a98f926"
)
end end
transaction_count = Transaction |> Repo.all |> Enum.count
transaction_count = Transaction |> Repo.all() |> Enum.count()
assert transaction_count == 1 assert transaction_count == 1
end end
end end
test "imports the receipt in another queue" do test "imports the receipt in another queue" do
transaction = insert(:transaction, hash: "0xf9a0959d5ccde33ec5221ddba1c6d7eaf9580a8d3512c7a1a60301362a98f926") transaction =
insert(
:transaction,
hash: "0xf9a0959d5ccde33ec5221ddba1c6d7eaf9580a8d3512c7a1a60301362a98f926"
)
use_cassette "import_transaction_perform_1" do use_cassette "import_transaction_perform_1" do
with_mock Exq, [enqueue: fn (_, _, _, _) -> insert(:receipt, transaction: transaction) end] do with_mock Exq, enqueue: fn _, _, _, _ -> insert(:receipt, transaction: transaction) end do
ImportTransaction.perform("0xf9a0959d5ccde33ec5221ddba1c6d7eaf9580a8d3512c7a1a60301362a98f926") ImportTransaction.perform(
"0xf9a0959d5ccde33ec5221ddba1c6d7eaf9580a8d3512c7a1a60301362a98f926"
)
receipt = Repo.one(Receipt) receipt = Repo.one(Receipt)
refute is_nil(receipt) refute is_nil(receipt)
end end
@ -42,15 +62,21 @@ defmodule Explorer.Workers.ImportTransactionTest do
end end
test "imports the receipt in another queue when a map is supplied" do test "imports the receipt in another queue when a map is supplied" do
transaction = insert(:transaction, hash: "0xf9a0959d5ccde33ec5221ddba1c6d7eaf9580a8d3512c7a1a60301362a98f926") transaction =
insert(
:transaction,
hash: "0xf9a0959d5ccde33ec5221ddba1c6d7eaf9580a8d3512c7a1a60301362a98f926"
)
use_cassette "import_transaction_perform_1" do use_cassette "import_transaction_perform_1" do
with_mock Exq, [enqueue: fn (_, _, _, _) -> insert(:receipt, transaction: transaction) end] do with_mock Exq, enqueue: fn _, _, _, _ -> insert(:receipt, transaction: transaction) end do
ImportTransaction.perform(%{ ImportTransaction.perform(%{
"hash" => "0xf9a0959d5ccde33ec5221ddba1c6d7eaf9580a8d3512c7a1a60301362a98f926", "hash" => "0xf9a0959d5ccde33ec5221ddba1c6d7eaf9580a8d3512c7a1a60301362a98f926",
"to" => "0xc001", "to" => "0xc001",
"from" => "0xbead5", "from" => "0xbead5",
"blockHash" => "0xcafe", "blockHash" => "0xcafe"
}) })
receipt = Repo.one(Receipt) receipt = Repo.one(Receipt)
refute is_nil(receipt) refute is_nil(receipt)
end end
@ -61,10 +87,21 @@ defmodule Explorer.Workers.ImportTransactionTest do
describe "perform_later/1" do describe "perform_later/1" do
test "imports the transaction in another queue" do test "imports the transaction in another queue" do
use_cassette "import_transaction_perform_1" do use_cassette "import_transaction_perform_1" do
with_mock Exq, [enqueue: fn (_, _, _, _) -> insert(:transaction, hash: "0xf9a0959d5ccde33ec5221ddba1c6d7eaf9580a8d3512c7a1a60301362a98f926") end] do with_mock Exq,
ImportTransaction.perform_later("0xf9a0959d5ccde33ec5221ddba1c6d7eaf9580a8d3512c7a1a60301362a98f926") enqueue: fn _, _, _, _ ->
insert(
:transaction,
hash: "0xf9a0959d5ccde33ec5221ddba1c6d7eaf9580a8d3512c7a1a60301362a98f926"
)
end do
ImportTransaction.perform_later(
"0xf9a0959d5ccde33ec5221ddba1c6d7eaf9580a8d3512c7a1a60301362a98f926"
)
transaction = Repo.one(Transaction) transaction = Repo.one(Transaction)
assert transaction.hash == "0xf9a0959d5ccde33ec5221ddba1c6d7eaf9580a8d3512c7a1a60301362a98f926"
assert transaction.hash ==
"0xf9a0959d5ccde33ec5221ddba1c6d7eaf9580a8d3512c7a1a60301362a98f926"
end end
end end
end end

@ -9,23 +9,23 @@ defmodule Explorer.Workers.RefreshBalanceTest do
describe "perform/0" do describe "perform/0" do
test "refreshes credit balances" do test "refreshes credit balances" do
with_mock Exq, [enqueue: fn (_, _, _, [type]) -> RefreshBalance.perform(type) end] do with_mock Exq, enqueue: fn _, _, _, [type] -> RefreshBalance.perform(type) end do
address = insert(:address) address = insert(:address)
transaction = insert(:transaction, value: 20) transaction = insert(:transaction, value: 20)
insert(:to_address, address: address, transaction: transaction) insert(:to_address, address: address, transaction: transaction)
insert(:receipt, transaction: transaction, status: 1) insert(:receipt, transaction: transaction, status: 1)
RefreshBalance.perform RefreshBalance.perform()
assert Repo.one(Credit).value == Decimal.new(20) assert Repo.one(Credit).value == Decimal.new(20)
end end
end end
test "refreshes debit balances" do test "refreshes debit balances" do
with_mock Exq, [enqueue: fn (_, _, _, [type]) -> RefreshBalance.perform(type) end] do with_mock Exq, enqueue: fn _, _, _, [type] -> RefreshBalance.perform(type) end do
address = insert(:address) address = insert(:address)
transaction = insert(:transaction, value: 20) transaction = insert(:transaction, value: 20)
insert(:from_address, address: address, transaction: transaction) insert(:from_address, address: address, transaction: transaction)
insert(:receipt, transaction: transaction, status: 1) insert(:receipt, transaction: transaction, status: 1)
RefreshBalance.perform RefreshBalance.perform()
assert Repo.one(Debit).value == Decimal.new(20) assert Repo.one(Debit).value == Decimal.new(20)
end end
end end

@ -7,8 +7,8 @@ defmodule ExplorerWeb.AddressControllerTest do
describe "GET show/3" do describe "GET show/3" do
test "returns an address", %{conn: conn} do test "returns an address", %{conn: conn} do
address = insert(:address, hash: "0x9") address = insert(:address, hash: "0x9")
Credit.refresh Credit.refresh()
Debit.refresh Debit.refresh()
conn = get(conn, "/en/addresses/0x9") conn = get(conn, "/en/addresses/0x9")
assert conn.assigns.address.id == address.id assert conn.assigns.address.id == address.id
end end

@ -13,7 +13,10 @@ defmodule ExplorerWeb.AddressTransactionFromControllerTest do
other_address = insert(:address) other_address = insert(:address)
insert(:to_address, transaction: transaction, address: other_address) insert(:to_address, transaction: transaction, address: other_address)
insert(:from_address, transaction: transaction, address: address) insert(:from_address, transaction: transaction, address: address)
conn = get(conn, address_transaction_from_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
conn =
get(conn, address_transaction_from_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
assert conn.assigns.transactions.total_entries == 1 assert conn.assigns.transactions.total_entries == 1
assert List.first(conn.assigns.transactions.entries).hash == "0xsnacks" assert List.first(conn.assigns.transactions.entries).hash == "0xsnacks"
end end
@ -27,7 +30,10 @@ defmodule ExplorerWeb.AddressTransactionFromControllerTest do
other_address = insert(:address) other_address = insert(:address)
insert(:to_address, transaction: transaction, address: address) insert(:to_address, transaction: transaction, address: address)
insert(:from_address, transaction: transaction, address: other_address) insert(:from_address, transaction: transaction, address: other_address)
conn = get(conn, address_transaction_from_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
conn =
get(conn, address_transaction_from_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
assert conn.assigns.transactions.total_entries == 0 assert conn.assigns.transactions.total_entries == 0
end end
@ -38,7 +44,10 @@ defmodule ExplorerWeb.AddressTransactionFromControllerTest do
address = insert(:address) address = insert(:address)
insert(:to_address, transaction: transaction, address: address) insert(:to_address, transaction: transaction, address: address)
insert(:from_address, transaction: transaction, address: address) insert(:from_address, transaction: transaction, address: address)
conn = get(conn, address_transaction_from_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
conn =
get(conn, address_transaction_from_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
assert conn.assigns.transactions.total_entries == 0 assert conn.assigns.transactions.total_entries == 0
end end
@ -49,7 +58,10 @@ defmodule ExplorerWeb.AddressTransactionFromControllerTest do
insert(:block_transaction, transaction: transaction, block: block) insert(:block_transaction, transaction: transaction, block: block)
address = insert(:address) address = insert(:address)
insert(:to_address, transaction: transaction, address: address) insert(:to_address, transaction: transaction, address: address)
conn = get(conn, address_transaction_from_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
conn =
get(conn, address_transaction_from_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
assert conn.assigns.transactions.total_entries == 0 assert conn.assigns.transactions.total_entries == 0
end end
@ -60,7 +72,10 @@ defmodule ExplorerWeb.AddressTransactionFromControllerTest do
insert(:block_transaction, transaction: transaction, block: block) insert(:block_transaction, transaction: transaction, block: block)
address = insert(:address) address = insert(:address)
insert(:from_address, transaction: transaction, address: address) insert(:from_address, transaction: transaction, address: address)
conn = get(conn, address_transaction_from_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
conn =
get(conn, address_transaction_from_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
assert conn.assigns.transactions.total_entries == 0 assert conn.assigns.transactions.total_entries == 0
end end
end end

@ -13,7 +13,10 @@ defmodule ExplorerWeb.AddressTransactionToControllerTest do
other_address = insert(:address) other_address = insert(:address)
insert(:to_address, transaction: transaction, address: address) insert(:to_address, transaction: transaction, address: address)
insert(:from_address, transaction: transaction, address: other_address) insert(:from_address, transaction: transaction, address: other_address)
conn = get(conn, address_transaction_to_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
conn =
get(conn, address_transaction_to_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
assert conn.assigns.transactions.total_entries == 1 assert conn.assigns.transactions.total_entries == 1
assert List.first(conn.assigns.transactions.entries).hash == "0xsnacks" assert List.first(conn.assigns.transactions.entries).hash == "0xsnacks"
end end
@ -27,7 +30,10 @@ defmodule ExplorerWeb.AddressTransactionToControllerTest do
other_address = insert(:address) other_address = insert(:address)
insert(:to_address, transaction: transaction, address: other_address) insert(:to_address, transaction: transaction, address: other_address)
insert(:from_address, transaction: transaction, address: address) insert(:from_address, transaction: transaction, address: address)
conn = get(conn, address_transaction_to_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
conn =
get(conn, address_transaction_to_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
assert conn.assigns.transactions.total_entries == 0 assert conn.assigns.transactions.total_entries == 0
end end
@ -38,7 +44,10 @@ defmodule ExplorerWeb.AddressTransactionToControllerTest do
address = insert(:address) address = insert(:address)
insert(:to_address, transaction: transaction, address: address) insert(:to_address, transaction: transaction, address: address)
insert(:from_address, transaction: transaction, address: address) insert(:from_address, transaction: transaction, address: address)
conn = get(conn, address_transaction_to_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
conn =
get(conn, address_transaction_to_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
assert conn.assigns.transactions.total_entries == 0 assert conn.assigns.transactions.total_entries == 0
end end
@ -49,7 +58,10 @@ defmodule ExplorerWeb.AddressTransactionToControllerTest do
insert(:block_transaction, transaction: transaction, block: block) insert(:block_transaction, transaction: transaction, block: block)
address = insert(:address) address = insert(:address)
insert(:to_address, transaction: transaction, address: address) insert(:to_address, transaction: transaction, address: address)
conn = get(conn, address_transaction_to_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
conn =
get(conn, address_transaction_to_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
assert conn.assigns.transactions.total_entries == 0 assert conn.assigns.transactions.total_entries == 0
end end
@ -60,7 +72,10 @@ defmodule ExplorerWeb.AddressTransactionToControllerTest do
insert(:block_transaction, transaction: transaction, block: block) insert(:block_transaction, transaction: transaction, block: block)
address = insert(:address) address = insert(:address)
insert(:from_address, transaction: transaction, address: address) insert(:from_address, transaction: transaction, address: address)
conn = get(conn, address_transaction_to_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
conn =
get(conn, address_transaction_to_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
assert conn.assigns.transactions.total_entries == 0 assert conn.assigns.transactions.total_entries == 0
end end
end end

@ -11,9 +11,11 @@ defmodule ExplorerWeb.BlockControllerTest do
describe "GET index/2" do describe "GET index/2" do
test "returns all blocks", %{conn: conn} do test "returns all blocks", %{conn: conn} do
block_ids = insert_list(4, :block) |> Enum.map(fn (block) -> block.number end) |> Enum.reverse block_ids =
insert_list(4, :block) |> Enum.map(fn block -> block.number end) |> Enum.reverse()
conn = get(conn, "/en/blocks") conn = get(conn, "/en/blocks")
assert conn.assigns.blocks |> Enum.map(fn (block) -> block.number end) == block_ids assert conn.assigns.blocks |> Enum.map(fn block -> block.number end) == block_ids
end end
test "returns a block with two transactions", %{conn: conn} do test "returns a block with two transactions", %{conn: conn} do
@ -23,7 +25,7 @@ defmodule ExplorerWeb.BlockControllerTest do
other_transaction = insert(:transaction) other_transaction = insert(:transaction)
insert(:block_transaction, block: block, transaction: other_transaction) insert(:block_transaction, block: block, transaction: other_transaction)
conn = get(conn, "/en/blocks") conn = get(conn, "/en/blocks")
assert conn.assigns.blocks.entries |> Enum.count == 1 assert conn.assigns.blocks.entries |> Enum.count() == 1
end end
end end
end end

@ -1,11 +1,12 @@
defmodule ExplorerWeb.ChainControllerTest do defmodule ExplorerWeb.ChainControllerTest do
use ExplorerWeb.ConnCase use ExplorerWeb.ConnCase
import ExplorerWeb.Router.Helpers, only: [chain_path: 3, block_path: 4, transaction_path: 4, address_path: 4] import ExplorerWeb.Router.Helpers,
only: [chain_path: 3, block_path: 4, transaction_path: 4, address_path: 4]
describe "GET index/2 without a locale" do describe "GET index/2 without a locale" do
test "redirects to the en locale", %{conn: conn} do test "redirects to the en locale", %{conn: conn} do
conn = get conn, "/" conn = get(conn, "/")
assert(redirected_to(conn) == "/en") assert(redirected_to(conn) == "/en")
end end
@ -13,14 +14,14 @@ defmodule ExplorerWeb.ChainControllerTest do
describe "GET index/2 with a locale" do describe "GET index/2 with a locale" do
test "returns a welcome message", %{conn: conn} do test "returns a welcome message", %{conn: conn} do
conn = get conn, chain_path(ExplorerWeb.Endpoint, :show, %{locale: :en}) conn = get(conn, chain_path(ExplorerWeb.Endpoint, :show, %{locale: :en}))
assert(html_response(conn, 200) =~ "POA") assert(html_response(conn, 200) =~ "POA")
end end
test "returns a block", %{conn: conn} do test "returns a block", %{conn: conn} do
insert(:block, %{number: 23}) insert(:block, %{number: 23})
conn = get conn, "/en" conn = get(conn, "/en")
assert(List.first(conn.assigns.chain.blocks).number == 23) assert(List.first(conn.assigns.chain.blocks).number == 23)
end end
@ -28,18 +29,23 @@ defmodule ExplorerWeb.ChainControllerTest do
test "excludes all but the most recent five blocks", %{conn: conn} do test "excludes all but the most recent five blocks", %{conn: conn} do
old_block = insert(:block) old_block = insert(:block)
insert_list(5, :block) insert_list(5, :block)
conn = get conn, "/en" conn = get(conn, "/en")
refute(Enum.member?(conn.assigns.chain.blocks, old_block)) refute(Enum.member?(conn.assigns.chain.blocks, old_block))
end end
test "only returns transactions with an associated block", %{conn: conn} do test "only returns transactions with an associated block", %{conn: conn} do
block = insert(:block, number: 33) block = insert(:block, number: 33)
insert(:transaction, id: 10, hash: "0xDECAFBAD") |> with_block(block) |> with_addresses(%{to: "0xsleepypuppy", from: "0xilovefrogs"})
insert(:transaction, id: 10, hash: "0xDECAFBAD") |> with_block(block)
|> with_addresses(%{to: "0xsleepypuppy", from: "0xilovefrogs"})
insert(:transaction, id: 30) insert(:transaction, id: 30)
conn = get conn, "/en" conn = get(conn, "/en")
transaction_ids = conn.assigns.chain.transactions
|> Enum.map(fn (transaction) -> transaction.id end) transaction_ids =
conn.assigns.chain.transactions
|> Enum.map(fn transaction -> transaction.id end)
assert(Enum.member?(transaction_ids, 10)) assert(Enum.member?(transaction_ids, 10))
refute(Enum.member?(transaction_ids, 30)) refute(Enum.member?(transaction_ids, 30))
@ -47,8 +53,11 @@ defmodule ExplorerWeb.ChainControllerTest do
test "returns a transaction", %{conn: conn} do test "returns a transaction", %{conn: conn} do
block = insert(:block, number: 33) block = insert(:block, number: 33)
insert(:transaction, hash: "0xDECAFBAD") |> with_block(block) |> with_addresses(%{to: "0xsleepypuppy", from: "0xilovefrogs"})
conn = get conn, "/en" insert(:transaction, hash: "0xDECAFBAD") |> with_block(block)
|> with_addresses(%{to: "0xsleepypuppy", from: "0xilovefrogs"})
conn = get(conn, "/en")
assert(List.first(conn.assigns.chain.transactions).hash == "0xDECAFBAD") assert(List.first(conn.assigns.chain.transactions).hash == "0xDECAFBAD")
end end
@ -57,27 +66,27 @@ defmodule ExplorerWeb.ChainControllerTest do
describe "GET q/2" do describe "GET q/2" do
test "finds a block by block number", %{conn: conn} do test "finds a block by block number", %{conn: conn} do
insert(:block, number: 37) insert(:block, number: 37)
conn = get conn, "/en/search?q=37" conn = get(conn, "/en/search?q=37")
assert redirected_to(conn) == block_path(conn, :show, "en", "37") assert redirected_to(conn) == block_path(conn, :show, "en", "37")
end end
test "finds a transaction by hash", %{conn: conn} do test "finds a transaction by hash", %{conn: conn} do
transaction = insert(:transaction) |> with_block() |> with_addresses transaction = insert(:transaction) |> with_block() |> with_addresses
conn = get conn, "/en/search?q=#{transaction.hash}" conn = get(conn, "/en/search?q=#{transaction.hash}")
assert redirected_to(conn) == transaction_path(conn, :show, "en", transaction.hash) assert redirected_to(conn) == transaction_path(conn, :show, "en", transaction.hash)
end end
test "finds an address by hash", %{conn: conn} do test "finds an address by hash", %{conn: conn} do
address = insert(:address) address = insert(:address)
conn = get conn, "en/search?q=#{address.hash}" conn = get(conn, "en/search?q=#{address.hash}")
assert redirected_to(conn) == address_path(conn, :show, "en", address.hash) assert redirected_to(conn) == address_path(conn, :show, "en", address.hash)
end end
test "redirects to 404 when it finds nothing", %{conn: conn} do test "redirects to 404 when it finds nothing", %{conn: conn} do
conn = get conn, "en/search?q=zaphod" conn = get(conn, "en/search?q=zaphod")
assert conn.status == 404 assert conn.status == 404
end end
end end

@ -51,8 +51,15 @@ defmodule ExplorerWeb.PendingTransactionControllerTest do
transaction = insert(:transaction) transaction = insert(:transaction)
insert(:to_address, transaction: transaction, address: address) insert(:to_address, transaction: transaction, address: address)
insert(:from_address, transaction: transaction, address: address) insert(:from_address, transaction: transaction, address: address)
conn = get(conn, pending_transaction_path(ExplorerWeb.Endpoint, :index, :en), last_seen: transaction.id)
assert conn.assigns.transactions.entries == [] conn =
get(
conn,
pending_transaction_path(ExplorerWeb.Endpoint, :index, :en),
last_seen: transaction.id
)
assert conn.assigns.transactions.entries == []
end end
end end
end end

@ -47,12 +47,14 @@ defmodule ExplorerWeb.TransactionControllerTest do
insert(:to_address, transaction: transaction, address: address) insert(:to_address, transaction: transaction, address: address)
insert(:from_address, transaction: transaction, address: address) insert(:from_address, transaction: transaction, address: address)
conn = get(conn, "/en/transactions", last_seen: transaction.id) conn = get(conn, "/en/transactions", last_seen: transaction.id)
assert conn.assigns.transactions.entries == [] assert conn.assigns.transactions.entries == []
end end
end end
describe "GET show/3" do describe "GET show/3" do
test "when there is an associated block, it returns a transaction with block data", %{conn: conn} do test "when there is an associated block, it returns a transaction with block data", %{
conn: conn
} do
block = insert(:block, %{number: 777}) block = insert(:block, %{number: 777})
transaction = insert(:transaction, hash: "0x8") |> with_block(block) |> with_addresses transaction = insert(:transaction, hash: "0x8") |> with_block(block) |> with_addresses
conn = get(conn, "/en/transactions/0x8") conn = get(conn, "/en/transactions/0x8")

@ -29,11 +29,15 @@ defmodule ExplorerWeb.UserListTest do
end end
test "search for transactions", %{session: session} do test "search for transactions", %{session: session} do
insert(:transaction, hash: "0xdeadbeef000000000000000000000000000000000", input: "socks") |> with_addresses() insert(:transaction, hash: "0xdeadbeef000000000000000000000000000000000", input: "socks")
|> with_addresses()
session session
|> visit("/") |> visit("/")
|> fill_in(css(".header__cell--search-input"), with: "0xdeadbeef000000000000000000000000000000000") |> fill_in(
css(".header__cell--search-input"),
with: "0xdeadbeef000000000000000000000000000000000"
)
|> send_keys([:enter]) |> send_keys([:enter])
|> assert_has(css(".transaction__item", text: "socks")) |> assert_has(css(".transaction__item", text: "socks"))
end end
@ -43,30 +47,42 @@ defmodule ExplorerWeb.UserListTest do
session session
|> visit("/") |> visit("/")
|> fill_in(css(".header__cell--search-input"), with: "0xBAADF00D00000000000000000000000000000000") |> fill_in(
css(".header__cell--search-input"),
with: "0xBAADF00D00000000000000000000000000000000"
)
|> send_keys([:enter]) |> send_keys([:enter])
|> assert_has(css(".address__subheading", text: "0xBAADF00D00000000000000000000000000000000")) |> assert_has(css(".address__subheading", text: "0xBAADF00D00000000000000000000000000000000"))
end end
test "views blocks", %{session: session} do test "views blocks", %{session: session} do
insert_list(4, :block, %{number: 1, timestamp: Timex.now |> Timex.shift(hours: -1), gas_used: 10}) insert_list(4, :block, %{
fifth_block = insert(:block, %{ number: 1,
number: 311, timestamp: Timex.now() |> Timex.shift(hours: -1),
hash: "0xMrCoolBlock", gas_used: 10
timestamp: Timex.now |> Timex.shift(hours: -1),
miner: "Heathcliff",
size: 9999999,
nonce: "once upon a nonce",
gas_used: 1010101,
gas_limit: 5030101
}) })
transaction = insert(:transaction, hash: "0xfaschtnacht") |> with_block(fifth_block) |> with_addresses
fifth_block =
insert(:block, %{
number: 311,
hash: "0xMrCoolBlock",
timestamp: Timex.now() |> Timex.shift(hours: -1),
miner: "Heathcliff",
size: 9_999_999,
nonce: "once upon a nonce",
gas_used: 1_010_101,
gas_limit: 5_030_101
})
transaction =
insert(:transaction, hash: "0xfaschtnacht") |> with_block(fifth_block) |> with_addresses
insert(:transaction, hash: "0xpaczki") |> with_block(fifth_block) |> with_addresses insert(:transaction, hash: "0xpaczki") |> with_block(fifth_block) |> with_addresses
insert(:transaction) |> with_block(fifth_block) |> with_addresses insert(:transaction) |> with_block(fifth_block) |> with_addresses
insert(:receipt, transaction: transaction) insert(:receipt, transaction: transaction)
Credit.refresh Credit.refresh()
Debit.refresh Debit.refresh()
session session
|> visit("/en") |> visit("/en")
@ -80,7 +96,6 @@ defmodule ExplorerWeb.UserListTest do
session session
|> click(link("Blocks")) |> click(link("Blocks"))
|> assert_has(css(".blocks__column--height", text: "311")) |> assert_has(css(".blocks__column--height", text: "311"))
|> click(link("311")) |> click(link("311"))
|> assert_has(css(".block__item", text: "0xMrCoolBlock")) |> assert_has(css(".block__item", text: "0xMrCoolBlock"))
|> assert_has(css(".block__item", text: "Heathcliff")) |> assert_has(css(".block__item", text: "Heathcliff"))
@ -89,32 +104,37 @@ defmodule ExplorerWeb.UserListTest do
|> assert_has(css(".block__item", text: "5,030,101")) |> assert_has(css(".block__item", text: "5,030,101"))
|> assert_has(css(".block__item", text: "once upon a nonce")) |> assert_has(css(".block__item", text: "once upon a nonce"))
|> assert_has(css(".block__item", text: "1,010,101")) |> assert_has(css(".block__item", text: "1,010,101"))
|> click(css(".block__link", text: "Transactions")) |> click(css(".block__link", text: "Transactions"))
|> assert_has(css(".transactions__link--long-hash", text: "0xfaschtnacht")) |> assert_has(css(".transactions__link--long-hash", text: "0xfaschtnacht"))
end end
test "views transactions", %{session: session} do test "views transactions", %{session: session} do
block = insert(:block, %{ block =
number: 555, insert(:block, %{
timestamp: Timex.now |> Timex.shift(hours: -2), number: 555,
gas_used: 123987, timestamp: Timex.now() |> Timex.shift(hours: -2),
}) gas_used: 123_987
})
for _ <- 0..3, do: insert(:transaction) |> with_block(block) |> with_addresses for _ <- 0..3, do: insert(:transaction) |> with_block(block) |> with_addresses
insert(:transaction, hash: "0xC001", gas: 5891) |> with_block |> with_addresses insert(:transaction, hash: "0xC001", gas: 5891) |> with_block |> with_addresses
lincoln = insert(:address, hash: "0xlincoln") lincoln = insert(:address, hash: "0xlincoln")
taft = insert(:address, hash: "0xhowardtaft") taft = insert(:address, hash: "0xhowardtaft")
transaction = insert(:transaction,
hash: "0xSk8", transaction =
value: 5656, insert(
gas: 1230000000000123123, :transaction,
gas_price: 7890000000898912300045, hash: "0xSk8",
input: "0x00012", value: 5656,
nonce: 99045, gas: 1_230_000_000_000_123_123,
inserted_at: Timex.parse!("1970-01-01T00:00:18-00:00", "{ISO:Extended}"), gas_price: 7_890_000_000_898_912_300_045,
updated_at: Timex.parse!("1980-01-01T00:00:18-00:00", "{ISO:Extended}") input: "0x00012",
) nonce: 99045,
inserted_at: Timex.parse!("1970-01-01T00:00:18-00:00", "{ISO:Extended}"),
updated_at: Timex.parse!("1980-01-01T00:00:18-00:00", "{ISO:Extended}")
)
insert(:block_transaction, block: block, transaction: transaction) insert(:block_transaction, block: block, transaction: transaction)
insert(:from_address, address: taft, transaction: transaction) insert(:from_address, address: taft, transaction: transaction)
insert(:to_address, address: lincoln, transaction: transaction) insert(:to_address, address: lincoln, transaction: transaction)
@ -130,8 +150,8 @@ defmodule ExplorerWeb.UserListTest do
internal = insert(:internal_transaction, transaction_id: transaction.id) internal = insert(:internal_transaction, transaction_id: transaction.id)
Credit.refresh Credit.refresh()
Debit.refresh Debit.refresh()
session session
|> visit("/en") |> visit("/en")
@ -139,15 +159,12 @@ defmodule ExplorerWeb.UserListTest do
|> assert_has(css(".transactions__column--hash", count: 5)) |> assert_has(css(".transactions__column--hash", count: 5))
|> assert_has(css(".transactions__column--value", count: 5)) |> assert_has(css(".transactions__column--value", count: 5))
|> assert_has(css(".transactions__column--age", count: 5, visible: false)) |> assert_has(css(".transactions__column--age", count: 5, visible: false))
|> visit("/transactions") |> visit("/transactions")
|> click(css(".transactions__tab-link", text: "Pending")) |> click(css(".transactions__tab-link", text: "Pending"))
|> click(css(".transactions__link", text: "0xC001")) |> click(css(".transactions__link", text: "0xC001"))
|> assert_has(css(".transaction__item-value--status", text: "Pending")) |> assert_has(css(".transaction__item-value--status", text: "Pending"))
|> visit("/transactions") |> visit("/transactions")
|> refute_has(css(".transactions__column--block", text: "Pending")) |> refute_has(css(".transactions__column--block", text: "Pending"))
|> click(link("0xSk8")) |> click(link("0xSk8"))
|> assert_has(css(".transaction__subheading", text: "0xSk8")) |> assert_has(css(".transaction__subheading", text: "0xSk8"))
|> assert_has(css(".transaction__item", text: "123,987")) |> assert_has(css(".transaction__item", text: "123,987"))
@ -163,20 +180,15 @@ defmodule ExplorerWeb.UserListTest do
|> assert_has(css(".transaction__item", text: "block confirmations")) |> assert_has(css(".transaction__item", text: "block confirmations"))
|> assert_has(css(".transaction__item", text: "48 years ago")) |> assert_has(css(".transaction__item", text: "48 years ago"))
|> assert_has(css(".transaction__item", text: "38 years ago")) |> assert_has(css(".transaction__item", text: "38 years ago"))
|> click(link("Internal Transactions")) |> click(link("Internal Transactions"))
|> assert_has(css(".internal-transaction__table", text: internal.call_type)) |> assert_has(css(".internal-transaction__table", text: internal.call_type))
|> visit("/en/transactions/0xSk8") |> visit("/en/transactions/0xSk8")
|> click(link("Logs")) |> click(link("Logs"))
|> assert_has(css(".transaction-log__link", text: "0xlincoln")) |> assert_has(css(".transaction-log__link", text: "0xlincoln"))
|> click(link("0xlincoln")) |> click(link("0xlincoln"))
|> assert_has(css(".address__subheading", text: "0xlincoln")) |> assert_has(css(".address__subheading", text: "0xlincoln"))
|> click(css(".address__link", text: "Transactions To")) |> click(css(".address__link", text: "Transactions To"))
|> assert_has(css(".transactions__link--long-hash", text: "0xSk8")) |> assert_has(css(".transactions__link--long-hash", text: "0xSk8"))
|> click(css(".address__link", text: "Transactions From")) |> click(css(".address__link", text: "Transactions From"))
|> assert_has(css(".transactions__link--long-hash", text: "0xrazerscooter")) |> assert_has(css(".transactions__link--long-hash", text: "0xrazerscooter"))
end end

@ -5,17 +5,14 @@ defmodule ExplorerWeb.ErrorViewTest do
import Phoenix.View import Phoenix.View
test "renders 404.html" do test "renders 404.html" do
assert render_to_string(ExplorerWeb.ErrorView, "404.html", []) == assert render_to_string(ExplorerWeb.ErrorView, "404.html", []) == "Page not found"
"Page not found"
end end
test "render 500.html" do test "render 500.html" do
assert render_to_string(ExplorerWeb.ErrorView, "500.html", []) == assert render_to_string(ExplorerWeb.ErrorView, "500.html", []) == "Internal server error"
"Internal server error"
end end
test "render any other" do test "render any other" do
assert render_to_string(ExplorerWeb.ErrorView, "505.html", []) == assert render_to_string(ExplorerWeb.ErrorView, "505.html", []) == "Internal server error"
"Internal server error"
end end
end end

@ -25,13 +25,13 @@ defmodule ExplorerWeb.ChannelCase do
end end
end end
setup tags do setup tags do
:ok = Ecto.Adapters.SQL.Sandbox.checkout(Explorer.Repo) :ok = Ecto.Adapters.SQL.Sandbox.checkout(Explorer.Repo)
unless tags[:async] do unless tags[:async] do
Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo, {:shared, self()}) Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo, {:shared, self()})
end end
:ok :ok
end end
end end

@ -31,9 +31,11 @@ defmodule ExplorerWeb.ConnCase do
@dialyzer {:nowarn_function, __ex_unit_setup_0: 1} @dialyzer {:nowarn_function, __ex_unit_setup_0: 1}
setup tags do setup tags do
:ok = Ecto.Adapters.SQL.Sandbox.checkout(Explorer.Repo) :ok = Ecto.Adapters.SQL.Sandbox.checkout(Explorer.Repo)
unless tags[:async] do unless tags[:async] do
Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo, {:shared, self()}) Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo, {:shared, self()})
end end
{:ok, conn: Phoenix.ConnTest.build_conn()} {:ok, conn: Phoenix.ConnTest.build_conn()}
end end
end end

@ -13,7 +13,7 @@ defmodule Explorer.BlockFactory do
size: Enum.random(1..100_000), size: Enum.random(1..100_000),
gas_limit: Enum.random(1..100_000), gas_limit: Enum.random(1..100_000),
gas_used: Enum.random(1..100_000), gas_used: Enum.random(1..100_000),
timestamp: DateTime.utc_now, timestamp: DateTime.utc_now()
} }
end end
end end

@ -9,11 +9,11 @@ defmodule Explorer.InternalTransactionFactory do
from_address_id: insert(:address).id, from_address_id: insert(:address).id,
to_address_id: insert(:address).id, to_address_id: insert(:address).id,
transaction_id: insert(:transaction).id, transaction_id: insert(:transaction).id,
value: Enum.random(1..100_000), value: Enum.random(1..100_000),
gas: Enum.random(1..100_000), gas: Enum.random(1..100_000),
gas_used: Enum.random(1..100_000), gas_used: Enum.random(1..100_000),
input: sequence("0x"), input: sequence("0x"),
output: sequence("0x"), output: sequence("0x")
} }
end end
end end

@ -9,7 +9,7 @@ defmodule Explorer.LogFactory do
first_topic: nil, first_topic: nil,
second_topic: nil, second_topic: nil,
third_topic: nil, third_topic: nil,
fourth_topic: nil, fourth_topic: nil
} }
end end
end end

@ -6,7 +6,7 @@ defmodule Explorer.ReceiptFactory do
cumulative_gas_used: Enum.random(21_000..100_000), cumulative_gas_used: Enum.random(21_000..100_000),
gas_used: Enum.random(21_000..100_000), gas_used: Enum.random(21_000..100_000),
status: Enum.random(1..2), status: Enum.random(1..2),
index: sequence(""), index: sequence("")
} }
end end
end end

@ -18,13 +18,21 @@ defmodule Explorer.TransactionFactory do
s: sequence("0x"), s: sequence("0x"),
standard_v: sequence("0x"), standard_v: sequence("0x"),
transaction_index: sequence("0x"), transaction_index: sequence("0x"),
v: sequence("0x"), v: sequence("0x")
} }
end end
def with_addresses(transaction, %{to: to, from: from} \\ %{to: nil, from: nil}) do def with_addresses(transaction, %{to: to, from: from} \\ %{to: nil, from: nil}) do
to_address = if to, do: Repo.get_by(Address, %{hash: to}) || insert(:address, hash: to), else: insert(:address) to_address =
from_address = if from, do: Repo.get_by(Address, %{hash: from}) ||insert(:address, hash: from), else: insert(:address) if to,
do: Repo.get_by(Address, %{hash: to}) || insert(:address, hash: to),
else: insert(:address)
from_address =
if from,
do: Repo.get_by(Address, %{hash: from}) || insert(:address, hash: from),
else: insert(:address)
insert(:to_address, %{transaction_id: transaction.id, address_id: to_address.id}) insert(:to_address, %{transaction_id: transaction.id, address_id: to_address.id})
insert(:from_address, %{transaction_id: transaction.id, address_id: from_address.id}) insert(:from_address, %{transaction_id: transaction.id, address_id: from_address.id})
transaction transaction
@ -37,7 +45,7 @@ defmodule Explorer.TransactionFactory do
end end
def list_with_block(transactions, block \\ nil) do def list_with_block(transactions, block \\ nil) do
Enum.map(transactions, fn(transaction) -> with_block(transaction, block) end) Enum.map(transactions, fn transaction -> with_block(transaction, block) end)
end end
end end
end end

@ -1,8 +1,8 @@
ExUnit.configure(formatters: [JUnitFormatter, ExUnit.CLIFormatter]) ExUnit.configure(formatters: [JUnitFormatter, ExUnit.CLIFormatter])
ExUnit.start ExUnit.start()
{:ok, _} = Application.ensure_all_started(:wallaby) {:ok, _} = Application.ensure_all_started(:wallaby)
Application.put_env(:wallaby, :base_url, ExplorerWeb.Endpoint.url) Application.put_env(:wallaby, :base_url, ExplorerWeb.Endpoint.url())
{:ok, _} = Application.ensure_all_started(:ex_machina) {:ok, _} = Application.ensure_all_started(:ex_machina)

Loading…
Cancel
Save