From 3c4a863cb7c898a6c551a51288a6badf809fd6f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rich=CE=9Brd?= Date: Fri, 10 Mar 2023 14:41:19 -0400 Subject: [PATCH] feat(c-bindings): custom gossipsub params (#489) --- library/README.md | 102 +++++++++++++ library/api.go | 30 ++++ mobile/api.go | 111 +------------- mobile/config.go | 374 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 514 insertions(+), 103 deletions(-) create mode 100644 mobile/config.go diff --git a/library/README.md b/library/README.md index a8dabda9..d5f598f7 100644 --- a/library/README.md +++ b/library/README.md @@ -272,6 +272,7 @@ interface JsonConfig { keepAliveInterval?: number; relay?: boolean; relayTopics?: Array; + gossipsubParameters?: GossipSubParameters; minPeersToPublish?: number; filter?: boolean; discV5?: boolean; @@ -306,6 +307,7 @@ If a key is `undefined`, or `null`, a default value will be set. Default `true`. - `relayTopics`: Array of pubsub topics that WakuRelay will automatically subscribe to when the node starts Default `[]` +- `gossipSubParameters`: custom gossipsub parameters. See `GossipSubParameters` section for defaults - `minPeersToPublish`: The minimum number of peers required on a topic to allow broadcasting a message. Default `0`. - `filter`: Enable filter protocol. @@ -338,6 +340,106 @@ For example: } ``` + +### `GossipsubParameters` type + +Type holding custom gossipsub configuration: + +```ts +interface GossipSubParameters { + D?: number; + D_low?: number; + D_high?: number; + D_score?: number; + D_out?: number; + HistoryLength?: number; + HistoryGossip?: number; + D_lazy?: number; + GossipFactor?: number; + GossipRetransmission?: number; + HeartbeatInitialDelayMs?: number; + HeartbeatIntervalSeconds?: number; + SlowHeartbeatWarning?: number; + FanoutTTLSeconds?: number; + PrunePeers?: number; + PruneBackoffSeconds?: number; + UnsubscribeBackoffSeconds?: number; + Connectors?: number; + MaxPendingConnections?: number; + ConnectionTimeoutSeconds?: number; + DirectConnectTicks?: number; + DirectConnectInitialDelaySeconds?: number; + OpportunisticGraftTicks?: number; + OpportunisticGraftPeers?: number; + GraftFloodThresholdSeconds?: number; + MaxIHaveLength?: number; + MaxIHaveMessages?: number; + IWantFollowupTimeSeconds?: number; +} +``` + +Fields: + +All fields are optional. +If a key is `undefined`, or `null`, a default value will be set. + +- `d`: optimal degree for a GossipSub topic mesh. + Default `6` +- `dLow`: lower bound on the number of peers we keep in a GossipSub topic mesh + Default `5` +- `dHigh`: upper bound on the number of peers we keep in a GossipSub topic mesh. + Default `12` +- `dScore`: affects how peers are selected when pruning a mesh due to over subscription. + Default `4` +- `dOut`: sets the quota for the number of outbound connections to maintain in a topic mesh. + Default `2` +- `historyLength`: controls the size of the message cache used for gossip. + Default `5` +- `historyGossip`: controls how many cached message ids we will advertise in IHAVE gossip messages. + Default `3` +- `dLazy`: affects how many peers we will emit gossip to at each heartbeat. + Default `6` +- `gossipFactor`: affects how many peers we will emit gossip to at each heartbeat. + Default `0.25` +- `gossipRetransmission`: controls how many times we will allow a peer to request the same message id through IWANT gossip before we start ignoring them. + Default `3` +- `heartbeatInitialDelayMs`: short delay in milliseconds before the heartbeat timer begins after the router is initialized. + Default `100` milliseconds +- `heartbeatIntervalSeconds`: controls the time between heartbeats. + Default `1` second +- `slowHeartbeatWarning`: duration threshold for heartbeat processing before emitting a warning. + Default `0.1` +- `fanoutTTLSeconds`: controls how long we keep track of the fanout state. + Default `60` seconds +- `prunePeers`: controls the number of peers to include in prune Peer eXchange. + Default `16` +- `pruneBackoffSeconds`: controls the backoff time for pruned peers. + Default `60` seconds +- `unsubscribeBackoffSeconds`: controls the backoff time to use when unsuscribing from a topic. + Default `10` seconds +- `connectors`: number of active connection attempts for peers obtained through PX. + Default `8` +- `maxPendingConnections`: maximum number of pending connections for peers attempted through px. + Default `128` +- `connectionTimeoutSeconds`: timeout in seconds for connection attempts. + Default `30` seconds +- `directConnectTicks`: the number of heartbeat ticks for attempting to reconnect direct peers that are not currently connected. + Default `300` +- `directConnectInitialDelaySeconds`: initial delay before opening connections to direct peers. + Default `1` second +- `opportunisticGraftTicks`: number of heartbeat ticks for attempting to improve the mesh with opportunistic grafting. + Default `60` +- `opportunisticGraftPeers`: the number of peers to opportunistically graft. + Default `2` +- `graftFloodThresholdSeconds`: If a GRAFT comes before GraftFloodThresholdSeconds has elapsed since the last PRUNE, then there is an extra score penalty applied to the peer through P7. + Default `10` seconds +- `maxIHaveLength`: max number of messages to include in an IHAVE message, also controls the max number of IHAVE ids we will accept and request with IWANT from a peer within a heartbeat. + Default `5000` +- `maxIHaveMessages`: max number of IHAVE messages to accept from a peer within a heartbeat. + Default `10` +- `iWantFollowupTimeSeconds`: Time to wait for a message requested through IWANT following an IHAVE advertisement. + Default `3` seconds + ### `extern char* waku_new(char* jsonConfig)` Instantiates a Waku node. diff --git a/library/api.go b/library/api.go index 4199198e..ea95fb15 100644 --- a/library/api.go +++ b/library/api.go @@ -28,6 +28,36 @@ func main() {} // - keepAliveInterval: interval in seconds to ping all peers // - relay: Enable WakuRelay. Default `true` // - relayTopics: Array of pubsub topics that WakuRelay will automatically subscribe to when the node starts +// - gossipsubParams: an object containing custom gossipsub parameters. All attributes are optional, and if not specified, it will use default values. +// - d: optimal degree for a GossipSub topic mesh. Default `6` +// - dLow: lower bound on the number of peers we keep in a GossipSub topic mesh. Default `5` +// - dHigh: upper bound on the number of peers we keep in a GossipSub topic mesh. Default `12` +// - dScore: affects how peers are selected when pruning a mesh due to over subscription. Default `4` +// - dOut: sets the quota for the number of outbound connections to maintain in a topic mesh. Default `2` +// - historyLength: controls the size of the message cache used for gossip. Default `5` +// - historyGossip: controls how many cached message ids we will advertise in IHAVE gossip messages. Default `3` +// - dLazy: affects how many peers we will emit gossip to at each heartbeat. Default `6` +// - gossipFactor: affects how many peers we will emit gossip to at each heartbeat. Default `0.25` +// - gossipRetransmission: controls how many times we will allow a peer to request the same message id through IWANT gossip before we start ignoring them. Default `3` +// - heartbeatInitialDelayMs: short delay in milliseconds before the heartbeat timer begins after the router is initialized. Default `100` milliseconds +// - heartbeatIntervalSeconds: controls the time between heartbeats. Default `1` second +// - slowHeartbeatWarning: duration threshold for heartbeat processing before emitting a warning. Default `0.1` +// - fanoutTTLSeconds: controls how long we keep track of the fanout state. Default `60` seconds +// - prunePeers: controls the number of peers to include in prune Peer eXchange. Default `16` +// - pruneBackoffSeconds: controls the backoff time for pruned peers. Default `60` seconds +// - unsubscribeBackoffSeconds: controls the backoff time to use when unsuscribing from a topic. Default `10` seconds +// - connectors: number of active connection attempts for peers obtained through PX. Default `8` +// - maxPendingConnections: maximum number of pending connections for peers attempted through px. Default `128` +// - connectionTimeoutSeconds: timeout in seconds for connection attempts. Default `30` seconds +// - directConnectTicks: the number of heartbeat ticks for attempting to reconnect direct peers that are not currently connected. Default `300` +// - directConnectInitialDelaySeconds: initial delay before opening connections to direct peers. Default `1` second +// - opportunisticGraftTicks: number of heartbeat ticks for attempting to improve the mesh with opportunistic grafting. Default `60` +// - opportunisticGraftPeers: the number of peers to opportunistically graft. Default `2` +// - graftFloodThresholdSeconds: If a GRAFT comes before GraftFloodThresholdSeconds has elapsed since the last PRUNE, then there is an extra score penalty applied to the peer through P7. Default `10` seconds +// - maxIHaveLength: max number of messages to include in an IHAVE message, also controls the max number of IHAVE ids we will accept and request with IWANT from a peer within a heartbeat. Default `5000` +// - maxIHaveMessages: max number of IHAVE messages to accept from a peer within a heartbeat. Default `10` +// - iWantFollowupTimeSeconds: Time to wait for a message requested through IWANT following an IHAVE advertisement. Default `3` seconds +// // - minPeersToPublish: The minimum number of peers required on a topic to allow broadcasting a message. Default `0` // - filter: Enable Filter. Default `false` // - discV5: Enable DiscoveryV5. Default `false` diff --git a/mobile/api.go b/mobile/api.go index 961291c5..81cdcb22 100644 --- a/mobile/api.go +++ b/mobile/api.go @@ -21,6 +21,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/secp256k1" "github.com/ethereum/go-ethereum/p2p/enode" + pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/peer" libp2pProtocol "github.com/libp2p/go-libp2p/core/protocol" "github.com/multiformats/go-multiaddr" @@ -54,108 +55,6 @@ func randomHex(n int) (string, error) { return hex.EncodeToString(bytes), nil } -type wakuConfig struct { - Host *string `json:"host,omitempty"` - Port *int `json:"port,omitempty"` - AdvertiseAddress *string `json:"advertiseAddr,omitempty"` - NodeKey *string `json:"nodeKey,omitempty"` - LogLevel *string `json:"logLevel,omitempty"` - KeepAliveInterval *int `json:"keepAliveInterval,omitempty"` - EnableRelay *bool `json:"relay"` - RelayTopics []string `json:"relayTopics,omitempty"` - EnableFilter *bool `json:"filter,omitempty"` - MinPeersToPublish *int `json:"minPeersToPublish,omitempty"` - EnableDiscV5 *bool `json:"discV5,omitempty"` - DiscV5BootstrapNodes []string `json:"discV5BootstrapNodes,omitempty"` - DiscV5UDPPort *uint `json:"discV5UDPPort,omitempty"` - EnableStore *bool `json:"store,omitempty"` - DatabaseURL *string `json:"databaseURL,omitempty"` - RetentionMaxMessages *int `json:"storeRetentionMaxMessages,omitempty"` - RetentionTimeSeconds *int `json:"storeRetentionTimeSeconds,omitempty"` -} - -var defaultHost = "0.0.0.0" -var defaultPort = 60000 -var defaultKeepAliveInterval = 20 -var defaultEnableRelay = true -var defaultMinPeersToPublish = 0 -var defaultEnableFilter = false -var defaultEnableDiscV5 = false -var defaultDiscV5UDPPort = uint(9000) -var defaultLogLevel = "INFO" -var defaultEnableStore = false -var defaultDatabaseURL = "sqlite3://store.db" -var defaultRetentionMaxMessages = 10000 -var defaultRetentionTimeSeconds = 30 * 24 * 60 * 60 // 30d - -func getConfig(configJSON string) (wakuConfig, error) { - var config wakuConfig - if configJSON != "" { - err := json.Unmarshal([]byte(configJSON), &config) - if err != nil { - return wakuConfig{}, err - } - } - - if config.Host == nil { - config.Host = &defaultHost - } - - if config.EnableRelay == nil { - config.EnableRelay = &defaultEnableRelay - } - - if config.EnableFilter == nil { - config.EnableFilter = &defaultEnableFilter - } - - if config.EnableDiscV5 == nil { - config.EnableDiscV5 = &defaultEnableDiscV5 - } - - if config.Host == nil { - config.Host = &defaultHost - } - - if config.Port == nil { - config.Port = &defaultPort - } - - if config.DiscV5UDPPort == nil { - config.DiscV5UDPPort = &defaultDiscV5UDPPort - } - - if config.KeepAliveInterval == nil { - config.KeepAliveInterval = &defaultKeepAliveInterval - } - - if config.MinPeersToPublish == nil { - config.MinPeersToPublish = &defaultMinPeersToPublish - } - - if config.LogLevel == nil { - config.LogLevel = &defaultLogLevel - } - - if config.EnableStore == nil { - config.EnableStore = &defaultEnableStore - } - - if config.DatabaseURL == nil { - config.DatabaseURL = &defaultDatabaseURL - } - - if config.RetentionMaxMessages == nil { - config.RetentionMaxMessages = &defaultRetentionMaxMessages - } - - if config.RetentionTimeSeconds == nil { - config.RetentionTimeSeconds = &defaultRetentionTimeSeconds - } - - return config, nil -} - func NewNode(configJSON string) string { if wakuState.node != nil { return MakeJSONResponse(errors.New("go-waku already initialized. stop it first")) @@ -196,7 +95,13 @@ func NewNode(configJSON string) string { } if *config.EnableRelay { - opts = append(opts, node.WithWakuRelayAndMinPeers(*config.MinPeersToPublish)) + var pubsubOpt []pubsub.Option + if config.GossipSubParams != nil { + params := GetGossipSubParams(config.GossipSubParams) + pubsubOpt = append(pubsubOpt, pubsub.WithGossipSubParams(params)) + } + + opts = append(opts, node.WithWakuRelayAndMinPeers(*config.MinPeersToPublish, pubsubOpt...)) } if *config.EnableFilter { diff --git a/mobile/config.go b/mobile/config.go new file mode 100644 index 00000000..4ddc74d2 --- /dev/null +++ b/mobile/config.go @@ -0,0 +1,374 @@ +package gowaku + +import ( + "encoding/json" + "time" + + pubsub "github.com/libp2p/go-libp2p-pubsub" +) + +type wakuConfig struct { + Host *string `json:"host,omitempty"` + Port *int `json:"port,omitempty"` + AdvertiseAddress *string `json:"advertiseAddr,omitempty"` + NodeKey *string `json:"nodeKey,omitempty"` + LogLevel *string `json:"logLevel,omitempty"` + KeepAliveInterval *int `json:"keepAliveInterval,omitempty"` + EnableRelay *bool `json:"relay"` + RelayTopics []string `json:"relayTopics,omitempty"` + GossipSubParams *GossipSubParams `json:"gossipsubParams,omitempty"` + EnableFilter *bool `json:"filter,omitempty"` + MinPeersToPublish *int `json:"minPeersToPublish,omitempty"` + EnableDiscV5 *bool `json:"discV5,omitempty"` + DiscV5BootstrapNodes []string `json:"discV5BootstrapNodes,omitempty"` + DiscV5UDPPort *uint `json:"discV5UDPPort,omitempty"` + EnableStore *bool `json:"store,omitempty"` + DatabaseURL *string `json:"databaseURL,omitempty"` + RetentionMaxMessages *int `json:"storeRetentionMaxMessages,omitempty"` + RetentionTimeSeconds *int `json:"storeRetentionTimeSeconds,omitempty"` +} + +var defaultHost = "0.0.0.0" +var defaultPort = 60000 +var defaultKeepAliveInterval = 20 +var defaultEnableRelay = true +var defaultMinPeersToPublish = 0 +var defaultEnableFilter = false +var defaultEnableDiscV5 = false +var defaultDiscV5UDPPort = uint(9000) +var defaultLogLevel = "INFO" +var defaultEnableStore = false +var defaultDatabaseURL = "sqlite3://store.db" +var defaultRetentionMaxMessages = 10000 +var defaultRetentionTimeSeconds = 30 * 24 * 60 * 60 // 30d + +// GossipSubParams defines all the gossipsub specific parameters. +type GossipSubParams struct { + // overlay parameters. + + // D sets the optimal degree for a GossipSub topic mesh. For example, if D == 6, + // each peer will want to have about six peers in their mesh for each topic they're subscribed to. + // D should be set somewhere between Dlo and Dhi. + D *int `json:"d,omitempty"` + + // Dlo sets the lower bound on the number of peers we keep in a GossipSub topic mesh. + // If we have fewer than Dlo peers, we will attempt to graft some more into the mesh at + // the next heartbeat. + Dlo *int `json:"dLow,omitempty"` + + // Dhi sets the upper bound on the number of peers we keep in a GossipSub topic mesh. + // If we have more than Dhi peers, we will select some to prune from the mesh at the next heartbeat. + Dhi *int `json:"dHigh,omitempty"` + + // Dscore affects how peers are selected when pruning a mesh due to over subscription. + // At least Dscore of the retained peers will be high-scoring, while the remainder are + // chosen randomly. + Dscore *int `json:"dScore,omitempty"` + + // Dout sets the quota for the number of outbound connections to maintain in a topic mesh. + // When the mesh is pruned due to over subscription, we make sure that we have outbound connections + // to at least Dout of the survivor peers. This prevents sybil attackers from overwhelming + // our mesh with incoming connections. + // + // Dout must be set below Dlo, and must not exceed D / 2. + Dout *int `json:"dOut,omitempty"` + + // gossip parameters + + // HistoryLength controls the size of the message cache used for gossip. + // The message cache will remember messages for HistoryLength heartbeats. + HistoryLength *int `json:"historyLength,omitempty"` + + // HistoryGossip controls how many cached message ids we will advertise in + // IHAVE gossip messages. When asked for our seen message IDs, we will return + // only those from the most recent HistoryGossip heartbeats. The slack between + // HistoryGossip and HistoryLength allows us to avoid advertising messages + // that will be expired by the time they're requested. + // + // HistoryGossip must be less than or equal to HistoryLength to + // avoid a runtime panic. + HistoryGossip *int `json:"historyGossip,omitempty"` + + // Dlazy affects how many peers we will emit gossip to at each heartbeat. + // We will send gossip to at least Dlazy peers outside our mesh. The actual + // number may be more, depending on GossipFactor and how many peers we're + // connected to. + Dlazy *int `json:"dLazy,omitempty"` + + // GossipFactor affects how many peers we will emit gossip to at each heartbeat. + // We will send gossip to GossipFactor * (total number of non-mesh peers), or + // Dlazy, whichever is greater. + GossipFactor *float64 `json:"gossipFactor,omitempty"` + + // GossipRetransmission controls how many times we will allow a peer to request + // the same message id through IWANT gossip before we start ignoring them. This is designed + // to prevent peers from spamming us with requests and wasting our resources. + GossipRetransmission *int `json:"gossipRetransmission,omitempty"` + + // heartbeat interval + + // HeartbeatInitialDelayMs is the short delay before the heartbeat timer begins + // after the router is initialized. + HeartbeatInitialDelayMs *int `json:"heartbeatInitialDelayMs,omitempty"` + + // HeartbeatIntervalSeconds controls the time between heartbeats. + HeartbeatIntervalSeconds *int `json:"heartbeatIntervalSeconds,omitempty"` + + // SlowHeartbeatWarning is the duration threshold for heartbeat processing before emitting + // a warning; this would be indicative of an overloaded peer. + SlowHeartbeatWarning *float64 `json:"slowHeartbeatWarning,omitempty"` + + // FanoutTTLSeconds controls how long we keep track of the fanout state. If it's been + // FanoutTTLSeconds since we've published to a topic that we're not subscribed to, + // we'll delete the fanout map for that topic. + FanoutTTLSeconds *int `json:"fanoutTTLSeconds,omitempty"` + + // PrunePeers controls the number of peers to include in prune Peer eXchange. + // When we prune a peer that's eligible for PX (has a good score, etc), we will try to + // send them signed peer records for up to PrunePeers other peers that we + // know of. + PrunePeers *int `json:"prunePeers,omitempty"` + + // PruneBackoffSeconds controls the backoff time for pruned peers. This is how long + // a peer must wait before attempting to graft into our mesh again after being pruned. + // When pruning a peer, we send them our value of PruneBackoff so they know + // the minimum time to wait. Peers running older versions may not send a backoff time, + // so if we receive a prune message without one, we will wait at least PruneBackoff + // before attempting to re-graft. + PruneBackoffSeconds *int `json:"pruneBackoffSeconds,omitempty"` + + // UnsubscribeBackoffSeconds controls the backoff time to use when unsuscribing + // from a topic. A peer should not resubscribe to this topic before this + // duration. + UnsubscribeBackoffSeconds *int `json:"unsubscribeBackoffSeconds,omitempty"` + + // Connectors controls the number of active connection attempts for peers obtained through PX. + Connectors *int `json:"connectors,omitempty"` + + // MaxPendingConnections sets the maximum number of pending connections for peers attempted through px. + MaxPendingConnections *int `json:"maxPendingConnections,omitempty"` + + // ConnectionTimeoutSeconds controls the timeout for connection attempts. + ConnectionTimeoutSeconds *int `json:"connectionTimeoutSeconds,omitempty"` + + // DirectConnectTicks is the number of heartbeat ticks for attempting to reconnect direct peers + // that are not currently connected. + DirectConnectTicks *uint64 `json:"directConnectTicks,omitempty"` + + // DirectConnectInitialDelaySeconds is the initial delay before opening connections to direct peers + DirectConnectInitialDelaySeconds *int `json:"directConnectInitialDelaySeconds,omitempty"` + + // OpportunisticGraftTicks is the number of heartbeat ticks for attempting to improve the mesh + // with opportunistic grafting. Every OpportunisticGraftTicks we will attempt to select some + // high-scoring mesh peers to replace lower-scoring ones, if the median score of our mesh peers falls + // below a threshold (see https://godoc.org/github.com/libp2p/go-libp2p-pubsub#PeerScoreThresholds). + OpportunisticGraftTicks *uint64 `json:"opportunisticGraftTicks,omitempty"` + + // OpportunisticGraftPeers is the number of peers to opportunistically graft. + OpportunisticGraftPeers *int `json:"opportunisticGraftPeers,omitempty"` + + // If a GRAFT comes before GraftFloodThresholdSeconds has elapsed since the last PRUNE, + // then there is an extra score penalty applied to the peer through P7. + GraftFloodThresholdSeconds *int `json:"graftFloodThresholdSeconds,omitempty"` + + // MaxIHaveLength is the maximum number of messages to include in an IHAVE message. + // Also controls the maximum number of IHAVE ids we will accept and request with IWANT from a + // peer within a heartbeat, to protect from IHAVE floods. You should adjust this value from the + // default if your system is pushing more than 5000 messages in HistoryGossip heartbeats; + // with the defaults this is 1666 messages/s. + MaxIHaveLength *int `json:"maxIHaveLength,omitempty"` + + // MaxIHaveMessages is the maximum number of IHAVE messages to accept from a peer within a heartbeat. + MaxIHaveMessages *int `json:"maxIHaveMessages,omitempty"` + + // Time to wait for a message requested through IWANT following an IHAVE advertisement. + // If the message is not received within this window, a broken promise is declared and + // the router may apply bahavioural penalties. + IWantFollowupTimeSeconds *int `json:"iWantFollowupTimeSeconds,omitempty"` +} + +func getConfig(configJSON string) (wakuConfig, error) { + var config wakuConfig + if configJSON != "" { + err := json.Unmarshal([]byte(configJSON), &config) + if err != nil { + return wakuConfig{}, err + } + } + + if config.Host == nil { + config.Host = &defaultHost + } + + if config.EnableRelay == nil { + config.EnableRelay = &defaultEnableRelay + } + + if config.EnableFilter == nil { + config.EnableFilter = &defaultEnableFilter + } + + if config.EnableDiscV5 == nil { + config.EnableDiscV5 = &defaultEnableDiscV5 + } + + if config.Host == nil { + config.Host = &defaultHost + } + + if config.Port == nil { + config.Port = &defaultPort + } + + if config.DiscV5UDPPort == nil { + config.DiscV5UDPPort = &defaultDiscV5UDPPort + } + + if config.KeepAliveInterval == nil { + config.KeepAliveInterval = &defaultKeepAliveInterval + } + + if config.MinPeersToPublish == nil { + config.MinPeersToPublish = &defaultMinPeersToPublish + } + + if config.LogLevel == nil { + config.LogLevel = &defaultLogLevel + } + + if config.EnableStore == nil { + config.EnableStore = &defaultEnableStore + } + + if config.DatabaseURL == nil { + config.DatabaseURL = &defaultDatabaseURL + } + + if config.RetentionMaxMessages == nil { + config.RetentionMaxMessages = &defaultRetentionMaxMessages + } + + if config.RetentionTimeSeconds == nil { + config.RetentionTimeSeconds = &defaultRetentionTimeSeconds + } + + return config, nil +} + +func GetGossipSubParams(cfg *GossipSubParams) pubsub.GossipSubParams { + params := pubsub.DefaultGossipSubParams() + + if cfg.D != nil { + params.D = *cfg.D + } + + if cfg.Dlo != nil { + params.Dlo = *cfg.Dlo + } + + if cfg.Dhi != nil { + params.Dhi = *cfg.Dhi + } + + if cfg.Dscore != nil { + params.Dscore = *cfg.Dscore + } + + if cfg.Dout != nil { + params.Dout = *cfg.Dout + } + + if cfg.HistoryLength != nil { + params.HistoryLength = *cfg.HistoryLength + } + + if cfg.HistoryGossip != nil { + params.HistoryGossip = *cfg.HistoryGossip + } + + if cfg.Dlazy != nil { + params.Dlazy = *cfg.Dlazy + } + + if cfg.GossipFactor != nil { + params.GossipFactor = *cfg.GossipFactor + } + + if cfg.GossipRetransmission != nil { + params.GossipRetransmission = *cfg.GossipRetransmission + } + + if cfg.HeartbeatInitialDelayMs != nil { + params.HeartbeatInitialDelay = time.Duration(*cfg.HeartbeatInitialDelayMs) * time.Millisecond + } + + if cfg.HeartbeatIntervalSeconds != nil { + params.HeartbeatInterval = time.Duration(*cfg.HeartbeatIntervalSeconds) * time.Second + } + + if cfg.SlowHeartbeatWarning != nil { + params.SlowHeartbeatWarning = *cfg.SlowHeartbeatWarning + } + + if cfg.FanoutTTLSeconds != nil { + params.FanoutTTL = time.Duration(*cfg.FanoutTTLSeconds) * time.Second + } + + if cfg.PrunePeers != nil { + params.PrunePeers = *cfg.PrunePeers + } + + if cfg.PruneBackoffSeconds != nil { + params.PruneBackoff = time.Duration(*cfg.PruneBackoffSeconds) * time.Second + } + + if cfg.UnsubscribeBackoffSeconds != nil { + params.UnsubscribeBackoff = time.Duration(*cfg.UnsubscribeBackoffSeconds) * time.Second + } + + if cfg.Connectors != nil { + params.Connectors = *cfg.Connectors + } + + if cfg.MaxPendingConnections != nil { + params.MaxPendingConnections = *cfg.MaxPendingConnections + } + + if cfg.ConnectionTimeoutSeconds != nil { + params.ConnectionTimeout = time.Duration(*cfg.ConnectionTimeoutSeconds) * time.Second + } + + if cfg.DirectConnectTicks != nil { + params.DirectConnectTicks = *cfg.DirectConnectTicks + } + + if cfg.DirectConnectInitialDelaySeconds != nil { + params.DirectConnectInitialDelay = time.Duration(*cfg.DirectConnectInitialDelaySeconds) * time.Second + } + + if cfg.OpportunisticGraftTicks != nil { + params.OpportunisticGraftTicks = *cfg.OpportunisticGraftTicks + } + + if cfg.OpportunisticGraftPeers != nil { + params.OpportunisticGraftPeers = *cfg.OpportunisticGraftPeers + } + + if cfg.GraftFloodThresholdSeconds != nil { + params.GraftFloodThreshold = time.Duration(*cfg.GraftFloodThresholdSeconds) * time.Second + } + + if cfg.MaxIHaveLength != nil { + params.MaxIHaveLength = *cfg.MaxIHaveLength + } + + if cfg.MaxIHaveMessages != nil { + params.MaxIHaveMessages = *cfg.MaxIHaveMessages + } + + if cfg.IWantFollowupTimeSeconds != nil { + params.IWantFollowupTime = time.Duration(*cfg.IWantFollowupTimeSeconds) * time.Second + } + + return params +}