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

Commit

Permalink
Merge pull request #1412 from helium/dpezely/routers-by-netid-to-oui-01
Browse files Browse the repository at this point in the history
HIP-46: facilitate routing by netid-to-oui
  • Loading branch information
jadeallenx authored Jun 30, 2022
2 parents db20b6d + bcd0bae commit 764efb4
Show file tree
Hide file tree
Showing 8 changed files with 339 additions and 25 deletions.
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

0 comments on commit 764efb4

Please sign in to comment.