Skip to main content

GhostTracking

GhostTracking defeats timing analysis and fee fingerprinting through two complementary mechanisms: timing randomization and fee normalization.

The Problem

Even on privacy-focused blockchains, transaction metadata leaks information:

  • Timing correlation: If Alice sends a transaction at 14:32:05 and Bob receives one at 14:32:06, an observer can infer a link
  • Fee fingerprinting: Different wallets produce slightly different fee amounts, creating unique "fingerprints"

GhostTracking neutralizes both vectors.

Timing Randomizer

Before submitting a transaction, GhostTracking adds a random delay between 500ms and 8000ms. This breaks the temporal correlation between sender and receiver.

const tracking = new GhostTrackingService();

tracking.enable({
timing: { minDelayMs: 500, maxDelayMs: 8000 },
feeNormalization: { flatFee: 1_000_000n, poolContractAddress: '0x...' }
});

// Each call waits a random 0.5–8 seconds before resolving
const delayMs = await tracking.applyTimingDelay();

The delay range is configurable. Wider ranges provide stronger privacy but increase perceived latency.

Fee Normalization

All transactions are presented with the same flat fee, regardless of the actual network fee. The FeePool contract absorbs the difference:

  • If the actual fee is lower than the flat fee, the surplus goes into the pool
  • If the actual fee is higher than the flat fee, the pool subsidizes the difference
const normalizedFee = tracking.normalizeFee(actualFee);
// Always returns the configured flatFee

const flatFee = tracking.getFlatFee();
// e.g., 1_000_000n

This creates a uniform fee profile across all Knight Shield users, making fee-based fingerprinting useless.

On-Chain Support

The FeePool Compact contract manages the normalization pool:

Circuits:
- initialize(flatFee) — set the flat fee amount
- deposit(userId, amount) — add funds to the pool
- normalizeFee(userId, actualFee) — record normalization event
- getPoolBalance() — current pool balance
- getTotalNormalized() — total fees normalized

See FeePool contract for the full API.

Enabling/Disabling

GhostTracking can be toggled at runtime:

tracking.enable(config);   // Start randomizing and normalizing
tracking.disable(); // Stop — transactions proceed normally

tracking.isEnabled(); // Check current state

On mobile, the GhostTracking screen provides a toggle switch. The GhostContext manages the service lifecycle.

Integration with TransferHelper

The TransferHelper in core has built-in timing randomizer support:

const helper = new TransferHelper(wallet, tokenRegistry);

helper.enableTimingRandomizer(500, 8000);
// All subsequent sendNative() and sendToken() calls
// will include a random delay

helper.disableTimingRandomizer();

This is independent of the GhostTrackingService — it provides a simpler API for timing-only protection without fee normalization.

Configuration

ParameterTypeDefaultDescription
minDelayMsnumber500Minimum random delay in milliseconds
maxDelayMsnumber8000Maximum random delay in milliseconds
flatFeebigint1_000_000nFlat fee for normalization
poolContractAddressstringDeployed FeePool contract address