Merge pull request #1140 from poanetwork/ams-top-accounts

Add nonce field to addresses table
pull/1148/head
Andrew Cravenho 6 years ago committed by GitHub
commit 6a9e4a6895
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  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. 26
      apps/explorer/priv/repo/migrations/20181106152300_add_nonce_to_addresses.exs
  5. 13
      apps/explorer/priv/repo/migrations/20181126203826_add_index_to_addresses.exs
  6. 55
      apps/explorer/priv/repo/migrations/scripts/20181126182700_migrate_address_nonce.sql
  7. 99
      apps/indexer/lib/indexer/address_extraction.ex
  8. 16
      apps/indexer/test/indexer/address_extraction_test.exs

@ -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,26 @@
defmodule Explorer.Repo.Migrations.AddNonceToAddresses do
@moduledoc """
Use `priv/repo/migrations/scripts/20181126182700_migrate_address_nonce.sql` to migrate data.
```sh
mix ecto.migrate
psql -d $DATABASE -a -f priv/repo/migrations/scripts/20181126182700_migrate_address_nonce.sql
```
"""
use Ecto.Migration
def up do
# Add nonce
alter table(:addresses) do
add(:nonce, :integer)
end
end
def down do
# Remove nonce
alter table(:addresses) do
remove(:nonce)
end
end
end

@ -0,0 +1,13 @@
defmodule Explorer.Repo.Migrations.AddIndexToAddresses do
use Ecto.Migration
def up do
execute(
"CREATE INDEX addresses_fetched_coin_balance_hash_index ON addresses (fetched_coin_balance DESC, hash ASC) WHERE fetched_coin_balance > 0"
)
end
def down do
execute("DROP INDEX addresses_fetched_coin_balance_hash_index")
end
end

@ -0,0 +1,55 @@
DO $$
DECLARE
row_count integer := 1;
batch_size integer := 50000; -- HOW MANY ITEMS WILL BE UPDATED AT TIME
iterator integer := batch_size;
affected integer;
BEGIN
DROP TABLE IF EXISTS addresses_nonce_temp;
-- CREATES TEMP TABLE TO STORE THE ADDRESS NONCE TO BE UPDATED
CREATE TEMP TABLE addresses_nonce_temp(
from_address_hash bytea,
nonce integer,
row_number integer
);
INSERT INTO addresses_nonce_temp
SELECT DISTINCT ON (from_address_hash)
from_address_hash,
nonce,
ROW_NUMBER () OVER ()
FROM transactions
ORDER BY from_address_hash, nonce DESC;
row_count := (SELECT count(*) FROM addresses_nonce_temp);
RAISE NOTICE '% items to be updated', row_count;
-- ITERATES THROUGH THE ITEMS UNTIL THE TEMP TABLE IS EMPTY
WHILE row_count > 0 LOOP
-- UPDATES THE ADDRESS NONCE AND RETURNS THE ADDRESS_HASH
WITH updated_addresses AS (
UPDATE addresses SET nonce = addresses_nonce_temp.nonce
FROM addresses_nonce_temp
WHERE addresses_nonce_temp.from_address_hash = addresses.hash
AND addresses_nonce_temp.row_number <= iterator
RETURNING addresses_nonce_temp.from_address_hash
)
DELETE FROM addresses_nonce_temp
WHERE (from_address_hash) IN (select from_address_hash from updated_addresses);
GET DIAGNOSTICS affected = ROW_COUNT;
RAISE NOTICE '-> % addresses updated!', affected;
-- COMMITS THE BATCH UPDATES
CHECKPOINT;
-- UPDATES THE COUNTER SO IT DOESN'T TURN INTO A INFINITE LOOP
row_count := (SELECT count(*) FROM addresses_nonce_temp);
iterator := iterator + batch_size;
RAISE NOTICE '-> % counter', row_count;
RAISE NOTICE '-> % next batch', iterator;
END LOOP;
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
} }
] ]
@ -279,11 +285,13 @@ defmodule Indexer.AddressExtraction do
...> transactions: [ ...> transactions: [
...> %{ ...> %{
...> block_number: 3, ...> block_number: 3,
...> to_address_hash: "0x0000000000000000000000000000000000000001" ...> to_address_hash: "0x0000000000000000000000000000000000000001",
...> nonce: 5
...> }, ...> },
...> %{ ...> %{
...> block_number: 2, ...> block_number: 2,
...> from_address_hash: "0x0000000000000000000000000000000000000001" ...> from_address_hash: "0x0000000000000000000000000000000000000001",
...> nonce: 4
...> } ...> }
...> ], ...> ],
...> logs: [ ...> logs: [
@ -297,7 +305,8 @@ defmodule Indexer.AddressExtraction do
[ [
%{ %{
fetched_coin_balance_block_number: 7, fetched_coin_balance_block_number: 7,
hash: "0x0000000000000000000000000000000000000001" hash: "0x0000000000000000000000000000000000000001",
nonce: 4
} }
] ]
@ -316,11 +325,13 @@ defmodule Indexer.AddressExtraction do
...> transactions: [ ...> transactions: [
...> %{ ...> %{
...> block_number: 2, ...> block_number: 2,
...> from_address_hash: "0x0000000000000000000000000000000000000001" ...> from_address_hash: "0x0000000000000000000000000000000000000001",
...> nonce: 4
...> }, ...> },
...> %{ ...> %{
...> block_number: 3, ...> block_number: 3,
...> to_address_hash: "0x0000000000000000000000000000000000000001" ...> to_address_hash: "0x0000000000000000000000000000000000000001",
...> nonce: 5
...> } ...> }
...> ] ...> ]
...> } ...> }
@ -329,7 +340,8 @@ defmodule Indexer.AddressExtraction do
%{ %{
contract_code: "0x", contract_code: "0x",
fetched_coin_balance_block_number: 3, fetched_coin_balance_block_number: 3,
hash: "0x0000000000000000000000000000000000000001" hash: "0x0000000000000000000000000000000000000001",
nonce: 4
} }
] ]
@ -366,6 +378,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()
} }
@ -445,38 +458,42 @@ defmodule Indexer.AddressExtraction do
# Ensure that when `:addresses` or `:address_coin_balances` are present, their :fetched_coin_balance will win # Ensure that when `:addresses` or `:address_coin_balances` are present, their :fetched_coin_balance will win
defp merge_addresses(%{hash: hash} = first, %{hash: hash} = second) do defp merge_addresses(%{hash: hash} = first, %{hash: hash} = second) do
case {first[:fetched_coin_balance], second[:fetched_coin_balance]} do merged_addresses =
{nil, nil} -> case {first[:fetched_coin_balance], second[:fetched_coin_balance]} do
first {nil, nil} ->
|> Map.merge(second) first
|> Map.put( |> Map.merge(second)
:fetched_coin_balance_block_number, |> Map.put(
max_nil_last( :fetched_coin_balance_block_number,
Map.get(first, :fetched_coin_balance_block_number), max_nil_last(
Map.get(second, :fetched_coin_balance_block_number) Map.get(first, :fetched_coin_balance_block_number),
Map.get(second, :fetched_coin_balance_block_number)
)
) )
)
{nil, _} ->
{nil, _} -> # merge in `second` so its balance and block_number wins
# merge in `second` so its balance and block_number wins
Map.merge(first, second)
{_, nil} ->
# merge in `first` so its balance and block_number wins
Map.merge(second, first)
{_, _} ->
if greater_than_nil_last(
Map.get(first, :fetched_coin_balance_block_number),
Map.get(second, :fetched_coin_balance_block_number)
) do
# merge in `first` so its block number wins
Map.merge(second, first)
else
# merge in `second` so its block number wins
Map.merge(first, second) Map.merge(first, second)
end
end {_, nil} ->
# merge in `first` so its balance and block_number wins
Map.merge(second, first)
{_, _} ->
if greater_than_nil_last(
Map.get(first, :fetched_coin_balance_block_number),
Map.get(second, :fetched_coin_balance_block_number)
) do
# merge in `first` so its block number wins
Map.merge(second, first)
else
# merge in `second` so its block number wins
Map.merge(first, second)
end
end
merged_addresses
|> 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

@ -76,7 +76,8 @@ defmodule Indexer.AddressExtractionTest do
%{ %{
fetched_coin_balance_block_number: 2, fetched_coin_balance_block_number: 2,
contract_code: "0x1", contract_code: "0x1",
hash: "0x0000000000000000000000000000000000000001" hash: "0x0000000000000000000000000000000000000001",
nonce: nil
} }
] ]
end end
@ -95,7 +96,8 @@ defmodule Indexer.AddressExtractionTest do
transaction = %{ transaction = %{
block_number: 3, block_number: 3,
from_address_hash: gen_hash(), from_address_hash: gen_hash(),
to_address_hash: gen_hash() to_address_hash: gen_hash(),
nonce: 7
} }
log = %{address_hash: gen_hash(), block_number: 4} log = %{address_hash: gen_hash(), block_number: 4}
@ -136,7 +138,11 @@ defmodule Indexer.AddressExtractionTest do
contract_code: internal_transaction.created_contract_code, contract_code: internal_transaction.created_contract_code,
fetched_coin_balance_block_number: internal_transaction.block_number fetched_coin_balance_block_number: internal_transaction.block_number
}, },
%{hash: transaction.from_address_hash, fetched_coin_balance_block_number: transaction.block_number}, %{
hash: transaction.from_address_hash,
fetched_coin_balance_block_number: transaction.block_number,
nonce: 7
},
%{hash: transaction.to_address_hash, fetched_coin_balance_block_number: transaction.block_number}, %{hash: transaction.to_address_hash, fetched_coin_balance_block_number: transaction.block_number},
%{hash: log.address_hash, fetched_coin_balance_block_number: log.block_number}, %{hash: log.address_hash, fetched_coin_balance_block_number: log.block_number},
%{ %{
@ -176,7 +182,7 @@ defmodule Indexer.AddressExtractionTest do
blockchain_data = %{ blockchain_data = %{
blocks: [%{miner_hash: hash, number: 34}], blocks: [%{miner_hash: hash, number: 34}],
transactions: [%{block_number: 34, from_address_hash: hash}], transactions: [%{block_number: 34, from_address_hash: hash, nonce: 12}],
internal_transactions: [ internal_transactions: [
%{ %{
block_number: 34, block_number: 34,
@ -188,7 +194,7 @@ defmodule Indexer.AddressExtractionTest do
assert AddressExtraction.extract_addresses(blockchain_data) == assert AddressExtraction.extract_addresses(blockchain_data) ==
[ [
%{hash: hash, fetched_coin_balance_block_number: 34, contract_code: "code"} %{hash: hash, fetched_coin_balance_block_number: 34, contract_code: "code", nonce: 12}
] ]
end end

Loading…
Cancel
Save