diff --git a/mixing/mixclient/client.go b/mixing/mixclient/client.go index 17458bddf7..ca980d68e0 100644 --- a/mixing/mixclient/client.go +++ b/mixing/mixclient/client.go @@ -43,8 +43,6 @@ const pairingVersion byte = 2 const ( timeoutDuration = 30 * time.Second maxJitter = timeoutDuration / 10 - msgJitter = 300 * time.Millisecond - peerJitter = maxJitter - msgJitter ) // expiredPRErr indicates that a dicemix session failed to complete due to the @@ -397,10 +395,12 @@ type Client struct { testWaiting chan struct{} testTickC chan time.Time testHooks map[hook]hookFunc + + msgJitter time.Duration } // NewClient creates a wallet's mixing client manager. -func NewClient(w Wallet) *Client { +func NewClient(w Wallet, msgJitter time.Duration) *Client { var prFlags byte err := solverrpc.StartSolver() if err == nil { @@ -421,6 +421,7 @@ func NewClient(w Wallet) *Client { blake256Hasher: blake256.NewHasher256(), epoch: w.Mixpool().Epoch(), stopping: make(chan struct{}), + msgJitter: msgJitter, } } @@ -567,7 +568,7 @@ func (c *Client) sendLocalPeerMsgs(ctx context.Context, deadline time.Time, s *s continue } msg := delayedMsg{ - sendTime: now.Add(p.msgJitter()), + sendTime: now.Add(p.msgJitter(c.msgJitter)), deadline: deadline, m: nil, p: p, @@ -694,7 +695,10 @@ func (c *Client) waitForEpoch(ctx context.Context) (time.Time, error) { } } -func (p *peer) msgJitter() time.Duration { +func (p *peer) msgJitter(msgJitter time.Duration) time.Duration { + if msgJitter == 0 { + return p.jitter + } return p.jitter + rand.Duration(msgJitter) } @@ -717,7 +721,7 @@ func (c *Client) prDelay(ctx context.Context, p *peer) error { wait = sendAfter.Sub(now) sendBefore = sendBefore.Add(c.epoch) } - wait += p.msgJitter() + rand.Duration(sendBefore.Sub(now)) + wait += p.msgJitter(c.msgJitter) + rand.Duration(sendBefore.Sub(now)) timer := time.NewTimer(wait) select { case <-ctx.Done(): @@ -963,7 +967,7 @@ func (c *Client) epochTicker(ctx context.Context) error { } // Dicemix performs a new mixing session for a coinjoin mix transaction. -func (c *Client) Dicemix(ctx context.Context, cj *CoinJoin) error { +func (c *Client) Dicemix(ctx context.Context, cj *CoinJoin, peerJitter time.Duration) error { select { case <-c.warming: case <-ctx.Done(): @@ -977,7 +981,7 @@ func (c *Client) Dicemix(ctx context.Context, cj *CoinJoin) error { p := &peer{ client: c, - jitter: rand.Duration(peerJitter), + jitter: peerJitter, res: make(chan error, 1), pub: pub, priv: priv, @@ -2063,7 +2067,12 @@ DCs: return sesRun, err } - time.Sleep(lowestJitter + rand.Duration(msgJitter)) + var msgJitter time.Duration + if c.msgJitter > 0 { + msgJitter = rand.Duration(c.msgJitter) + } + time.Sleep(lowestJitter + msgJitter) + err = c.wallet.PublishTransaction(context.Background(), cj.tx) if err != nil { return sesRun, err diff --git a/mixing/mixclient/client_test.go b/mixing/mixclient/client_test.go index 012dece7ab..e76c03c494 100644 --- a/mixing/mixclient/client_test.go +++ b/mixing/mixclient/client_test.go @@ -36,6 +36,10 @@ func requireCsppsolver(t *testing.T) { var ( testStartingHeight uint32 = 100 testStartingBlock = chainhash.Hash{100} + + // Zero jitter so tests can run as fast as possible. + msgJitter = time.Duration(0) + peerJitter = time.Duration(0) ) const ( @@ -44,7 +48,7 @@ const ( ) func newTestClient(w *testWallet, logger slog.Logger) *Client { - c := NewClient(w) + c := NewClient(w, msgJitter) c.testWaiting = make(chan struct{}) c.testTickC = make(chan time.Time) c.SetLogger(logger) @@ -231,7 +235,7 @@ func TestHonest(t *testing.T) { for i := range peers { p := peers[i] g.Go(func() error { - return c.Dicemix(ctx, p.cj) + return c.Dicemix(ctx, p.cj, peerJitter) }) } @@ -346,7 +350,7 @@ func testDisruption(t *testing.T, misbehavingID *identity, h hook, f hookFunc) { for _, p := range peers[:len(peers)/2] { p := p g.Go(func() error { - err := c.Dicemix(ctx, p.cj) + err := c.Dicemix(ctx, p.cj, peerJitter) var e *testPeerBlamedError if errors.As(err, &e) { blameErrC <- e @@ -359,7 +363,7 @@ func testDisruption(t *testing.T, misbehavingID *identity, h hook, f hookFunc) { for _, p := range peers[len(peers)/2:] { p := p g.Go(func() error { - err := c2.Dicemix(ctx, p.cj) + err := c2.Dicemix(ctx, p.cj, peerJitter) var e *testPeerBlamedError if errors.As(err, &e) { blameErrC <- e