Packages

A standalone Elixir implementation of the Libp2p networking stack

Current section

Files

Jump to
libp2p_elixir lib libp2p identify_pb.ex
Raw

lib/libp2p/identify_pb.ex

defmodule Libp2p.IdentifyPB do
@moduledoc """
Protobuf encoding/decoding for the Identify message.
This targets `/ipfs/id/1.0.0` and implements the classic fields:
(field numbers per `identify/src/generated/structs.proto` in rust-libp2p)
1 publicKey (bytes) -- libp2p-crypto PublicKey protobuf
2 listenAddrs (repeated bytes) -- multiaddr bytes
3 protocols (repeated string)
4 observedAddr (bytes) -- multiaddr bytes
5 protocolVersion (string)
6 agentVersion (string)
8 signedPeerRecord (bytes) -- optional (ignored for now)
"""
alias Libp2p.Protobuf
@type t :: %{
protocol_version: binary() | nil,
agent_version: binary() | nil,
public_key: binary() | nil,
listen_addrs: [binary()],
observed_addr: binary() | nil,
protocols: [binary()],
signed_peer_record: binary() | nil
}
@spec encode(t()) :: binary()
def encode(msg) when is_map(msg) do
out = []
out =
if Map.get(msg, :public_key) != nil do
[Protobuf.encode_len_field(1, msg.public_key) | out]
else
out
end
out =
(Map.get(msg, :listen_addrs, []) || [])
|> Enum.reduce(out, fn a, acc -> [Protobuf.encode_len_field(2, a) | acc] end)
out =
(Map.get(msg, :protocols, []) || [])
|> Enum.reduce(out, fn p, acc -> [Protobuf.encode_len_field(3, p) | acc] end)
out =
if Map.get(msg, :observed_addr) != nil do
[Protobuf.encode_len_field(4, msg.observed_addr) | out]
else
out
end
out =
if Map.get(msg, :protocol_version) != nil do
[Protobuf.encode_len_field(5, msg.protocol_version) | out]
else
out
end
out =
if Map.get(msg, :agent_version) != nil do
[Protobuf.encode_len_field(6, msg.agent_version) | out]
else
out
end
out =
if Map.get(msg, :signed_peer_record) != nil do
[Protobuf.encode_len_field(8, msg.signed_peer_record) | out]
else
out
end
out |> Enum.reverse() |> IO.iodata_to_binary()
end
@spec decode(binary()) :: t()
def decode(bin) when is_binary(bin) do
fields = Protobuf.decode_fields(bin)
%{
public_key: get_opt(fields, 1),
listen_addrs: get_rep(fields, 2),
protocols: get_rep(fields, 3),
observed_addr: get_opt(fields, 4),
protocol_version: get_opt(fields, 5),
agent_version: get_opt(fields, 6),
signed_peer_record: get_opt(fields, 8)
}
end
defp get_opt(fields, n) do
case Enum.find(fields, fn {n2, w, _v} -> n2 == n and w == 2 end) do
{^n, 2, v} when is_binary(v) -> v
_ -> nil
end
end
defp get_rep(fields, n) do
fields
|> Enum.filter(fn {n2, w, _v} -> n2 == n and w == 2 end)
|> Enum.map(fn {_n, _w, v} -> v end)
end
end