Add nonce field to Address object and addresses table.

This caches the highest nonce value seen for sent transactions for a given address.

The top addresses query no longer needs to scan the transactions table for nonce values.

The database migration script adds the nonce field to the addresses table and sets
the nonce value according to the contents of the transactions table.
pull/1140/head
svenski123 6 years ago committed by Amanda Sposito
parent 2895b6c61b
commit 29199eb54e
  1. 5
      apps/explorer/lib/explorer/chain.ex
  2. 6
      apps/explorer/lib/explorer/chain/address.ex
  3. 14
      apps/explorer/lib/explorer/chain/import/addresses.ex
  4. 30
      apps/explorer/priv/repo/migrations/201811061523_add_nonce_to_addresses.exs
  5. 18
      apps/indexer/lib/indexer/address_extraction.ex

@ -899,12 +899,9 @@ defmodule Explorer.Chain do
def list_top_addresses do def list_top_addresses do
query = query =
from(a in Address, from(a in Address,
left_join: t in Transaction,
on: a.hash == t.from_address_hash,
where: a.fetched_coin_balance > ^0, where: a.fetched_coin_balance > ^0,
group_by: [a.hash, a.fetched_coin_balance],
order_by: [desc: a.fetched_coin_balance, asc: a.hash], order_by: [desc: a.fetched_coin_balance, asc: a.hash],
select: {a, fragment("coalesce(1 + max(?), 0)", t.nonce)}, select: {a, fragment("coalesce(1 + ?, 0)", a.nonce)},
limit: 250 limit: 250
) )

@ -8,7 +8,7 @@ defmodule Explorer.Chain.Address do
alias Ecto.Changeset alias Ecto.Changeset
alias Explorer.Chain.{Address, Block, Data, Hash, InternalTransaction, SmartContract, Token, Wei} alias Explorer.Chain.{Address, Block, Data, Hash, InternalTransaction, SmartContract, Token, Wei}
@optional_attrs ~w(contract_code fetched_coin_balance fetched_coin_balance_block_number)a @optional_attrs ~w(contract_code fetched_coin_balance fetched_coin_balance_block_number nonce)a
@required_attrs ~w(hash)a @required_attrs ~w(hash)a
@allowed_attrs @optional_attrs ++ @required_attrs @allowed_attrs @optional_attrs ++ @required_attrs
@ -34,7 +34,8 @@ defmodule Explorer.Chain.Address do
contract_code: Data.t() | nil, contract_code: Data.t() | nil,
names: %Ecto.Association.NotLoaded{} | [Address.Name.t()], names: %Ecto.Association.NotLoaded{} | [Address.Name.t()],
inserted_at: DateTime.t(), inserted_at: DateTime.t(),
updated_at: DateTime.t() updated_at: DateTime.t(),
nonce: non_neg_integer() | nil
} }
@primary_key {:hash, Hash.Address, autogenerate: false} @primary_key {:hash, Hash.Address, autogenerate: false}
@ -42,6 +43,7 @@ defmodule Explorer.Chain.Address do
field(:fetched_coin_balance, Wei) field(:fetched_coin_balance, Wei)
field(:fetched_coin_balance_block_number, :integer) field(:fetched_coin_balance_block_number, :integer)
field(:contract_code, Data) field(:contract_code, Data)
field(:nonce, :integer)
has_one(:smart_contract, SmartContract) has_one(:smart_contract, SmartContract)
has_one(:token, Token, foreign_key: :contract_address_hash) has_one(:token, Token, foreign_key: :contract_address_hash)

@ -95,18 +95,10 @@ defmodule Explorer.Chain.Import.Addresses do
# MAX on two columns # MAX on two columns
fetched_coin_balance_block_number: fetched_coin_balance_block_number:
fragment( fragment(
""" "GREATEST(EXCLUDED.fetched_coin_balance_block_number, ?)",
CASE WHEN EXCLUDED.fetched_coin_balance_block_number IS NOT NULL AND
(? IS NULL OR
EXCLUDED.fetched_coin_balance_block_number >= ?) THEN
EXCLUDED.fetched_coin_balance_block_number
ELSE ?
END
""",
address.fetched_coin_balance_block_number,
address.fetched_coin_balance_block_number,
address.fetched_coin_balance_block_number address.fetched_coin_balance_block_number
) ),
nonce: fragment("GREATEST(EXCLUDED.nonce, ?)", address.nonce)
] ]
] ]
) )

@ -0,0 +1,30 @@
defmodule Explorer.Repo.Migrations.AddNonceToAddresses do
use Ecto.Migration
def up do
# Add nonce
alter table(:addresses) do
add(:nonce, :integer)
end
# Populate nonce field from transactions table
execute("""
WITH t AS (
SELECT from_address_hash AS hash, MAX(nonce) AS nonce
FROM transactions
GROUP BY hash
)
UPDATE addresses AS a
SET nonce = t.nonce
FROM t
WHERE a.hash = t.hash
""")
end
def down do
# Remove nonce
alter table(:addresses) do
remove(:nonce)
end
end
end

@ -84,7 +84,8 @@ defmodule Indexer.AddressExtraction do
], ],
[ [
%{from: :block_number, to: :fetched_coin_balance_block_number}, %{from: :block_number, to: :fetched_coin_balance_block_number},
%{from: :from_address_hash, to: :hash} %{from: :from_address_hash, to: :hash},
%{from: :nonce, to: :nonce}
], ],
[ [
%{from: :block_number, to: :fetched_coin_balance_block_number}, %{from: :block_number, to: :fetched_coin_balance_block_number},
@ -136,6 +137,7 @@ defmodule Indexer.AddressExtraction do
required(:hash) => String.t(), required(:hash) => String.t(),
required(:fetched_coin_balance_block_number) => non_neg_integer(), required(:fetched_coin_balance_block_number) => non_neg_integer(),
optional(:fetched_coin_balance) => non_neg_integer(), optional(:fetched_coin_balance) => non_neg_integer(),
optional(:nonce) => non_neg_integer(),
optional(:contract_code) => String.t() optional(:contract_code) => String.t()
} }
@ -209,11 +211,13 @@ defmodule Indexer.AddressExtraction do
...> %{ ...> %{
...> block_number: 1, ...> block_number: 1,
...> from_address_hash: "0x0000000000000000000000000000000000000001", ...> from_address_hash: "0x0000000000000000000000000000000000000001",
...> to_address_hash: "0x0000000000000000000000000000000000000002" ...> to_address_hash: "0x0000000000000000000000000000000000000002",
...> nonce: 3
...> }, ...> },
...> %{ ...> %{
...> block_number: 2, ...> block_number: 2,
...> from_address_hash: "0x0000000000000000000000000000000000000003" ...> from_address_hash: "0x0000000000000000000000000000000000000003",
...> nonce: 4
...> } ...> }
...> ] ...> ]
...> } ...> }
@ -221,7 +225,8 @@ defmodule Indexer.AddressExtraction do
[ [
%{ %{
fetched_coin_balance_block_number: 1, fetched_coin_balance_block_number: 1,
hash: "0x0000000000000000000000000000000000000001" hash: "0x0000000000000000000000000000000000000001",
nonce: 3
}, },
%{ %{
fetched_coin_balance_block_number: 1, fetched_coin_balance_block_number: 1,
@ -229,7 +234,8 @@ defmodule Indexer.AddressExtraction do
}, },
%{ %{
fetched_coin_balance_block_number: 2, fetched_coin_balance_block_number: 2,
hash: "0x0000000000000000000000000000000000000003" hash: "0x0000000000000000000000000000000000000003",
nonce: 4
} }
] ]
@ -366,6 +372,7 @@ defmodule Indexer.AddressExtraction do
%{ %{
required(:block_number) => non_neg_integer(), required(:block_number) => non_neg_integer(),
required(:from_address_hash) => String.t(), required(:from_address_hash) => String.t(),
required(:nonce) => non_neg_integer(),
optional(:to_address_hash) => String.t(), optional(:to_address_hash) => String.t(),
optional(:created_contract_address_hash) => String.t() optional(:created_contract_address_hash) => String.t()
} }
@ -477,6 +484,7 @@ defmodule Indexer.AddressExtraction do
Map.merge(first, second) Map.merge(first, second)
end end
end end
|> Map.put(:nonce, max_nil_last(first[:nonce], second[:nonce]))
end end
# `nil > 5 == true`, but we want numbers instead # `nil > 5 == true`, but we want numbers instead

Loading…
Cancel
Save