Current section
Files
Jump to
Current section
Files
lib/libp2p/pubsub/message_pb.ex
defmodule Libp2p.Pubsub.MessagePB do
@moduledoc """
Protobuf encoding/decoding for pubsub `Message`.
Schema (from `third_party/libp2p_specs/pubsub/README.md`):
```protobuf
syntax = "proto2";
message Message {
optional string from = 1;
optional bytes data = 2;
optional bytes seqno = 3;
required string topic = 4;
optional bytes signature = 5;
optional bytes key = 6;
}
```
This module supports `StrictNoSign` validation helpers.
"""
alias Libp2p.Protobuf
@type t :: %{
topic: binary(),
data: binary() | nil,
from: binary() | nil,
seqno: binary() | nil,
signature: binary() | nil,
key: binary() | nil
}
@spec encode(t()) :: binary()
def encode(%{topic: topic} = msg) when is_binary(topic) do
# tag order: 1..6
out = []
out =
if Map.get(msg, :from) != nil, do: [Protobuf.encode_len_field(1, msg.from) | out], else: out
out =
if Map.get(msg, :data) != nil, do: [Protobuf.encode_len_field(2, msg.data) | out], else: out
out =
if Map.get(msg, :seqno) != nil,
do: [Protobuf.encode_len_field(3, msg.seqno) | out],
else: out
out = [Protobuf.encode_len_field(4, topic) | out]
out =
if Map.get(msg, :signature) != nil,
do: [Protobuf.encode_len_field(5, msg.signature) | out],
else: out
out =
if Map.get(msg, :key) != nil, do: [Protobuf.encode_len_field(6, msg.key) | out], else: out
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)
topic =
case Enum.find(fields, fn {n, _w, _v} -> n == 4 end) do
{4, 2, v} when is_binary(v) -> v
_ -> raise ArgumentError, "missing Message.topic"
end
%{
from: get_opt(fields, 1),
data: get_opt(fields, 2),
seqno: get_opt(fields, 3),
topic: topic,
signature: get_opt(fields, 5),
key: get_opt(fields, 6)
}
end
@doc """
Validate `StrictNoSign` policy: `from`, `seqno`, `signature`, `key` MUST be absent.
"""
@spec validate_strict_no_sign!(t()) :: :ok
def validate_strict_no_sign!(msg) when is_map(msg) do
cond do
Map.get(msg, :from) != nil ->
raise ArgumentError, "StrictNoSign violation: from present"
Map.get(msg, :seqno) != nil ->
raise ArgumentError, "StrictNoSign violation: seqno present"
Map.get(msg, :signature) != nil ->
raise ArgumentError, "StrictNoSign violation: signature present"
Map.get(msg, :key) != nil ->
raise ArgumentError, "StrictNoSign violation: key present"
true ->
:ok
end
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
end