-
Notifications
You must be signed in to change notification settings - Fork 0
Seamless Integration of Mix Protocol with Existing libp2p Protocols #15
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
- Added NoRespPing codec - Added protocol handler callback
- Tests end-to-end functionality of mix protocol using custom protocol NoRespPing
- Added callbacks for entry and exit mix interface
Nice, but it looks like there is a dependency on other protocols Lines 8 to 13 in 1ed184b
Or is this code supposed to be part of user of mix e.g waku? |
@@ -9,6 +10,7 @@ type ProtocolType* = enum | |||
GossipSub12 = GossipSubCodec_12 | |||
GossipSub11 = GossipSubCodec_11 | |||
GossipSub10 = GossipSubCodec_10 | |||
NoRespPing = NoRespPingCodec |
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.
Is ProtocolType
still needed, or would it be possible to just pass the codec string in callHandler without having to do a conversion to this type? The advantage of it would be that it would not be necessary to modify this enum when we want to add an additional protocol.
@@ -9,6 +10,7 @@ type ProtocolType* = enum | |||
GossipSub12 = GossipSubCodec_12 | |||
GossipSub11 = GossipSubCodec_11 | |||
GossipSub10 = GossipSubCodec_10 | |||
NoRespPing = NoRespPingCodec | |||
OtherProtocol = "other" # Placeholder for other protocols | |||
|
|||
type ProtocolHandler* = |
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.
This is not introduced in this PR but it would be ideal if the {.async.} tag had a raises
to indicate which exceptions can be thrown
|
||
method callHandler*( | ||
switch: Switch, conn: Connection, proto: ProtocolType | ||
): Future[void] {.base, async.} = |
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.
Same here:
): Future[void] {.base, async.} = | |
): Future[void] {.base, async: (raises[]).} = |
It's likely this would throw an LPError
or CancelledError
, but not sure
type | ||
NoRespPingHandler* {.public.} = | ||
proc(peer: PeerId): Future[void] {.gcsafe, raises: [].} |
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.
Maybe let's try this?
type | |
NoRespPingHandler* {.public.} = | |
proc(peer: PeerId): Future[void] {.gcsafe, raises: [].} | |
type | |
NoRespPingHandler* {.public.} = | |
proc(peer: PeerId): Future[void] {.async:(raises: [CancelledError]).} |
else: | ||
self.message = @[] | ||
|
||
# ToDo: Check readLine, readVarint, readLp implementations |
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.
These implementations seem very similar to those in libp2p LpStream (libp2p/stream/connection.nim
), Do you think we could inherit from those instead of duplicating its implementation?
let paddedMsg = padMessage(mixMsg, peerID) | ||
let paddedMsg = padMessage(serialized, peerID) | ||
|
||
info "# Sent: ", sender = multiAddr, message = msg |
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.
Should probably be trace
or debug
let parts = multiAddrs[0].split("/p2p/") | ||
if parts.len != 2: | ||
error "Invalid multiaddress format", parts = parts | ||
return | ||
|
||
let firstMixAddr = MultiAddress.init(parts[0]).valueOr: | ||
error "Failed to initialize MultiAddress", err = error | ||
return | ||
|
||
let firstMixPeerId = PeerId.init(parts[1]).valueOr: | ||
error "Failed to initialize PeerId", err = error | ||
return |
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.
I see we try to split multiaddresses by "p2p" in diff areas of the code. Maybe we can use some utils functions to extract the peerId and the address from a multiaddress:
func stripPeerId(multiaddr: MultiAddress): MultiAddress =
if not multiaddr.contains(multiCodec("p2p")).get():
return multiaddr
var cleanAddr = MultiAddress.init()
for item in multiaddr.items:
if item.value.protoName().get() != "p2p":
# Add all parts except p2p peerId
discard cleanAddr.append(item.value)
return cleanAddr
proc peerID*(ma: MultiAddress): Result[PeerId, string] =
let p2pPart = ?ma[^1]
if ?p2pPart.protoCode != multiCodec("p2p"):
return err("Missing p2p part from multiaddress!")
ok(?PeerId.init(?p2pPart.protoArgument()).orErr("invalid peerid"))
Then we can use them like this
let ma = MultiAddress.init(parts[0]).valueOr:
error "Failed to initialize MultiAddress", err = error
return
let firstMixPeerId = ma.peerID().valueOr:
error "Failed to initialize PeerId", err = error
return
let firstMixAddr = ma.stripPeerId()
let multiAddr = MultiAddress.init(multiAddrStr.split("/p2p/")[0]).valueOr: | ||
error "Failed to initialize MultiAddress", err = error | ||
return |
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.
Using the function from prev comment, it's likely we can do some quick changes to avoid having to initialize a multiaddress here:
MixNodeInfo
multiAddr
field should be aMultiaddress
instead of astring
. The multiaddress initialization can happen when you are loading the node info from a file.getMixNodeInfo
would then return aMultiaddress
instead of a string.
That would allow you to do something like:
let multiAddr = mixNodeMultiAddr.stripPeerId()
proc noRespPing*(p: NoRespPing, conn: Connection): Future[seq[byte]] {.async, public.} = | ||
trace "initiating ping" | ||
var randomBuf: array[NoRespPingSize, byte] | ||
hmacDrbgGenerate(p.rng[], randomBuf) | ||
trace "sending ping" | ||
await conn.write(@randomBuf) | ||
trace "sent ping: ", ping = @randomBuf | ||
return @randomBuf |
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.
Does this mean that protocols that want to use mix would need to implement a function that would allow them to use a provided conn
instead of use one obtained from the switch?
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.
Do you foresee any issues with this approach? I don't believe there are any. The mix and the top-level protocol are part of the same switch, so ideally, they can communicate directly.
We’re establishing a virtual connection here to allow us to intercept messages from the top-level protocol easily. As a result, creating an MixEntryConnection
is necessary for this to work as intended.
- Simplify with valueOr Co-authored-by: richΛrd <[email protected]>
Summary
This pull request introduces seamless integration of existing libp2p protocols with the Mix Protocol. By abstracting the entry and exit connections to the Mix protocol, this implementation allows existing protocols to anonymize messages without requiring any modifications to their code.
Key Abstractions
To achieve this functionality, we introduce two abstractions:
MixEntryConnection:
MixDialer
) to route them through the mix network.NoRespPing.noRespPing
is called,MixEntryConnection
ensures that the message is anonymized and routed through the mix network.MixExitConnection:
NoRespPing
) to process these messages seamlessly.Implementation Details
MixDialer Callback:
MixDialer
callback is defined to route outgoing messages throughanonymizeLocalProtocolSend
in theMixProtocol
.MixEntryConnection
, which invokes it whenever an outgoing message is written.Protocol Handler Callback:
Simulation:
NoRespPing
protocol is used to test end-to-end functionality.MixEntryConnection
at the sender side and processed byMixExitConnection
at the receiver side.Run PoC
Benefits