Merge pull request #929 from poanetwork/frg-refactor-transactions-count

Refactor the transactions count by address to use the accounts nonce
pull/939/head
Felipe Renan 6 years ago committed by GitHub
commit 3cb65c056c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 38
      apps/block_scout_web/assets/__tests__/pages/address.js
  2. 14
      apps/block_scout_web/assets/js/pages/address.js
  3. 2
      apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex
  4. 2
      apps/block_scout_web/lib/block_scout_web/templates/address/index.html.eex
  5. 3
      apps/block_scout_web/lib/block_scout_web/templates/address/overview.html.eex
  6. 2
      apps/block_scout_web/lib/block_scout_web/views/address_view.ex
  7. 20
      apps/block_scout_web/priv/gettext/default.pot
  8. 20
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  9. 41
      apps/explorer/lib/explorer/chain.ex
  10. 17
      apps/explorer/lib/explorer/chain/transaction.ex
  11. 36
      apps/explorer/test/explorer/chain/transaction_test.exs
  12. 22
      apps/explorer/test/explorer/chain_test.exs

@ -265,11 +265,14 @@ describe('RECEIVED_NEW_PENDING_TRANSACTION_BATCH', () => {
describe('RECEIVED_NEW_TRANSACTION_BATCH', () => { describe('RECEIVED_NEW_TRANSACTION_BATCH', () => {
test('single transaction', () => { test('single transaction', () => {
const state = initialState const state = Object.assign({}, initialState, {
addressHash: '0x111'
})
const action = { const action = {
type: 'RECEIVED_NEW_TRANSACTION_BATCH', type: 'RECEIVED_NEW_TRANSACTION_BATCH',
msgs: [{ msgs: [{
transactionHtml: 'test' transactionHtml: 'test',
fromAddressHash: '0x111'
}] }]
} }
const output = reducer(state, action) const output = reducer(state, action)
@ -310,7 +313,6 @@ describe('RECEIVED_NEW_TRANSACTION_BATCH', () => {
expect(output.newTransactions).toEqual([]) expect(output.newTransactions).toEqual([])
expect(output.batchCountAccumulator).toEqual(11) expect(output.batchCountAccumulator).toEqual(11)
expect(output.transactionCount).toEqual(11)
}) })
test('single transaction after single transaction', () => { test('single transaction after single transaction', () => {
const state = Object.assign({}, initialState, { const state = Object.assign({}, initialState, {
@ -379,6 +381,28 @@ describe('RECEIVED_NEW_TRANSACTION_BATCH', () => {
expect(output.newTransactions).toEqual([]) expect(output.newTransactions).toEqual([])
expect(output.batchCountAccumulator).toEqual(22) expect(output.batchCountAccumulator).toEqual(22)
}) })
test('increments the transactions count only when the address sent transactions', () => {
const state = Object.assign({}, initialState, {
newTransactions: [],
addressHash: '0x111',
transactionCount: 1
})
const action = {
type: 'RECEIVED_NEW_TRANSACTION_BATCH',
msgs: [{
transactionHtml: 'test 12',
fromAddressHash: '0x111',
toAddressHash: '0x222'
},{
transactionHtml: 'test 13',
fromAddressHash: '0x222',
toAddressHash: '0x111'
}]
}
const output = reducer(state, action)
expect(output.transactionCount).toEqual(2)
})
test('after disconnection', () => { test('after disconnection', () => {
const state = Object.assign({}, initialState, { const state = Object.assign({}, initialState, {
channelDisconnected: true channelDisconnected: true
@ -397,12 +421,14 @@ describe('RECEIVED_NEW_TRANSACTION_BATCH', () => {
test('on page 2', () => { test('on page 2', () => {
const state = Object.assign({}, initialState, { const state = Object.assign({}, initialState, {
beyondPageOne: true, beyondPageOne: true,
transactionCount: 1 transactionCount: 1,
addressHash: '0x111'
}) })
const action = { const action = {
type: 'RECEIVED_NEW_TRANSACTION_BATCH', type: 'RECEIVED_NEW_TRANSACTION_BATCH',
msgs: [{ msgs: [{
transactionHtml: 'test' transactionHtml: 'test',
fromAddressHash: '0x111'
}] }]
} }
const output = reducer(state, action) const output = reducer(state, action)
@ -488,7 +514,6 @@ describe('RECEIVED_NEW_TRANSACTION_BATCH', () => {
expect(output.newTransactions).toEqual(['test']) expect(output.newTransactions).toEqual(['test'])
expect(output.batchCountAccumulator).toEqual(0) expect(output.batchCountAccumulator).toEqual(0)
expect(output.transactionCount).toEqual(1)
}) })
test('large batch of transactions', () => { test('large batch of transactions', () => {
const state = initialState const state = initialState
@ -532,6 +557,5 @@ describe('RECEIVED_NEW_TRANSACTION_BATCH', () => {
const output = reducer(state, action) const output = reducer(state, action)
expect(output.newTransactions).toEqual([]) expect(output.newTransactions).toEqual([])
expect(output.transactionCount).toEqual(11)
}) })
}) })

@ -11,6 +11,18 @@ import { loadTokenBalanceDropdown } from '../lib/token_balance_dropdown'
const BATCH_THRESHOLD = 10 const BATCH_THRESHOLD = 10
const incrementTransactionsCount = (transactions, addressHash, currentValue) => {
const reducer = (accumulator, {fromAddressHash}) => {
if (fromAddressHash === addressHash) {
accumulator++
}
return accumulator
}
return transactions.reduce(reducer, currentValue)
}
export const initialState = { export const initialState = {
addressHash: null, addressHash: null,
balance: null, balance: null,
@ -109,7 +121,7 @@ export function reducer (state = initialState, action) {
case 'RECEIVED_NEW_TRANSACTION_BATCH': { case 'RECEIVED_NEW_TRANSACTION_BATCH': {
if (state.channelDisconnected) return state if (state.channelDisconnected) return state
const transactionCount = state.transactionCount + action.msgs.length const transactionCount = incrementTransactionsCount(action.msgs, state.addressHash, state.transactionCount)
if (state.beyondPageOne) return Object.assign({}, state, { transactionCount }) if (state.beyondPageOne) return Object.assign({}, state, { transactionCount })

@ -18,7 +18,7 @@ defmodule BlockScoutWeb.AddressController do
end end
def transaction_count(%Address{} = address) do def transaction_count(%Address{} = address) do
Chain.address_to_transactions_estimated_count(address) Chain.total_transactions_sent_by_address(address)
end end
def validation_count(%Address{} = address) do def validation_count(%Address{} = address) do

@ -25,7 +25,7 @@
<!-- number of txns for this address --> <!-- number of txns for this address -->
<span> <span>
<span data-test="transaction_count"><%= transaction_count(address) %></span> <span data-test="transaction_count"><%= transaction_count(address) %></span>
<%= gettext "Transactions" %> <%= gettext "Transactions sent" %>
</span> </span>
</div> </div>

@ -24,7 +24,8 @@
<span class="mr-4 mb-2"> <span class="mr-4 mb-2">
<span data-selector="transaction-count"> <span data-selector="transaction-count">
<%= Cldr.Number.to_string!(@transaction_count, format: "#,###") %> <%= Cldr.Number.to_string!(@transaction_count, format: "#,###") %>
</span> <%= gettext("Transactions") %> </span>
<%= gettext("Transactions sent") %>
<%= if validator?(@validation_count) do %> <%= if validator?(@validation_count) do %>
<span data-selector="validation-count"> <span data-selector="validation-count">
<%= Cldr.Number.to_string!(@validation_count, format: "#,###") %> <%= Cldr.Number.to_string!(@validation_count, format: "#,###") %>

@ -160,7 +160,7 @@ defmodule BlockScoutWeb.AddressView do
def token_title(%Token{name: name, symbol: symbol}), do: "#{name} (#{symbol})" def token_title(%Token{name: name, symbol: symbol}), do: "#{name} (#{symbol})"
def transaction_count(%Address{} = address) do def transaction_count(%Address{} = address) do
Chain.address_to_transactions_estimated_count(address) Chain.total_transactions_sent_by_address(address)
end end
def trimmed_hash(%Hash{} = hash) do def trimmed_hash(%Hash{} = hash) do

@ -124,7 +124,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:31 #: lib/block_scout_web/templates/address/_tabs.html.eex:31
#: lib/block_scout_web/templates/address/_tabs.html.eex:88 #: lib/block_scout_web/templates/address/_tabs.html.eex:88
#: lib/block_scout_web/templates/address/overview.html.eex:31 #: lib/block_scout_web/templates/address/overview.html.eex:32
#: lib/block_scout_web/templates/address_validation/index.html.eex:32 #: lib/block_scout_web/templates/address_validation/index.html.eex:32
#: lib/block_scout_web/templates/address_validation/index.html.eex:81 #: lib/block_scout_web/templates/address_validation/index.html.eex:81
#: lib/block_scout_web/templates/address_validation/index.html.eex:108 #: lib/block_scout_web/templates/address_validation/index.html.eex:108
@ -143,8 +143,8 @@ msgid "Clear"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:73 #: lib/block_scout_web/templates/address/overview.html.eex:74
#: lib/block_scout_web/templates/address/overview.html.eex:81 #: lib/block_scout_web/templates/address/overview.html.eex:82
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:81 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:81
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:89 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:89
msgid "Close" msgid "Close"
@ -272,7 +272,7 @@ msgid "Copyright %{year} POA"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:42 #: lib/block_scout_web/templates/address/overview.html.eex:43
msgid "Created by" msgid "Created by"
msgstr "" msgstr ""
@ -621,7 +621,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:13 #: lib/block_scout_web/templates/address/overview.html.eex:13
#: lib/block_scout_web/templates/address/overview.html.eex:72 #: lib/block_scout_web/templates/address/overview.html.eex:73
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:13 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:13
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:13 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:13
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:80 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:80
@ -876,8 +876,6 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:5 #: lib/block_scout_web/templates/address/_tabs.html.eex:5
#: lib/block_scout_web/templates/address/_tabs.html.eex:71 #: lib/block_scout_web/templates/address/_tabs.html.eex:71
#: lib/block_scout_web/templates/address/index.html.eex:28
#: lib/block_scout_web/templates/address/overview.html.eex:27
#: lib/block_scout_web/templates/address_transaction/index.html.eex:63 #: lib/block_scout_web/templates/address_transaction/index.html.eex:63
#: lib/block_scout_web/templates/address_validation/index.html.eex:11 #: lib/block_scout_web/templates/address_validation/index.html.eex:11
#: lib/block_scout_web/templates/address_validation/index.html.eex:65 #: lib/block_scout_web/templates/address_validation/index.html.eex:65
@ -993,7 +991,7 @@ msgid "Yes"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:48 #: lib/block_scout_web/templates/address/overview.html.eex:49
msgid "at" msgid "at"
msgstr "" msgstr ""
@ -1176,3 +1174,9 @@ msgstr ""
#: lib/block_scout_web/templates/layout/app.html.eex:47 #: lib/block_scout_web/templates/layout/app.html.eex:47
msgid "Indexing Tokens" msgid "Indexing Tokens"
msgstr "" msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/index.html.eex:28
#: lib/block_scout_web/templates/address/overview.html.eex:28
msgid "Transactions sent"
msgstr ""

@ -124,7 +124,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:31 #: lib/block_scout_web/templates/address/_tabs.html.eex:31
#: lib/block_scout_web/templates/address/_tabs.html.eex:88 #: lib/block_scout_web/templates/address/_tabs.html.eex:88
#: lib/block_scout_web/templates/address/overview.html.eex:31 #: lib/block_scout_web/templates/address/overview.html.eex:32
#: lib/block_scout_web/templates/address_validation/index.html.eex:32 #: lib/block_scout_web/templates/address_validation/index.html.eex:32
#: lib/block_scout_web/templates/address_validation/index.html.eex:81 #: lib/block_scout_web/templates/address_validation/index.html.eex:81
#: lib/block_scout_web/templates/address_validation/index.html.eex:108 #: lib/block_scout_web/templates/address_validation/index.html.eex:108
@ -143,8 +143,8 @@ msgid "Clear"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:73 #: lib/block_scout_web/templates/address/overview.html.eex:74
#: lib/block_scout_web/templates/address/overview.html.eex:81 #: lib/block_scout_web/templates/address/overview.html.eex:82
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:81 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:81
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:89 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:89
msgid "Close" msgid "Close"
@ -272,7 +272,7 @@ msgid "Copyright %{year} POA"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:42 #: lib/block_scout_web/templates/address/overview.html.eex:43
msgid "Created by" msgid "Created by"
msgstr "" msgstr ""
@ -621,7 +621,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:13 #: lib/block_scout_web/templates/address/overview.html.eex:13
#: lib/block_scout_web/templates/address/overview.html.eex:72 #: lib/block_scout_web/templates/address/overview.html.eex:73
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:13 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:13
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:13 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:13
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:80 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:80
@ -876,8 +876,6 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:5 #: lib/block_scout_web/templates/address/_tabs.html.eex:5
#: lib/block_scout_web/templates/address/_tabs.html.eex:71 #: lib/block_scout_web/templates/address/_tabs.html.eex:71
#: lib/block_scout_web/templates/address/index.html.eex:28
#: lib/block_scout_web/templates/address/overview.html.eex:27
#: lib/block_scout_web/templates/address_transaction/index.html.eex:63 #: lib/block_scout_web/templates/address_transaction/index.html.eex:63
#: lib/block_scout_web/templates/address_validation/index.html.eex:11 #: lib/block_scout_web/templates/address_validation/index.html.eex:11
#: lib/block_scout_web/templates/address_validation/index.html.eex:65 #: lib/block_scout_web/templates/address_validation/index.html.eex:65
@ -993,7 +991,7 @@ msgid "Yes"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:48 #: lib/block_scout_web/templates/address/overview.html.eex:49
msgid "at" msgid "at"
msgstr "" msgstr ""
@ -1176,3 +1174,9 @@ msgstr ""
#: lib/block_scout_web/templates/layout/app.html.eex:47 #: lib/block_scout_web/templates/layout/app.html.eex:47
msgid "Indexing Tokens" msgid "Indexing Tokens"
msgstr "" msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/index.html.eex:28
#: lib/block_scout_web/templates/address/overview.html.eex:28
msgid "Transactions sent"
msgstr ""

@ -180,35 +180,22 @@ defmodule Explorer.Chain do
end end
@doc """ @doc """
Gets an estimated count of `t:Explorer.Chain.Transaction.t/0` to or from the `address` based on the estimated rows Get the total number of transactions sent by the given address according to the last block indexed.
resulting in an EXPLAIN of the query plan for the count query.
We have to increment +1 in the last nonce result because it works like an array position, the first
nonce has the value 0. When last nonce is nil, it considers that the given address has 0 transactions.
""" """
@spec address_to_transactions_estimated_count(Address.t()) :: non_neg_integer() @spec total_transactions_sent_by_address(Address.t()) :: non_neg_integer()
def address_to_transactions_estimated_count(%Address{hash: address_hash}) do def total_transactions_sent_by_address(%Address{hash: address_hash}) do
{:ok, %Postgrex.Result{rows: result}} = last_nonce =
Repo.query( address_hash
""" |> Transaction.last_nonce_by_address_query()
EXPLAIN SELECT COUNT(DISTINCT t.hash) FROM |> Repo.one()
(
SELECT t0.hash FROM transactions AS t0 WHERE t0.from_address_hash = $1
UNION
SELECT t0.hash FROM transactions AS t0 WHERE t0.to_address_hash = $1
UNION
SELECT t0.hash FROM transactions AS t0 WHERE t0.created_contract_address_hash = $1
UNION
SELECT tt.transaction_hash AS hash FROM token_transfers AS tt
WHERE tt.from_address_hash = $1
UNION
SELECT tt.transaction_hash AS hash FROM token_transfers AS tt
WHERE tt.to_address_hash = $1
) as t
""",
[address_hash.bytes]
)
{[unique_explain], _} = List.pop_at(result, 1) case last_nonce do
[[_ | [rows]]] = Regex.scan(~r/rows=(\d+)/, unique_explain) nil -> 0
String.to_integer(rows) value -> value + 1
end
end end
@doc """ @doc """

@ -543,4 +543,21 @@ defmodule Explorer.Chain.Transaction do
distinct: :hash distinct: :hash
) )
end end
@doc """
Builds an `Ecto.Query` to fetch the last nonce from the given address hash.
The last nonce value means the total of transactions that the given address has sent through the
chain. Also, the query uses the last `block_number` to get the last nonce because this column is
indexed in DB, then the query is faster than ordering by last nonce.
"""
def last_nonce_by_address_query(address_hash) do
from(
t in Transaction,
select: t.nonce,
where: t.from_address_hash == ^address_hash,
order_by: [desc: :block_number],
limit: 1
)
end
end end

@ -177,4 +177,40 @@ defmodule Explorer.Chain.TransactionTest do
assert result == [transaction_c.block_number, transaction_b.block_number, transaction_a.block_number] assert result == [transaction_c.block_number, transaction_b.block_number, transaction_a.block_number]
end end
end end
describe "last_nonce_by_address_query/1" do
test "returns the nonce value from the last block" do
address = insert(:address)
:transaction
|> insert(nonce: 100, from_address: address)
|> with_block(insert(:block, number: 1000))
:transaction
|> insert(nonce: 300, from_address: address)
|> with_block(insert(:block, number: 2000))
last_nonce =
address.hash
|> Transaction.last_nonce_by_address_query()
|> Repo.one()
assert last_nonce == 300
end
test "considers only from_address in transactions" do
address = insert(:address)
:transaction
|> insert(nonce: 100, to_address: address)
|> with_block(insert(:block, number: 1000))
last_nonce =
address.hash
|> Transaction.last_nonce_by_address_query()
|> Repo.one()
assert last_nonce == nil
end
end
end end

@ -344,9 +344,25 @@ defmodule Explorer.ChainTest do
end end
end end
describe "address_to_transactions_estimated_count/1" do describe "total_transactions_sent_by_address/1" do
test "returns integer" do test "increments +1 in the last nonce result" do
assert is_integer(Chain.address_to_transactions_estimated_count(build(:address))) address = insert(:address)
:transaction
|> insert(nonce: 100, from_address: address)
|> with_block(insert(:block, number: 1000))
assert Chain.total_transactions_sent_by_address(address) == 101
end
test "returns 0 when the address did not send transactions" do
address = insert(:address)
:transaction
|> insert(nonce: 100, to_address: address)
|> with_block(insert(:block, number: 1000))
assert Chain.total_transactions_sent_by_address(address) == 0
end end
end end

Loading…
Cancel
Save