Skip to content

Commit

Permalink
Get chain and limit on the requested address
Browse files Browse the repository at this point in the history
  • Loading branch information
Samuel committed Aug 18, 2022
1 parent 6e377d8 commit e43fdb2
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 122 deletions.
59 changes: 40 additions & 19 deletions lib/archethic.ex
Original file line number Diff line number Diff line change
Expand Up @@ -189,28 +189,49 @@ defmodule Archethic do
|> P2P.nearest_nodes()
|> Enum.filter(&Node.locally_available?/1)

try do
{local_chain, paging_address} =
case TransactionChain.get_last_local_address(address) do
nil -> {[], nil}
last_address -> {TransactionChain.get_locally(last_address), last_address}
end

remote_chain =
if address != paging_address do
address
|> TransactionChain.stream_remotely(nodes, paging_address)
|> Enum.to_list()
|> List.flatten()
%{transactions: local_chain, last_address: last_local_address} =
address
# We directly check if the transaction exists and retrieve the genesis
# (If we store the transaction we will know the genesis by design. No need to make an extra call)
|> TransactionChain.stream()
|> stream_accept_until(address)
|> Enum.reduce_while(%{transactions: [], last_address: nil}, fn transaction, acc ->
# We stop at the desire the transaction
if acc.last_address == address do
{:halt, acc}
else
[]
new_acc =
acc
|> Map.update!(:transactions, &(&1 ++ [transaction]))
# We log the last local address
|> Map.put(:last_address, transaction.address)

{:cont, new_acc}
end
end)

remote_chain =
if address != last_local_address do
address
|> TransactionChain.stream_remotely(nodes, last_local_address)
|> Stream.flat_map(& &1)
|> stream_accept_until(address)
|> Enum.to_list()
else
[]
end

{:ok, local_chain ++ remote_chain}
end

{:ok, local_chain ++ remote_chain}
catch
_ ->
{:error, :network_issue}
end
defp stream_accept_until(transactions, address) do
Stream.transform(transactions, nil, fn
_tx, ^address ->
{:halt, address}

tx, _ ->
{[tx], tx.address}
end)
end

@doc """
Expand Down
15 changes: 3 additions & 12 deletions lib/archethic/transaction_chain.ex
Original file line number Diff line number Diff line change
Expand Up @@ -601,7 +601,7 @@ defmodule Archethic.TransactionChain do
Stream the transactions from a chain
"""
@spec stream(binary(), list()) :: Enumerable.t() | list(Transaction.t())
def stream(address, fields) do
def stream(address, fields \\ []) do
Stream.resource(
fn -> DB.get_transaction_chain(address, fields, []) end,
fn
Expand Down Expand Up @@ -714,17 +714,8 @@ defmodule Archethic.TransactionChain do
|> List.first()
end

# Get transaction chain size to calculate timeout
chain_size =
case Archethic.get_transaction_chain_length(address) do
{:ok, chain_size} ->
chain_size

_ ->
1
end

timeout = Message.get_max_timeout() + Message.get_max_timeout() * chain_size
# We got transactions by batch of 10 transactions
timeout = Message.get_max_timeout() + Message.get_max_timeout() * 10

case P2P.quorum_read(
nodes,
Expand Down
97 changes: 6 additions & 91 deletions test/archethic_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ defmodule ArchethicTest do
alias Archethic.P2P.Message.GetTransactionChain
alias Archethic.P2P.Message.GetTransactionChainLength
alias Archethic.P2P.Message.GetTransactionInputs
alias Archethic.P2P.Message.GetFirstAddress

alias Archethic.P2P.Message.FirstAddress
alias Archethic.P2P.Message.LastTransactionAddress
alias Archethic.P2P.Message.NotFound
alias Archethic.P2P.Message.Ok
Expand Down Expand Up @@ -309,12 +307,6 @@ defmodule ArchethicTest do
%Transaction{address: "@Alice2"}
]
}}

_, %GetTransactionChainLength{}, _ ->
%TransactionChainLength{length: 3}

_, %GetFirstAddress{}, _ ->
{:ok, %NotFound{}}
end)

assert {:ok,
Expand Down Expand Up @@ -348,38 +340,27 @@ defmodule ArchethicTest do
})

MockDB
|> stub(:get_last_chain_address, fn _address ->
"@Alice5"
end)

MockDB
|> stub(:get_transaction_chain, fn _address, _, _ ->
|> stub(:get_transaction_chain, fn "@Alice6", _, _ ->
{[
%Transaction{address: "@Alice0"},
%Transaction{address: "@Alice1"},
%Transaction{address: "@Alice2"},
%Transaction{address: "@Alice3"},
%Transaction{address: "@Alice4"},
%Transaction{address: "@Alice5"}
%Transaction{address: "@Alice4"}
], false, nil}
end)

MockClient
|> stub(:send_message, fn
_, %GetTransactionChain{address: "@Alice2", paging_state: "@Alice5"}, _ ->
_, %GetTransactionChain{address: "@Alice6", paging_state: "@Alice4"}, _ ->
{:ok,
%TransactionList{
transactions: [
%Transaction{address: "@Alice5"},
%Transaction{address: "@Alice6"},
%Transaction{address: "@Alice7"}
]
}}

_, %GetTransactionChainLength{}, _ ->
%TransactionChainLength{length: 2}

_, %GetFirstAddress{}, _ ->
{:ok, %FirstAddress{address: "@Alice0"}}
end)

assert {:ok,
Expand All @@ -390,74 +371,8 @@ defmodule ArchethicTest do
%Transaction{address: "@Alice3"},
%Transaction{address: "@Alice4"},
%Transaction{address: "@Alice5"},
%Transaction{address: "@Alice6"},
%Transaction{address: "@Alice7"}
]} = Archethic.get_transaction_chain("@Alice2")
end

test "should get_transaction_chain from network when GetFirstAddress fails" do
P2P.add_and_connect_node(%Node{
ip: {127, 0, 0, 1},
port: 3000,
first_public_key: Crypto.last_node_public_key(),
last_public_key: Crypto.last_node_public_key(),
network_patch: "AAA",
geo_patch: "AAA"
})

P2P.add_and_connect_node(%Node{
ip: {127, 0, 0, 1},
port: 3000,
first_public_key: "key1",
last_public_key: "key1",
network_patch: "AAA",
geo_patch: "AAA",
available?: true,
authorized?: true,
authorization_date: DateTime.utc_now()
})

MockClient
|> expect(:send_message, fn _, %GetFirstAddress{}, _ ->
{:ok, %NotFound{}}
end)

MockDB
|> stub(:get_last_chain_address, fn address ->
address
end)

MockClient
|> stub(:send_message, fn
_, %GetTransactionChain{address: "@Alice2", paging_state: nil}, _ ->
{:ok,
%TransactionList{
transactions: [
%Transaction{address: "@Alice0"},
%Transaction{address: "@Alice1"},
%Transaction{address: "@Alice2"},
%Transaction{address: "@Alice3"},
%Transaction{address: "@Alice4"},
%Transaction{address: "@Alice5"}
]
}}

_, %GetTransactionChainLength{}, _ ->
%TransactionChainLength{length: 6}

_, %GetFirstAddress{}, _ ->
{:ok, %NotFound{}}
end)

assert {:ok,
[
%Transaction{address: "@Alice0"},
%Transaction{address: "@Alice1"},
%Transaction{address: "@Alice2"},
%Transaction{address: "@Alice3"},
%Transaction{address: "@Alice4"},
%Transaction{address: "@Alice5"}
]} = Archethic.get_transaction_chain("@Alice2")
%Transaction{address: "@Alice6"}
]} = Archethic.get_transaction_chain("@Alice6")
end
end

Expand Down

0 comments on commit e43fdb2

Please sign in to comment.