Skip to content

Commit

Permalink
Prevent HTTP/WS request when node is bootstraping (#788)
Browse files Browse the repository at this point in the history
* Return a 503 when node is bootstraping

* add test for the WS

* use an alias

* rename file
  • Loading branch information
bchamagne committed Jan 2, 2023
1 parent 4af445b commit a2aa6ec
Show file tree
Hide file tree
Showing 14 changed files with 115 additions and 65 deletions.
19 changes: 9 additions & 10 deletions lib/archethic/beacon_chain/slot_timer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ defmodule Archethic.BeaconChain.SlotTimer do
alias Archethic.BeaconChain
alias Archethic.BeaconChain.SubsetRegistry
alias Archethic.BeaconChain.SummaryTimer
alias Archethic.Bootstrap

alias Archethic.DB

Expand Down Expand Up @@ -87,18 +88,16 @@ defmodule Archethic.BeaconChain.SlotTimer do
interval = Keyword.get(opts, :interval)
:ets.insert(@slot_timer_ets, {:interval, interval})

case :persistent_term.get(:archethic_up, nil) do
nil ->
Logger.info("Slot Timer: Waiting for Node to complete Bootstrap.")

Archethic.PubSub.register_to_node_up()
{:ok, %{interval: interval}}
if Bootstrap.done?() do
Logger.info("Slot Timer: Starting...")
next_time = next_slot(DateTime.utc_now())

:up ->
Logger.info("Slot Timer: Starting...")
next_time = next_slot(DateTime.utc_now())
{:ok, %{interval: interval, timer: schedule_new_slot(interval), next_time: next_time}}
else
Logger.info("Slot Timer: Waiting for Node to complete Bootstrap.")

{:ok, %{interval: interval, timer: schedule_new_slot(interval), next_time: next_time}}
Archethic.PubSub.register_to_node_up()
{:ok, %{interval: interval}}
end
end

Expand Down
4 changes: 4 additions & 0 deletions lib/archethic/bootstrap.ex
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ defmodule Archethic.Bootstrap do
])
end

def done?() do
:persistent_term.get(:archethic_up, nil) == :up
end

@doc """
Start the bootstrap workflow.
Expand Down
22 changes: 10 additions & 12 deletions lib/archethic/oracle_chain/scheduler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ defmodule Archethic.OracleChain.Scheduler do
Manage the scheduling of the oracle transactions
"""

alias Archethic.Bootstrap
alias Archethic.Crypto

alias Archethic.Election
Expand Down Expand Up @@ -60,20 +61,17 @@ defmodule Archethic.OracleChain.Scheduler do
|> Map.put(:polling_interval, polling_interval)
|> Map.put(:summary_interval, summary_interval)

case :persistent_term.get(:archethic_up, nil) do
nil ->
# node still bootstrapping , wait for it to finish Bootstrap
Logger.info(" Oracle Scheduler: Waiting for Node to complete Bootstrap. ")

PubSub.register_to_node_up()
if Bootstrap.done?() do
# when node is already bootstrapped, - handles scheduler crash
{state, new_state_data, events} = start_scheduler(state_data)
{:ok, state, new_state_data, events}
else
# node still bootstrapping , wait for it to finish Bootstrap
Logger.info(" Oracle Scheduler: Waiting for Node to complete Bootstrap. ")

{:ok, :idle, state_data}
PubSub.register_to_node_up()

# wait for node UP
:up ->
# when node is already bootstrapped, - handles scheduler crash
{state, new_state_data, events} = start_scheduler(state_data)
{:ok, state, new_state_data, events}
{:ok, :idle, state_data}
end
end

Expand Down
19 changes: 8 additions & 11 deletions lib/archethic/reward/scheduler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ defmodule Archethic.Reward.Scheduler do
@vsn Mix.Project.config()[:version]

alias Archethic.{
Bootstrap,
Crypto,
PubSub,
DB,
Expand All @@ -29,18 +30,14 @@ defmodule Archethic.Reward.Scheduler do
# Set trap_exit globally for the process
Process.flag(:trap_exit, true)

case :persistent_term.get(:archethic_up, nil) do
nil ->
Logger.info(" Reward Scheduler: Waiting for Node to complete Bootstrap. ")

PubSub.register_to_node_up()
{:ok, :idle, state_data}

# wait for node up
if Bootstrap.done?() do
{state, new_state_data, events} = start_scheduler(state_data)
{:ok, state, new_state_data, events}
else
Logger.info(" Reward Scheduler: Waiting for Node to complete Bootstrap. ")

:up ->
{state, new_state_data, events} = start_scheduler(state_data)
{:ok, state, new_state_data, events}
PubSub.register_to_node_up()
{:ok, :idle, state_data}
end
end

Expand Down
3 changes: 2 additions & 1 deletion lib/archethic/self_repair/sync.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ defmodule Archethic.SelfRepair.Sync do
alias Archethic.BeaconChain.Subset.P2PSampling
alias Archethic.BeaconChain.Summary
alias Archethic.BeaconChain.SummaryAggregate
alias Archethic.Bootstrap

alias Archethic.Crypto

Expand Down Expand Up @@ -270,7 +271,7 @@ defmodule Archethic.SelfRepair.Sync do

new_available_nodes = P2P.authorized_and_available_nodes(availability_update)

if :persistent_term.get(:archethic_up, nil) == :up do
if Bootstrap.done?() do
SelfRepair.start_notifier(
previous_available_nodes,
new_available_nodes,
Expand Down
18 changes: 8 additions & 10 deletions lib/archethic/shared_secrets/node_renewal_scheduler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ defmodule Archethic.SharedSecrets.NodeRenewalScheduler do

alias Archethic

alias Archethic.Bootstrap
alias Archethic.Election

alias Archethic.Crypto
Expand Down Expand Up @@ -52,17 +53,14 @@ defmodule Archethic.SharedSecrets.NodeRenewalScheduler do
# Set trap_exit globally for the process
Process.flag(:trap_exit, true)

case :persistent_term.get(:archethic_up, nil) do
nil ->
Logger.info("Node Renewal Scheduler: Waiting for node to complete Bootstrap. ")

PubSub.register_to_node_up()
{:ok, :idle, state_data}
if Bootstrap.done?() do
{state, new_state_data, events} = start_scheduler(state_data)
{:ok, state, new_state_data, events}
else
Logger.info("Node Renewal Scheduler: Waiting for node to complete Bootstrap. ")

# wait for node ups
:up ->
{state, new_state_data, events} = start_scheduler(state_data)
{:ok, state, new_state_data, events}
PubSub.register_to_node_up()
{:ok, :idle, state_data}
end
end

Expand Down
11 changes: 10 additions & 1 deletion lib/archethic_web/channels/user_socket.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ defmodule ArchethicWeb.UserSocket do
use Absinthe.Phoenix.Socket,
schema: ArchethicWeb.GraphQLSchema

alias Archethic.Bootstrap

require Logger

## Channels
# channel "room:*", ArchethicWeb.RoomChannel

Expand All @@ -21,7 +25,12 @@ defmodule ArchethicWeb.UserSocket do
# See `Phoenix.Token` documentation for examples in
# performing token verification on connect.
def connect(_params, socket, _connect_info) do
{:ok, socket}
if Bootstrap.done?() do
{:ok, socket}
else
Logger.debug("Received a websocket connect but node is bootstraping")
:error
end
end

# Socket id's are topics that allow you to identify all sockets for a given user:
Expand Down
19 changes: 0 additions & 19 deletions lib/archethic_web/controllers/up_controler.ex

This file was deleted.

12 changes: 12 additions & 0 deletions lib/archethic_web/controllers/up_controller.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
defmodule ArchethicWeb.UpController do
@moduledoc false

use ArchethicWeb, :controller

@doc """
The logic to respond 503 when node is not bootstraped is moved in a plug
"""
def up(conn, _) do
resp(conn, 200, "up")
end
end
22 changes: 22 additions & 0 deletions lib/archethic_web/endpoint.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ defmodule ArchethicWeb.Endpoint do
use Phoenix.Endpoint, otp_app: :archethic
use Absinthe.Phoenix.Endpoint

alias Archethic.Bootstrap

require Logger

plug(:archethic_up)

# 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 Expand Up @@ -51,4 +57,20 @@ defmodule ArchethicWeb.Endpoint do
plug(Plug.Session, @session_options)
plug(CORSPlug, origin: "*")
plug(ArchethicWeb.RouterDispatch)

# don't serve anything before the node is bootstraped
#
# ps: this handle only HTTP(S) requests
# for WS, see archethic_web/channels/user_socket.ex
defp archethic_up(conn, _opts) do
if Bootstrap.done?() do
conn
else
Logger.debug("Received a web request but node is bootstraping")

conn
|> send_resp(503, "")
|> halt()
end
end
end
1 change: 0 additions & 1 deletion lib/archethic_web/supervisor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ defmodule ArchethicWeb.Supervisor do
TransactionCache,
TopTransactionsCache,
{Phoenix.PubSub, [name: ArchethicWeb.PubSub, adapter: Phoenix.PubSub.PG2]},
# Start the endpoint when the application starts
Endpoint,
{Absinthe.Subscription, Endpoint},
TransactionSubscriber
Expand Down
21 changes: 21 additions & 0 deletions test/archethic_web/controllers/api/up_controller_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
defmodule ArchethicWeb.UpControllerTest do
@moduledoc false
use ArchethicCase
use ArchethicWeb.ConnCase

test "should return 503 when bootstrap is not over", %{conn: conn} do
:persistent_term.put(:archethic_up, nil)

conn = get(conn, "/up")

assert "" = response(conn, 503)
end

test "should return 200 when bootstrap is over", %{conn: conn} do
:persistent_term.put(:archethic_up, :up)

conn = get(conn, "/up")

assert "up" = response(conn, 200)
end
end
6 changes: 6 additions & 0 deletions test/archethic_web/graphql_schema_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -545,4 +545,10 @@ defmodule ArchethicWeb.GraphQLSchemaTest do
assert recv_addr == Base.encode16(addr)
end
end

test "should fail to connect if node is bootstraping" do
:persistent_term.put(:archethic_up, nil)

assert_raise MatchError, fn -> get_socket() end
end
end
3 changes: 3 additions & 0 deletions test/support/conn_case.ex
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ defmodule ArchethicWeb.ConnCase do
end

setup _tags do
# mark the node as bootstraped
:persistent_term.put(:archethic_up, :up)

start_supervised!(FaucetRateLimiter)
{:ok, conn: ConnTest.build_conn()}
end
Expand Down

0 comments on commit a2aa6ec

Please sign in to comment.