-
Notifications
You must be signed in to change notification settings - Fork 183
Add signed peer record transfer support in circuit-relay V2 protocol. #848
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
@Antrikshgwal protoc --proto_path=. --python_out=. --pyi_out=. `libp2p/relay/circuit_v2/pb/circuit.proto` Now further, you should set up the following utilities in the curcuit_v2 module:
For reference, look at After that you should look for code-snippets where a response_bytes = await stream.read()
if not response_bytes:
logger.error("No response received from relay %s", peer_id)
return False
# Parse response
response = HopMessage()
response.ParseFromString(response_bytes)
# Check if reservation was successful
if response.type == HopMessage.RESERVE and response.HasField(
"status"
): and add the # Consume the source signed_peer_record if sent
if not maybe_consume_signed_record(message, self.host):
logger.error(
"Received an invalid-signed-record, dropping the stream"
)
await stream.close()
return And in the same way, when we are braodcasting messages, you need to send a singned-record via the Now an example of how I did it in dht: # Add sender's signed-peer-record
envelope_bytes, bool = env_to_send_in_RPC(self.host)
response.senderRecord = envelope_bytes You have to add these while we are sending you requests and responses both. CCing @seetadev |
@Antrikshgwal and @lla-dane : This is excellent work — really glad to see steady progress on bringing Signed Peer Record (SPR) support into Circuit Relay V2. Completely agree with the design: without SPR propagation, relayed peers don’t have a verifiable identity layer, and that leaves gaps in the trust model. Extending the protocol to carry signed_peer_record in HOP/STOP flows is exactly the right direction, and it aligns well with the precedent we already have in KAD-DHT and Gossipsub. That consistency across protocols will make adoption and reasoning about peer identity much smoother. Appreciate the concrete steps outlined in the WIP PR: Updating the protobuf definitions with optional signed_peer_record. Implementing both sending and verifying SPRs in HOP/STOP handlers. Enforcing that relayed connections only succeed when records are valid. This design ensures that relay connections carry the same authenticity guarantees as direct connections, which is a big step forward for overall security. @lla-dane’s feedback on implementation steps is indeed very important — especially around integrating maybe_consume_signed_record, env_to_send_in_RPC, and issue_and_cache_local_record. These utilities have already proven reliable in DHT, so reusing the same patterns here is a smart move. Adding these hooks in the message receive/send paths for HOP and STOP will give us robust, testable behavior. Looking forward to the rebased PR with the protobuf updates, SPR utilities wired in, and commit history tidied up. Great momentum here — keep it up! |
@lla-dane and @Antrikshgwal : Ran the CI/CD pipeline. Please take your time in resolving the issues. |
Hey @seetadev! I have a doubt, according to the proto file I pushed, we are sending records in the hop messages and receiving records in the stop messages. Then, according to the diagram, Peer B can't receive records of Peer A through the relay circuit. I am not sure if it is the intended behaviour. |
libp2p/relay/circuit_v2/utils.py
Outdated
def issue_and_cache_local_record(host: IHost) -> bytes: | ||
env = create_signed_peer_record( | ||
host.get_id(), | ||
host.get_addrs(), | ||
host.get_private_key(), | ||
) | ||
# Cache it for next time use | ||
host.get_peerstore().set_local_record(env) | ||
return env.marshal_envelope() | ||
|
||
|
||
def env_to_send_in_RPC(host: IHost) -> tuple[bytes, bool]: | ||
listen_addrs_set = {addr for addr in host.get_addrs()} | ||
local_env = host.get_peerstore().get_local_record() | ||
|
||
if local_env is None: | ||
# No cached SPR yet -> create one | ||
return issue_and_cache_local_record(host), True | ||
else: | ||
record_addrs_set = local_env._env_addrs_set() | ||
if record_addrs_set == listen_addrs_set: | ||
# Perfect match -> reuse cached envelope | ||
return local_env.marshal_envelope(), False | ||
else: | ||
# Addresses changed -> issue a new SPR and cache it | ||
return issue_and_cache_local_record(host), True No newline at end of file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
libp2p/relay/circuit_v2/utils.py
Outdated
def maybe_consume_signed_record(msg: HopMessage | StopMessage, host: IHost) -> bool: | ||
if isinstance(msg, StopMessage): | ||
if msg.HasField("senderRecord"): | ||
try: | ||
# Convert the signed-peer-record(Envelope) from | ||
# protobuf bytes | ||
envelope, _ = consume_envelope( | ||
msg.senderRecord, | ||
"libp2p-peer-record", | ||
) | ||
# Use the default TTL of 2 hours (7200 seconds) | ||
if not host.get_peerstore().consume_peer_record(envelope, 7200): | ||
logger.error("Updating the certified-addr-book was unsuccessful") | ||
except Exception as e: | ||
logger.error("Error updating the certified addr book for peer: %s", e) | ||
return False | ||
elif isinstance(msg,HopMessage): | ||
if msg.HasField("signedRecord"): | ||
try: | ||
# Convert the signed-peer-record(Envelope) from | ||
# protobuf bytes | ||
envelope, _ = consume_envelope( | ||
msg.signedRecord, | ||
"libp2p-peer-record", | ||
) | ||
# Use the default TTL of 2 hours (7200 seconds) | ||
if not host.get_peerstore().consume_peer_record(envelope, 7200): | ||
logger.error("Failed to update the Certified-Addr-Book") | ||
except Exception as e: | ||
logger.error( | ||
"Error updating the certified-addr-book: %s", | ||
e, | ||
) | ||
return False | ||
return True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add docstrings for this function. Add the checker for verifying if the peer-id who sent the record is same as the senderRecord's peer id. Refer the corresponding function in #815.
@Antrikshgwal : Hi Antriksh. Thank you for submitting the PR and making improvements. Appreciate it. Please resolve the CI/CD issues. CCing @lla-dane and @sumanjeet0012, who will help you arrive at a good conclusion on the issue. Also, please add the test suite like the one @lla-dane added for adding signed peer record transfer support in kad-dht and pub-sub/gossipsub. Please consult with @lla-dane in detail as you make improvements. |
The CI/CD pipeline currently has a couple of issues: 1. Failing Test CasesCurrently 4 circuit relay tests are failing
2. Linting IssuesThere are several linting issues:
3. Documentation IssueStatus: Module import error during documentation generation Error Message:
Root Cause: The relay module is not properly available in libp2p Solution:
@Antrikshgwal Let me know if you need any help. |
Hi @sumanjeet0012 |
"shift create_signed_records in peerstore"
@Antrikshgwal : Hi Antriksh. Re-ran the CI/CD pipeline. Please resolve the CI/CD issues. CCing @lla-dane and @sumanjeet0012, who will help you arrive at a good conclusion on the issue. Also, please add the test suite like the one @lla-dane added for adding signed peer record transfer support in kad-dht and pub-sub/gossipsub. Please consult with @lla-dane in detail as you make improvements. |
@Antrikshgwal: Fixed the linter issues in this commit: lla-dane@bcb58ba |
@Antrikshgwal : Thank you for the improvements. Also, thank you @lla-dane for fixing the lint issue. @lla-dane : Re-ran the CI/CD pipeline. Looks like your changes are yet to be merged. Looking forward to arriving at a good conclusion on the issue. @Antrikshgwal : Please add the test suite like the one @lla-dane added for adding signed peer record transfer support in kad-dht and pub-sub/gossipsub. Please consult with @lla-dane in detail as you make improvements. Also, wish to ask if you have added newsfragment file. |
Like both them are just envelope wrapped peer-records only right? When the client contacts the relay, it uses HopMessages and sends its signed peer record in the signedRecord parameter. And when there is communication between relay and destination-peer, the sender includes their signed-record in the senderRecord component. Right ? I would suggest, at both the places (HopMessage and StopMessage schema), stick to a single name only for the signed-peer-records, preferably senderRecord, for less confusion. |
Yeah, makes sense. |
Will do. Thanks for the suggestion. |
Added the Envelope parameter in |
added SPR in every HOP and STOP message. |
Also modified the protobuf schema. Now the record containing field in both HOP and STOP schemas is named |
@lla-dane Please let me know if further changes has to be made. |
@Antrikshgwal : Great, thank you so much for addressing the feedback by @lla-dane. Appreciate it. Please add a test suite and newsfragment too. |
@lla-dane and @Antrikshgwal : Please try and complete this important PR before the maintainer's call this week. We plan to do a py-libp2p release once websockets support is added and the corresponding PR merged. Would like your opened PRs to be reviewed, merged and landed in the new release too. |
Sure, I’m waiting on @lla-dane ’s review before proceeding |
@seetadev @Antrikshgwal: Apologize for the delay on this PR, was engaged in a few other things, will surely post a feedback on this PR today. |
peer=client_host.get_id().to_bytes(), | ||
senderRecord=corrupted_env.to_bytes(), # Invalid SPR |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change this line:
senderRecord=corrupted_env.to_bytes()
to this:
senderRecord=corrupted_env.marshal_envelope()
@Antrikshgwal, make this change the failing test will be resolved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed corrupted_env.to_bytes() to corrupted_env.marshal_envelope().
@seetadev @Antrikshgwal: Looked through the repo, it is nicely done in all the message transfer interfaces. Make a change comment on one of the failing tests, after that the CI runs will pass. It would be great if @pacrob could also give it a quick look to see if anything is missing. |
@Antrikshgwal : Wish to share that I re-ran the CI/CD pipeline and there were multiple issues. Wish if you could add @Winter-Soren as a collaborator. He will help you resolve the CI/CD issues that are happening and are yet to be resolved. @lla-dane will definitely share feedback on the PR once CI/CD issues have been resolved. @lla-dane : @pacrob , @acul71 & I will be spending time this week on the upcoming release of Py-libp2p. Wish to share that we have tested the py-multiaddr release in multiple PRs in py-libp2p. The new release of both py-cid & py-multiaddr has landed well. |
if not (isinstance(peer_id, ID) and record.peer_id == peer_id): | ||
return False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here you reject unless peer_id
is included, but none of the calls to this function in circuit_v2/protocol.py
include an argument for peer_id
, causing them all to always return False
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pacrob Apologies, I added peer id support in the function after using it here and somehow forgot to update these calls. Now the maybe_consume_signed_records
calls also send relevant peer ids.
docs/conf.py
Outdated
# Prevent autodoc from trying to import module from tests.factories | ||
autodoc_mock_imports = ["tests.factories"] | ||
autodoc_mock_imports = ["tests.factories" | ||
"libp2p.relay.circuit_v2.lib" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, the Sphinx autodoc was trying to import libp2p.relay.circuit_v2.lib
during the docs build, but the module isn’t present in the repo. I have added a comment stating the issue.
libp2p/relay/circuit_v2/transport.py
Outdated
if resp.HasField("senderRecord"): | ||
if not maybe_consume_signed_record(resp, self.host, relay_peer_id): | ||
logger.error( | ||
"Received an invalid-signed-record, dropping the stream" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit - writing it as "invalid-signed-record" implies there is a variable of that name. Clarify that it's an "invalid senderRecord" or "invalid Signed Peer Record" or something similar. 3 instances in this file and one in discovery.py
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pacrob Changed invalid-signed-record to invalid senderRecord.
What was wrong?
As tracked by #791, the Relay V2 protocol needs to support Signed Peer Record (SPR) transfer to establish secure and verifiable connection between peers relayed through intermediaries. Currently, Circuit Relay V2 only handles HOP and STOP requests without propagating authenticated peer information.
Issue #847
How was it fixed?
This WIP PR aims to:
Extend the Circuit Relay V2 protobuf definitions to include an optional signed_peer_record field in HOP and STOP messages.
Implement handlers in the Relay V2 module to:
Send the local peer’s SPR along with HOP and STOP requests.
Receive and verify incoming SPRs against the peer’s PeerId.
Validate that relayed connections are only established when valid SPRs are exchanged.
This design follows the integration strategy used in KAD-DHT and Gossipsub, ensuring consistency across protocols.
The goal is to improve the trustworthiness of peer discovery and relay connections by embedding verifiable identity metadata directly in Relay V2 message flows.
To-Do
Cute Animal Picture