Skip to content
This repository was archived by the owner on Mar 5, 2024. It is now read-only.

HIP-46: facilitate routing by netid-to-oui #1412

Merged
merged 1 commit into from
Jun 30, 2022
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
4 changes: 4 additions & 0 deletions include/blockchain_vars.hrl
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,9 @@
%% Block size limit variable (in bytes). Set to 25 * 1024 * 1024.
-define(block_size_limit, block_size_limit).

%% List of peer routers associating NetID to allocated OUI
-define(routers_by_netid_to_oui, routers_by_netid_to_oui).

%% ------------------------------------------------------------------
%% Token version (aka support multiple tokens). Set to 2 (pos_integer).
-define(token_version, token_version).
Expand All @@ -629,3 +632,4 @@
-define(deprecate_security_exchange_v1, deprecate_security_exchange_v1).
%% How many reward server keys to allow
-define(allowed_num_reward_server_keys, allowed_num_reward_server_keys).

1 change: 1 addition & 0 deletions rebar.config
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
{e2qc, ".*", {git, "https://github.com/helium/e2qc", {branch, "master"}}},
{vincenty, ".*", {git, "https://github.com/helium/vincenty", {branch, "master"}}},
{helium_proto, {git, "https://github.com/helium/proto.git", {branch, "master"}}},
{lorawan, {git, "https://github.com/helium/erlang-lorawan.git", {branch, "master"}}},
{merkerl, ".*", {git, "https://github.com/helium/merkerl.git", {branch, "master"}}},
{xxhash, {git, "https://github.com/pierreis/erlang-xxhash", {branch, "master"}}},
{exor_filter, ".*", {git, "https://github.com/mpope9/exor_filter", {branch, "master"}}},
Expand Down
4 changes: 4 additions & 0 deletions rebar.lock
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@
{git,"https://github.com/helium/libp2p-crypto.git",
{ref,"a969bb7affc7d3fa1472aef80d736c467d3f6d45"}},
1},
{<<"lorawan">>,
{git,"https://github.com/helium/erlang-lorawan.git",
{ref,"49925a083347e91e74daee3516a2105e82ef6c07"}},
0},
{<<"merkerl">>,
{git,"https://github.com/helium/merkerl.git",
{ref,"26ddcaf7f3c2c76eebf6f9258822f923ce69cb75"}},
Expand Down
143 changes: 128 additions & 15 deletions src/state_channel/blockchain_state_channels_client.erl
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
-include("blockchain.hrl").
-include("blockchain_rocks.hrl").
-include("blockchain_vars.hrl").
-include_lib("grpc/autogen/server/state_channel_pb.hrl").

-define(SERVER, ?MODULE).
-define(ROUTING_CACHE, sc_client_routing).
Expand All @@ -49,7 +50,8 @@
packets = #{} :: #{pid() => queue:queue(blockchain_helium_packet_v1:packet())},
waiting = #{} :: waiting(),
pending_closes = [] :: list(), %% TODO GC these
sc_client_transport_handler :: atom()
sc_client_transport_handler :: atom(),
routers = [] :: list(netid_to_oui())
}).

-type state() :: #state{}.
Expand All @@ -59,6 +61,27 @@
-type waiting_packet() :: {Packet :: blockchain_helium_packet_v1:packet(), Region :: atom(), ReceivedTime :: non_neg_integer()}.
-type waiting_key() :: non_neg_integer() | string().
-type waiting() :: #{waiting_key() => [waiting_packet()]}.
-type netid_to_oui() :: {pos_integer(), pos_integer()}.

-ifdef(TEST).

-export([
set_routers/2,
get_routers/1,
get_waiting/1,
handle_route_by_netid/6
]).

-spec set_routers(list(string()), blockchain:blockchain()) -> state().
set_routers(Routers, Chain) -> #state{chain=Chain, routers=Routers}.

-spec get_routers(state()) -> list(netid_to_oui()).
get_routers(State) -> State#state.routers.

-spec get_waiting(state()) -> waiting().
get_waiting(State) -> State#state.waiting.

-endif.

%% ------------------------------------------------------------------
%% API Function Definitions
Expand Down Expand Up @@ -154,18 +177,19 @@ handle_cast({banner, Banner, HandlerPid}, #state{sc_client_transport_handler = H
{noreply, maybe_send_packets(AddressOrOUI, HandlerPid, State)}
end
end;
%% Handle Uplink packets
handle_cast({packet,
#packet_pb{routing = #routing_information_pb{data = {devaddr, DevAddr}}} = Packet,
DefaultRouters, Region, ReceivedTime},
State)
when is_integer(DevAddr) andalso DevAddr > 0 ->
State2 =
handle_route_by_netid(Packet, DevAddr, DefaultRouters, Region, ReceivedTime, State),
{noreply, State2};
%% Handle Join packets
handle_cast({packet, Packet, DefaultRouters, Region, ReceivedTime}, #state{chain=Chain}=State) ->
State2 =
case find_routing(Packet, Chain) of
{error, _Reason} ->
lager:notice(
"failed to find router for join packet with routing information ~p:~p, trying default routers",
[blockchain_helium_packet_v1:routing_info(Packet), _Reason]
),
handle_packet(Packet, DefaultRouters, Region, ReceivedTime, State);
{ok, Routes} ->
handle_packet(Packet, Routes, Region, ReceivedTime, State)
end,
handle_packet_routing(Packet, Chain, DefaultRouters, Region, ReceivedTime, State),
{noreply, State2};
handle_cast({reject, Rejection, HandlerPid}, State) ->
lager:warning("Got rejection: ~p for: ~p, dropping packet", [Rejection, HandlerPid]),
Expand Down Expand Up @@ -197,10 +221,13 @@ handle_info(post_init, #state{chain=undefined}=State) ->
erlang:send_after(500, self(), post_init),
{noreply, State};
Chain ->
{noreply, State#state{chain=Chain}}
%% Also scan incoming blocks for updates; see add_block event.
State1 = chain_var_routers_by_netid_to_oui(Chain, State),
{noreply, State1#state{chain=Chain}}
end;
handle_info({blockchain_event, {new_chain, NC}}, State) ->
{noreply, State#state{chain=NC}};
State1 = chain_var_routers_by_netid_to_oui(NC, State),
{noreply, State1#state{chain=NC}};
handle_info({dial_fail, AddressOrOUI, _Reason}, State0) ->
Packets = get_waiting_packet(AddressOrOUI, State0),
lager:error("failed to dial ~p: ~p dropping ~p packets", [AddressOrOUI, _Reason, erlang:length(Packets)+1]),
Expand Down Expand Up @@ -229,7 +256,8 @@ handle_info({dial_success, OUIOrAddress, Stream}, State0) ->
_ ->
{noreply, maybe_send_packets(OUIOrAddress, Stream, State1)}
end;
handle_info({blockchain_event, {add_block, _BlockHash, true, Ledger}}, State) ->
handle_info({blockchain_event, {add_block, _BlockHash, true, Ledger}}, State)
when Ledger =/= undefined ->
SCs = state_channels(State),
{ok, LedgerHeight} = blockchain_ledger_v1:current_height(Ledger),
SCGrace = case blockchain:config(?sc_grace_blocks, Ledger) of
Expand All @@ -247,7 +275,8 @@ handle_info({blockchain_event, {add_block, _BlockHash, true, Ledger}}, State) ->
ok
end
end, SCs),
{noreply, State};
State1 = chain_var_ledger_routers_by_netid_to_oui(Ledger, State),
{noreply, State1};
handle_info({blockchain_event, {add_block, BlockHash, false, Ledger}},
#state{chain=Chain, pubkey_bin=PubkeyBin, sig_fun=SigFun, pending_closes=PendingCloses}=State) when Chain /= undefined ->
Block =
Expand Down Expand Up @@ -1068,6 +1097,90 @@ debug_multiple_scs(SC, KnownSCs) ->
ok
end.

-spec chain_var_routers_by_netid_to_oui(
Chain :: undefined | blockchain:blockchain(),
State :: state()
) -> State1 :: state().
chain_var_routers_by_netid_to_oui(undefined, State) ->
Ledger = blockchain:ledger(),
chain_var_ledger_routers_by_netid_to_oui(Ledger, State);
chain_var_routers_by_netid_to_oui(Chain, State) ->
Ledger = blockchain:ledger(Chain),
chain_var_ledger_routers_by_netid_to_oui(Ledger, State).

-spec chain_var_ledger_routers_by_netid_to_oui(
Ledger :: blockchain:ledger(),
State :: state()
) -> State1 :: state().
chain_var_ledger_routers_by_netid_to_oui(Ledger, State) ->
Routers =
case blockchain_ledger_v1:config(?routers_by_netid_to_oui, Ledger) of
{ok, Bin} -> binary_to_term(Bin);
_ -> []
end,
State#state{routers=Routers}.

-spec handle_route_by_netid(
Packet :: blockchain_helium_packet_v1:packet(),
DevAddr :: number() | binary(),
DefaultRouters :: [blockchain_ledger_routing_v1:routing()],
Region :: atom(),
ReceivedTime :: non_neg_integer(),
State :: state()
) -> State1 :: state().
handle_route_by_netid(Packet, DevAddr, DefaultRouters, Region, ReceivedTime, State) ->
#state{chain=Chain, routers=RoamingRouters} = State,
OurNetID = application:get_env(blockchain, devaddr_prefix, $H),
case lora_subnet:parse_netid(DevAddr) of
{ok, OurNetID} ->
handle_packet_routing(Packet, Chain, DefaultRouters, Region, ReceivedTime, State);
{ok, ExtractedNetID} ->
FoldFn =
fun({NetID, OUI}, Acc) when NetID == ExtractedNetID ->
Ledger = blockchain:ledger(Chain),
case blockchain_ledger_v1:find_routing(OUI, Ledger) of
{ok, Route} -> [Route|Acc];
_ -> Acc
end;
({_OtherNetID, _}, Acc) ->
Acc
end,
RoutesOrAddresses =
case lists:foldl(FoldFn, [], RoamingRouters) of
[] ->
lager:debug("no routes found for netid ~p", [ExtractedNetID]),
DefaultRouters;
Routes ->
lager:debug("found ~p for netid ~p", [[blockchain_ledger_routing_v1:oui(R) || R <- Routes], ExtractedNetID]),
Routes
end,
handle_packet(Packet, RoutesOrAddresses, Region, ReceivedTime, State);
_Error ->
%% Drop undeliverable packet
lager:warning("failed to route ~p with devaddr=~p", [_Error, DevAddr]),
State
end.

-spec handle_packet_routing(
Packet :: blockchain_helium_packet_v1:packet(),
Chain :: blockchain:blockchain(),
DefaultRouters :: [string()] | [blockchain_ledger_routing_v1:routing()],
Region :: atom(),
ReceivedTime :: non_neg_integer(),
State :: state()
) -> state().
handle_packet_routing(Packet, Chain, DefaultRouters, Region, ReceivedTime, State) ->
case find_routing(Packet, Chain) of
{error, _Reason} ->
lager:warning(
"failed to find router for join packet with routing information ~p:~p, trying default routers",
[blockchain_helium_packet_v1:routing_info(Packet), _Reason]
),
handle_packet(Packet, DefaultRouters, Region, ReceivedTime, State);
{ok, Routes} ->
lager:debug("found routes ~p", [Routes]),
handle_packet(Packet, Routes, Region, ReceivedTime, State)
end.

print_routes(RoutesOrAddresses) ->
lists:map(fun(RouteOrAddress) ->
Expand Down
41 changes: 41 additions & 0 deletions src/transactions/v1/blockchain_txn_vars_v1.erl
Original file line number Diff line number Diff line change
Expand Up @@ -1553,6 +1553,14 @@ validate_var(?discard_zero_freq_witness, Value) ->
validate_var(?block_size_limit, Value) ->
validate_int(Value, "block_size_limit", 1*1024*1024, 512*1024*1024, false);

validate_var(?routers_by_netid_to_oui, Bin) when is_binary(Bin) ->
%% Even though Rust will need to process the structure too,
%% gateway-rs fetches this via Validator, which in turn converts
%% to ProtoBuf.
validate_routers_by_netid_to_oui(binary_to_term(Bin));
validate_var(?routers_by_netid_to_oui, _NotBinary) ->
throw({error, {invalid_routers_by_netid_to_oui, expect_binary_list_of_pairs}});

validate_var(?token_version, Value) ->
case Value of
2 -> ok; %% Add support for multiple tokens
Expand Down Expand Up @@ -1685,6 +1693,26 @@ validate_region_params(Var, Value) ->
purge_pocs(Ledger) ->
blockchain_ledger_v1:purge_pocs(Ledger).

validate_routers_by_netid_to_oui(List) when is_list(List) ->
validate_routers_by_netid_to_oui(List, 1);
validate_routers_by_netid_to_oui(BadValue) ->
throw({error, {invalid_routers_by_netid_to_oui, {badvalue, BadValue},
expect_list_of_pairs}}).

validate_routers_by_netid_to_oui([{NetID, OUI} | T], Index)
when is_integer(NetID) andalso is_integer(OUI) andalso
NetID >= 0 andalso NetID =< 16#FFFFFF andalso
OUI >= 0 andalso OUI =< 16#FFFFFFFFFFFFFFFF->
validate_routers_by_netid_to_oui(T, Index + 1);
validate_routers_by_netid_to_oui([], _) ->
true;
validate_routers_by_netid_to_oui([H | _T], Index) ->
throw({error, {invalid_routers_by_netid_to_oui, {{index, Index}, {entry, H}},
expect_pair}});
validate_routers_by_netid_to_oui(BadValue, Index) ->
throw({error, {invalid_routers_by_netid_to_oui, {{index, Index}, {value, BadValue}},
expect_list_of_pairs}}).

%% ------------------------------------------------------------------
%% EUNIT Tests
%% ------------------------------------------------------------------
Expand Down Expand Up @@ -1744,5 +1772,18 @@ to_json_test() ->
[type, hash, vars, version_predicate, proof, master_key, key_proof, cancels, unsets, nonce])),
?assertEqual(<<"f is for ffffff\0">>, base64:decode(maps:get(f, maps:get(vars, Json)))).

routers_test() ->
?assertEqual(true, validate_routers_by_netid_to_oui([{0, 0}])),
?assertEqual(true, validate_routers_by_netid_to_oui([{0, 0}, {1, 1}])),
?assertException(throw, {error, {invalid_routers_by_netid_to_oui, {{index, 2}, _}, _}},
validate_routers_by_netid_to_oui([{0, 0}, {}])),
?assertException(throw, {error, {invalid_routers_by_netid_to_oui, {{index, 1}, _}, _}},
validate_routers_by_netid_to_oui([{non_integer, 0}])),
?assertException(throw, {error, {invalid_routers_by_netid_to_oui, {{index, 1}, _}, _}},
validate_routers_by_netid_to_oui([{0, non_integer}])),
?assertException(throw, {error, {invalid_routers_by_netid_to_oui, _, _}},
validate_routers_by_netid_to_oui({{0, 0}, {1, 1}})),
?assertException(throw, {error, {invalid_routers_by_netid_to_oui, {{index, 2}, _}, _}},
validate_routers_by_netid_to_oui([{0, 0}, {1, 2, 3}])).

-endif.
5 changes: 4 additions & 1 deletion test/blockchain_ct_utils.erl
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,10 @@ raw_vars(Vars) ->
?max_subnet_size => 65536,
?min_subnet_size => 8,
?max_subnet_num => 20,
?dc_payload_size => 24
?dc_payload_size => 24,
?routers_by_netid_to_oui =>
term_to_binary([{16#000009, 1}, {16#600025, 1},
{16#000037, 1}, {16#60003A, 1}])
},

maps:merge(DefVars, Vars).
Expand Down
Loading