From a15237f73df95a30383cccb541096a139f998be3 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Wed, 24 Sep 2025 13:32:14 -0700 Subject: [PATCH 01/28] Test Partial Message in interop tester adds Go implementation of partial messages and changes to the test runner to support it. --- gossipsub-interop/README.md | 8 + gossipsub-interop/experiment.py | 51 ++++++ gossipsub-interop/go-libp2p/experiment.go | 177 +++++++++++++++++++- gossipsub-interop/go-libp2p/go.mod | 3 +- gossipsub-interop/go-libp2p/go.sum | 4 + gossipsub-interop/go-libp2p/instruction.go | 33 ++++ gossipsub-interop/go-libp2p/main.go | 7 +- gossipsub-interop/go-libp2p/partial.go | 145 ++++++++++++++++ gossipsub-interop/go-libp2p/partial_test.go | 101 +++++++++++ gossipsub-interop/script_instruction.py | 16 ++ 10 files changed, 538 insertions(+), 7 deletions(-) create mode 100644 gossipsub-interop/go-libp2p/partial.go create mode 100644 gossipsub-interop/go-libp2p/partial_test.go diff --git a/gossipsub-interop/README.md b/gossipsub-interop/README.md index 959b1a169..e5f5bcd2a 100644 --- a/gossipsub-interop/README.md +++ b/gossipsub-interop/README.md @@ -76,6 +76,14 @@ After implementing it, make sure to add build commands in the Makefile's `binari Finally, add it to the `composition` function in `experiment.py`. +## Examples + +Minimal test of partial messages + +```bash +uv run run.py --node_count 2 --composition "all-go" --scenario "partial-messages" +``` + ## Future work (contributions welcome) - Add more scenarios. diff --git a/gossipsub-interop/experiment.py b/gossipsub-interop/experiment.py index 64135b3ca..f33dfa9fe 100644 --- a/gossipsub-interop/experiment.py +++ b/gossipsub-interop/experiment.py @@ -38,11 +38,62 @@ def spread_heartbeat_delay( return instructions +def partial_message_scenario( + disable_gossip: bool, node_count: int +) -> List[ScriptInstruction]: + instructions: List[ScriptInstruction] = [] + gs_params = GossipSubParams() + if disable_gossip: + gs_params.Dlazy = 0 + gs_params.GossipFactor = 0 + instructions.extend(spread_heartbeat_delay(node_count, gs_params)) + + number_of_conns_per_node = 20 + if number_of_conns_per_node >= node_count: + number_of_conns_per_node = node_count - 1 + instructions.extend(random_network_mesh(node_count, number_of_conns_per_node)) + + topic = "a-subnet" + instructions.append( + script_instruction.SubscribeToTopic(topicID=topic, partial=True) + ) + + groupID = random.randint(0, (2**8) - 1) + + # Wait for some setup time + elapsed_seconds = 30 + instructions.append(script_instruction.WaitUntil(elapsedSeconds=elapsed_seconds)) + + # Assign random parts to each node + for i in range(node_count): + parts = random.randint(0, 255) + instructions.append( + script_instruction.IfNodeIDEquals( + nodeID=i, + instruction=script_instruction.AddPartialMessage( + topicID=topic, groupID=groupID, parts=parts + ), + ) + ) + + instructions.append( + script_instruction.PublishPartial(topicID=topic, groupID=groupID) + ) + + # Wait for everything to flush + elapsed_seconds += 10 + instructions.append(script_instruction.WaitUntil(elapsedSeconds=elapsed_seconds)) + + return instructions + + def scenario( scenario_name: str, node_count: int, disable_gossip: bool ) -> ExperimentParams: instructions: List[ScriptInstruction] = [] match scenario_name: + case "partial-messages": + instructions = partial_message_scenario(disable_gossip, node_count) case "subnet-blob-msg": gs_params = GossipSubParams() if disable_gossip: diff --git a/gossipsub-interop/go-libp2p/experiment.go b/gossipsub-interop/go-libp2p/experiment.go index 8bb61f19b..be0116e37 100644 --- a/gossipsub-interop/go-libp2p/experiment.go +++ b/gossipsub-interop/go-libp2p/experiment.go @@ -9,6 +9,8 @@ import ( "time" pubsub "github.com/libp2p/go-libp2p-pubsub" + "github.com/libp2p/go-libp2p-pubsub/partialmessages" + pubsub_pb "github.com/libp2p/go-libp2p-pubsub/pb" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" ) @@ -22,6 +24,123 @@ type HostConnector interface { ConnectTo(ctx context.Context, h host.Host, targetNodeId int) error } +type incomingPartialRPC struct { + from peer.ID + topic string + groupID []byte + iwant []byte + ihave []byte + partialMessageBytes []byte +} + +type partialMsgWithTopic struct { + topic string + msg *PartialMessage +} +type publishReq struct { + topic string + groupID []byte +} + +type partialMsgManager struct { + *slog.Logger + // Close this channel to terminate the manager + done chan struct{} + incomingRPC chan incomingPartialRPC + publish chan publishReq + add chan partialMsgWithTopic + // map -> topic -> groupID -> *PartialMessage + partialMessages map[string]map[string]*PartialMessage + + pubsub *pubsub.PubSub +} + +func (m *partialMsgManager) start(logger *slog.Logger, pubsub *pubsub.PubSub) { + m.Logger = logger + m.done = make(chan struct{}) + m.incomingRPC = make(chan incomingPartialRPC, 1) + m.publish = make(chan publishReq) + m.add = make(chan partialMsgWithTopic) + m.partialMessages = make(map[string]map[string]*PartialMessage) + m.pubsub = pubsub + go m.run() +} +func (m *partialMsgManager) close() { + if m.done != nil { + close(m.done) + } +} + +func (m *partialMsgManager) run() { + for { + select { + case rpc := <-m.incomingRPC: + m.Info("Received partial RPC") + m.handleRPC(rpc) + case req := <-m.add: + m.Info("Adding partial message") + m.addMsg(req) + case req := <-m.publish: + m.Info("publishing partial message") + pm := m.partialMessages[req.topic][string(req.groupID)] + m.pubsub.PublishPartialMessage(req.topic, pm, partialmessages.PublishOptions{}) + case <-m.done: + return + } + } +} + +func (m *partialMsgManager) addMsg(req partialMsgWithTopic) { + _, ok := m.partialMessages[req.topic] + if !ok { + m.partialMessages[req.topic] = make(map[string]*PartialMessage) + } + _, ok = m.partialMessages[req.topic][string(req.msg.GroupID())] + if !ok { + m.partialMessages[req.topic][string(req.msg.GroupID())] = req.msg + } +} + +func (m *partialMsgManager) handleRPC(rpc incomingPartialRPC) { + _, ok := m.partialMessages[rpc.topic] + if !ok { + m.partialMessages[rpc.topic] = make(map[string]*PartialMessage) + } + pm, ok := m.partialMessages[rpc.topic][string(rpc.groupID)] + if !ok { + pm = &PartialMessage{} + copy(pm.groupID[:], rpc.groupID) + m.partialMessages[rpc.topic][string(rpc.groupID)] = pm + } + + // Extend first, so we don't request something we just got. + if len(rpc.partialMessageBytes) != 0 { + pm.Extend(rpc.partialMessageBytes) + } + + missing, _ := pm.MissingParts() + if len(missing) == 0 { + m.Info("All parts received") + } + + var shouldRepublish bool + pmHas, _ := pm.AvailableParts() + if len(rpc.iwant) != 0 { + if rpc.iwant[0]&pmHas[0] != 0 { + shouldRepublish = true + } + } + if len(rpc.ihave) != 0 { + if (rpc.ihave[0] & (^pmHas[0])) != 0 { + shouldRepublish = true + } + } + + if shouldRepublish { + m.pubsub.PublishPartialMessage(rpc.topic, pm, partialmessages.PublishOptions{}) + } +} + type scriptedNode struct { nodeID int h host.Host @@ -32,6 +151,8 @@ type scriptedNode struct { topics map[string]*pubsub.Topic startTime time.Time subCtx context.Context + + partialMsgMgr partialMsgManager } func newScriptedNode( @@ -57,16 +178,40 @@ func newScriptedNode( return n, nil } +func (n *scriptedNode) close() error { + n.partialMsgMgr.close() + return nil +} + func (n *scriptedNode) runInstruction(ctx context.Context, instruction ScriptInstruction) error { // Process each script instruction switch a := instruction.(type) { case InitGossipSubInstruction: - psOpts := pubsubOptions(n.slogger, a.GossipSubParams) + pme := &partialmessages.PartialMessageExtension{ + Logger: slog.Default(), + ValidateRPC: func(from peer.ID, rpc *pubsub_pb.PartialMessagesExtension) error { + // Not doing any validation for now + return nil + }, + OnIncomingRPC: func(from peer.ID, topic string, groupID, iwant, ihave, partialMessageBytes []byte) { + n.partialMsgMgr.incomingRPC <- incomingPartialRPC{ + from: from, + topic: topic, + groupID: groupID, + iwant: iwant, + ihave: ihave, + partialMessageBytes: partialMessageBytes, + } + }, + } + + psOpts := pubsubOptions(n.slogger, a.GossipSubParams, pme) ps, err := pubsub.NewGossipSub(ctx, n.h, psOpts...) if err != nil { return err } n.pubsub = ps + n.partialMsgMgr.start(n.slogger, ps) case ConnectInstruction: for _, targetNodeId := range a.ConnectTo { err := n.connector.ConnectTo(ctx, n.h, targetNodeId) @@ -87,7 +232,7 @@ func (n *scriptedNode) runInstruction(ctx context.Context, instruction ScriptIns time.Sleep(waitTime) } case PublishInstruction: - topic, err := n.getTopic(a.TopicID) + topic, err := n.getTopic(a.TopicID, false) if err != nil { return fmt.Errorf("failed to get topic %s: %w", a.TopicID, err) } @@ -101,7 +246,7 @@ func (n *scriptedNode) runInstruction(ctx context.Context, instruction ScriptIns } n.logger.Printf("Published message %d\n", a.MessageID) case SubscribeToTopicInstruction: - topic, err := n.getTopic(a.TopicID) + topic, err := n.getTopic(a.TopicID, a.Partial) if err != nil { return fmt.Errorf("failed to get topic %s: %w", a.TopicID, err) } @@ -132,6 +277,23 @@ func (n *scriptedNode) runInstruction(ctx context.Context, instruction ScriptIns return pubsub.ValidationAccept }) + case AddPartialMessage: + pm := &PartialMessage{} + binary.BigEndian.AppendUint64(pm.groupID[:0], uint64(a.GroupID)) + pm.FillParts(uint8(a.Parts)) + n.partialMsgMgr.add <- partialMsgWithTopic{ + topic: a.TopicID, + msg: pm, + } + + case PublishPartialInstruction: + var groupID [8]byte + binary.BigEndian.AppendUint64(groupID[:0], uint64(a.GroupID)) + n.partialMsgMgr.publish <- publishReq{ + topic: a.TopicID, + groupID: groupID[:], + } + default: return fmt.Errorf("unknown instruction type: %T", instruction) } @@ -139,14 +301,18 @@ func (n *scriptedNode) runInstruction(ctx context.Context, instruction ScriptIns return nil } -func (n *scriptedNode) getTopic(topicStr string) (*pubsub.Topic, error) { +func (n *scriptedNode) getTopic(topicStr string, partial bool) (*pubsub.Topic, error) { if n.topics == nil { n.topics = make(map[string]*pubsub.Topic) } t, ok := n.topics[topicStr] if !ok { var err error - t, err = n.pubsub.Join(topicStr) + var opts []pubsub.TopicOpt + if partial { + opts = append(opts, pubsub.RequestPartialMessages()) + } + t, err = n.pubsub.Join(topicStr, opts...) if err != nil { return nil, err } @@ -160,6 +326,7 @@ func RunExperiment(ctx context.Context, startTime time.Time, logger *log.Logger, if err != nil { return err } + defer n.close() for _, instruction := range params.Script { if err := n.runInstruction(ctx, instruction); err != nil { diff --git a/gossipsub-interop/go-libp2p/go.mod b/gossipsub-interop/go-libp2p/go.mod index 477b01ceb..a34b62cc4 100644 --- a/gossipsub-interop/go-libp2p/go.mod +++ b/gossipsub-interop/go-libp2p/go.mod @@ -4,7 +4,8 @@ go 1.24.1 require ( github.com/libp2p/go-libp2p v0.41.1 - github.com/libp2p/go-libp2p-pubsub v0.13.1 + // fork with partial messages + github.com/libp2p/go-libp2p-pubsub v0.14.4-0.20250925234347-91dd1a5e4596 ) require ( diff --git a/gossipsub-interop/go-libp2p/go.sum b/gossipsub-interop/go-libp2p/go.sum index 9453d0bc0..5ee347170 100644 --- a/gossipsub-interop/go-libp2p/go.sum +++ b/gossipsub-interop/go-libp2p/go.sum @@ -139,6 +139,10 @@ github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl9 github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8= github.com/libp2p/go-libp2p-pubsub v0.13.1 h1:tV3ttzzZSCk0EtEXnxVmWIXgjVxXx+20Jwjbs/Ctzjo= github.com/libp2p/go-libp2p-pubsub v0.13.1/go.mod h1:MKPU5vMI8RRFyTP0HfdsF9cLmL1nHAeJm44AxJGJx44= +github.com/libp2p/go-libp2p-pubsub v0.14.4-0.20250924192223-5297a51ffd50 h1:g5aj1tw4GHg3Oee8pv0/zn5nyxegTD1FuDYfrqXVbUc= +github.com/libp2p/go-libp2p-pubsub v0.14.4-0.20250924192223-5297a51ffd50/go.mod h1:lr4oE8bFgQaifRcoc2uWhWWiK6tPdOEKpUuR408GFN4= +github.com/libp2p/go-libp2p-pubsub v0.14.4-0.20250925234347-91dd1a5e4596 h1:qBAupWsLXtQAd6wJp7nmTkLcwnKsscwR7NKxlPeDFtk= +github.com/libp2p/go-libp2p-pubsub v0.14.4-0.20250925234347-91dd1a5e4596/go.mod h1:lr4oE8bFgQaifRcoc2uWhWWiK6tPdOEKpUuR408GFN4= github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg= github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= diff --git a/gossipsub-interop/go-libp2p/instruction.go b/gossipsub-interop/go-libp2p/instruction.go index 9c1149499..1ef2f2f8b 100644 --- a/gossipsub-interop/go-libp2p/instruction.go +++ b/gossipsub-interop/go-libp2p/instruction.go @@ -40,6 +40,26 @@ type WaitUntilInstruction struct { // isInstruction implements the ScriptInstruction interface func (WaitUntilInstruction) isInstruction() {} +type AddPartialMessage struct { + Type string `json:"type"` + TopicID string `json:"topicID"` + GroupID int `json:"groupID"` + Parts int `json:"parts"` +} + +// isInstruction implements the ScriptInstruction interface +func (AddPartialMessage) isInstruction() {} + +// PublishPartialInstruction represents a partial publish instruction in the script +type PublishPartialInstruction struct { + Type string `json:"type"` + TopicID string `json:"topicID"` + GroupID int `json:"groupID"` +} + +// isInstruction implements the ScriptInstruction interface +func (PublishPartialInstruction) isInstruction() {} + // PublishInstruction represents a publish instruction in the script type PublishInstruction struct { Type string `json:"type"` @@ -55,6 +75,7 @@ func (PublishInstruction) isInstruction() {} type SubscribeToTopicInstruction struct { Type string `json:"type"` TopicID string `json:"topicID"` + Partial bool `json:"partial"` } // isInstruction implements the ScriptInstruction interface @@ -147,6 +168,18 @@ func UnmarshalScriptInstruction(data []byte) (ScriptInstruction, error) { return nil, err } return instruction, nil + case "addPartialMessage": + var instruction AddPartialMessage + if err := json.Unmarshal(data, &instruction); err != nil { + return nil, err + } + return instruction, nil + case "publishPartial": + var instruction PublishPartialInstruction + if err := json.Unmarshal(data, &instruction); err != nil { + return nil, err + } + return instruction, nil case "initGossipSub": var tempInstruction struct { diff --git a/gossipsub-interop/go-libp2p/main.go b/gossipsub-interop/go-libp2p/main.go index ebf589072..17b3cb491 100644 --- a/gossipsub-interop/go-libp2p/main.go +++ b/gossipsub-interop/go-libp2p/main.go @@ -16,6 +16,7 @@ import ( "github.com/libp2p/go-libp2p" pubsub "github.com/libp2p/go-libp2p-pubsub" + "github.com/libp2p/go-libp2p-pubsub/partialmessages" pubsubpb "github.com/libp2p/go-libp2p-pubsub/pb" "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/host" @@ -27,7 +28,7 @@ var ( ) // pubsubOptions creates a list of options to configure our router with. -func pubsubOptions(slogger *slog.Logger, params pubsub.GossipSubParams) []pubsub.Option { +func pubsubOptions(slogger *slog.Logger, params pubsub.GossipSubParams, pme *partialmessages.PartialMessageExtension) []pubsub.Option { tr := gossipTracer{logger: slogger.With("service", "gossipsub")} psOpts := []pubsub.Option{ pubsub.WithMessageSignaturePolicy(pubsub.StrictNoSign), @@ -43,6 +44,10 @@ func pubsubOptions(slogger *slog.Logger, params pubsub.GossipSubParams) []pubsub pubsub.WithEventTracer(&tr), } + if pme != nil { + psOpts = append(psOpts, pubsub.WithPartialMessagesExtension(pme)) + } + return psOpts } diff --git a/gossipsub-interop/go-libp2p/partial.go b/gossipsub-interop/go-libp2p/partial.go new file mode 100644 index 000000000..4fbc8e7eb --- /dev/null +++ b/gossipsub-interop/go-libp2p/partial.go @@ -0,0 +1,145 @@ +package main + +import ( + "bytes" + "encoding/binary" + "errors" + "math/bits" + + partialmessages "github.com/libp2p/go-libp2p-pubsub/partialmessages" + "github.com/libp2p/go-libp2p/core/peer" +) + +const partLen = 1024 + +type PartialMessage struct { + groupID [8]byte + parts [8][]byte // each part is partLen sized or nil if empty +} + +// FillParts is used to initialize this PartialMessage for testing by filling in +// the parts it should have. The algorithm is simple: +// - treat the groupID as our starting uint64 number BigEndian +// - every part is viewed as 128 uint64s BigEndian. +// - We count up uint64s starting from the group ID == part[0][0:8] until part[7][1016:1024] == groupID + 1024-1 +func (p *PartialMessage) FillParts(bitmap byte) error { + start := binary.BigEndian.Uint64(p.groupID[:]) + for i := range p.parts { + if bitmap&(1< 0 { + out[0] |= 1 << i + } + } + return out, nil +} + +// GroupID implements partialmessages.PartialMessage. +func (p *PartialMessage) GroupID() []byte { + return p.groupID[:] +} + +// MissingParts implements partialmessages.PartialMessage. +func (p *PartialMessage) MissingParts() ([]byte, error) { + b, _ := p.AvailableParts() + b[0] = ^b[0] + if b[0] == 0 { + return nil, nil + } + return b, nil +} + +func (p *PartialMessage) Extend(data []byte) error { + if len(data) < 1+len(p.groupID) { + return errors.New("invalid data length") + } + partBitmap := data[0] + data = data[1:] + + groupID := data[len(data)-len(p.groupID):] + data = data[:len(data)-len(p.groupID)] + if !bytes.Equal(p.groupID[:], groupID) { + return errors.New("invalid group ID") + } + if len(data)%partLen != 0 { + return errors.New("invalid data length") + } + + for i := range p.parts { + if len(data) == 0 { + break + } + if partBitmap&(1< Date: Fri, 26 Sep 2025 21:03:05 +0100 Subject: [PATCH 02/28] add rust implementation for partial messages --- gossipsub-interop/rust-libp2p/Cargo.lock | 240 ++++++++---------- gossipsub-interop/rust-libp2p/Cargo.toml | 8 +- gossipsub-interop/rust-libp2p/src/bitmap.rs | 170 +++++++++++++ .../rust-libp2p/src/experiment.rs | 35 ++- gossipsub-interop/rust-libp2p/src/main.rs | 13 +- .../rust-libp2p/src/script_instruction.rs | 22 +- 6 files changed, 337 insertions(+), 151 deletions(-) create mode 100644 gossipsub-interop/rust-libp2p/src/bitmap.rs diff --git a/gossipsub-interop/rust-libp2p/Cargo.lock b/gossipsub-interop/rust-libp2p/Cargo.lock index 2136a94f3..c07302102 100644 --- a/gossipsub-interop/rust-libp2p/Cargo.lock +++ b/gossipsub-interop/rust-libp2p/Cargo.lock @@ -52,18 +52,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "ahash" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - [[package]] name = "aho-corasick" version = "1.1.3" @@ -143,9 +131,9 @@ checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "asn1-rs" -version = "0.6.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048" +checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60" dependencies = [ "asn1-rs-derive", "asn1-rs-impl", @@ -153,15 +141,15 @@ dependencies = [ "nom", "num-traits", "rusticata-macros", - "thiserror 1.0.69", + "thiserror 2.0.12", "time", ] [[package]] name = "asn1-rs-derive" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" +checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ "proc-macro2", "quote", @@ -222,17 +210,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "async-recursion" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "async-trait" version = "0.1.88" @@ -265,11 +242,12 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "attohttpc" -version = "0.24.1" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d9a9bf8b79a749ee0b911b91b671cc2b6c670bdbc7e3dfd537576ddc94bb2a2" +checksum = "16e2cdb6d5ed835199484bb92bb8b3edd526effe995c61732580439c1a67e2e9" dependencies = [ - "http 0.2.12", + "base64", + "http", "log", "url", ] @@ -530,6 +508,12 @@ dependencies = [ "libc", ] +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + [[package]] name = "crossbeam-channel" version = "0.5.15" @@ -652,9 +636,9 @@ dependencies = [ [[package]] name = "der-parser" -version = "9.0.0" +version = "10.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553" +checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" dependencies = [ "asn1-rs", "displaydoc", @@ -784,7 +768,6 @@ checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ "curve25519-dalek 4.1.3", "ed25519 2.2.3", - "rand_core 0.6.4", "serde", "sha2 0.10.9", "subtle", @@ -1088,7 +1071,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http 1.3.1", + "http", "indexmap", "slab", "tokio", @@ -1096,15 +1079,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", -] - [[package]] name = "hashbrown" version = "0.15.3" @@ -1118,11 +1092,11 @@ dependencies = [ [[package]] name = "hashlink" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.14.5", + "hashbrown", ] [[package]] @@ -1157,11 +1131,10 @@ checksum = "b07f60793ff0a4d9cef0f18e63b5357e06209987153a64648c972c1e5aff336f" [[package]] name = "hickory-proto" -version = "0.25.0-alpha.5" +version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d00147af6310f4392a31680db52a3ed45a2e0f68eb18e8c3fe5537ecc96d9e2" +checksum = "f8a6fe56c0038198998a6f217ca4e7ef3a5e51f46163bd6dd60b5c71ca6c6502" dependencies = [ - "async-recursion", "async-trait", "cfg-if", "data-encoding", @@ -1173,6 +1146,7 @@ dependencies = [ "ipnet", "once_cell", "rand 0.9.1", + "ring", "socket2", "thiserror 2.0.12", "tinyvec", @@ -1183,9 +1157,9 @@ dependencies = [ [[package]] name = "hickory-resolver" -version = "0.25.0-alpha.5" +version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5762f69ebdbd4ddb2e975cd24690bf21fe6b2604039189c26acddbc427f12887" +checksum = "dc62a9a99b0bfb44d2ab95a7208ac952d31060efc16241c87eaf36406fecf87a" dependencies = [ "cfg-if", "futures-util", @@ -1231,17 +1205,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - [[package]] name = "http" version = "1.3.1" @@ -1260,7 +1223,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.3.1", + "http", ] [[package]] @@ -1271,7 +1234,7 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.3.1", + "http", "http-body", "pin-project-lite", ] @@ -1292,7 +1255,7 @@ dependencies = [ "futures-channel", "futures-util", "h2", - "http 1.3.1", + "http", "http-body", "httparse", "itoa", @@ -1311,7 +1274,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.3.1", + "http", "http-body", "hyper", "libc", @@ -1464,20 +1427,20 @@ dependencies = [ [[package]] name = "igd-next" -version = "0.15.1" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76b0d7d4541def58a37bf8efc559683f21edce7c82f0d866c93ac21f7e098f93" +checksum = "516893339c97f6011282d5825ac94fc1c7aad5cad26bdc2d0cee068c0bf97f97" dependencies = [ "async-trait", "attohttpc", "bytes", "futures", - "http 1.3.1", + "http", "http-body-util", "hyper", "hyper-util", "log", - "rand 0.8.5", + "rand 0.9.1", "tokio", "url", "xmltree", @@ -1490,7 +1453,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown 0.15.3", + "hashbrown", ] [[package]] @@ -1567,9 +1530,9 @@ checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libp2p" -version = "0.55.0" +version = "0.56.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b72dc443ddd0254cb49a794ed6b6728400ee446a0f7ab4a07d0209ee98de20e9" +checksum = "ce71348bf5838e46449ae240631117b487073d5f347c06d434caddcb91dceb5a" dependencies = [ "bytes", "either", @@ -1580,7 +1543,6 @@ dependencies = [ "libp2p-connection-limits", "libp2p-core", "libp2p-dns", - "libp2p-gossipsub", "libp2p-identify", "libp2p-identity", "libp2p-mdns", @@ -1600,9 +1562,9 @@ dependencies = [ [[package]] name = "libp2p-allow-block-list" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38944b7cb981cc93f2f0fb411ff82d0e983bd226fbcc8d559639a3a73236568b" +checksum = "d16ccf824ee859ca83df301e1c0205270206223fd4b1f2e512a693e1912a8f4a" dependencies = [ "libp2p-core", "libp2p-identity", @@ -1611,9 +1573,9 @@ dependencies = [ [[package]] name = "libp2p-connection-limits" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efe9323175a17caa8a2ed4feaf8a548eeef5e0b72d03840a0eab4bcb0210ce1c" +checksum = "a18b8b607cf3bfa2f8c57db9c7d8569a315d5cc0a282e6bfd5ebfc0a9840b2a0" dependencies = [ "libp2p-core", "libp2p-identity", @@ -1622,9 +1584,9 @@ dependencies = [ [[package]] name = "libp2p-core" -version = "0.43.0" +version = "0.43.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193c75710ba43f7504ad8f58a62ca0615b1d7e572cb0f1780bc607252c39e9ef" +checksum = "4d28e2d2def7c344170f5c6450c0dbe3dfef655610dbfde2f6ac28a527abbe36" dependencies = [ "either", "fnv", @@ -1634,7 +1596,6 @@ dependencies = [ "multiaddr", "multihash", "multistream-select", - "once_cell", "parking_lot", "pin-project", "quick-protobuf", @@ -1648,9 +1609,9 @@ dependencies = [ [[package]] name = "libp2p-dns" -version = "0.43.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b780a1150214155b0ed1cdf09fbd2e1b0442604f9146a431d1b21d23eef7bd7" +checksum = "0b770c1c8476736ca98c578cba4b505104ff8e842c2876b528925f9766379f9a" dependencies = [ "async-trait", "futures", @@ -1664,9 +1625,8 @@ dependencies = [ [[package]] name = "libp2p-gossipsub" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d558548fa3b5a8e9b66392f785921e363c57c05dcadfda4db0d41ae82d313e4a" +version = "0.50.0" +source = "git+https://github.com/jxs/rust-libp2p/?branch=gossipsub-partial-messages#45c2bc1d83e685a5972f4e6b862c5e743357e66c" dependencies = [ "async-channel", "asynchronous-codec", @@ -1683,7 +1643,6 @@ dependencies = [ "libp2p-core", "libp2p-identity", "libp2p-swarm", - "prometheus-client", "quick-protobuf", "quick-protobuf-codec", "rand 0.8.5", @@ -1695,9 +1654,9 @@ dependencies = [ [[package]] name = "libp2p-identify" -version = "0.46.0" +version = "0.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c06862544f02d05d62780ff590cc25a75f5c2b9df38ec7a370dcae8bb873cf" +checksum = "8ab792a8b68fdef443a62155b01970c81c3aadab5e659621b063ef252a8e65e8" dependencies = [ "asynchronous-codec", "either", @@ -1716,9 +1675,9 @@ dependencies = [ [[package]] name = "libp2p-identity" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbb68ea10844211a59ce46230909fd0ea040e8a192454d4cc2ee0d53e12280eb" +checksum = "3104e13b51e4711ff5738caa1fb54467c8604c2e94d607e27745bcf709068774" dependencies = [ "bs58", "ed25519-dalek 2.1.1", @@ -1734,9 +1693,9 @@ dependencies = [ [[package]] name = "libp2p-mdns" -version = "0.47.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d0ba095e1175d797540e16b62e7576846b883cb5046d4159086837b36846cc" +checksum = "c66872d0f1ffcded2788683f76931be1c52e27f343edb93bc6d0bcd8887be443" dependencies = [ "futures", "hickory-proto", @@ -1753,13 +1712,12 @@ dependencies = [ [[package]] name = "libp2p-metrics" -version = "0.16.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ce58c64292e87af624fcb86465e7dd8342e46a388d71e8fec0ab37ee789630a" +checksum = "805a555148522cb3414493a5153451910cb1a146c53ffbf4385708349baf62b7" dependencies = [ "futures", "libp2p-core", - "libp2p-gossipsub", "libp2p-identify", "libp2p-identity", "libp2p-swarm", @@ -1770,9 +1728,9 @@ dependencies = [ [[package]] name = "libp2p-noise" -version = "0.46.0" +version = "0.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afcc133e0f3cea07acde6eb8a9665cb11b600bd61110b010593a0210b8153b16" +checksum = "bc73eacbe6462a0eb92a6527cac6e63f02026e5407f8831bde8293f19217bfbf" dependencies = [ "asynchronous-codec", "bytes", @@ -1781,7 +1739,6 @@ dependencies = [ "libp2p-identity", "multiaddr", "multihash", - "once_cell", "quick-protobuf", "rand 0.8.5", "snow", @@ -1794,9 +1751,9 @@ dependencies = [ [[package]] name = "libp2p-quic" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41432a159b00424a0abaa2c80d786cddff81055ac24aa127e0cf375f7858d880" +checksum = "8dc448b2de9f4745784e3751fe8bc6c473d01b8317edd5ababcb0dec803d843f" dependencies = [ "futures", "futures-timer", @@ -1816,9 +1773,9 @@ dependencies = [ [[package]] name = "libp2p-swarm" -version = "0.46.0" +version = "0.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "803399b4b6f68adb85e63ab573ac568154b193e9a640f03e0f2890eabbcb37f8" +checksum = "6aa762e5215919a34e31c35d4b18bf2e18566ecab7f8a3d39535f4a3068f8b62" dependencies = [ "either", "fnv", @@ -1829,7 +1786,6 @@ dependencies = [ "libp2p-swarm-derive", "lru", "multistream-select", - "once_cell", "rand 0.8.5", "smallvec", "tokio", @@ -1839,21 +1795,20 @@ dependencies = [ [[package]] name = "libp2p-swarm-derive" -version = "0.35.0" +version = "0.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206e0aa0ebe004d778d79fb0966aa0de996c19894e2c0605ba2f8524dd4443d8" +checksum = "dd297cf53f0cb3dee4d2620bb319ae47ef27c702684309f682bdb7e55a18ae9c" dependencies = [ "heck", - "proc-macro2", "quote", "syn", ] [[package]] name = "libp2p-tcp" -version = "0.43.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65346fb4d36035b23fec4e7be4c320436ba53537ce9b6be1d1db1f70c905cad0" +checksum = "65b4e030c52c46c8d01559b2b8ca9b7c4185f10576016853129ca1fe5cd1a644" dependencies = [ "futures", "futures-timer", @@ -1867,9 +1822,9 @@ dependencies = [ [[package]] name = "libp2p-tls" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42bbf5084fb44133267ad4caaa72a253d68d709edd2ed1cf9b42431a8ead8fd5" +checksum = "96ff65a82e35375cbc31ebb99cacbbf28cb6c4fefe26bf13756ddcf708d40080" dependencies = [ "futures", "futures-rustls", @@ -1878,7 +1833,7 @@ dependencies = [ "rcgen", "ring", "rustls", - "rustls-webpki 0.101.7", + "rustls-webpki", "thiserror 2.0.12", "x509-parser", "yasna", @@ -1886,9 +1841,9 @@ dependencies = [ [[package]] name = "libp2p-upnp" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d457b9ecceb66e7199f049926fad447f1f17f040e8d29d690c086b4cab8ed14a" +checksum = "4757e65fe69399c1a243bbb90ec1ae5a2114b907467bf09f3575e899815bb8d3" dependencies = [ "futures", "futures-timer", @@ -1901,9 +1856,9 @@ dependencies = [ [[package]] name = "libp2p-websocket" -version = "0.45.0" +version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bf5d48a4d8fad8a49fbf23816a878cac25623549f415d74da8ef4327e6196a9" +checksum = "520e29066a48674c007bc11defe5dce49908c24cafd8fad2f5e1a6a8726ced53" dependencies = [ "either", "futures", @@ -1917,7 +1872,7 @@ dependencies = [ "thiserror 2.0.12", "tracing", "url", - "webpki-roots", + "webpki-roots 0.26.11", ] [[package]] @@ -1992,7 +1947,7 @@ version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown 0.15.3", + "hashbrown", ] [[package]] @@ -2277,9 +2232,9 @@ dependencies = [ [[package]] name = "oid-registry" -version = "0.7.1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d8034d9489cdaf79228eb9f6a3b8d7bb32ba00d6645ebd48eef4077ceb5bd9" +checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" dependencies = [ "asn1-rs", ] @@ -2289,6 +2244,10 @@ name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +dependencies = [ + "critical-section", + "portable-atomic", +] [[package]] name = "opaque-debug" @@ -2474,9 +2433,9 @@ dependencies = [ [[package]] name = "prometheus-client" -version = "0.22.3" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "504ee9ff529add891127c4827eb481bd69dc0ebc72e9a682e187db4caa60c3ca" +checksum = "cf41c1a7c32ed72abe5082fb19505b969095c12da9f5732a4bc9878757fd087c" dependencies = [ "dtoa", "itoa", @@ -2814,6 +2773,7 @@ dependencies = [ "futures", "hostname", "libp2p", + "libp2p-gossipsub", "log", "serde", "serde_json", @@ -2881,7 +2841,7 @@ dependencies = [ "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.103.3", + "rustls-webpki", "subtle", "zeroize", ] @@ -2896,16 +2856,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rustls-webpki" -version = "0.101.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "rustls-webpki" version = "0.103.3" @@ -3668,9 +3618,21 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.4" +version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.2", +] + +[[package]] +name = "webpki-roots" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" +dependencies = [ + "rustls-pki-types", +] [[package]] name = "widestring" @@ -3980,9 +3942,9 @@ dependencies = [ [[package]] name = "x509-parser" -version = "0.16.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69" +checksum = "4569f339c0c402346d4a75a9e39cf8dad310e287eef1ff56d4c68e5067f53460" dependencies = [ "asn1-rs", "data-encoding", @@ -3991,7 +3953,7 @@ dependencies = [ "nom", "oid-registry", "rusticata-macros", - "thiserror 1.0.69", + "thiserror 2.0.12", "time", ] diff --git a/gossipsub-interop/rust-libp2p/Cargo.toml b/gossipsub-interop/rust-libp2p/Cargo.toml index 0eaa0523a..100e4b906 100644 --- a/gossipsub-interop/rust-libp2p/Cargo.toml +++ b/gossipsub-interop/rust-libp2p/Cargo.toml @@ -4,8 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -libp2p = { version = "0.55.0", features = [ - "gossipsub", +libp2p = { version = "0.56.0", features = [ "dns", "tcp", "tokio", @@ -34,3 +33,8 @@ byteorder = "1.4.3" hostname = "0.3.1" ed25519-dalek = "1.0.1" thiserror = "2.0.12" + +[dependencies.libp2p-gossipsub] +git = "https://github.com/jxs/rust-libp2p/" +branch = "gossipsub-partial-messages" +features = ["partial_messages"] diff --git a/gossipsub-interop/rust-libp2p/src/bitmap.rs b/gossipsub-interop/rust-libp2p/src/bitmap.rs new file mode 100644 index 000000000..365925e6a --- /dev/null +++ b/gossipsub-interop/rust-libp2p/src/bitmap.rs @@ -0,0 +1,170 @@ +use libp2p_gossipsub::{Partial, PartialMessageError}; + +/// A fixed-size bitmap composed of `TOTAL_FIELDS` fields, +/// each field storing `FIELD_SIZE` bytes (i.e., `FIELD_SIZE * 8` bits). +#[derive(Debug, Clone)] +pub(crate) struct Bitmap { + fields: [[u8; 1024]; 8], + group_id: [u8; 8], +} + +impl Bitmap { + pub(crate) fn new(group_id: [u8; 8]) -> Self { + Self { + fields: [[0; 1024]; 8], + group_id, + } + } + pub(crate) fn fill_parts(&mut self, metadata: u8) { + let mut parts = [[0u8; 1024]; 8]; + + // Convert group_id to u64 using big-endian + let start = u64::from_be_bytes(self.group_id); + + for (i, p) in parts.iter_mut().enumerate() { + if (metadata & (1 << i)) == 0 { + continue; + } + + let mut counter = start + (i as u64) * (1024 / 8); + let mut part = [0u8; 1024]; + + for j in 0..(1024 / 8) { + let bytes = counter.to_be_bytes(); + let offset = j * 8; + part[offset..offset + 8].copy_from_slice(&bytes); + counter += 1; + } + + *p = part; + } + } +} + +impl Partial for Bitmap { + fn group_id(&self) -> impl AsRef<[u8]> { + &self.group_id + } + + fn missing_parts(&self) -> Option> { + let Some(available) = self.available_parts() else { + return Some([255; 1]); + }; + let missing = !available.as_ref()[0]; + if missing == 0 { + None + } else { + Some([missing; 1]) + } + } + + fn available_parts(&self) -> Option> { + let mut available = [0; 8]; + let mut filled = false; + + for (i, field) in self.fields.iter().enumerate() { + if !field.is_empty() { + filled = true; + available[0] |= 1 << i; + } + } + filled.then_some(available) + } + + fn partial_message_bytes_from_metadata( + &self, + metadata: impl AsRef<[u8]>, + ) -> Result<(impl AsRef<[u8]>, Option>), PartialMessageError> { + let mut metadata = metadata.as_ref(); + if metadata.is_empty() { + metadata = &[0xff]; + } + + if metadata.len() != 1 { + return Err(PartialMessageError::InvalidFormat); + } + + let bitmap = metadata[0]; + let mut response_bitmap: u8 = 0; + let mut remaining = bitmap; + + // Estimate output size: 1 byte header + FIELD_SIZE * num parts + group_id + let part_count = bitmap.count_ones() as usize; + let mut data = Vec::with_capacity(1 + 1024 * part_count + self.group_id.len()); + + data.push(0); + + for (i, field) in self.fields.iter().enumerate() { + if (bitmap >> i) & 1 == 0 { + continue; + } + + if field.iter().all(|&b| b == 0) { + continue; // Not available + } + + response_bitmap |= 1 << i; + remaining ^= 1 << i; + + data.extend_from_slice(field); + } + + if response_bitmap == 0 { + return Ok((Vec::::new(), Some(vec![bitmap]))); + } + + // Set the correct bitmap in the first byte + data[0] = response_bitmap; + data.extend_from_slice(&self.group_id); + + let remaining = if remaining == 0 { + None + } else { + Some(vec![remaining]) + }; + + Ok((data, remaining)) + } + + fn extend_from_encoded_partial_message( + &mut self, + data: &[u8], + ) -> Result<(), PartialMessageError> { + if data.len() < 1 + self.group_id.len() { + return Err(PartialMessageError::InvalidFormat); + } + + let bitmap = data[0]; + let data = &data[1..]; + let (data, group_id) = data.split_at(data.len() - self.group_id.len()); + if group_id != self.group_id { + return Err(PartialMessageError::WrongGroup { + received: group_id.to_vec(), + }); + } + + if data.len() % 1024 != 0 { + return Err(PartialMessageError::InvalidFormat); + } + + let mut offset = 0; + for (i, field) in self.fields.iter_mut().enumerate() { + if (bitmap >> i) & 1 == 0 { + continue; + } + + if field.iter().any(|&b| b != 0) { + continue; + } + + if offset + 1024 > data.len() { + return Err(PartialMessageError::InvalidFormat); + } + + field.copy_from_slice(&data[offset..offset + 1024]); + offset += 1024; + } + + Ok(()) + } +} diff --git a/gossipsub-interop/rust-libp2p/src/experiment.rs b/gossipsub-interop/rust-libp2p/src/experiment.rs index ae01e3d4f..42b5b0b4c 100644 --- a/gossipsub-interop/rust-libp2p/src/experiment.rs +++ b/gossipsub-interop/rust-libp2p/src/experiment.rs @@ -1,14 +1,15 @@ use byteorder::{BigEndian, ByteOrder}; use futures::channel::mpsc; use futures::{SinkExt, StreamExt}; -use libp2p::gossipsub::{self, IdentTopic}; use libp2p::swarm::{NetworkBehaviour, SwarmEvent}; use libp2p::{identify, Swarm}; +use libp2p_gossipsub::{self as gossipsub, IdentTopic}; use slog::{error, info, Logger}; use std::collections::HashMap; use std::time::{Duration, Instant}; use tokio::time::sleep; +use crate::bitmap::Bitmap; use crate::connector; use crate::script_instruction::{ExperimentParams, NodeID, ScriptInstruction}; @@ -39,6 +40,7 @@ pub struct ScriptedNode { topics: HashMap, topic_validation_delays: HashMap, start_time: Instant, + partials: HashMap>, } impl ScriptedNode { @@ -61,6 +63,7 @@ impl ScriptedNode { start_time, gossipsub_validation_rx, gossipsub_validation_tx, + partials: HashMap::new(), } } @@ -244,6 +247,36 @@ impl ScriptedNode { "InitGossipSub instruction already processed" ); } + ScriptInstruction::AddPartialMessage { + parts, + topic_id, + group_id, + .. + } => { + let topic_partials = self.partials.entry(topic_id).or_default(); + let group_id = group_id.to_be_bytes(); + let mut partial = Bitmap::new(group_id); + partial.fill_parts(parts); + topic_partials.insert(group_id, partial); + } + ScriptInstruction::PublishPartial { + topic_id, group_id, .. + } => { + let group_id = group_id.to_be_bytes(); + let topic_partials = self + .partials + .get(&topic_id) + .ok_or(format!("Topic {topic_id} doesn't exist"))?; + + let partial = topic_partials + .get(&group_id) + .ok_or(format!("GroupId {group_id:?} doesn't exist"))?; + let topic = IdentTopic::new(topic_id); + self.swarm + .behaviour_mut() + .gossipsub + .publish_partial(topic, partial.clone())?; + } } Ok(()) diff --git a/gossipsub-interop/rust-libp2p/src/main.rs b/gossipsub-interop/rust-libp2p/src/main.rs index 197bec11c..8580c66b5 100644 --- a/gossipsub-interop/rust-libp2p/src/main.rs +++ b/gossipsub-interop/rust-libp2p/src/main.rs @@ -1,15 +1,13 @@ use clap::Parser; use libp2p::{ - core::upgrade, - gossipsub::{self, MessageAuthenticity, MessageId, ValidationMode}, - identify, - identity::Keypair, - noise, tcp, yamux, PeerId, Swarm, Transport, + core::upgrade, identify, identity::Keypair, noise, tcp, yamux, PeerId, Swarm, Transport, }; +use libp2p_gossipsub::{self, MessageAuthenticity, MessageId, ValidationMode}; use slog::{o, Drain, FnValue, Logger, PushFnValue, Record}; use std::time::Instant; use tracing_subscriber::{layer::SubscriberExt, Layer}; +mod bitmap; mod connector; mod experiment; mod log_filter; @@ -116,7 +114,7 @@ async fn main() -> Result<(), Box> { ); params.into() } - None => gossipsub::ConfigBuilder::default(), + None => libp2p_gossipsub::ConfigBuilder::default(), }; config_builder .validation_mode(ValidationMode::Anonymous) @@ -127,7 +125,8 @@ async fn main() -> Result<(), Box> { // Create gossipsub configuration let gossipsub_config = config_builder.build().expect("Valid gossipsub config"); // Create gossipsub behavior - let gossipsub = gossipsub::Behaviour::new(MessageAuthenticity::Anonymous, gossipsub_config)?; + let gossipsub = + libp2p_gossipsub::Behaviour::new(MessageAuthenticity::Anonymous, gossipsub_config)?; let identify = identify::Behaviour::new(identify::Config::new( "/interop/1.0.0".into(), local_key.public(), diff --git a/gossipsub-interop/rust-libp2p/src/script_instruction.rs b/gossipsub-interop/rust-libp2p/src/script_instruction.rs index 1ae63464b..fc5f5c389 100644 --- a/gossipsub-interop/rust-libp2p/src/script_instruction.rs +++ b/gossipsub-interop/rust-libp2p/src/script_instruction.rs @@ -4,8 +4,8 @@ use std::fmt::Display; use std::{path::Path, time::Duration}; use byteorder::{ByteOrder, LittleEndian}; -use libp2p::gossipsub::ConfigBuilder; use libp2p::identity::Keypair; +use libp2p_gossipsub::ConfigBuilder; use serde::{Deserialize, Serialize}; /// NodeID is a unique identifier for a node in the network. @@ -90,6 +90,24 @@ pub enum ScriptInstruction { InitGossipSub { gossip_sub_params: Box, }, + #[serde(rename = "addPartialMessage", rename_all = "camelCase")] + AddPartialMessage { + #[serde(rename = "r#type")] + message_type: String, + parts: u8, + #[serde(rename = "topicID")] + topic_id: String, + #[serde(rename = "groupID")] + group_id: u64, + }, + #[serde(rename = "publishPartial", rename_all = "camelCase")] + PublishPartial { + #[serde(rename = "r#type")] + message_type: String, + topic_id: String, + #[serde(rename = "groupID")] + group_id: u64, + }, } /// ExperimentParams contains all parameters for an experiment. @@ -246,7 +264,7 @@ impl From for ConfigBuilder { builder.prune_backoff(Duration::from_nanos(prune_backoff as u64)); } if let Some(unsubscribe_backoff) = params.unsubscribe_backoff { - builder.unsubscribe_backoff(Duration::from_nanos(unsubscribe_backoff as u64).as_secs()); + builder.unsubscribe_backoff(Duration::from_nanos(unsubscribe_backoff as u64)); } if let Some(opportunistic_graft_ticks) = params.opportunistic_graft_ticks { builder.opportunistic_graft_ticks(opportunistic_graft_ticks); From 813a6df55b44aaed77d73df3526126cdef4c75fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveira?= Date: Fri, 26 Sep 2025 23:20:59 +0100 Subject: [PATCH 03/28] remove instructions type field --- gossipsub-interop/rust-libp2p/src/script_instruction.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/gossipsub-interop/rust-libp2p/src/script_instruction.rs b/gossipsub-interop/rust-libp2p/src/script_instruction.rs index fc5f5c389..8d8baa4dd 100644 --- a/gossipsub-interop/rust-libp2p/src/script_instruction.rs +++ b/gossipsub-interop/rust-libp2p/src/script_instruction.rs @@ -92,8 +92,6 @@ pub enum ScriptInstruction { }, #[serde(rename = "addPartialMessage", rename_all = "camelCase")] AddPartialMessage { - #[serde(rename = "r#type")] - message_type: String, parts: u8, #[serde(rename = "topicID")] topic_id: String, @@ -102,8 +100,6 @@ pub enum ScriptInstruction { }, #[serde(rename = "publishPartial", rename_all = "camelCase")] PublishPartial { - #[serde(rename = "r#type")] - message_type: String, topic_id: String, #[serde(rename = "groupID")] group_id: u64, From 130dcc2e7fb7aa19b8198eae248ab2650834cb67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveira?= Date: Mon, 29 Sep 2025 17:57:32 +0100 Subject: [PATCH 04/28] fix PublishPartial group_id rename --- gossipsub-interop/rust-libp2p/src/script_instruction.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/gossipsub-interop/rust-libp2p/src/script_instruction.rs b/gossipsub-interop/rust-libp2p/src/script_instruction.rs index 8d8baa4dd..156443248 100644 --- a/gossipsub-interop/rust-libp2p/src/script_instruction.rs +++ b/gossipsub-interop/rust-libp2p/src/script_instruction.rs @@ -100,6 +100,7 @@ pub enum ScriptInstruction { }, #[serde(rename = "publishPartial", rename_all = "camelCase")] PublishPartial { + #[serde(rename = "topicID")] topic_id: String, #[serde(rename = "groupID")] group_id: u64, From 98268aa2583bfd63b7d6bc6529489f3daa754cc1 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Mon, 29 Sep 2025 11:39:22 -0700 Subject: [PATCH 05/28] gossipsub-interop: partial messages, special case 2 nodes --- gossipsub-interop/experiment.py | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/gossipsub-interop/experiment.py b/gossipsub-interop/experiment.py index f33dfa9fe..6117f53e9 100644 --- a/gossipsub-interop/experiment.py +++ b/gossipsub-interop/experiment.py @@ -65,16 +65,36 @@ def partial_message_scenario( instructions.append(script_instruction.WaitUntil(elapsedSeconds=elapsed_seconds)) # Assign random parts to each node - for i in range(node_count): - parts = random.randint(0, 255) + if node_count == 2: + # If just two nodes, make sure we can always generate a full message + part = random.randint(0, 255) instructions.append( script_instruction.IfNodeIDEquals( - nodeID=i, + nodeID=0, instruction=script_instruction.AddPartialMessage( - topicID=topic, groupID=groupID, parts=parts + topicID=topic, groupID=groupID, parts=part ), ) ) + instructions.append( + script_instruction.IfNodeIDEquals( + nodeID=1, + instruction=script_instruction.AddPartialMessage( + topicID=topic, groupID=groupID, parts=(0xFF ^ part) + ), + ) + ) + else: + for i in range(node_count): + parts = random.randint(0, 255) + instructions.append( + script_instruction.IfNodeIDEquals( + nodeID=i, + instruction=script_instruction.AddPartialMessage( + topicID=topic, groupID=groupID, parts=parts + ), + ) + ) instructions.append( script_instruction.PublishPartial(topicID=topic, groupID=groupID) From 41949a49264f6257dbebd7d661925a30e4833533 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Mon, 29 Sep 2025 11:40:20 -0700 Subject: [PATCH 06/28] fixup add partial to gossipsub interop subscript tester --- gossipsub-interop/script_instruction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gossipsub-interop/script_instruction.py b/gossipsub-interop/script_instruction.py index 721d595eb..b34dbce4c 100644 --- a/gossipsub-interop/script_instruction.py +++ b/gossipsub-interop/script_instruction.py @@ -54,7 +54,7 @@ class Publish(BaseModel): class SubscribeToTopic(BaseModel): type: Literal["subscribeToTopic"] = "subscribeToTopic" topicID: str - partial: bool + partial: bool = False class SetTopicValidationDelay(BaseModel): From 25fa6fd2789d994e3de99379aeb9d0a50677e57e Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Mon, 29 Sep 2025 11:41:15 -0700 Subject: [PATCH 07/28] workaround for rust bug --- gossipsub-interop/experiment.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gossipsub-interop/experiment.py b/gossipsub-interop/experiment.py index 6117f53e9..257bd015c 100644 --- a/gossipsub-interop/experiment.py +++ b/gossipsub-interop/experiment.py @@ -53,6 +53,9 @@ def partial_message_scenario( number_of_conns_per_node = node_count - 1 instructions.extend(random_network_mesh(node_count, number_of_conns_per_node)) + # Deal with rust not sending extensions before subscriptions (BUG!) + elapsed_seconds = 10 + instructions.append(script_instruction.WaitUntil(elapsedSeconds=elapsed_seconds)) topic = "a-subnet" instructions.append( script_instruction.SubscribeToTopic(topicID=topic, partial=True) From f2de541688101811664e694dc5ca25c460c483e0 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Mon, 29 Sep 2025 11:41:25 -0700 Subject: [PATCH 08/28] gossipsub-interop(rust-libp2p): fix instruction parsing From b6870258f30b6f21a11728966c9b5603f8575280 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Mon, 29 Sep 2025 11:42:01 -0700 Subject: [PATCH 09/28] gossipsub-interop(rust-libp2p): Fix cargo warning --- gossipsub-interop/rust-libp2p/src/log_filter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gossipsub-interop/rust-libp2p/src/log_filter.rs b/gossipsub-interop/rust-libp2p/src/log_filter.rs index 45106482b..471082e99 100644 --- a/gossipsub-interop/rust-libp2p/src/log_filter.rs +++ b/gossipsub-interop/rust-libp2p/src/log_filter.rs @@ -63,7 +63,7 @@ pub fn gossipsub_filter( #[test] fn test_layer_transforms_duplicate_trace() { - use libp2p::gossipsub::MessageId; + use libp2p_gossipsub::MessageId; use slog::{o, Drain, FnValue, PushFnValue, Record}; let stdout_drain = slog_json::Json::new(std::io::stdout()) From b656686fea28891717c4044971ab1aca45da7ec0 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Mon, 29 Sep 2025 11:42:22 -0700 Subject: [PATCH 10/28] gossipsub-interop(rust-libp2p): fix bitmap's available/missing methods --- gossipsub-interop/rust-libp2p/src/bitmap.rs | 32 ++++++--------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/gossipsub-interop/rust-libp2p/src/bitmap.rs b/gossipsub-interop/rust-libp2p/src/bitmap.rs index 365925e6a..5e502e4be 100644 --- a/gossipsub-interop/rust-libp2p/src/bitmap.rs +++ b/gossipsub-interop/rust-libp2p/src/bitmap.rs @@ -5,6 +5,7 @@ use libp2p_gossipsub::{Partial, PartialMessageError}; #[derive(Debug, Clone)] pub(crate) struct Bitmap { fields: [[u8; 1024]; 8], + set: u8, group_id: [u8; 8], } @@ -12,6 +13,7 @@ impl Bitmap { pub(crate) fn new(group_id: [u8; 8]) -> Self { Self { fields: [[0; 1024]; 8], + set: 0, group_id, } } @@ -20,6 +22,7 @@ impl Bitmap { // Convert group_id to u64 using big-endian let start = u64::from_be_bytes(self.group_id); + self.set |= metadata; for (i, p) in parts.iter_mut().enumerate() { if (metadata & (1 << i)) == 0 { @@ -47,28 +50,11 @@ impl Partial for Bitmap { } fn missing_parts(&self) -> Option> { - let Some(available) = self.available_parts() else { - return Some([255; 1]); - }; - let missing = !available.as_ref()[0]; - if missing == 0 { - None - } else { - Some([missing; 1]) - } + Some([0xff ^ self.set; 1]) } fn available_parts(&self) -> Option> { - let mut available = [0; 8]; - let mut filled = false; - - for (i, field) in self.fields.iter().enumerate() { - if !field.is_empty() { - filled = true; - available[0] |= 1 << i; - } - } - filled.then_some(available) + Some([self.set; 1]) } fn partial_message_bytes_from_metadata( @@ -98,8 +84,7 @@ impl Partial for Bitmap { if (bitmap >> i) & 1 == 0 { continue; } - - if field.iter().all(|&b| b == 0) { + if (self.set >> i) & 1 == 0 { continue; // Not available } @@ -153,14 +138,15 @@ impl Partial for Bitmap { continue; } - if field.iter().any(|&b| b != 0) { - continue; + if (self.set >> i) & 1 == 1 { + continue; // we already ahve this } if offset + 1024 > data.len() { return Err(PartialMessageError::InvalidFormat); } + self.set |= 1 << i; field.copy_from_slice(&data[offset..offset + 1024]); offset += 1024; } From d4071afd3f4495cccb73b9e24b959c8ef26290f4 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Mon, 29 Sep 2025 11:42:58 -0700 Subject: [PATCH 11/28] gossipsub-interop(rust-libp2p): implement republishing logic --- .../rust-libp2p/src/experiment.rs | 105 +++++++++++++----- 1 file changed, 76 insertions(+), 29 deletions(-) diff --git a/gossipsub-interop/rust-libp2p/src/experiment.rs b/gossipsub-interop/rust-libp2p/src/experiment.rs index 42b5b0b4c..6edd8ffe1 100644 --- a/gossipsub-interop/rust-libp2p/src/experiment.rs +++ b/gossipsub-interop/rust-libp2p/src/experiment.rs @@ -3,7 +3,7 @@ use futures::channel::mpsc; use futures::{SinkExt, StreamExt}; use libp2p::swarm::{NetworkBehaviour, SwarmEvent}; use libp2p::{identify, Swarm}; -use libp2p_gossipsub::{self as gossipsub, IdentTopic}; +use libp2p_gossipsub::{self as gossipsub, IdentTopic, Partial}; use slog::{error, info, Logger}; use std::collections::HashMap; use std::time::{Duration, Instant}; @@ -144,40 +144,87 @@ impl ScriptedNode { } event = self.swarm.select_next_some() => { // Process any messages that arrive during sleep - if let SwarmEvent::Behaviour(MyBehaviorEvent::Gossipsub(gossipsub::Event::Message { - propagation_source: peer_id, - message_id, - message, - })) = event { - let topic = message.topic.into_string(); - if message.data.len() >= 8 { - info!(self.stdout_logger, "Received Message"; - "topic" => &topic, - "id" => format_message_id(&message.data), - "from" => peer_id.to_string()); - } - // Shadow doesn’t model CPU execution time, - // instructions execute instantly in the simulations. - // Usually in lighthouse blob verification takes ~5ms, - // so calling `thread::sleep` aims at replicating the same behaviour. - // See https://github.com/shadow/shadow/issues/2060 for more info. + match event { + SwarmEvent::Behaviour(MyBehaviorEvent::Gossipsub(gossipsub::Event::Message { + propagation_source: peer_id, + message_id, + message, + })) => { + let topic = message.topic.into_string(); + if message.data.len() >= 8 { + info!(self.stdout_logger, "Received Message"; + "topic" => &topic, + "id" => format_message_id(&message.data), + "from" => peer_id.to_string()); + } + // Shadow doesn’t model CPU execution time, + // instructions execute instantly in the simulations. + // Usually in lighthouse blob verification takes ~5ms, + // so calling `thread::sleep` aims at replicating the same behaviour. + // See https://github.com/shadow/shadow/issues/2060 for more info. - let mut tx = self.gossipsub_validation_tx.clone(); - if let Some(&delay) = self.topic_validation_delays.get(&topic) { - tokio::spawn(async move { - sleep(delay).await; + let mut tx = self.gossipsub_validation_tx.clone(); + if let Some(&delay) = self.topic_validation_delays.get(&topic) { + tokio::spawn(async move { + sleep(delay).await; + tx.send(ValidationResult { + peer_id, + msg_id: message_id, + result: gossipsub::MessageAcceptance::Accept, + }).await.unwrap(); + }); + } else { tx.send(ValidationResult { peer_id, msg_id: message_id, result: gossipsub::MessageAcceptance::Accept, }).await.unwrap(); - }); - } else { - tx.send(ValidationResult { - peer_id, - msg_id: message_id, - result: gossipsub::MessageAcceptance::Accept, - }).await.unwrap(); + } + } + SwarmEvent::Behaviour(MyBehaviorEvent::Gossipsub(gossipsub::Event::Partial {group_id, topic_id, propagation_source, message, iwant: _, ihave })) => { + let topic_partials = self + .partials + .get_mut(topic_id.as_str()) + .ok_or(format!("Topic {topic_id} doesn't exist"))?; + let partial = topic_partials + .get_mut(group_id.as_slice()) + .ok_or(format!("GroupId {group_id:?} doesn't exist"))?; + + let before_extension = partial.available_parts().map(|x| x.as_ref().to_vec()); + if let Some(message) = message { + if !message.is_empty() { + info!(self.stderr_logger, "new data len is {}", message.len()); + partial.extend_from_encoded_partial_message(&message)?; + } + } + let after_extension = partial.available_parts().map(|x| x.as_ref().to_vec()); + + let mut should_republish = false; + if before_extension != after_extension { + info!(self.stderr_logger, "Got new data. Will republish. {before_extension:?} {after_extension:?}"); + if after_extension == Some(Vec::::from([255])) { + info!(self.stdout_logger, "Received Full Partial Message"; + // "topic" => topic_id, + // "group_id" => group_id, + "from" => propagation_source.to_string()); + } + + should_republish = true; + } + if !should_republish && ihave != after_extension { + info!(self.stderr_logger, "I have something the peer doesn't or vice versa. {ihave:?} {after_extension:?}"); + should_republish = true; + } + + if should_republish { + self.swarm + .behaviour_mut() + .gossipsub + .publish_partial(topic_id, partial.clone())?; + } + } + ev => { + info!(self.stderr_logger, "Some other event, {:?}", ev) } } } From fc64c48c71ef974c682e6b3e7ad316dc027d4685 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Mon, 29 Sep 2025 11:44:16 -0700 Subject: [PATCH 12/28] debug print lines go-libp2p debugging lines debug print rust --- gossipsub-interop/go-libp2p/experiment.go | 4 +++- gossipsub-interop/go-libp2p/main.go | 5 ++++- gossipsub-interop/network_graph.py | 2 +- gossipsub-interop/rust-libp2p/src/experiment.rs | 14 ++++++++++++++ 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/gossipsub-interop/go-libp2p/experiment.go b/gossipsub-interop/go-libp2p/experiment.go index be0116e37..aef2fdc15 100644 --- a/gossipsub-interop/go-libp2p/experiment.go +++ b/gossipsub-interop/go-libp2p/experiment.go @@ -3,6 +3,7 @@ package main import ( "context" "encoding/binary" + "encoding/hex" "fmt" "log" "log/slog" @@ -81,8 +82,8 @@ func (m *partialMsgManager) run() { m.Info("Adding partial message") m.addMsg(req) case req := <-m.publish: - m.Info("publishing partial message") pm := m.partialMessages[req.topic][string(req.groupID)] + m.Info("publishing partial message", "groupID", hex.EncodeToString(pm.GroupID()), "topic", req.topic) m.pubsub.PublishPartialMessage(req.topic, pm, partialmessages.PublishOptions{}) case <-m.done: return @@ -187,6 +188,7 @@ func (n *scriptedNode) runInstruction(ctx context.Context, instruction ScriptIns // Process each script instruction switch a := instruction.(type) { case InitGossipSubInstruction: + slog.SetLogLoggerLevel(slog.LevelDebug) pme := &partialmessages.PartialMessageExtension{ Logger: slog.Default(), ValidateRPC: func(from peer.ID, rpc *pubsub_pb.PartialMessagesExtension) error { diff --git a/gossipsub-interop/go-libp2p/main.go b/gossipsub-interop/go-libp2p/main.go index 17b3cb491..fd66c02f3 100644 --- a/gossipsub-interop/go-libp2p/main.go +++ b/gossipsub-interop/go-libp2p/main.go @@ -42,6 +42,7 @@ func pubsubOptions(slogger *slog.Logger, params pubsub.GossipSubParams, pme *par pubsub.WithMaxMessageSize(10 * 1 << 20), pubsub.WithGossipSubParams(params), pubsub.WithEventTracer(&tr), + pubsub.WithRPCLogger(slogger), } if pme != nil { @@ -125,7 +126,9 @@ func main() { } logger := log.New(os.Stderr, "", log.LstdFlags|log.Lmicroseconds) - slogger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) + slogger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ + Level: slog.LevelDebug, + })) connector := &ShadowConnector{} err = RunExperiment(ctx, startTime, logger, slogger, h, nodeId, connector, params) diff --git a/gossipsub-interop/network_graph.py b/gossipsub-interop/network_graph.py index 12f3a35f7..e63e6da85 100644 --- a/gossipsub-interop/network_graph.py +++ b/gossipsub-interop/network_graph.py @@ -177,7 +177,7 @@ def generate_graph( # For Debugging: "environment": { # "GOLOG_LOG_LEVEL": "debug", - # "RUST_LOG": "debug", + "RUST_LOG": "debug", }, "path": binary_path, } diff --git a/gossipsub-interop/rust-libp2p/src/experiment.rs b/gossipsub-interop/rust-libp2p/src/experiment.rs index 6edd8ffe1..cc9cd312e 100644 --- a/gossipsub-interop/rust-libp2p/src/experiment.rs +++ b/gossipsub-interop/rust-libp2p/src/experiment.rs @@ -215,6 +215,8 @@ impl ScriptedNode { info!(self.stderr_logger, "I have something the peer doesn't or vice versa. {ihave:?} {after_extension:?}"); should_republish = true; } + info!(self.stderr_logger, "I have {:?}", after_extension); + info!(self.stderr_logger, "Peer has {:?}", ihave); if should_republish { self.swarm @@ -303,7 +305,15 @@ impl ScriptedNode { let topic_partials = self.partials.entry(topic_id).or_default(); let group_id = group_id.to_be_bytes(); let mut partial = Bitmap::new(group_id); + info!( + self.stderr_logger, + "partial message for group {group_id:?} parts {parts:?}" + ); partial.fill_parts(parts); + let avail = partial + .available_parts() + .map(|parts| parts.as_ref().to_vec()); + info!(self.stderr_logger, "available parts: {avail:?}"); topic_partials.insert(group_id, partial); } ScriptInstruction::PublishPartial { @@ -319,6 +329,10 @@ impl ScriptedNode { .get(&group_id) .ok_or(format!("GroupId {group_id:?} doesn't exist"))?; let topic = IdentTopic::new(topic_id); + info!(self.stdout_logger, "Publish Partial called"; + "group_id" => format!("{group_id:?}"), + "topic_id" => format!("{topic:?}"), + ); self.swarm .behaviour_mut() .gossipsub From 315a1e86a785ff1bd5802b2e136d0256924111f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveira?= Date: Mon, 29 Sep 2025 22:59:59 +0100 Subject: [PATCH 13/28] update rust version to latest rust-libp2p --- gossipsub-interop/experiment.py | 3 - gossipsub-interop/rust-libp2p/Cargo.lock | 1085 +++++++++-------- gossipsub-interop/rust-libp2p/src/bitmap.rs | 8 +- .../rust-libp2p/src/experiment.rs | 18 +- 4 files changed, 604 insertions(+), 510 deletions(-) diff --git a/gossipsub-interop/experiment.py b/gossipsub-interop/experiment.py index 257bd015c..6117f53e9 100644 --- a/gossipsub-interop/experiment.py +++ b/gossipsub-interop/experiment.py @@ -53,9 +53,6 @@ def partial_message_scenario( number_of_conns_per_node = node_count - 1 instructions.extend(random_network_mesh(node_count, number_of_conns_per_node)) - # Deal with rust not sending extensions before subscriptions (BUG!) - elapsed_seconds = 10 - instructions.append(script_instruction.WaitUntil(elapsedSeconds=elapsed_seconds)) topic = "a-subnet" instructions.append( script_instruction.SubscribeToTopic(topicID=topic, partial=True) diff --git a/gossipsub-interop/rust-libp2p/Cargo.lock b/gossipsub-interop/rust-libp2p/Cargo.lock index c07302102..188e316b1 100644 --- a/gossipsub-interop/rust-libp2p/Cargo.lock +++ b/gossipsub-interop/rust-libp2p/Cargo.lock @@ -4,18 +4,18 @@ version = 4 [[package]] name = "addr2line" -version = "0.24.2" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" dependencies = [ "gimli", ] [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aead" @@ -67,11 +67,20 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" -version = "0.6.18" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" dependencies = [ "anstyle", "anstyle-parse", @@ -84,44 +93,44 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.2" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.7" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", - "once_cell", - "windows-sys 0.59.0", + "once_cell_polyfill", + "windows-sys 0.60.2", ] [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "arrayref" @@ -141,7 +150,7 @@ dependencies = [ "nom", "num-traits", "rusticata-macros", - "thiserror 2.0.12", + "thiserror 2.0.17", "time", ] @@ -153,7 +162,7 @@ checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", "synstructure", ] @@ -165,14 +174,14 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] name = "async-channel" -version = "2.3.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" dependencies = [ "concurrent-queue", "event-listener-strategy", @@ -182,11 +191,11 @@ dependencies = [ [[package]] name = "async-io" -version = "2.4.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" dependencies = [ - "async-lock", + "autocfg", "cfg-if", "concurrent-queue", "futures-io", @@ -195,30 +204,18 @@ dependencies = [ "polling", "rustix", "slab", - "tracing", - "windows-sys 0.59.0", -] - -[[package]] -name = "async-lock" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" -dependencies = [ - "event-listener", - "event-listener-strategy", - "pin-project-lite", + "windows-sys 0.61.1", ] [[package]] name = "async-trait" -version = "0.1.88" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -254,15 +251,15 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "backtrace" -version = "0.3.75" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" dependencies = [ "addr2line", "cfg-if", @@ -270,7 +267,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -279,6 +276,16 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" +[[package]] +name = "base256emoji" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e9430d9a245a77c92176e649af6e275f20839a48389859d1661e9a128d077c" +dependencies = [ + "const-str", + "match-lookup", +] + [[package]] name = "base64" version = "0.22.1" @@ -287,9 +294,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.7.3" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" [[package]] name = "bitflags" @@ -299,9 +306,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" [[package]] name = "blake2" @@ -341,9 +348,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "byteorder" @@ -359,18 +366,19 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.2.22" +version = "1.2.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1" +checksum = "e1354349954c6fc9cb0deab020f27f783cf0b604e8bb754dc4658ecf0d29c35f" dependencies = [ + "find-msvc-tools", "shlex", ] [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "cfg_aliases" @@ -402,6 +410,19 @@ dependencies = [ "zeroize", ] +[[package]] +name = "chrono" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + [[package]] name = "cipher" version = "0.4.4" @@ -415,9 +436,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.38" +version = "4.5.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" +checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" dependencies = [ "clap_builder", "clap_derive", @@ -425,9 +446,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.38" +version = "4.5.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" +checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" dependencies = [ "anstream", "anstyle", @@ -437,27 +458,27 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.32" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "colorchoice" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "concurrent-queue" @@ -474,6 +495,12 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "const-str" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f421161cb492475f1661ddc9815a745a1c894592070661180fdec3d4872e9c3" + [[package]] name = "core-foundation" version = "0.9.4" @@ -595,7 +622,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -621,7 +648,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" dependencies = [ "data-encoding", - "syn", + "syn 2.0.106", ] [[package]] @@ -650,9 +677,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.4.0" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "a41953f86f8a05768a6cda24def994fd2f424b04ec5c719cf89989779f199071" dependencies = [ "powerfmt", ] @@ -677,27 +704,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - [[package]] name = "displaydoc" version = "0.2.5" @@ -706,19 +712,19 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] name = "dns-lookup" -version = "2.0.4" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5766087c2235fec47fafa4cfecc81e494ee679d0fd4a59887ea0919bfb0e4fc" +checksum = "cf5597a4b7fe5275fc9dcf88ce26326bc8e4cb87d0130f33752d4c5f717793cf" dependencies = [ "cfg-if", "libc", - "socket2", - "windows-sys 0.48.0", + "socket2 0.6.0", + "windows-sys 0.60.2", ] [[package]] @@ -762,9 +768,9 @@ dependencies = [ [[package]] name = "ed25519-dalek" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" dependencies = [ "curve25519-dalek 4.1.3", "ed25519 2.2.3", @@ -789,7 +795,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -800,19 +806,19 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.11" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.1", ] [[package]] name = "event-listener" -version = "5.4.0" +version = "5.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" dependencies = [ "concurrent-queue", "parking", @@ -835,6 +841,12 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +[[package]] +name = "find-msvc-tools" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959" + [[package]] name = "fnv" version = "1.0.7" @@ -849,9 +861,9 @@ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] @@ -917,9 +929,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ "futures-core", "pin-project-lite", @@ -933,7 +945,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -983,19 +995,6 @@ dependencies = [ "slab", ] -[[package]] -name = "generator" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6bd114ceda131d3b1d665eba35788690ad37f5916457286b32ab6fd3c438dd" -dependencies = [ - "cfg-if", - "libc", - "log", - "rustversion", - "windows 0.58.0", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -1026,7 +1025,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "wasm-bindgen", ] @@ -1040,7 +1039,7 @@ dependencies = [ "js-sys", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasi 0.14.7+wasi-0.2.4", "wasm-bindgen", ] @@ -1056,15 +1055,15 @@ dependencies = [ [[package]] name = "gimli" -version = "0.31.1" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" [[package]] name = "h2" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ "atomic-waker", "bytes", @@ -1081,22 +1080,28 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.3" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", "foldhash", ] +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + [[package]] name = "hashlink" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown", + "hashbrown 0.15.5", ] [[package]] @@ -1107,21 +1112,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[package]] -name = "hermit-abi" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" - -[[package]] -name = "hermit-abi" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hex_fmt" @@ -1145,10 +1138,10 @@ dependencies = [ "idna", "ipnet", "once_cell", - "rand 0.9.1", + "rand 0.9.2", "ring", - "socket2", - "thiserror 2.0.12", + "socket2 0.5.10", + "thiserror 2.0.17", "tinyvec", "tokio", "tracing", @@ -1168,10 +1161,10 @@ dependencies = [ "moka", "once_cell", "parking_lot", - "rand 0.9.1", + "rand 0.9.2", "resolv-conf", "smallvec", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tracing", ] @@ -1247,19 +1240,21 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hyper" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" dependencies = [ + "atomic-waker", "bytes", "futures-channel", - "futures-util", + "futures-core", "h2", "http", "http-body", "httparse", "itoa", "pin-project-lite", + "pin-utils", "smallvec", "tokio", "want", @@ -1267,24 +1262,49 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.11" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" +checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" dependencies = [ "bytes", "futures-channel", + "futures-core", "futures-util", "http", "http-body", "hyper", "libc", "pin-project-lite", - "socket2", + "socket2 0.6.0", "tokio", "tower-service", "tracing", ] +[[package]] +name = "iana-time-zone" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core 0.62.1", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "icu_collections" version = "2.0.0" @@ -1334,9 +1354,9 @@ checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2549ca8c7241c82f59c80ba2a6f415d931c5b58d24fb8412caa1a1f02c49139a" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" dependencies = [ "displaydoc", "icu_collections", @@ -1350,9 +1370,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8197e866e47b68f8f7d95249e172903bec06004b18b2937f1095d40a0c57de04" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" [[package]] name = "icu_provider" @@ -1373,9 +1393,9 @@ dependencies = [ [[package]] name = "idna" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", "smallvec", @@ -1422,7 +1442,7 @@ dependencies = [ "rtnetlink", "system-configuration", "tokio", - "windows 0.53.0", + "windows", ] [[package]] @@ -1440,7 +1460,7 @@ dependencies = [ "hyper", "hyper-util", "log", - "rand 0.9.1", + "rand 0.9.2", "tokio", "url", "xmltree", @@ -1448,12 +1468,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.9.0" +version = "2.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.16.0", ] [[package]] @@ -1465,13 +1485,24 @@ dependencies = [ "generic-array", ] +[[package]] +name = "io-uring" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" +dependencies = [ + "bitflags 2.9.4", + "cfg-if", + "libc", +] + [[package]] name = "ipconfig" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "socket2", + "socket2 0.5.10", "widestring", "windows-sys 0.48.0", "winreg", @@ -1489,7 +1520,7 @@ version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ - "hermit-abi 0.5.1", + "hermit-abi", "libc", "windows-sys 0.59.0", ] @@ -1508,9 +1539,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" dependencies = [ "once_cell", "wasm-bindgen", @@ -1524,9 +1555,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.172" +version = "0.2.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" [[package]] name = "libp2p" @@ -1557,7 +1588,7 @@ dependencies = [ "multiaddr", "pin-project", "rw-stream-sink", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -1601,7 +1632,7 @@ dependencies = [ "quick-protobuf", "rand 0.8.5", "rw-stream-sink", - "thiserror 2.0.12", + "thiserror 2.0.17", "tracing", "unsigned-varint 0.8.0", "web-time", @@ -1626,7 +1657,7 @@ dependencies = [ [[package]] name = "libp2p-gossipsub" version = "0.50.0" -source = "git+https://github.com/jxs/rust-libp2p/?branch=gossipsub-partial-messages#45c2bc1d83e685a5972f4e6b862c5e743357e66c" +source = "git+https://github.com/jxs/rust-libp2p/?branch=gossipsub-partial-messages#5e98f1a1689eb74d119fe816b7ed0b4a3ee2d655" dependencies = [ "async-channel", "asynchronous-codec", @@ -1669,7 +1700,7 @@ dependencies = [ "quick-protobuf", "quick-protobuf-codec", "smallvec", - "thiserror 2.0.12", + "thiserror 2.0.17", "tracing", ] @@ -1680,13 +1711,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3104e13b51e4711ff5738caa1fb54467c8604c2e94d607e27745bcf709068774" dependencies = [ "bs58", - "ed25519-dalek 2.1.1", + "ed25519-dalek 2.2.0", "hkdf", "multihash", "quick-protobuf", "rand 0.8.5", "sha2 0.10.9", - "thiserror 2.0.12", + "thiserror 2.0.17", "tracing", "zeroize", ] @@ -1705,7 +1736,7 @@ dependencies = [ "libp2p-swarm", "rand 0.8.5", "smallvec", - "socket2", + "socket2 0.5.10", "tokio", "tracing", ] @@ -1743,7 +1774,7 @@ dependencies = [ "rand 0.8.5", "snow", "static_assertions", - "thiserror 2.0.12", + "thiserror 2.0.17", "tracing", "x25519-dalek", "zeroize", @@ -1765,8 +1796,8 @@ dependencies = [ "rand 0.8.5", "ring", "rustls", - "socket2", - "thiserror 2.0.12", + "socket2 0.5.10", + "thiserror 2.0.17", "tokio", "tracing", ] @@ -1801,7 +1832,7 @@ checksum = "dd297cf53f0cb3dee4d2620bb319ae47ef27c702684309f682bdb7e55a18ae9c" dependencies = [ "heck", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -1815,7 +1846,7 @@ dependencies = [ "if-watch", "libc", "libp2p-core", - "socket2", + "socket2 0.5.10", "tokio", "tracing", ] @@ -1834,7 +1865,7 @@ dependencies = [ "ring", "rustls", "rustls-webpki", - "thiserror 2.0.12", + "thiserror 2.0.17", "x509-parser", "yasna", ] @@ -1869,7 +1900,7 @@ dependencies = [ "pin-project-lite", "rw-stream-sink", "soketto", - "thiserror 2.0.12", + "thiserror 2.0.17", "tracing", "url", "webpki-roots 0.26.11", @@ -1884,27 +1915,17 @@ dependencies = [ "either", "futures", "libp2p-core", - "thiserror 2.0.12", + "thiserror 2.0.17", "tracing", "yamux 0.12.1", - "yamux 0.13.4", -] - -[[package]] -name = "libredox" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" -dependencies = [ - "bitflags 2.9.0", - "libc", + "yamux 0.13.6", ] [[package]] name = "linux-raw-sys" -version = "0.4.15" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" @@ -1914,9 +1935,9 @@ checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", @@ -1924,22 +1945,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.27" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" - -[[package]] -name = "loom" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" -dependencies = [ - "cfg-if", - "generator", - "scoped-tls", - "tracing", - "tracing-subscriber", -] +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "lru" @@ -1947,7 +1955,7 @@ version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown", + "hashbrown 0.15.5", ] [[package]] @@ -1956,6 +1964,17 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +[[package]] +name = "match-lookup" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1265724d8cb29dbbc2b0f06fffb8bf1a8c0cf73a78eede9ba73a4a66c52a981e" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "match_cfg" version = "0.1.0" @@ -1964,18 +1983,18 @@ checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" [[package]] name = "matchers" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" dependencies = [ - "regex-automata 0.1.10", + "regex-automata", ] [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "minimal-lexical" @@ -1985,40 +2004,39 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", ] [[package]] name = "mio" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", ] [[package]] name = "moka" -version = "0.12.10" +version = "0.12.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9321642ca94a4282428e6ea4af8cc2ca4eac48ac7a6a4ea8f33f76d0ce70926" +checksum = "8261cd88c312e0004c1d51baad2980c66528dfdb2bee62003e643a4d8f86b077" dependencies = [ "crossbeam-channel", "crossbeam-epoch", "crossbeam-utils", - "loom", + "equivalent", "parking_lot", "portable-atomic", "rustc_version", "smallvec", "tagptr", - "thiserror 1.0.69", "uuid", ] @@ -2043,11 +2061,12 @@ dependencies = [ [[package]] name = "multibase" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" +checksum = "8694bb4835f452b0e3bb06dbebb1d6fc5385b6ca1caf2e55fd165c042390ec77" dependencies = [ "base-x", + "base256emoji", "data-encoding", "data-encoding-macro", ] @@ -2124,7 +2143,7 @@ dependencies = [ "log", "netlink-packet-core", "netlink-sys", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -2169,12 +2188,11 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.46.0" +version = "0.50.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" dependencies = [ - "overload", - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -2213,19 +2231,19 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" dependencies = [ - "hermit-abi 0.3.9", + "hermit-abi", "libc", ] [[package]] name = "object" -version = "0.36.7" +version = "0.37.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" dependencies = [ "memchr", ] @@ -2250,16 +2268,16 @@ dependencies = [ ] [[package]] -name = "opaque-debug" -version = "0.3.1" +name = "once_cell_polyfill" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" [[package]] -name = "overload" -version = "0.1.1" +name = "opaque-debug" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "parking" @@ -2269,9 +2287,9 @@ checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", "parking_lot_core", @@ -2279,9 +2297,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", @@ -2308,9 +2326,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pin-project" @@ -2329,7 +2347,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -2356,17 +2374,16 @@ dependencies = [ [[package]] name = "polling" -version = "3.7.4" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi 0.4.0", + "hermit-abi", "pin-project-lite", "rustix", - "tracing", - "windows-sys 0.59.0", + "windows-sys 0.61.1", ] [[package]] @@ -2394,15 +2411,15 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "potential_utf" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" dependencies = [ "zerovec", ] @@ -2424,9 +2441,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -2451,7 +2468,7 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -2478,9 +2495,9 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" dependencies = [ "bytes", "cfg_aliases", @@ -2490,8 +2507,8 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2", - "thiserror 2.0.12", + "socket2 0.6.0", + "thiserror 2.0.17", "tokio", "tracing", "web-time", @@ -2499,20 +2516,20 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.12" +version = "0.11.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" dependencies = [ "bytes", "getrandom 0.3.3", "lru-slab", - "rand 0.9.1", + "rand 0.9.2", "ring", "rustc-hash", "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.12", + "thiserror 2.0.17", "tinyvec", "tracing", "web-time", @@ -2520,32 +2537,32 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.12" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee4e529991f949c5e25755532370b8af5d114acae52326361d68d47af64aa842" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2", + "socket2 0.6.0", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "quote" -version = "1.0.40" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "rand" @@ -2573,9 +2590,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", @@ -2662,73 +2679,47 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.12" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "bitflags 2.9.0", -] - -[[package]] -name = "redox_users" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" -dependencies = [ - "getrandom 0.2.16", - "libredox", - "thiserror 1.0.69", + "bitflags 2.9.4", ] [[package]] name = "regex" -version = "1.11.1" +version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", + "regex-automata", + "regex-syntax", ] [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.5", + "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" [[package]] name = "resolv-conf" -version = "0.7.3" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7c8f7f733062b66dc1c63f9db168ac0b97a9210e247fa90fdc9ad08f51b302" +checksum = "6b3789b30bd25ba102de4beabd95d21ac45b69b1be7d14522bab988c526d6799" [[package]] name = "ring" @@ -2782,7 +2773,7 @@ dependencies = [ "slog-async", "slog-json", "slog-term", - "thiserror 2.0.12", + "thiserror 2.0.17", "time", "tokio", "tracing", @@ -2791,9 +2782,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc-hash" @@ -2821,22 +2812,22 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.44" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.4", "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.61.1", ] [[package]] name = "rustls" -version = "0.23.27" +version = "0.23.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" +checksum = "cd3c25631629d034ce7cd9940adc9d45762d46de2b0f57193c4443b92c6d4d40" dependencies = [ "once_cell", "ring", @@ -2858,9 +2849,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.3" +version = "0.103.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" +checksum = "8572f3c2cb9934231157b45499fc41e1f58c589fdfb81a844ba873265e80f8eb" dependencies = [ "ring", "rustls-pki-types", @@ -2869,9 +2860,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "rw-stream-sink" @@ -2890,12 +2881,6 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "scopeguard" version = "1.2.0" @@ -2904,40 +2889,51 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "itoa", "memchr", "ryu", "serde", + "serde_core", ] [[package]] @@ -2992,9 +2988,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.5" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ "libc", ] @@ -3016,12 +3012,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.9" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "slog" @@ -3055,10 +3048,11 @@ dependencies = [ [[package]] name = "slog-term" -version = "2.9.1" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6e022d0b998abfe5c3782c1f03551a596269450ccd677ea51c56f8b214610e8" +checksum = "5cb1fc680b38eed6fad4c02b3871c09d2c81db8c96aa4e9c0a34904c830f09b5" dependencies = [ + "chrono", "is-terminal", "slog", "term", @@ -3068,9 +3062,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "snow" @@ -3091,14 +3085,24 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", "windows-sys 0.52.0", ] +[[package]] +name = "socket2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "soketto" version = "0.8.1" @@ -3150,9 +3154,20 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.101" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -3167,7 +3182,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -3176,7 +3191,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.4", "core-foundation", "system-configuration-sys", ] @@ -3205,13 +3220,11 @@ checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" [[package]] name = "term" -version = "0.7.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +checksum = "2111ef44dae28680ae9752bb89409e7310ca33a8c621ebe7b106cf5c928b3ac0" dependencies = [ - "dirs-next", - "rustversion", - "winapi", + "windows-sys 0.61.1", ] [[package]] @@ -3225,11 +3238,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl 2.0.17", ] [[package]] @@ -3240,35 +3253,34 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] name = "thread_local" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ "cfg-if", - "once_cell", ] [[package]] name = "time" -version = "0.3.41" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ "deranged", "itoa", @@ -3281,15 +3293,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" dependencies = [ "num-conv", "time-core", @@ -3307,9 +3319,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" dependencies = [ "tinyvec_macros", ] @@ -3322,20 +3334,22 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.45.0" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", "bytes", + "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", + "slab", + "socket2 0.6.0", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3346,14 +3360,14 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] name = "tokio-util" -version = "0.7.15" +version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" dependencies = [ "bytes", "futures-core", @@ -3381,20 +3395,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", "valuable", @@ -3413,14 +3427,14 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ "matchers", "nu-ansi-term", "once_cell", - "regex", + "regex-automata", "sharded-slab", "smallvec", "thread_local", @@ -3443,9 +3457,9 @@ checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" [[package]] name = "universal-hash" @@ -3477,13 +3491,14 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.4" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] [[package]] @@ -3500,11 +3515,13 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.16.0" +version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" dependencies = [ "getrandom 0.3.3", + "js-sys", + "wasm-bindgen", ] [[package]] @@ -3536,49 +3553,60 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" -version = "0.14.2+wasi-0.2.4" +version = "0.14.7+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" +dependencies = [ + "wasip2", +] + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", + "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.100" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" dependencies = [ "bumpalo", "log", "proc-macro2", "quote", - "syn", + "syn 2.0.106", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3586,22 +3614,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" dependencies = [ "unicode-ident", ] @@ -3672,16 +3700,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" -dependencies = [ - "windows-core 0.58.0", - "windows-targets 0.52.6", -] - [[package]] name = "windows-core" version = "0.53.0" @@ -3694,39 +3712,45 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.58.0" +version = "0.62.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +checksum = "6844ee5416b285084d3d3fffd743b925a6c9385455f64f6d4fa3031c4c2749a9" dependencies = [ "windows-implement", "windows-interface", - "windows-result 0.2.0", + "windows-link", + "windows-result 0.4.0", "windows-strings", - "windows-targets 0.52.6", ] [[package]] name = "windows-implement" -version = "0.58.0" +version = "0.60.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +checksum = "edb307e42a74fb6de9bf3a02d9712678b22399c87e6fa869d6dfcd8c1b7754e0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] name = "windows-interface" -version = "0.58.0" +version = "0.59.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +checksum = "c0abd1ddbc6964ac14db11c7213d6532ef34bd9aa042c2e5935f59d7908b46a5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] +[[package]] +name = "windows-link" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" + [[package]] name = "windows-result" version = "0.1.2" @@ -3738,21 +3762,20 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.2.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f" dependencies = [ - "windows-targets 0.52.6", + "windows-link", ] [[package]] name = "windows-strings" -version = "0.1.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda" dependencies = [ - "windows-result 0.2.0", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -3782,6 +3805,24 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.4", +] + +[[package]] +name = "windows-sys" +version = "0.61.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f109e41dd4a3c848907eb83d5a42ea98b3769495597450cf6d153507b166f0f" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -3806,13 +3847,30 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d42b7b7f66d2a06854650af09cfdf8713e427a439c97ad65a6375318033ac4b" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -3825,6 +3883,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -3837,6 +3901,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -3849,12 +3919,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -3867,6 +3949,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -3879,6 +3967,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -3891,6 +3985,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -3903,6 +4003,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "winreg" version = "0.50.0" @@ -3914,13 +4020,10 @@ dependencies = [ ] [[package]] -name = "wit-bindgen-rt" -version = "0.39.0" +name = "wit-bindgen" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags 2.9.0", -] +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "writeable" @@ -3953,15 +4056,15 @@ dependencies = [ "nom", "oid-registry", "rusticata-macros", - "thiserror 2.0.12", + "thiserror 2.0.17", "time", ] [[package]] name = "xml-rs" -version = "0.8.26" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda" +checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7" [[package]] name = "xmltree" @@ -3989,16 +4092,16 @@ dependencies = [ [[package]] name = "yamux" -version = "0.13.4" +version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17610762a1207ee816c6fadc29220904753648aba0a9ed61c7b8336e80a559c4" +checksum = "2b2dd50a6d6115feb3e5d7d0efd45e8ca364b6c83722c1e9c602f5764e0e9597" dependencies = [ "futures", "log", "nohash-hasher", "parking_lot", "pin-project", - "rand 0.8.5", + "rand 0.9.2", "static_assertions", "web-time", ] @@ -4032,28 +4135,28 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.25" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.25" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -4073,15 +4176,15 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", "synstructure", ] [[package]] name = "zeroize" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" dependencies = [ "zeroize_derive", ] @@ -4094,7 +4197,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -4110,9 +4213,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.2" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" dependencies = [ "yoke", "zerofrom", @@ -4127,5 +4230,5 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] diff --git a/gossipsub-interop/rust-libp2p/src/bitmap.rs b/gossipsub-interop/rust-libp2p/src/bitmap.rs index 5e502e4be..33310b754 100644 --- a/gossipsub-interop/rust-libp2p/src/bitmap.rs +++ b/gossipsub-interop/rust-libp2p/src/bitmap.rs @@ -49,12 +49,8 @@ impl Partial for Bitmap { &self.group_id } - fn missing_parts(&self) -> Option> { - Some([0xff ^ self.set; 1]) - } - - fn available_parts(&self) -> Option> { - Some([self.set; 1]) + fn parts_metadata(&self) -> impl AsRef<[u8]> { + [self.set; 1] } fn partial_message_bytes_from_metadata( diff --git a/gossipsub-interop/rust-libp2p/src/experiment.rs b/gossipsub-interop/rust-libp2p/src/experiment.rs index cc9cd312e..42b982a66 100644 --- a/gossipsub-interop/rust-libp2p/src/experiment.rs +++ b/gossipsub-interop/rust-libp2p/src/experiment.rs @@ -181,7 +181,7 @@ impl ScriptedNode { }).await.unwrap(); } } - SwarmEvent::Behaviour(MyBehaviorEvent::Gossipsub(gossipsub::Event::Partial {group_id, topic_id, propagation_source, message, iwant: _, ihave })) => { + SwarmEvent::Behaviour(MyBehaviorEvent::Gossipsub(gossipsub::Event::Partial {group_id, topic_id, propagation_source, message, metadata })) => { let topic_partials = self .partials .get_mut(topic_id.as_str()) @@ -190,19 +190,19 @@ impl ScriptedNode { .get_mut(group_id.as_slice()) .ok_or(format!("GroupId {group_id:?} doesn't exist"))?; - let before_extension = partial.available_parts().map(|x| x.as_ref().to_vec()); + let before_extension = partial.parts_metadata().as_ref().to_vec(); if let Some(message) = message { if !message.is_empty() { info!(self.stderr_logger, "new data len is {}", message.len()); partial.extend_from_encoded_partial_message(&message)?; } } - let after_extension = partial.available_parts().map(|x| x.as_ref().to_vec()); + let after_extension = partial.parts_metadata().as_ref().to_vec(); let mut should_republish = false; if before_extension != after_extension { info!(self.stderr_logger, "Got new data. Will republish. {before_extension:?} {after_extension:?}"); - if after_extension == Some(Vec::::from([255])) { + if after_extension == vec![255] { info!(self.stdout_logger, "Received Full Partial Message"; // "topic" => topic_id, // "group_id" => group_id, @@ -211,12 +211,12 @@ impl ScriptedNode { should_republish = true; } - if !should_republish && ihave != after_extension { - info!(self.stderr_logger, "I have something the peer doesn't or vice versa. {ihave:?} {after_extension:?}"); + if !should_republish && metadata.as_ref() != Some(&after_extension) { + info!(self.stderr_logger, "I have something the peer doesn't or vice versa. {metadata:?} {after_extension:?}"); should_republish = true; } info!(self.stderr_logger, "I have {:?}", after_extension); - info!(self.stderr_logger, "Peer has {:?}", ihave); + info!(self.stderr_logger, "Peer has {:?}", metadata); if should_republish { self.swarm @@ -310,9 +310,7 @@ impl ScriptedNode { "partial message for group {group_id:?} parts {parts:?}" ); partial.fill_parts(parts); - let avail = partial - .available_parts() - .map(|parts| parts.as_ref().to_vec()); + let avail = partial.parts_metadata().as_ref().to_vec(); info!(self.stderr_logger, "available parts: {avail:?}"); topic_partials.insert(group_id, partial); } From ddf72bfd25ba293ec9bfeb93ca0689e4e9fdb7bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveira?= Date: Tue, 30 Sep 2025 16:05:45 +0100 Subject: [PATCH 14/28] update rust-libp2p dep --- gossipsub-interop/rust-libp2p/Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gossipsub-interop/rust-libp2p/Cargo.lock b/gossipsub-interop/rust-libp2p/Cargo.lock index 188e316b1..9519a2c8c 100644 --- a/gossipsub-interop/rust-libp2p/Cargo.lock +++ b/gossipsub-interop/rust-libp2p/Cargo.lock @@ -1657,7 +1657,7 @@ dependencies = [ [[package]] name = "libp2p-gossipsub" version = "0.50.0" -source = "git+https://github.com/jxs/rust-libp2p/?branch=gossipsub-partial-messages#5e98f1a1689eb74d119fe816b7ed0b4a3ee2d655" +source = "git+https://github.com/jxs/rust-libp2p/?branch=gossipsub-partial-messages#f77490cc0a580f133bc9fbeb94c83faefd57369a" dependencies = [ "async-channel", "asynchronous-codec", From eeb6ed14233f04cb74619128c27fc07310f9252b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveira?= Date: Wed, 1 Oct 2025 15:02:25 +0100 Subject: [PATCH 15/28] update for latest rust-libp2p changes --- gossipsub-interop/rust-libp2p/Cargo.lock | 2 +- gossipsub-interop/rust-libp2p/src/experiment.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gossipsub-interop/rust-libp2p/Cargo.lock b/gossipsub-interop/rust-libp2p/Cargo.lock index 9519a2c8c..e71e8933b 100644 --- a/gossipsub-interop/rust-libp2p/Cargo.lock +++ b/gossipsub-interop/rust-libp2p/Cargo.lock @@ -1657,7 +1657,7 @@ dependencies = [ [[package]] name = "libp2p-gossipsub" version = "0.50.0" -source = "git+https://github.com/jxs/rust-libp2p/?branch=gossipsub-partial-messages#f77490cc0a580f133bc9fbeb94c83faefd57369a" +source = "git+https://github.com/jxs/rust-libp2p/?branch=gossipsub-partial-messages#438a297e1e62dba28f6d60234ca09717b82c8fc2" dependencies = [ "async-channel", "asynchronous-codec", diff --git a/gossipsub-interop/rust-libp2p/src/experiment.rs b/gossipsub-interop/rust-libp2p/src/experiment.rs index 42b982a66..260522b8e 100644 --- a/gossipsub-interop/rust-libp2p/src/experiment.rs +++ b/gossipsub-interop/rust-libp2p/src/experiment.rs @@ -267,7 +267,7 @@ impl ScriptedNode { ScriptInstruction::SubscribeToTopic { topic_id } => { let topic = self.get_topic(&topic_id); - match self.swarm.behaviour_mut().gossipsub.subscribe(&topic) { + match self.swarm.behaviour_mut().gossipsub.subscribe(&topic, true) { Ok(_) => { info!(self.stderr_logger, "Subscribed to topic {}", topic_id); } From 2a0c229c50bb8ffaf685ae8ad326001db83d363b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveira?= Date: Wed, 1 Oct 2025 16:37:31 +0100 Subject: [PATCH 16/28] update rust-libp2p sim to the updated scriptparams --- gossipsub-interop/rust-libp2p/src/experiment.rs | 9 +++++++-- gossipsub-interop/rust-libp2p/src/script_instruction.rs | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/gossipsub-interop/rust-libp2p/src/experiment.rs b/gossipsub-interop/rust-libp2p/src/experiment.rs index 260522b8e..7d8c8609e 100644 --- a/gossipsub-interop/rust-libp2p/src/experiment.rs +++ b/gossipsub-interop/rust-libp2p/src/experiment.rs @@ -264,10 +264,15 @@ impl ScriptedNode { } } } - ScriptInstruction::SubscribeToTopic { topic_id } => { + ScriptInstruction::SubscribeToTopic { topic_id, partial } => { let topic = self.get_topic(&topic_id); - match self.swarm.behaviour_mut().gossipsub.subscribe(&topic, true) { + match self + .swarm + .behaviour_mut() + .gossipsub + .subscribe(&topic, partial) + { Ok(_) => { info!(self.stderr_logger, "Subscribed to topic {}", topic_id); } diff --git a/gossipsub-interop/rust-libp2p/src/script_instruction.rs b/gossipsub-interop/rust-libp2p/src/script_instruction.rs index 156443248..cc3eb5ed0 100644 --- a/gossipsub-interop/rust-libp2p/src/script_instruction.rs +++ b/gossipsub-interop/rust-libp2p/src/script_instruction.rs @@ -77,6 +77,7 @@ pub enum ScriptInstruction { SubscribeToTopic { #[serde(rename = "topicID")] topic_id: String, + partial: bool, }, #[serde(rename = "setTopicValidationDelay", rename_all = "camelCase")] From 624bbc469e56c28bc0ef73bec34de0e83152704a Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Wed, 1 Oct 2025 09:38:11 -0700 Subject: [PATCH 17/28] gossipsub-interop: lint fixes --- gossipsub-interop/script_instruction.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/gossipsub-interop/script_instruction.py b/gossipsub-interop/script_instruction.py index b34dbce4c..413e16275 100644 --- a/gossipsub-interop/script_instruction.py +++ b/gossipsub-interop/script_instruction.py @@ -2,8 +2,6 @@ from typing import List, Literal, TypeAlias, Union from pydantic import BaseModel -from dataclasses import Field -from dataclasses import field NodeID: TypeAlias = int From 131b03f050e645775c41f65497ed690ac19c6003 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Wed, 1 Oct 2025 10:05:27 -0700 Subject: [PATCH 18/28] gossipsub-intero(go): update to latest partial message impl --- gossipsub-interop/go-libp2p/experiment.go | 66 ++++++++++----------- gossipsub-interop/go-libp2p/go.mod | 4 +- gossipsub-interop/go-libp2p/go.sum | 12 ++-- gossipsub-interop/go-libp2p/partial.go | 53 +++++------------ gossipsub-interop/go-libp2p/partial_test.go | 7 +++ 5 files changed, 61 insertions(+), 81 deletions(-) diff --git a/gossipsub-interop/go-libp2p/experiment.go b/gossipsub-interop/go-libp2p/experiment.go index aef2fdc15..9010c2ead 100644 --- a/gossipsub-interop/go-libp2p/experiment.go +++ b/gossipsub-interop/go-libp2p/experiment.go @@ -26,12 +26,8 @@ type HostConnector interface { } type incomingPartialRPC struct { - from peer.ID - topic string - groupID []byte - iwant []byte - ihave []byte - partialMessageBytes []byte + pubsub_pb.PartialMessagesExtension + from peer.ID } type partialMsgWithTopic struct { @@ -103,42 +99,49 @@ func (m *partialMsgManager) addMsg(req partialMsgWithTopic) { } func (m *partialMsgManager) handleRPC(rpc incomingPartialRPC) { - _, ok := m.partialMessages[rpc.topic] + _, ok := m.partialMessages[rpc.GetTopicID()] if !ok { - m.partialMessages[rpc.topic] = make(map[string]*PartialMessage) + m.partialMessages[rpc.GetTopicID()] = make(map[string]*PartialMessage) } - pm, ok := m.partialMessages[rpc.topic][string(rpc.groupID)] + pm, ok := m.partialMessages[rpc.GetTopicID()][string(rpc.GroupID)] if !ok { pm = &PartialMessage{} - copy(pm.groupID[:], rpc.groupID) - m.partialMessages[rpc.topic][string(rpc.groupID)] = pm + copy(pm.groupID[:], rpc.GroupID) + m.partialMessages[rpc.GetTopicID()][string(rpc.GroupID)] = pm } // Extend first, so we don't request something we just got. - if len(rpc.partialMessageBytes) != 0 { - pm.Extend(rpc.partialMessageBytes) + beforeExtend := pm.PartsMetadata()[0] + if len(rpc.PartialMessage) != 0 { + err := pm.Extend(rpc.PartialMessage) + if err != nil { + m.Error("Failed to extend partial message", "err", err) + return + } } + afterExtend := pm.PartsMetadata()[0] + var shouldRepublish bool + if beforeExtend != afterExtend { + m.Info("Extended partial message") + shouldRepublish = true - missing, _ := pm.MissingParts() - if len(missing) == 0 { - m.Info("All parts received") } - var shouldRepublish bool - pmHas, _ := pm.AvailableParts() - if len(rpc.iwant) != 0 { - if rpc.iwant[0]&pmHas[0] != 0 { - shouldRepublish = true - } + has := pm.PartsMetadata() + if has[0] == 0xff { + m.Info("All parts received") } - if len(rpc.ihave) != 0 { - if (rpc.ihave[0] & (^pmHas[0])) != 0 { - shouldRepublish = true + + pmHas := pm.PartsMetadata() + if !shouldRepublish && len(rpc.PartsMetadata) == 1 { + shouldRepublish = pmHas[0] != rpc.PartsMetadata[0] + if shouldRepublish { + m.Info("Republishing partial message because a peer has something I want") } } if shouldRepublish { - m.pubsub.PublishPartialMessage(rpc.topic, pm, partialmessages.PublishOptions{}) + m.pubsub.PublishPartialMessage(rpc.GetTopicID(), pm, partialmessages.PublishOptions{}) } } @@ -195,15 +198,12 @@ func (n *scriptedNode) runInstruction(ctx context.Context, instruction ScriptIns // Not doing any validation for now return nil }, - OnIncomingRPC: func(from peer.ID, topic string, groupID, iwant, ihave, partialMessageBytes []byte) { + OnIncomingRPC: func(from peer.ID, rpc *pubsub_pb.PartialMessagesExtension) error { n.partialMsgMgr.incomingRPC <- incomingPartialRPC{ - from: from, - topic: topic, - groupID: groupID, - iwant: iwant, - ihave: ihave, - partialMessageBytes: partialMessageBytes, + from: from, + PartialMessagesExtension: *rpc, } + return nil }, } diff --git a/gossipsub-interop/go-libp2p/go.mod b/gossipsub-interop/go-libp2p/go.mod index a34b62cc4..ea6eabceb 100644 --- a/gossipsub-interop/go-libp2p/go.mod +++ b/gossipsub-interop/go-libp2p/go.mod @@ -5,7 +5,7 @@ go 1.24.1 require ( github.com/libp2p/go-libp2p v0.41.1 // fork with partial messages - github.com/libp2p/go-libp2p-pubsub v0.14.4-0.20250925234347-91dd1a5e4596 + github.com/libp2p/go-libp2p-pubsub v0.14.4-0.20250930192224-4bec5400d65c ) require ( @@ -108,5 +108,5 @@ require ( golang.org/x/text v0.22.0 // indirect golang.org/x/tools v0.30.0 // indirect google.golang.org/protobuf v1.36.5 // indirect - lukechampine.com/blake3 v1.4.0 // indirect + lukechampine.com/blake3 v1.4.1 // indirect ) diff --git a/gossipsub-interop/go-libp2p/go.sum b/gossipsub-interop/go-libp2p/go.sum index 5ee347170..61349cdaf 100644 --- a/gossipsub-interop/go-libp2p/go.sum +++ b/gossipsub-interop/go-libp2p/go.sum @@ -137,12 +137,8 @@ github.com/libp2p/go-libp2p v0.41.1 h1:8ecNQVT5ev/jqALTvisSJeVNvXYJyK4NhQx1nNRXQ github.com/libp2p/go-libp2p v0.41.1/go.mod h1:DcGTovJzQl/I7HMrby5ZRjeD0kQkGiy+9w6aEkSZpRI= github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94= github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8= -github.com/libp2p/go-libp2p-pubsub v0.13.1 h1:tV3ttzzZSCk0EtEXnxVmWIXgjVxXx+20Jwjbs/Ctzjo= -github.com/libp2p/go-libp2p-pubsub v0.13.1/go.mod h1:MKPU5vMI8RRFyTP0HfdsF9cLmL1nHAeJm44AxJGJx44= -github.com/libp2p/go-libp2p-pubsub v0.14.4-0.20250924192223-5297a51ffd50 h1:g5aj1tw4GHg3Oee8pv0/zn5nyxegTD1FuDYfrqXVbUc= -github.com/libp2p/go-libp2p-pubsub v0.14.4-0.20250924192223-5297a51ffd50/go.mod h1:lr4oE8bFgQaifRcoc2uWhWWiK6tPdOEKpUuR408GFN4= -github.com/libp2p/go-libp2p-pubsub v0.14.4-0.20250925234347-91dd1a5e4596 h1:qBAupWsLXtQAd6wJp7nmTkLcwnKsscwR7NKxlPeDFtk= -github.com/libp2p/go-libp2p-pubsub v0.14.4-0.20250925234347-91dd1a5e4596/go.mod h1:lr4oE8bFgQaifRcoc2uWhWWiK6tPdOEKpUuR408GFN4= +github.com/libp2p/go-libp2p-pubsub v0.14.4-0.20250930192224-4bec5400d65c h1:L7Ub8HdUIAgQLoBdJZVH5YLxN2UWDwz0+mHtyVcGsk0= +github.com/libp2p/go-libp2p-pubsub v0.14.4-0.20250930192224-4bec5400d65c/go.mod h1:lr4oE8bFgQaifRcoc2uWhWWiK6tPdOEKpUuR408GFN4= github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg= github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= @@ -530,7 +526,7 @@ grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJd honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -lukechampine.com/blake3 v1.4.0 h1:xDbKOZCVbnZsfzM6mHSYcGRHZ3YrLDzqz8XnV4uaD5w= -lukechampine.com/blake3 v1.4.0/go.mod h1:MQJNQCTnR+kwOP/JEZSxj3MaQjp80FOFSNMMHXcSeX0= +lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg= +lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/gossipsub-interop/go-libp2p/partial.go b/gossipsub-interop/go-libp2p/partial.go index 4fbc8e7eb..e27490549 100644 --- a/gossipsub-interop/go-libp2p/partial.go +++ b/gossipsub-interop/go-libp2p/partial.go @@ -7,7 +7,6 @@ import ( "math/bits" partialmessages "github.com/libp2p/go-libp2p-pubsub/partialmessages" - "github.com/libp2p/go-libp2p/core/peer" ) const partLen = 1024 @@ -17,6 +16,17 @@ type PartialMessage struct { parts [8][]byte // each part is partLen sized or nil if empty } +// PartsMetadata implements partialmessages.PartialMessage. +func (p *PartialMessage) PartsMetadata() []byte { + out := []byte{0} + for i := range p.parts { + if len(p.parts[i]) > 0 { + out[0] |= 1 << i + } + } + return out +} + // FillParts is used to initialize this PartialMessage for testing by filling in // the parts it should have. The algorithm is simple: // - treat the groupID as our starting uint64 number BigEndian @@ -38,32 +48,11 @@ func (p *PartialMessage) FillParts(bitmap byte) error { return nil } -// AvailableParts implements partialmessages.PartialMessage. -func (p *PartialMessage) AvailableParts() ([]byte, error) { - out := []byte{0} - for i := range p.parts { - if len(p.parts[i]) > 0 { - out[0] |= 1 << i - } - } - return out, nil -} - // GroupID implements partialmessages.PartialMessage. func (p *PartialMessage) GroupID() []byte { return p.groupID[:] } -// MissingParts implements partialmessages.PartialMessage. -func (p *PartialMessage) MissingParts() ([]byte, error) { - b, _ := p.AvailableParts() - b[0] = ^b[0] - if b[0] == 0 { - return nil, nil - } - return b, nil -} - func (p *PartialMessage) Extend(data []byte) error { if len(data) < 1+len(p.groupID) { return errors.New("invalid data length") @@ -98,12 +87,8 @@ func (p *PartialMessage) Extend(data []byte) error { return nil } -// PartialMessageBytesFromMetadata implements partialmessages.PartialMessage. -func (p *PartialMessage) PartialMessageBytesFromMetadata(metadata []byte) ([]byte, []byte, error) { - if len(metadata) == 0 { - // Treat this as the same as a request for all parts - metadata = []byte{0xff} - } +// PartialMessageBytes implements partialmessages.PartialMessage. +func (p *PartialMessage) PartialMessageBytes(metadata []byte) ([]byte, []byte, error) { if len(metadata) != 1 { return nil, nil, errors.New("invalid metadata length") } @@ -112,7 +97,8 @@ func (p *PartialMessage) PartialMessageBytesFromMetadata(metadata []byte) ([]byt out = append(out, 0) // This byte will contain the parts we are including in the message remaining := []byte{metadata[0]} for i := range p.parts { - if metadata[0]&(1< Date: Sun, 12 Oct 2025 20:11:51 +0530 Subject: [PATCH 19/28] gossipsub-interop: update go-libp2p to the latest version --- gossipsub-interop/go-libp2p/main.go | 2 ++ gossipsub-interop/go-libp2p/partial.go | 28 ++++++++------------- gossipsub-interop/go-libp2p/partial_test.go | 13 +++++++++- 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/gossipsub-interop/go-libp2p/main.go b/gossipsub-interop/go-libp2p/main.go index fd66c02f3..4b2142cae 100644 --- a/gossipsub-interop/go-libp2p/main.go +++ b/gossipsub-interop/go-libp2p/main.go @@ -18,6 +18,7 @@ import ( pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p-pubsub/partialmessages" pubsubpb "github.com/libp2p/go-libp2p-pubsub/pb" + "github.com/libp2p/go-libp2p/core/connmgr" "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" @@ -120,6 +121,7 @@ func main() { libp2p.ListenAddrStrings("/ip4/0.0.0.0/tcp/9000"), // libp2p.ListenAddrStrings("/ip4/0.0.0.0/udp/9000/quic-v1"), libp2p.Identity(nodePrivKey(nodeId)), + libp2p.ConnectionManager(connmgr.NullConnMgr{}), ) if err != nil { panic(err) diff --git a/gossipsub-interop/go-libp2p/partial.go b/gossipsub-interop/go-libp2p/partial.go index e27490549..eac3de07d 100644 --- a/gossipsub-interop/go-libp2p/partial.go +++ b/gossipsub-interop/go-libp2p/partial.go @@ -6,7 +6,7 @@ import ( "errors" "math/bits" - partialmessages "github.com/libp2p/go-libp2p-pubsub/partialmessages" + "github.com/libp2p/go-libp2p-pubsub/partialmessages" ) const partLen = 1024 @@ -17,8 +17,8 @@ type PartialMessage struct { } // PartsMetadata implements partialmessages.PartialMessage. -func (p *PartialMessage) PartsMetadata() []byte { - out := []byte{0} +func (p *PartialMessage) PartsMetadata() partialmessages.PartsMetadata { + out := partialmessages.PartsMetadata{0} for i := range p.parts { if len(p.parts[i]) > 0 { out[0] |= 1 << i @@ -88,35 +88,29 @@ func (p *PartialMessage) Extend(data []byte) error { } // PartialMessageBytes implements partialmessages.PartialMessage. -func (p *PartialMessage) PartialMessageBytes(metadata []byte) ([]byte, []byte, error) { +func (p *PartialMessage) PartialMessageBytes(metadata partialmessages.PartsMetadata) ([]byte, error) { if len(metadata) != 1 { - return nil, nil, errors.New("invalid metadata length") + return nil, errors.New("invalid metadata length") } out := make([]byte, 0, 1+1024*(bits.OnesCount8(metadata[0]))+len(p.groupID)) out = append(out, 0) // This byte will contain the parts we are including in the message - remaining := []byte{metadata[0]} - for i := range p.parts { + for i, a := range p.parts { if metadata[0]&(1< 0 { + res[0] |= right[0] + } + return res +} + var _ partialmessages.InvariantChecker[*PartialMessage] = (*partialInvariantChecker)(nil) func TestPartialMessageInvariants(t *testing.T) { From abac98b133c907e9759bb5a2ce9b654f855a94ec Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Fri, 21 Nov 2025 18:35:28 -0800 Subject: [PATCH 20/28] gossipsub-interop: bump go-libp2p version --- gossipsub-interop/go-libp2p/go.mod | 2 +- gossipsub-interop/go-libp2p/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gossipsub-interop/go-libp2p/go.mod b/gossipsub-interop/go-libp2p/go.mod index ea6eabceb..286750480 100644 --- a/gossipsub-interop/go-libp2p/go.mod +++ b/gossipsub-interop/go-libp2p/go.mod @@ -5,7 +5,7 @@ go 1.24.1 require ( github.com/libp2p/go-libp2p v0.41.1 // fork with partial messages - github.com/libp2p/go-libp2p-pubsub v0.14.4-0.20250930192224-4bec5400d65c + github.com/libp2p/go-libp2p-pubsub v0.15.1-0.20251114013250-ca33674ef866 ) require ( diff --git a/gossipsub-interop/go-libp2p/go.sum b/gossipsub-interop/go-libp2p/go.sum index 61349cdaf..91134ea11 100644 --- a/gossipsub-interop/go-libp2p/go.sum +++ b/gossipsub-interop/go-libp2p/go.sum @@ -137,8 +137,8 @@ github.com/libp2p/go-libp2p v0.41.1 h1:8ecNQVT5ev/jqALTvisSJeVNvXYJyK4NhQx1nNRXQ github.com/libp2p/go-libp2p v0.41.1/go.mod h1:DcGTovJzQl/I7HMrby5ZRjeD0kQkGiy+9w6aEkSZpRI= github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94= github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8= -github.com/libp2p/go-libp2p-pubsub v0.14.4-0.20250930192224-4bec5400d65c h1:L7Ub8HdUIAgQLoBdJZVH5YLxN2UWDwz0+mHtyVcGsk0= -github.com/libp2p/go-libp2p-pubsub v0.14.4-0.20250930192224-4bec5400d65c/go.mod h1:lr4oE8bFgQaifRcoc2uWhWWiK6tPdOEKpUuR408GFN4= +github.com/libp2p/go-libp2p-pubsub v0.15.1-0.20251114013250-ca33674ef866 h1:QgcPFheZiAIfpYD3LmxWH33GAQn4W5/qsagUd472ZxI= +github.com/libp2p/go-libp2p-pubsub v0.15.1-0.20251114013250-ca33674ef866/go.mod h1:lr4oE8bFgQaifRcoc2uWhWWiK6tPdOEKpUuR408GFN4= github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg= github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= From 413ff78e9aae05a6f9a0a3a8607c9525334310c0 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Fri, 21 Nov 2025 18:44:09 -0800 Subject: [PATCH 21/28] add missing MergeMedata func --- gossipsub-interop/go-libp2p/experiment.go | 1 + gossipsub-interop/go-libp2p/partial.go | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/gossipsub-interop/go-libp2p/experiment.go b/gossipsub-interop/go-libp2p/experiment.go index 9010c2ead..fcad6f8d0 100644 --- a/gossipsub-interop/go-libp2p/experiment.go +++ b/gossipsub-interop/go-libp2p/experiment.go @@ -205,6 +205,7 @@ func (n *scriptedNode) runInstruction(ctx context.Context, instruction ScriptIns } return nil }, + MergePartsMetadata: MergeMetadata, } psOpts := pubsubOptions(n.slogger, a.GossipSubParams, pme) diff --git a/gossipsub-interop/go-libp2p/partial.go b/gossipsub-interop/go-libp2p/partial.go index eac3de07d..b8a3c9250 100644 --- a/gossipsub-interop/go-libp2p/partial.go +++ b/gossipsub-interop/go-libp2p/partial.go @@ -5,6 +5,7 @@ import ( "encoding/binary" "errors" "math/bits" + "slices" "github.com/libp2p/go-libp2p-pubsub/partialmessages" ) @@ -27,6 +28,18 @@ func (p *PartialMessage) PartsMetadata() partialmessages.PartsMetadata { return out } +func MergeMetadata(_topic string, left, right partialmessages.PartsMetadata) partialmessages.PartsMetadata { + // by convention let the left be the larger one + if len(right) > len(left) { + left, right = right, left + } + out := slices.Clone(left) + for i := range right { + out[i] |= right[i] + } + return out +} + // FillParts is used to initialize this PartialMessage for testing by filling in // the parts it should have. The algorithm is simple: // - treat the groupID as our starting uint64 number BigEndian From 4e289d625980de00b4065ad1b5b48cb3806bdd54 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Fri, 21 Nov 2025 19:07:21 -0800 Subject: [PATCH 22/28] properly return err on nested instruction --- gossipsub-interop/go-libp2p/experiment.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gossipsub-interop/go-libp2p/experiment.go b/gossipsub-interop/go-libp2p/experiment.go index fcad6f8d0..21472fa43 100644 --- a/gossipsub-interop/go-libp2p/experiment.go +++ b/gossipsub-interop/go-libp2p/experiment.go @@ -225,7 +225,10 @@ func (n *scriptedNode) runInstruction(ctx context.Context, instruction ScriptIns n.logger.Printf("Node %d connected to %d peers", n.nodeID, len(n.h.Network().Peers())) case IfNodeIDEqualsInstruction: if a.NodeID == n.nodeID { - n.runInstruction(ctx, a.Instruction) + err := n.runInstruction(ctx, a.Instruction) + if err != nil { + return err + } } case WaitUntilInstruction: targetTime := n.startTime.Add(time.Duration(a.ElapsedSeconds) * time.Second) From be279c79936e5f3e3db197c3ee01d597d7d7797b Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Fri, 21 Nov 2025 19:08:25 -0800 Subject: [PATCH 23/28] add partial_messages check --- gossipsub-interop/.gitignore | 2 + gossipsub-interop/Makefile | 2 +- gossipsub-interop/README.md | 4 +- gossipsub-interop/checks/partial_messages.py | 93 ++++++++++++++++++++ gossipsub-interop/run.py | 14 ++- 5 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 gossipsub-interop/checks/partial_messages.py diff --git a/gossipsub-interop/.gitignore b/gossipsub-interop/.gitignore index ae257c022..26b0c9768 100644 --- a/gossipsub-interop/.gitignore +++ b/gossipsub-interop/.gitignore @@ -1,5 +1,7 @@ *.swp /shadow.data +shadow-outputs/* +latest synctest*.data /shadow.yaml /graph.gml diff --git a/gossipsub-interop/Makefile b/gossipsub-interop/Makefile index 72c43d806..a1aaad7b1 100644 --- a/gossipsub-interop/Makefile +++ b/gossipsub-interop/Makefile @@ -7,7 +7,7 @@ binaries: # Clean all generated shadow simulation files clean: - rm -rf *.data || true + rm -rf shadow-outputs || true rm plots/* || true .PHONY: binaries all clean diff --git a/gossipsub-interop/README.md b/gossipsub-interop/README.md index e5f5bcd2a..fd177e204 100644 --- a/gossipsub-interop/README.md +++ b/gossipsub-interop/README.md @@ -81,9 +81,11 @@ Finally, add it to the `composition` function in `experiment.py`. Minimal test of partial messages ```bash -uv run run.py --node_count 2 --composition "all-go" --scenario "partial-messages" +uv run run.py --node_count 2 --composition "all-go" --scenario "partial-messages" && uv run checks/partial_messages.py latest/ ``` +That command runs the shadow simulation and then verifies the stdout logs have the expected message. + ## Future work (contributions welcome) - Add more scenarios. diff --git a/gossipsub-interop/checks/partial_messages.py b/gossipsub-interop/checks/partial_messages.py new file mode 100644 index 000000000..e8c445f18 --- /dev/null +++ b/gossipsub-interop/checks/partial_messages.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +"""Verify that each node stdout log contains the expected completion message.""" + +from __future__ import annotations + +import argparse +import sys +from pathlib import Path + +MESSAGE_SUBSTRING = '"msg":"All parts received"' + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description=( + "Validate that every node stdout log inside a Shadow output directory " + "contains the expected completion message." + ) + ) + parser.add_argument( + "shadow_output", + help="Path to the Shadow output directory (the one containing the hosts/ folder).", + ) + parser.add_argument( + "--count", + type=int, + default=1, + help="Minimum number of times each stdout log must contain the target message (default: 1).", + ) + return parser.parse_args() + + +def iter_stdout_logs(hosts_dir: Path): + """Yield all stdout log files under the given hosts directory.""" + for stdout_file in sorted(hosts_dir.rglob("*.stdout")): + if stdout_file.is_file(): + yield stdout_file + + +def count_occurrences(path: Path, needle: str) -> int: + """Count how many times the string appears inside the file.""" + with path.open("r", encoding="utf-8", errors="replace") as handle: + total = 0 + for chunk in iter(lambda: handle.read(4096), ""): + total += chunk.count(needle) + return total + + +def main() -> int: + args = parse_args() + base_dir = Path(args.shadow_output).expanduser().resolve() + if not base_dir.exists(): + print(f"shadow output directory does not exist: {base_dir}", file=sys.stderr) + return 1 + + hosts_dir = base_dir / "hosts" + if not hosts_dir.is_dir(): + print(f"hosts directory not found under: {base_dir}", file=sys.stderr) + return 1 + + stdout_logs = list(iter_stdout_logs(hosts_dir)) + if not stdout_logs: + print(f"no stdout logs found under: {hosts_dir}", file=sys.stderr) + return 1 + + missing = [] + for log_path in stdout_logs: + occurrences = count_occurrences(log_path, MESSAGE_SUBSTRING) + if occurrences < args.count: + missing.append((log_path, occurrences)) + + if missing: + print( + "The following stdout logs do not contain the required message:", + file=sys.stderr, + ) + for log_path, occurrences in missing: + rel_path = log_path.relative_to(base_dir) + print( + f" - {rel_path}: found {occurrences} occurrences (expected >= {args.count})", + file=sys.stderr, + ) + print(f"{len(missing)} / {len(stdout_logs)} logs missing the message.", file=sys.stderr) + return 1 + + print( + f"All {len(stdout_logs)} stdout logs under {hosts_dir} contain the required message." + ) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/gossipsub-interop/run.py b/gossipsub-interop/run.py index ef0eba34e..98adddc7b 100644 --- a/gossipsub-interop/run.py +++ b/gossipsub-interop/run.py @@ -1,14 +1,14 @@ #!/usr/bin/env python3 -from dataclasses import asdict import argparse import json import os import random import subprocess -from network_graph import generate_graph -import experiment +from dataclasses import asdict +import experiment from analyze_message_deliveries import analyse_message_deliveries +from network_graph import generate_graph params_file_name = "params.json" @@ -32,6 +32,9 @@ def main(): parser.add_argument("--output_dir", type=str, required=False) args = parser.parse_args() + shadow_outputs_dir = os.path.join(os.getcwd(), "shadow-outputs") + os.makedirs(shadow_outputs_dir, exist_ok=True) + if args.output_dir is None: try: git_describe = ( @@ -49,6 +52,9 @@ def main(): args.seed }-{timestamp}-{git_describe}.data" + if not os.path.isabs(args.output_dir): + args.output_dir = os.path.join(shadow_outputs_dir, args.output_dir) + random.seed(args.seed) binaries = experiment.composition(args.composition) @@ -96,7 +102,7 @@ def main(): link_name = "latest" if os.path.islink(link_name) or os.path.exists(link_name): os.remove(link_name) - os.symlink(os.path.join(os.getcwd(), args.output_dir), link_name) + os.symlink(args.output_dir, link_name) # Analyse message deliveries. Skip the first 4 as warmup messages analyse_message_deliveries(args.output_dir, f"{args.output_dir}/plots", 4) From c74b76e96626f8fccb8c543b8cd6314fe29a3e7e Mon Sep 17 00:00:00 2001 From: sukun Date: Sun, 12 Oct 2025 20:11:51 +0530 Subject: [PATCH 24/28] gossipsub-interop: add a new scenario for partial messages We create a chain topology 1 <-> 2 <-> ... <-> n. Provide each node with one part of the message. And then publish the message from one of the nodes. Eventually all the nodes should have all the messages. --- gossipsub-interop/experiment.py | 92 +++++++++++++++++++++-- gossipsub-interop/go-libp2p/experiment.go | 3 +- 2 files changed, 88 insertions(+), 7 deletions(-) diff --git a/gossipsub-interop/experiment.py b/gossipsub-interop/experiment.py index 6117f53e9..348ff4910 100644 --- a/gossipsub-interop/experiment.py +++ b/gossipsub-interop/experiment.py @@ -1,11 +1,11 @@ +import random from collections import defaultdict from dataclasses import dataclass, field from datetime import timedelta -import random -from typing import List, Dict, Set +from typing import Dict, List, Set -from script_instruction import GossipSubParams, ScriptInstruction, NodeID import script_instruction +from script_instruction import GossipSubParams, NodeID, ScriptInstruction @dataclass @@ -48,9 +48,7 @@ def partial_message_scenario( gs_params.GossipFactor = 0 instructions.extend(spread_heartbeat_delay(node_count, gs_params)) - number_of_conns_per_node = 20 - if number_of_conns_per_node >= node_count: - number_of_conns_per_node = node_count - 1 + number_of_conns_per_node = min(20, node_count - 1) instructions.extend(random_network_mesh(node_count, number_of_conns_per_node)) topic = "a-subnet" @@ -96,6 +94,8 @@ def partial_message_scenario( ) ) + # Everyone publishes their partial message. This is how nodes learn about + # each others parts and can request them. instructions.append( script_instruction.PublishPartial(topicID=topic, groupID=groupID) ) @@ -107,6 +107,84 @@ def partial_message_scenario( return instructions +def partial_message_chain_scenario( + disable_gossip: bool, node_count: int +) -> List[ScriptInstruction]: + instructions: List[ScriptInstruction] = [] + gs_params = GossipSubParams() + if disable_gossip: + gs_params.Dlazy = 0 + gs_params.GossipFactor = 0 + instructions.extend(spread_heartbeat_delay(node_count, gs_params)) + + # Create a bidirectional chain topology: 0<->1<->2....<->n-1 + # Each node connects to both previous and next (except first and last) + for i in range(node_count): + connections = [] + if i > 0: + connections.append(i - 1) # Connect to previous + if i < node_count - 1: + connections.append(i + 1) # Connect to next + + if connections: + instructions.append( + script_instruction.IfNodeIDEquals( + nodeID=i, + instruction=script_instruction.Connect(connectTo=connections), + ) + ) + + topic = "partial-msg-chain" + instructions.append( + script_instruction.SubscribeToTopic(topicID=topic, partial=True) + ) + + # Wait for setup time and mesh stabilization + elapsed_seconds = 30 + instructions.append(script_instruction.WaitUntil(elapsedSeconds=elapsed_seconds)) + + # 16 messages with 8 parts each + num_messages = 16 + num_parts = 8 + + # Assign parts to nodes in round-robin fashion + # Each message-part combination goes to exactly one node + for msg_idx in range(num_messages): + groupID = msg_idx # Unique group ID for each message + + # Assign each of the 8 parts to nodes in round-robin + for part_idx in range(num_parts): + node_idx = (msg_idx * num_parts + part_idx) % node_count + part_bitmap = 1 << part_idx # Single bit for this part + + instructions.append( + script_instruction.IfNodeIDEquals( + nodeID=node_idx, + instruction=script_instruction.AddPartialMessage( + topicID=topic, groupID=groupID, parts=part_bitmap + ), + ) + ) + + # Have multiple nodes with parts for each message try to publish + # This creates redundancy and ensures the exchange process starts + for msg_idx in range(num_messages): + groupID = msg_idx + + elapsed_seconds += 2 # Delay between message groups + instructions.append( + script_instruction.WaitUntil(elapsedSeconds=elapsed_seconds) + ) + instructions.append( + script_instruction.PublishPartial(topicID=topic, groupID=groupID) + ) + + # Wait for propagation and assembly + elapsed_seconds += 30 + instructions.append(script_instruction.WaitUntil(elapsedSeconds=elapsed_seconds)) + return instructions + + def scenario( scenario_name: str, node_count: int, disable_gossip: bool ) -> ExperimentParams: @@ -114,6 +192,8 @@ def scenario( match scenario_name: case "partial-messages": instructions = partial_message_scenario(disable_gossip, node_count) + case "partial-messages-chain": + instructions = partial_message_chain_scenario(disable_gossip, node_count) case "subnet-blob-msg": gs_params = GossipSubParams() if disable_gossip: diff --git a/gossipsub-interop/go-libp2p/experiment.go b/gossipsub-interop/go-libp2p/experiment.go index 21472fa43..4598cef0e 100644 --- a/gossipsub-interop/go-libp2p/experiment.go +++ b/gossipsub-interop/go-libp2p/experiment.go @@ -128,8 +128,9 @@ func (m *partialMsgManager) handleRPC(rpc incomingPartialRPC) { } has := pm.PartsMetadata() + gid := binary.BigEndian.Uint64(rpc.GroupID) if has[0] == 0xff { - m.Info("All parts received") + m.Info("All parts received", "group id", gid) } pmHas := pm.PartsMetadata() From 959ec03d43a411a7662b565a4d1ef91e88f414bf Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Fri, 21 Nov 2025 19:09:51 -0800 Subject: [PATCH 25/28] gossipsub-interop: add partial message fanout scenario test --- gossipsub-interop/experiment.py | 65 ++++++++++++++++++++++ gossipsub-interop/go-libp2p/experiment.go | 51 +++++++++++++---- gossipsub-interop/go-libp2p/instruction.go | 7 ++- gossipsub-interop/go-libp2p/partial.go | 9 +++ gossipsub-interop/script_instruction.py | 1 + 5 files changed, 119 insertions(+), 14 deletions(-) diff --git a/gossipsub-interop/experiment.py b/gossipsub-interop/experiment.py index 348ff4910..4973e77b6 100644 --- a/gossipsub-interop/experiment.py +++ b/gossipsub-interop/experiment.py @@ -185,6 +185,69 @@ def partial_message_chain_scenario( return instructions +def partial_message_fanout_scenario( + disable_gossip: bool, node_count: int +) -> List[ScriptInstruction]: + instructions: List[ScriptInstruction] = [] + gs_params = GossipSubParams() + if disable_gossip: + gs_params.Dlazy = 0 + gs_params.GossipFactor = 0 + instructions.extend(spread_heartbeat_delay(node_count, gs_params)) + + number_of_conns_per_node = min(20, node_count - 1) + instructions.extend(random_network_mesh(node_count, number_of_conns_per_node)) + + topic = "a-subnet" + for i in range(node_count): + # The first node will not subscribe to the topic. + if i == 0: + continue + + instructions.append( + script_instruction.IfNodeIDEquals( + nodeID=i, + instruction=script_instruction.SubscribeToTopic( + topicID=topic, partial=True + ), + ) + ) + + groupID = random.randint(0, (2**8) - 1) + + # Wait for some setup time + elapsed_seconds = 30 + instructions.append(script_instruction.WaitUntil(elapsedSeconds=elapsed_seconds)) + + # First node has everything + instructions.append( + script_instruction.IfNodeIDEquals( + nodeID=0, + instruction=script_instruction.AddPartialMessage( + topicID=topic, groupID=groupID, parts=0xFF + ), + ) + ) + + # First node publishes to a fanout set, here we are saying the first 7 nodes after the publisher + instructions.append( + script_instruction.IfNodeIDEquals( + nodeID=0, + instruction=script_instruction.PublishPartial( + topicID=topic, + groupID=groupID, + publishToNodeIDs=list(range(1, min(8, node_count))), + ), + ) + ) + + # Wait for everything to flush + elapsed_seconds += 10 + instructions.append(script_instruction.WaitUntil(elapsedSeconds=elapsed_seconds)) + + return instructions + + def scenario( scenario_name: str, node_count: int, disable_gossip: bool ) -> ExperimentParams: @@ -194,6 +257,8 @@ def scenario( instructions = partial_message_scenario(disable_gossip, node_count) case "partial-messages-chain": instructions = partial_message_chain_scenario(disable_gossip, node_count) + case "partial-messages-fanout": + instructions = partial_message_fanout_scenario(disable_gossip, node_count) case "subnet-blob-msg": gs_params = GossipSubParams() if disable_gossip: diff --git a/gossipsub-interop/go-libp2p/experiment.go b/gossipsub-interop/go-libp2p/experiment.go index 4598cef0e..e1df87a04 100644 --- a/gossipsub-interop/go-libp2p/experiment.go +++ b/gossipsub-interop/go-libp2p/experiment.go @@ -35,8 +35,9 @@ type partialMsgWithTopic struct { msg *PartialMessage } type publishReq struct { - topic string - groupID []byte + topic string + groupID []byte + publishToPeers []peer.ID } type partialMsgManager struct { @@ -80,7 +81,10 @@ func (m *partialMsgManager) run() { case req := <-m.publish: pm := m.partialMessages[req.topic][string(req.groupID)] m.Info("publishing partial message", "groupID", hex.EncodeToString(pm.GroupID()), "topic", req.topic) - m.pubsub.PublishPartialMessage(req.topic, pm, partialmessages.PublishOptions{}) + opts := partialmessages.PublishOptions{ + PublishToPeers: req.publishToPeers, + } + m.pubsub.PublishPartialMessage(req.topic, pm, opts) case <-m.done: return } @@ -120,10 +124,10 @@ func (m *partialMsgManager) handleRPC(rpc incomingPartialRPC) { } } afterExtend := pm.PartsMetadata()[0] - var shouldRepublish bool + var extended bool if beforeExtend != afterExtend { m.Info("Extended partial message") - shouldRepublish = true + extended = true } @@ -134,16 +138,26 @@ func (m *partialMsgManager) handleRPC(rpc incomingPartialRPC) { } pmHas := pm.PartsMetadata() - if !shouldRepublish && len(rpc.PartsMetadata) == 1 { - shouldRepublish = pmHas[0] != rpc.PartsMetadata[0] - if shouldRepublish { + var shouldRequest bool + if len(rpc.PartsMetadata) == 1 { + shouldRequest = pmHas[0] != rpc.PartsMetadata[0] + if shouldRequest { m.Info("Republishing partial message because a peer has something I want") } } - if shouldRepublish { + if extended { + // We've extended our set. Let our mesh peers know m.pubsub.PublishPartialMessage(rpc.GetTopicID(), pm, partialmessages.PublishOptions{}) } + + if shouldRequest { + // This peer has parts we are missing. Request them from the peer + // explicitly. + m.pubsub.PublishPartialMessage(rpc.GetTopicID(), pm, partialmessages.PublishOptions{ + PublishToPeers: []peer.ID{rpc.from}, + }) + } } type scriptedNode struct { @@ -288,6 +302,9 @@ func (n *scriptedNode) runInstruction(ctx context.Context, instruction ScriptIns pm := &PartialMessage{} binary.BigEndian.AppendUint64(pm.groupID[:0], uint64(a.GroupID)) pm.FillParts(uint8(a.Parts)) + if pm.complete() { + n.slogger.Info("All parts received", "group id", uint64(a.GroupID)) + } n.partialMsgMgr.add <- partialMsgWithTopic{ topic: a.TopicID, msg: pm, @@ -296,9 +313,21 @@ func (n *scriptedNode) runInstruction(ctx context.Context, instruction ScriptIns case PublishPartialInstruction: var groupID [8]byte binary.BigEndian.AppendUint64(groupID[:0], uint64(a.GroupID)) + var publishToPeers []peer.ID + if len(a.PublishToNodeIDs) > 0 { + publishToPeers = make([]peer.ID, 0, len(a.PublishToNodeIDs)) + for _, targetNodeID := range a.PublishToNodeIDs { + peerID, err := peer.IDFromPrivateKey(nodePrivKey(targetNodeID)) + if err != nil { + return fmt.Errorf("failed to derive peer ID for node %d: %w", targetNodeID, err) + } + publishToPeers = append(publishToPeers, peerID) + } + } n.partialMsgMgr.publish <- publishReq{ - topic: a.TopicID, - groupID: groupID[:], + topic: a.TopicID, + groupID: groupID[:], + publishToPeers: publishToPeers, } default: diff --git a/gossipsub-interop/go-libp2p/instruction.go b/gossipsub-interop/go-libp2p/instruction.go index 1ef2f2f8b..99e4b5625 100644 --- a/gossipsub-interop/go-libp2p/instruction.go +++ b/gossipsub-interop/go-libp2p/instruction.go @@ -52,9 +52,10 @@ func (AddPartialMessage) isInstruction() {} // PublishPartialInstruction represents a partial publish instruction in the script type PublishPartialInstruction struct { - Type string `json:"type"` - TopicID string `json:"topicID"` - GroupID int `json:"groupID"` + Type string `json:"type"` + TopicID string `json:"topicID"` + GroupID int `json:"groupID"` + PublishToNodeIDs []int `json:"publishToNodeIDs"` } // isInstruction implements the ScriptInstruction interface diff --git a/gossipsub-interop/go-libp2p/partial.go b/gossipsub-interop/go-libp2p/partial.go index b8a3c9250..f75cc0383 100644 --- a/gossipsub-interop/go-libp2p/partial.go +++ b/gossipsub-interop/go-libp2p/partial.go @@ -28,6 +28,15 @@ func (p *PartialMessage) PartsMetadata() partialmessages.PartsMetadata { return out } +func (p *PartialMessage) complete() bool { + for i := range p.parts { + if len(p.parts[i]) == 0 { + return false + } + } + return true +} + func MergeMetadata(_topic string, left, right partialmessages.PartsMetadata) partialmessages.PartsMetadata { // by convention let the left be the larger one if len(right) > len(left) { diff --git a/gossipsub-interop/script_instruction.py b/gossipsub-interop/script_instruction.py index 413e16275..1cf8cbf17 100644 --- a/gossipsub-interop/script_instruction.py +++ b/gossipsub-interop/script_instruction.py @@ -40,6 +40,7 @@ class PublishPartial(BaseModel): type: Literal["publishPartial"] = "publishPartial" topicID: str groupID: int # uint64 representing groupID + publishToNodeIDs: List[NodeID] | None = None class Publish(BaseModel): From 5b5c0e95004141459942b35df3cd18e025464371 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Fri, 21 Nov 2025 20:21:27 -0800 Subject: [PATCH 26/28] log "All parts received" only once per group id --- gossipsub-interop/go-libp2p/experiment.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gossipsub-interop/go-libp2p/experiment.go b/gossipsub-interop/go-libp2p/experiment.go index e1df87a04..b24d53de0 100644 --- a/gossipsub-interop/go-libp2p/experiment.go +++ b/gossipsub-interop/go-libp2p/experiment.go @@ -131,9 +131,8 @@ func (m *partialMsgManager) handleRPC(rpc incomingPartialRPC) { } - has := pm.PartsMetadata() gid := binary.BigEndian.Uint64(rpc.GroupID) - if has[0] == 0xff { + if extended && pm.complete() { m.Info("All parts received", "group id", gid) } From 07765dacc9db3ddfafa7b4711682fe3a576b51f9 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Fri, 21 Nov 2025 20:22:36 -0800 Subject: [PATCH 27/28] gossipsub-interop: Add a basic `make test` command to run all tests --- gossipsub-interop/Makefile | 15 ++++++++++++++- gossipsub-interop/README.md | 8 ++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/gossipsub-interop/Makefile b/gossipsub-interop/Makefile index a1aaad7b1..a0477058f 100644 --- a/gossipsub-interop/Makefile +++ b/gossipsub-interop/Makefile @@ -10,4 +10,17 @@ clean: rm -rf shadow-outputs || true rm plots/* || true -.PHONY: binaries all clean +test: + # Testing partial messages + @echo "Testing partial messages" + @uv run run.py --node_count 8 --composition "all-go" --scenario "partial-messages" && uv run checks/partial_messages.py latest --count 1 + + @echo "Testing partial messages chain" + @uv run run.py --node_count 8 --composition "all-go" --scenario "partial-messages-chain" && uv run checks/partial_messages.py latest --count 16 + + @echo "Testing fanout" + @uv run run.py --node_count 2 --composition "all-go" --scenario "partial-messages-fanout" && uv run checks/partial_messages.py latest/ + + + +.PHONY: binaries all clean test diff --git a/gossipsub-interop/README.md b/gossipsub-interop/README.md index fd177e204..310074788 100644 --- a/gossipsub-interop/README.md +++ b/gossipsub-interop/README.md @@ -86,6 +86,14 @@ uv run run.py --node_count 2 --composition "all-go" --scenario "partial-messages That command runs the shadow simulation and then verifies the stdout logs have the expected message. +## Tests + +```bash +make test +``` + +This runs various shadow simulations and checks. + ## Future work (contributions welcome) - Add more scenarios. From 9ccd0cd2b0945d69f23b864cae4e3ef8a4b0ed53 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Fri, 21 Nov 2025 20:31:44 -0800 Subject: [PATCH 28/28] gossipsub-interop: Change rust log to match checker --- gossipsub-interop/rust-libp2p/src/experiment.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gossipsub-interop/rust-libp2p/src/experiment.rs b/gossipsub-interop/rust-libp2p/src/experiment.rs index 7d8c8609e..c2285c53b 100644 --- a/gossipsub-interop/rust-libp2p/src/experiment.rs +++ b/gossipsub-interop/rust-libp2p/src/experiment.rs @@ -203,7 +203,7 @@ impl ScriptedNode { if before_extension != after_extension { info!(self.stderr_logger, "Got new data. Will republish. {before_extension:?} {after_extension:?}"); if after_extension == vec![255] { - info!(self.stdout_logger, "Received Full Partial Message"; + info!(self.stdout_logger, "All parts received"; // "topic" => topic_id, // "group_id" => group_id, "from" => propagation_source.to_string());