Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

api_nearest_endpoints_return_node_list_in_reverse_order_#905 #935

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/archethic_web/endpoint.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ defmodule ArchethicWeb.Endpoint do

plug(:archethic_up)

plug(ArchethicWeb.Plugs.RemoteIP)

# The session will be stored in the cookie and signed,
# this means its contents can be read but not tampered with.
# Set :encryption_salt if you would also like to encrypt it.
Expand Down
13 changes: 3 additions & 10 deletions lib/archethic_web/graphql_schema/resolver.ex
Original file line number Diff line number Diff line change
Expand Up @@ -316,16 +316,9 @@ defmodule ArchethicWeb.GraphQLSchema.Resolver do
defp transform_node_availabilities(<<>>, acc), do: acc

def nearest_endpoints(ip) do
geo_patch = P2P.get_geo_patch(ip)
nearest_nodes = P2P.nearest_nodes(P2P.authorized_and_available_nodes(), geo_patch)

Enum.map(
nearest_nodes,
&%{
ip: :inet.ntoa(&1.ip),
port: &1.http_port
}
)
P2P.authorized_and_available_nodes()
|> P2P.nearest_nodes(P2P.get_geo_patch(ip))
|> Enum.map(&%{ip: :inet.ntoa(&1.ip), port: &1.http_port})
end

def network_transactions(type, page) do
Expand Down
31 changes: 31 additions & 0 deletions lib/archethic_web/plugs/remote_ip.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
defmodule ArchethicWeb.Plugs.RemoteIP do
@moduledoc """
Get actual behind the reverse proxy IP address
"""

@behaviour Plug

def init(opts), do: opts

def call(conn, _) do
conn
|> Plug.Conn.get_req_header("x-forwarded-for")
|> List.first()
|> parse_ip(conn)
end

defp parse_ip(nil, conn), do: conn

defp parse_ip(ip_list, conn) do
ip_str =
String.split(ip_list, ",")
|> List.first()
|> String.trim()
|> String.to_charlist()

case :inet.parse_address(ip_str) do
{:ok, ip} -> Map.put(conn, :remote_ip, ip)
_ -> conn
end
end
end
2 changes: 1 addition & 1 deletion src/c/nat/miniupnp
157 changes: 122 additions & 35 deletions test/archethic_web/graphql_schema_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,20 @@ defmodule ArchethicWeb.GraphQLSchemaTest do
use ArchethicWeb.ConnCase
use ArchethicWeb.GraphQLSubscriptionCase

alias Archethic.Crypto

alias Archethic.BeaconChain.ReplicationAttestation
alias Archethic.BeaconChain.SummaryTimer
alias Archethic.BeaconChain.SummaryAggregate

alias Archethic.P2P
alias Archethic.P2P.Message.GetTransactionChainLength
alias Archethic.P2P.Message.TransactionChainLength
alias Archethic.P2P.Message.Balance
alias Archethic.P2P.Message.GenesisAddress
alias Archethic.P2P.Message.GetBalance
alias Archethic.P2P.Message.GetLastTransactionAddress
alias Archethic.P2P.Message.GetTransaction
alias Archethic.P2P.Message.GetTransactionChain
alias Archethic.P2P.Message.GetTransactionInputs
alias Archethic.P2P.Message.LastTransactionAddress
alias Archethic.P2P.Message.NotFound
alias Archethic.P2P.Message.TransactionInputList
alias Archethic.P2P.Message.TransactionList
alias Archethic.P2P.Message.GetGenesisAddress
alias Archethic.P2P.Message.GetBeaconSummariesAggregate
alias Archethic.P2P.Message.GetCurrentSummaries
alias Archethic.P2P.Node

alias Archethic.PubSub

alias Archethic.TransactionChain.Transaction
alias Archethic.TransactionChain.TransactionData
alias Archethic.TransactionChain.TransactionData.Ownership
alias Archethic.TransactionChain.TransactionInput
alias Archethic.TransactionChain.VersionedTransactionInput
alias Archethic.TransactionChain.TransactionSummary

alias Archethic.Mining
alias Archethic.{Crypto, BeaconChain, P2P, TransactionChain, Mining, PubSub}

alias BeaconChain.{ReplicationAttestation, SummaryAggregate, SummaryTimer}
alias TransactionChain.{Transaction, TransactionData, TransactionData.Ownership}
alias TransactionChain.{TransactionInput, TransactionSummary, VersionedTransactionInput}

alias P2P.{Node, Message}
alias Message.{GetTransactionChainLength, TransactionChainLength, Balance, GenesisAddress}
alias Message.{GetBalance, GetLastTransactionAddress, GetTransaction, NotFound}
alias Message.{GetTransactionChain, GetTransactionInputs, LastTransactionAddress}
alias Message.{TransactionInputList, TransactionList, GetGenesisAddress}
alias Message.{GetBeaconSummariesAggregate, GetCurrentSummaries}

alias ArchethicWeb.GraphQLSchema.Resolver

import Mox
@transaction_chain_page_size 10
Expand Down Expand Up @@ -856,6 +835,7 @@ defmodule ArchethicWeb.GraphQLSchemaTest do
P2P.add_and_connect_node(%Node{
ip: {127, 0, 0, 1},
port: 3004,
http_port: 3004,
first_public_key: <<0::8, 0::8, 1::8, :crypto.strong_rand_bytes(31)::binary>>,
last_public_key: "test",
available?: true
Expand Down Expand Up @@ -925,4 +905,111 @@ defmodule ArchethicWeb.GraphQLSchemaTest do

assert_raise MatchError, fn -> get_socket() end
end

describe "Nearest Endpoint" do
test "order of return", %{conn: conn} do
P2P.add_and_connect_node(%Node{
ip: {101, 10, 10, 1},
port: 40_005,
http_port: 4005,
first_public_key: Crypto.first_node_public_key(),
last_public_key: "key2",
network_patch: "AAA",
geo_patch: "AAA",
available?: true,
authorized?: true,
authorization_date: DateTime.utc_now(),
enrollment_date: DateTime.utc_now()
})

P2P.add_and_connect_node(%Node{
ip: {100, 10, 10, 1},
port: 40_005,
http_port: 4005,
first_public_key: "key2",
last_public_key: "key2",
network_patch: "ABC",
geo_patch: "AAA",
available?: true,
authorized?: true,
authorization_date: DateTime.utc_now(),
enrollment_date: DateTime.utc_now()
})

P2P.add_and_connect_node(%Node{
ip: {99, 10, 10, 1},
port: 40_004,
http_port: 40_004,
first_public_key: "key1",
last_public_key: "key1",
network_patch: "E0A",
geo_patch: "AAA",
available?: true,
authorized?: true,
authorization_date: DateTime.utc_now(),
enrollment_date: DateTime.utc_now()
})

P2P.add_and_connect_node(%Node{
ip: {147, 190, 18, 11},
port: 40_004,
http_port: 40_004,
first_public_key: "key3",
last_public_key: "key3",
network_patch: "ABB",
geo_patch: "AAA",
available?: true,
authorized?: true,
authorization_date: DateTime.utc_now(),
enrollment_date: DateTime.utc_now()
})

MockGeoIP
|> stub(:get_coordinates, fn _ ->
{48.8583701, 2.2922926}
end)

ip = {98, 6, 2, 5}

assert [
%{ip: '101.10.10.1', port: 4_005},
%{ip: '100.10.10.1', port: 4_005},
%{
ip: '147.190.18.11',
port: 40_004
},
%{ip: '99.10.10.1', port: 40_004}
] = Resolver.nearest_endpoints(ip)

conn = Map.put(conn, :remote_ip, ip)

conn =
post(conn, "/api", %{
"query" => "query { nearestEndpoints{ip,port} }"
})

%{
"data" => %{
"nearestEndpoints" => [
%{
"ip" => "101.10.10.1",
"port" => 4_005
},
%{
"ip" => "100.10.10.1",
"port" => 4_005
},
%{
"ip" => "147.190.18.11",
"port" => 40_004
},
%{
"ip" => "99.10.10.1",
"port" => 40_004
}
]
}
} = json_response(conn, 200)
end
end
end
25 changes: 25 additions & 0 deletions test/archethic_web/plugs/remote_ip_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
defmodule ArchethicWeb.Plugs.RemoteIPTest do
use ArchethicWeb.ConnCase, async: true

describe "Plug should get first x-forwarded remote ip " do
test "should modify remote ip if x-forwarded header exists", %{conn: conn} do
assert conn.remote_ip == {127, 0, 0, 1}

conn =
conn
|> put_req_header("x-forwarded-for", "122.15.183.19,93.5.9.0")

conn = ArchethicWeb.Plugs.RemoteIP.call(conn, [])

assert conn.remote_ip == {122, 15, 183, 19}
end

test "should not modifiy remote ip if x-forwarded header is empty", %{conn: conn} do
assert conn.remote_ip == {127, 0, 0, 1}

conn = ArchethicWeb.Plugs.RemoteIP.call(conn, [])

assert conn.remote_ip == {127, 0, 0, 1}
end
end
end