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', () => {
test('single transaction', () => {
const state = initialState
const state = Object.assign({}, initialState, {
addressHash: '0x111'
})
const action = {
type: 'RECEIVED_NEW_TRANSACTION_BATCH',
msgs: [{
transactionHtml: 'test'
transactionHtml: 'test',
fromAddressHash: '0x111'
}]
}
const output = reducer(state, action)
@ -310,7 +313,6 @@ describe('RECEIVED_NEW_TRANSACTION_BATCH', () => {
expect(output.newTransactions).toEqual([])
expect(output.batchCountAccumulator).toEqual(11)
expect(output.transactionCount).toEqual(11)
})
test('single transaction after single transaction', () => {
const state = Object.assign({}, initialState, {
@ -379,6 +381,28 @@ describe('RECEIVED_NEW_TRANSACTION_BATCH', () => {
expect(output.newTransactions).toEqual([])
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', () => {
const state = Object.assign({}, initialState, {
channelDisconnected: true
@ -397,12 +421,14 @@ describe('RECEIVED_NEW_TRANSACTION_BATCH', () => {
test('on page 2', () => {
const state = Object.assign({}, initialState, {
beyondPageOne: true,
transactionCount: 1
transactionCount: 1,
addressHash: '0x111'
})
const action = {
type: 'RECEIVED_NEW_TRANSACTION_BATCH',
msgs: [{
transactionHtml: 'test'
transactionHtml: 'test',
fromAddressHash: '0x111'
}]
}
const output = reducer(state, action)
@ -488,7 +514,6 @@ describe('RECEIVED_NEW_TRANSACTION_BATCH', () => {
expect(output.newTransactions).toEqual(['test'])
expect(output.batchCountAccumulator).toEqual(0)
expect(output.transactionCount).toEqual(1)
})
test('large batch of transactions', () => {
const state = initialState
@ -532,6 +557,5 @@ describe('RECEIVED_NEW_TRANSACTION_BATCH', () => {
const output = reducer(state, action)
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 incrementTransactionsCount = (transactions, addressHash, currentValue) => {
const reducer = (accumulator, {fromAddressHash}) => {
if (fromAddressHash === addressHash) {
accumulator++
}
return accumulator
}
return transactions.reduce(reducer, currentValue)
}
export const initialState = {
addressHash: null,
balance: null,
@ -109,7 +121,7 @@ export function reducer (state = initialState, action) {
case 'RECEIVED_NEW_TRANSACTION_BATCH': {
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 })

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

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

@ -24,7 +24,8 @@
<span class="mr-4 mb-2">
<span data-selector="transaction-count">
<%= Cldr.Number.to_string!(@transaction_count, format: "#,###") %>
</span> <%= gettext("Transactions") %>
</span>
<%= gettext("Transactions sent") %>
<%= if validator?(@validation_count) do %>
<span data-selector="validation-count">
<%= 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 transaction_count(%Address{} = address) do
Chain.address_to_transactions_estimated_count(address)
Chain.total_transactions_sent_by_address(address)
end
def trimmed_hash(%Hash{} = hash) do

@ -124,7 +124,7 @@ msgstr ""
#, elixir-format
#: 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/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:81
#: lib/block_scout_web/templates/address_validation/index.html.eex:108
@ -143,8 +143,8 @@ msgid "Clear"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:73
#: lib/block_scout_web/templates/address/overview.html.eex:81
#: lib/block_scout_web/templates/address/overview.html.eex:74
#: 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:89
msgid "Close"
@ -272,7 +272,7 @@ msgid "Copyright %{year} POA"
msgstr ""
#, 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"
msgstr ""
@ -621,7 +621,7 @@ msgstr ""
#, elixir-format
#: 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:80
@ -876,8 +876,6 @@ msgstr ""
#, elixir-format
#: 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/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_validation/index.html.eex:11
#: lib/block_scout_web/templates/address_validation/index.html.eex:65
@ -993,7 +991,7 @@ msgid "Yes"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:48
#: lib/block_scout_web/templates/address/overview.html.eex:49
msgid "at"
msgstr ""
@ -1176,3 +1174,9 @@ msgstr ""
#: lib/block_scout_web/templates/layout/app.html.eex:47
msgid "Indexing Tokens"
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
#: 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/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:81
#: lib/block_scout_web/templates/address_validation/index.html.eex:108
@ -143,8 +143,8 @@ msgid "Clear"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:73
#: lib/block_scout_web/templates/address/overview.html.eex:81
#: lib/block_scout_web/templates/address/overview.html.eex:74
#: 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:89
msgid "Close"
@ -272,7 +272,7 @@ msgid "Copyright %{year} POA"
msgstr ""
#, 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"
msgstr ""
@ -621,7 +621,7 @@ msgstr ""
#, elixir-format
#: 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:80
@ -876,8 +876,6 @@ msgstr ""
#, elixir-format
#: 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/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_validation/index.html.eex:11
#: lib/block_scout_web/templates/address_validation/index.html.eex:65
@ -993,7 +991,7 @@ msgid "Yes"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:48
#: lib/block_scout_web/templates/address/overview.html.eex:49
msgid "at"
msgstr ""
@ -1176,3 +1174,9 @@ msgstr ""
#: lib/block_scout_web/templates/layout/app.html.eex:47
msgid "Indexing Tokens"
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
@doc """
Gets an estimated count of `t:Explorer.Chain.Transaction.t/0` to or from the `address` based on the estimated rows
resulting in an EXPLAIN of the query plan for the count query.
Get the total number of transactions sent by the given address according to the last block indexed.
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()
def address_to_transactions_estimated_count(%Address{hash: address_hash}) do
{:ok, %Postgrex.Result{rows: result}} =
Repo.query(
"""
EXPLAIN SELECT COUNT(DISTINCT t.hash) FROM
(
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]
)
@spec total_transactions_sent_by_address(Address.t()) :: non_neg_integer()
def total_transactions_sent_by_address(%Address{hash: address_hash}) do
last_nonce =
address_hash
|> Transaction.last_nonce_by_address_query()
|> Repo.one()
{[unique_explain], _} = List.pop_at(result, 1)
[[_ | [rows]]] = Regex.scan(~r/rows=(\d+)/, unique_explain)
String.to_integer(rows)
case last_nonce do
nil -> 0
value -> value + 1
end
end
@doc """

@ -543,4 +543,21 @@ defmodule Explorer.Chain.Transaction do
distinct: :hash
)
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

@ -177,4 +177,40 @@ defmodule Explorer.Chain.TransactionTest do
assert result == [transaction_c.block_number, transaction_b.block_number, transaction_a.block_number]
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

@ -344,9 +344,25 @@ defmodule Explorer.ChainTest do
end
end
describe "address_to_transactions_estimated_count/1" do
test "returns integer" do
assert is_integer(Chain.address_to_transactions_estimated_count(build(:address)))
describe "total_transactions_sent_by_address/1" do
test "increments +1 in the last nonce result" do
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

Loading…
Cancel
Save