Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions libp2p/protocols/mix/delay_strategy.nim
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,28 @@ method generateForIntermediate*(
encodedDelayMs

const DefaultMeanDelayMs* = 100
const DefaultNegligibleProb* = 1e-6
## Probability below which the tail of the exponential distribution is truncated.
## Yields a maximum delay of mean * -ln(negligibleProb) ≈ mean * 13.8.

type ExponentialDelayStrategy* = ref object of DelayStrategy
## Recommended strategy: encodes mean delay, samples from exponential distribution.
## The distribution is truncated at -mean*ln(negligibleProb), discarding the
## impractically long tail while preserving the mixing properties.
meanDelayMs: uint16
negligibleProb: float64

proc new*(
T: typedesc[ExponentialDelayStrategy],
meanDelayMs: uint16 = DefaultMeanDelayMs,
rng: ref HmacDrbgContext,
negligibleProb: float64 = DefaultNegligibleProb,
): T =
doAssert(rng != nil, "random is not set")
T(meanDelayMs: meanDelayMs, rng: rng)
doAssert(
Comment thread
chaitanyaprem marked this conversation as resolved.
negligibleProb > 0.0 and negligibleProb < 1.0, "negligibleProb must be in (0, 1)"
)
T(meanDelayMs: meanDelayMs, rng: rng, negligibleProb: negligibleProb)

method generateForEntry*(
self: ExponentialDelayStrategy
Expand All @@ -59,11 +69,12 @@ method generateForEntry*(
method generateForIntermediate*(
self: ExponentialDelayStrategy, meanDelayMs: uint16
): uint16 {.gcsafe, raises: [].} =
## Samples from exponential distribution: delay = -mean * ln(U)
## Fall back to no delay in case of errors
## Samples from exponential distribution: delay = -mean * ln(U), truncated to
## -mean*ln(negligibleProb) to discard the impractically long tail.
if meanDelayMs == 0:
return 0u16
let maxDelayMs = -float64(meanDelayMs) * ln(self.negligibleProb)
Comment thread
chaitanyaprem marked this conversation as resolved.
let randVal = self.rng[].generate(uint64)
let u = (float64(randVal) + 1.0) / (float64(high(uint64)) + 1.0)
let delay = -float64(meanDelayMs) * ln(u)
min(delay, float64(high(uint16))).uint16
min(min(delay, maxDelayMs), float64(high(uint16))).uint16
Comment thread
chaitanyaprem marked this conversation as resolved.
23 changes: 22 additions & 1 deletion tests/libp2p/mix/test_delay_strategy.nim
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

{.used.}

import std/[sets]
import std/[math, sets]
import ../../../libp2p/protocols/mix/delay_strategy
import ../../tools/[unittest, crypto]

Expand Down Expand Up @@ -66,3 +66,24 @@ suite "DelayStrategy":
delays.incl(delay)

check delays.len > NumSamples div 2

test "ExponentialDelayStrategy truncates at negligible probability threshold":
let
meanDelayMs: uint16 = 100
negligibleProb = DefaultNegligibleProb
strategy = ExponentialDelayStrategy.new(meanDelayMs, rng(), negligibleProb)
# maxDelay = -mean * ln(negligibleProb)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably can be removed as the name of the variables are practically the same as those from the formula

Suggested change
# maxDelay = -mean * ln(negligibleProb)

maxDelayMs = uint16(-float64(meanDelayMs) * ln(negligibleProb))

for _ in 0 ..< 10000:
check strategy.generateForIntermediate(meanDelayMs) <= maxDelayMs

test "ExponentialDelayStrategy respects custom negligibleProb":
let
meanDelayMs: uint16 = 100
negligibleProb = 0.01 # aggressive truncation: max ≈ mean * 4.6
strategy = ExponentialDelayStrategy.new(meanDelayMs, rng(), negligibleProb)
maxDelayMs = uint16(-float64(meanDelayMs) * ln(negligibleProb))

for _ in 0 ..< 10000:
check strategy.generateForIntermediate(meanDelayMs) <= maxDelayMs
Loading