mirror of
https://github.com/varun-r-mallya/py-libp2p.git
synced 2026-02-12 16:10:57 +00:00
Implemented: Envelope wrapper class + linter hacks for protobuf checks
This commit is contained in:
@ -47,6 +47,7 @@ from libp2p.peer.peerinfo import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from libp2p.peer.peer_record import PeerRecord
|
||||||
from libp2p.pubsub.pubsub import (
|
from libp2p.pubsub.pubsub import (
|
||||||
Pubsub,
|
Pubsub,
|
||||||
)
|
)
|
||||||
@ -1752,6 +1753,59 @@ class IPeerRecord(ABC):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# -------------------------- envelope interface.py --------------------------
|
||||||
|
class IEnvelope(ABC):
|
||||||
|
@abstractmethod
|
||||||
|
def marshal_envelope(self) -> bytes:
|
||||||
|
"""
|
||||||
|
Serialize this Envelope into its protobuf wire format.
|
||||||
|
|
||||||
|
Converts all envelope fields into a `pb.Envelope` protobuf message
|
||||||
|
and returns the serialized bytes.
|
||||||
|
|
||||||
|
:return: Serialized envelope as bytes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def validate(self, domain: str) -> None:
|
||||||
|
"""
|
||||||
|
Verify the envelope's signature within the given domain scope.
|
||||||
|
|
||||||
|
This ensures that the envelope has not been tampered with
|
||||||
|
and was signed under the correct usage context.
|
||||||
|
|
||||||
|
:param domain: Domain string that contextualizes the signature.
|
||||||
|
:raises ValueError: If the signature is invalid.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def record(self) -> "PeerRecord":
|
||||||
|
"""
|
||||||
|
Lazily decode and return the embedded PeerRecord.
|
||||||
|
|
||||||
|
This method unmarshals the payload bytes into a `PeerRecord` instance,
|
||||||
|
using the registered codec to identify the type. The decoded result
|
||||||
|
is cached for future use.
|
||||||
|
|
||||||
|
:return: Decoded PeerRecord object.
|
||||||
|
:raises Exception: If decoding fails or payload type is unsupported.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def equal(self, other: Any) -> bool:
|
||||||
|
"""
|
||||||
|
Compare this Envelope with another for structural equality.
|
||||||
|
|
||||||
|
Two envelopes are considered equal if:
|
||||||
|
- They have the same public key
|
||||||
|
- The payload type and payload bytes match
|
||||||
|
- Their signatures are identical
|
||||||
|
|
||||||
|
:param other: Another object to compare.
|
||||||
|
:return: True if equal, False otherwise.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
# -------------------------- peerdata interface.py --------------------------
|
# -------------------------- peerdata interface.py --------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
252
libp2p/peer/envelope.py
Normal file
252
libp2p/peer/envelope.py
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from libp2p.crypto.ed25519 import Ed25519PublicKey
|
||||||
|
from libp2p.crypto.keys import PrivateKey, PublicKey
|
||||||
|
from libp2p.crypto.rsa import RSAPublicKey
|
||||||
|
from libp2p.crypto.secp256k1 import Secp256k1PublicKey
|
||||||
|
import libp2p.peer.pb.crypto_pb2 as cryto_pb
|
||||||
|
import libp2p.peer.pb.envelope_pb2 as pb
|
||||||
|
import libp2p.peer.pb.peer_record_pb2 as record_pb
|
||||||
|
from libp2p.peer.peer_record import PeerRecord, peer_record_from_protobuf
|
||||||
|
from libp2p.utils.varint import encode_uvarint
|
||||||
|
|
||||||
|
ENVELOPE_DOMAIN = "libp2p-peer-record"
|
||||||
|
PEER_RECORD_CODEC = b"\x03\x01"
|
||||||
|
|
||||||
|
|
||||||
|
class Envelope:
|
||||||
|
"""
|
||||||
|
A signed wrapper around a serialized libp2p record.
|
||||||
|
|
||||||
|
Envelopes are cryptographically signed by the author's private key
|
||||||
|
and are scoped to a specific 'domain' to prevent cross-protocol replay.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
public_key: The public key that can verify the envelope's signature.
|
||||||
|
payload_type: A multicodec code identifying the type of payload inside.
|
||||||
|
raw_payload: The raw serialized record data.
|
||||||
|
signature: Signature over the domain-scoped payload content.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
public_key: PublicKey
|
||||||
|
payload_type: bytes
|
||||||
|
raw_payload: bytes
|
||||||
|
signature: bytes
|
||||||
|
|
||||||
|
_cached_record: PeerRecord | None = None
|
||||||
|
_unmarshal_error: Exception | None = None
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
public_key: PublicKey,
|
||||||
|
payload_type: bytes,
|
||||||
|
raw_payload: bytes,
|
||||||
|
signature: bytes,
|
||||||
|
):
|
||||||
|
self.public_key = public_key
|
||||||
|
self.payload_type = payload_type
|
||||||
|
self.raw_payload = raw_payload
|
||||||
|
self.signature = signature
|
||||||
|
|
||||||
|
def marshal_envelope(self) -> bytes:
|
||||||
|
"""
|
||||||
|
Serialize this Envelope into its protobuf wire format.
|
||||||
|
|
||||||
|
Converts all envelope fields into a `pb.Envelope` protobuf message
|
||||||
|
and returns the serialized bytes.
|
||||||
|
|
||||||
|
:return: Serialized envelope as bytes.
|
||||||
|
"""
|
||||||
|
pb_env = pb.Envelope( # type: ignore[attr-defined]
|
||||||
|
public_key=pub_key_to_protobuf(self.public_key),
|
||||||
|
payload_type=self.payload_type,
|
||||||
|
payload=self.raw_payload,
|
||||||
|
signature=self.signature,
|
||||||
|
)
|
||||||
|
return pb_env.SerializeToString()
|
||||||
|
|
||||||
|
def validate(self, domain: str) -> None:
|
||||||
|
"""
|
||||||
|
Verify the envelope's signature within the given domain scope.
|
||||||
|
|
||||||
|
This ensures that the envelope has not been tampered with
|
||||||
|
and was signed under the correct usage context.
|
||||||
|
|
||||||
|
:param domain: Domain string that contextualizes the signature.
|
||||||
|
:raises ValueError: If the signature is invalid.
|
||||||
|
"""
|
||||||
|
unsigned = make_unsigned(domain, self.payload_type, self.raw_payload)
|
||||||
|
if not self.public_key.verify(unsigned, self.signature):
|
||||||
|
raise ValueError("Invalid envelope signature")
|
||||||
|
|
||||||
|
def record(self) -> PeerRecord:
|
||||||
|
"""
|
||||||
|
Lazily decode and return the embedded PeerRecord.
|
||||||
|
|
||||||
|
This method unmarshals the payload bytes into a `PeerRecord` instance,
|
||||||
|
using the registered codec to identify the type. The decoded result
|
||||||
|
is cached for future use.
|
||||||
|
|
||||||
|
:return: Decoded PeerRecord object.
|
||||||
|
:raises Exception: If decoding fails or payload type is unsupported.
|
||||||
|
"""
|
||||||
|
if self._cached_record is not None:
|
||||||
|
return self._cached_record
|
||||||
|
|
||||||
|
try:
|
||||||
|
if self.payload_type != PEER_RECORD_CODEC:
|
||||||
|
raise ValueError("Unsuported payload type in envelope")
|
||||||
|
msg = record_pb.PeerRecord() # type: ignore[attr-defined]
|
||||||
|
msg.ParseFromString(self.raw_payload)
|
||||||
|
|
||||||
|
self._cached_record = peer_record_from_protobuf(msg)
|
||||||
|
return self._cached_record
|
||||||
|
except Exception as e:
|
||||||
|
self._unmarshal_error = e
|
||||||
|
raise
|
||||||
|
|
||||||
|
def equal(self, other: Any) -> bool:
|
||||||
|
"""
|
||||||
|
Compare this Envelope with another for structural equality.
|
||||||
|
|
||||||
|
Two envelopes are considered equal if:
|
||||||
|
- They have the same public key
|
||||||
|
- The payload type and payload bytes match
|
||||||
|
- Their signatures are identical
|
||||||
|
|
||||||
|
:param other: Another object to compare.
|
||||||
|
:return: True if equal, False otherwise.
|
||||||
|
"""
|
||||||
|
if isinstance(other, Envelope):
|
||||||
|
return (
|
||||||
|
self.public_key.__eq__(other.public_key)
|
||||||
|
and self.payload_type == other.payload_type
|
||||||
|
and self.signature == other.signature
|
||||||
|
and self.raw_payload == other.raw_payload
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def pub_key_to_protobuf(pub_key: PublicKey) -> cryto_pb.PublicKey: # type: ignore[name-defined]
|
||||||
|
"""
|
||||||
|
Convert a Python PublicKey object to its protobuf equivalent.
|
||||||
|
|
||||||
|
:param pub_key: A libp2p-compatible PublicKey instance.
|
||||||
|
:return: Serialized protobuf PublicKey message.
|
||||||
|
"""
|
||||||
|
key_type = pub_key.get_type().value
|
||||||
|
data = pub_key.to_bytes()
|
||||||
|
protobuf_key = cryto_pb.PublicKey(Type=key_type, Data=data) # type: ignore[attr-defined]
|
||||||
|
return protobuf_key
|
||||||
|
|
||||||
|
|
||||||
|
def pub_key_from_protobuf(pb_key: cryto_pb.PublicKey) -> PublicKey: # type: ignore[name-defined]
|
||||||
|
"""
|
||||||
|
Parse a protobuf PublicKey message into a native libp2p PublicKey.
|
||||||
|
|
||||||
|
Supports Ed25519, RSA, and Secp256k1 key types.
|
||||||
|
|
||||||
|
:param pb_key: Protobuf representation of a public key.
|
||||||
|
:return: Parsed PublicKey object.
|
||||||
|
:raises ValueError: If the key type is unrecognized.
|
||||||
|
"""
|
||||||
|
if pb_key.Type == cryto_pb.KeyType.Ed25519: # type: ignore[attr-defined]
|
||||||
|
return Ed25519PublicKey.from_bytes(pb_key.Data)
|
||||||
|
elif pb_key.Type == cryto_pb.KeyType.RSA: # type: ignore[attr-defined]
|
||||||
|
return RSAPublicKey.from_bytes(pb_key.Data)
|
||||||
|
elif pb_key.Type == cryto_pb.KeyType.Secp256k1: # type: ignore[attr-defined]
|
||||||
|
return Secp256k1PublicKey.from_bytes(pb_key.Data)
|
||||||
|
# TODO: Add suport fot ECDSA parsing also
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Unknown key type: {pb_key.Type}")
|
||||||
|
|
||||||
|
|
||||||
|
def seal_record(record: PeerRecord, private_key: PrivateKey) -> Envelope:
|
||||||
|
"""
|
||||||
|
Create and sign a new Envelope from a PeerRecord.
|
||||||
|
|
||||||
|
The record is serialized and signed in the scope of its domain and codec.
|
||||||
|
The result is a self-contained, verifiable Envelope.
|
||||||
|
|
||||||
|
:param record: A PeerRecord to encapsulate.
|
||||||
|
:param private_key: The signer's private key.
|
||||||
|
:return: A signed Envelope instance.
|
||||||
|
"""
|
||||||
|
payload = record.marshal_record()
|
||||||
|
|
||||||
|
unsigned = make_unsigned(record.domain(), record.codec(), payload)
|
||||||
|
signature = private_key.sign(unsigned)
|
||||||
|
|
||||||
|
return Envelope(
|
||||||
|
public_key=private_key.get_public_key(),
|
||||||
|
payload_type=record.codec(),
|
||||||
|
raw_payload=payload,
|
||||||
|
signature=signature,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def consume_envelope(data: bytes, domain: str) -> tuple[Envelope, PeerRecord]:
|
||||||
|
"""
|
||||||
|
Parse, validate, and decode an Envelope from bytes.
|
||||||
|
|
||||||
|
Validates the envelope's signature using the given domain and decodes
|
||||||
|
the inner payload into a PeerRecord.
|
||||||
|
|
||||||
|
:param data: Serialized envelope bytes.
|
||||||
|
:param domain: Domain string to verify signature against.
|
||||||
|
:return: Tuple of (Envelope, PeerRecord).
|
||||||
|
:raises ValueError: If signature validation or decoding fails.
|
||||||
|
"""
|
||||||
|
env = unmarshal_envelope(data)
|
||||||
|
env.validate(domain)
|
||||||
|
record = env.record()
|
||||||
|
return env, record
|
||||||
|
|
||||||
|
|
||||||
|
def unmarshal_envelope(data: bytes) -> Envelope:
|
||||||
|
"""
|
||||||
|
Deserialize an Envelope from its wire format.
|
||||||
|
|
||||||
|
This parses the protobuf fields without verifying the signature.
|
||||||
|
|
||||||
|
:param data: Serialized envelope bytes.
|
||||||
|
:return: Parsed Envelope object.
|
||||||
|
:raises DecodeError: If protobuf parsing fails.
|
||||||
|
"""
|
||||||
|
pb_env = pb.Envelope() # type: ignore[attr-defined]
|
||||||
|
pb_env.ParseFromString(data)
|
||||||
|
pk = pub_key_from_protobuf(pb_env.public_key)
|
||||||
|
|
||||||
|
return Envelope(
|
||||||
|
public_key=pk,
|
||||||
|
payload_type=pb_env.payload_type,
|
||||||
|
raw_payload=pb_env.payload,
|
||||||
|
signature=pb_env.signature,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def make_unsigned(domain: str, payload_type: bytes, payload: bytes) -> bytes:
|
||||||
|
"""
|
||||||
|
Build a byte buffer to be signed for an Envelope.
|
||||||
|
|
||||||
|
The unsigned byte structure is:
|
||||||
|
varint(len(domain)) || domain ||
|
||||||
|
varint(len(payload_type)) || payload_type ||
|
||||||
|
varint(len(payload)) || payload
|
||||||
|
|
||||||
|
This is the exact input used during signing and verification.
|
||||||
|
|
||||||
|
:param domain: Domain string for signature scoping.
|
||||||
|
:param payload_type: Identifier for the type of payload.
|
||||||
|
:param payload: Raw serialized payload bytes.
|
||||||
|
:return: Byte buffer to be signed or verified.
|
||||||
|
"""
|
||||||
|
fields = [domain.encode(), payload_type, payload]
|
||||||
|
buf = bytearray()
|
||||||
|
|
||||||
|
for field in fields:
|
||||||
|
buf.extend(encode_uvarint(len(field)))
|
||||||
|
buf.extend(field)
|
||||||
|
|
||||||
|
return bytes(buf)
|
||||||
22
libp2p/peer/pb/crypto.proto
Normal file
22
libp2p/peer/pb/crypto.proto
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package libp2p.peer.pb.crypto;
|
||||||
|
|
||||||
|
option go_package = "github.com/libp2p/go-libp2p/core/crypto/pb";
|
||||||
|
|
||||||
|
enum KeyType {
|
||||||
|
RSA = 0;
|
||||||
|
Ed25519 = 1;
|
||||||
|
Secp256k1 = 2;
|
||||||
|
ECDSA = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PublicKey {
|
||||||
|
KeyType Type = 1;
|
||||||
|
bytes Data = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PrivateKey {
|
||||||
|
KeyType Type = 1;
|
||||||
|
bytes Data = 2;
|
||||||
|
}
|
||||||
30
libp2p/peer/pb/crypto_pb2.py
Normal file
30
libp2p/peer/pb/crypto_pb2.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||||
|
# source: libp2p/peer/pb/crypto.proto
|
||||||
|
# Protobuf Python Version: 4.25.3
|
||||||
|
"""Generated protocol buffer code."""
|
||||||
|
from google.protobuf import descriptor as _descriptor
|
||||||
|
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||||
|
from google.protobuf import symbol_database as _symbol_database
|
||||||
|
from google.protobuf.internal import builder as _builder
|
||||||
|
# @@protoc_insertion_point(imports)
|
||||||
|
|
||||||
|
_sym_db = _symbol_database.Default()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1blibp2p/peer/pb/crypto.proto\x12\x15libp2p.peer.pb.crypto\"G\n\tPublicKey\x12,\n\x04Type\x18\x01 \x01(\x0e\x32\x1e.libp2p.peer.pb.crypto.KeyType\x12\x0c\n\x04\x44\x61ta\x18\x02 \x01(\x0c\"H\n\nPrivateKey\x12,\n\x04Type\x18\x01 \x01(\x0e\x32\x1e.libp2p.peer.pb.crypto.KeyType\x12\x0c\n\x04\x44\x61ta\x18\x02 \x01(\x0c*9\n\x07KeyType\x12\x07\n\x03RSA\x10\x00\x12\x0b\n\x07\x45\x64\x32\x35\x35\x31\x39\x10\x01\x12\r\n\tSecp256k1\x10\x02\x12\t\n\x05\x45\x43\x44SA\x10\x03\x42,Z*github.com/libp2p/go-libp2p/core/crypto/pbb\x06proto3') # type: ignore[no-untyped-call]
|
||||||
|
_globals = globals()
|
||||||
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||||
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'libp2p.peer.pb.crypto_pb2', _globals)
|
||||||
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
|
_globals['DESCRIPTOR']._options = None
|
||||||
|
_globals['DESCRIPTOR']._serialized_options = b'Z*github.com/libp2p/go-libp2p/core/crypto/pb'
|
||||||
|
_globals['_KEYTYPE']._serialized_start=201
|
||||||
|
_globals['_KEYTYPE']._serialized_end=258
|
||||||
|
_globals['_PUBLICKEY']._serialized_start=54
|
||||||
|
_globals['_PUBLICKEY']._serialized_end=125
|
||||||
|
_globals['_PRIVATEKEY']._serialized_start=127
|
||||||
|
_globals['_PRIVATEKEY']._serialized_end=199
|
||||||
|
# @@protoc_insertion_point(module_scope)
|
||||||
14
libp2p/peer/pb/envelope.proto
Normal file
14
libp2p/peer/pb/envelope.proto
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package libp2p.peer.pb.record;
|
||||||
|
|
||||||
|
import "libp2p/peer/pb/crypto.proto";
|
||||||
|
|
||||||
|
option go_package = "github.com/libp2p/go-libp2p/core/record/pb";
|
||||||
|
|
||||||
|
message Envelope {
|
||||||
|
libp2p.peer.pb.crypto.PublicKey public_key = 1;
|
||||||
|
bytes payload_type = 2;
|
||||||
|
bytes payload = 3;
|
||||||
|
bytes signature = 5;
|
||||||
|
}
|
||||||
27
libp2p/peer/pb/envelope_pb2.py
Normal file
27
libp2p/peer/pb/envelope_pb2.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||||
|
# source: libp2p/peer/pb/envelope.proto
|
||||||
|
# Protobuf Python Version: 4.25.3
|
||||||
|
"""Generated protocol buffer code."""
|
||||||
|
from google.protobuf import descriptor as _descriptor
|
||||||
|
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||||
|
from google.protobuf import symbol_database as _symbol_database
|
||||||
|
from google.protobuf.internal import builder as _builder
|
||||||
|
# @@protoc_insertion_point(imports)
|
||||||
|
|
||||||
|
_sym_db = _symbol_database.Default()
|
||||||
|
|
||||||
|
|
||||||
|
from libp2p.peer.pb import crypto_pb2 as libp2p_dot_peer_dot_pb_dot_crypto__pb2
|
||||||
|
|
||||||
|
|
||||||
|
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1dlibp2p/peer/pb/envelope.proto\x12\x15libp2p.peer.pb.record\x1a\x1blibp2p/peer/pb/crypto.proto\"z\n\x08\x45nvelope\x12\x34\n\npublic_key\x18\x01 \x01(\x0b\x32 .libp2p.peer.pb.crypto.PublicKey\x12\x14\n\x0cpayload_type\x18\x02 \x01(\x0c\x12\x0f\n\x07payload\x18\x03 \x01(\x0c\x12\x11\n\tsignature\x18\x05 \x01(\x0c\x42,Z*github.com/libp2p/go-libp2p/core/record/pbb\x06proto3') # type: ignore[no-untyped-call]
|
||||||
|
_globals = globals()
|
||||||
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||||
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'libp2p.peer.pb.envelope_pb2', _globals)
|
||||||
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
|
_globals['DESCRIPTOR']._options = None
|
||||||
|
_globals['DESCRIPTOR']._serialized_options = b'Z*github.com/libp2p/go-libp2p/core/record/pb'
|
||||||
|
_globals['_ENVELOPE']._serialized_start=85
|
||||||
|
_globals['_ENVELOPE']._serialized_end=207
|
||||||
|
# @@protoc_insertion_point(module_scope)
|
||||||
85
tests/core/peer/test_envelope.py
Normal file
85
tests/core/peer/test_envelope.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
from multiaddr import Multiaddr
|
||||||
|
|
||||||
|
from libp2p.crypto.rsa import (
|
||||||
|
create_new_key_pair,
|
||||||
|
)
|
||||||
|
from libp2p.peer.envelope import (
|
||||||
|
Envelope,
|
||||||
|
consume_envelope,
|
||||||
|
make_unsigned,
|
||||||
|
seal_record,
|
||||||
|
unmarshal_envelope,
|
||||||
|
)
|
||||||
|
from libp2p.peer.id import ID
|
||||||
|
import libp2p.peer.pb.crypto_pb2 as crypto_pb
|
||||||
|
import libp2p.peer.pb.envelope_pb2 as env_pb
|
||||||
|
from libp2p.peer.peer_record import PeerRecord
|
||||||
|
|
||||||
|
DOMAIN = "libp2p-peer-record"
|
||||||
|
|
||||||
|
|
||||||
|
def test_basic_protobuf_serialization_deserialization():
|
||||||
|
pubkey = crypto_pb.PublicKey() # type: ignore[attr-defined]
|
||||||
|
pubkey.Type = crypto_pb.KeyType.Ed25519 # type: ignore[attr-defined]
|
||||||
|
pubkey.Data = b"\x01\x02\x03"
|
||||||
|
|
||||||
|
env = env_pb.Envelope() # type: ignore[attr-defined]
|
||||||
|
env.public_key.CopyFrom(pubkey)
|
||||||
|
env.payload_type = b"\x03\x01"
|
||||||
|
env.payload = b"test-payload"
|
||||||
|
env.signature = b"signature-bytes"
|
||||||
|
|
||||||
|
serialized = env.SerializeToString()
|
||||||
|
|
||||||
|
new_env = env_pb.Envelope() # type: ignore[attr-defined]
|
||||||
|
new_env.ParseFromString(serialized)
|
||||||
|
|
||||||
|
assert new_env.public_key.Type == crypto_pb.KeyType.Ed25519 # type: ignore[attr-defined]
|
||||||
|
assert new_env.public_key.Data == b"\x01\x02\x03"
|
||||||
|
assert new_env.payload_type == b"\x03\x01"
|
||||||
|
assert new_env.payload == b"test-payload"
|
||||||
|
assert new_env.signature == b"signature-bytes"
|
||||||
|
|
||||||
|
|
||||||
|
def test_enevelope_marshal_unmarshal_roundtrip():
|
||||||
|
keypair = create_new_key_pair()
|
||||||
|
pubkey = keypair.public_key
|
||||||
|
private_key = keypair.private_key
|
||||||
|
|
||||||
|
payload_type = b"\x03\x01"
|
||||||
|
payload = b"test-record"
|
||||||
|
sig = private_key.sign(make_unsigned(DOMAIN, payload_type, payload))
|
||||||
|
|
||||||
|
env = Envelope(pubkey, payload_type, payload, sig)
|
||||||
|
serialized = env.marshal_envelope()
|
||||||
|
new_env = unmarshal_envelope(serialized)
|
||||||
|
|
||||||
|
assert new_env.public_key == pubkey
|
||||||
|
assert new_env.payload_type == payload_type
|
||||||
|
assert new_env.raw_payload == payload
|
||||||
|
assert new_env.signature == sig
|
||||||
|
|
||||||
|
|
||||||
|
def test_seal_and_consume_envelope_roundtrip():
|
||||||
|
keypair = create_new_key_pair()
|
||||||
|
priv_key = keypair.private_key
|
||||||
|
pub_key = keypair.public_key
|
||||||
|
|
||||||
|
peer_id = ID.from_pubkey(pub_key)
|
||||||
|
addrs = [Multiaddr("/ip4/127.0.0.1/tcp/4001"), Multiaddr("/ip4/127.0.0.1/tcp/4002")]
|
||||||
|
seq = 12345
|
||||||
|
|
||||||
|
record = PeerRecord(peer_id=peer_id, addrs=addrs, seq=seq)
|
||||||
|
|
||||||
|
# Seal
|
||||||
|
envelope = seal_record(record, priv_key)
|
||||||
|
serialized = envelope.marshal_envelope()
|
||||||
|
|
||||||
|
# Consume
|
||||||
|
env, rec = consume_envelope(serialized, record.domain())
|
||||||
|
|
||||||
|
# Assertions
|
||||||
|
assert env.public_key == pub_key
|
||||||
|
assert rec.peer_id == peer_id
|
||||||
|
assert rec.seq == seq
|
||||||
|
assert rec.addrs == addrs
|
||||||
Reference in New Issue
Block a user