Use network traffic observations
MODIFY
WalletWasabi.Tests/UnitTests/IoTests.cs
WalletWasabi.Tests/UnitTests/IoTests.cs
@@ -5,14 +5,41 @@ using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using NBitcoin;
using WalletWasabi.Helpers;
using WalletWasabi.Io;
+using WalletWasabi.Services;
using Xunit;
namespace WalletWasabi.Tests.UnitTests
{
public class IoTests
{
+ [Fact]
+ public void X()
+ {
+ var lockTimeZero = int.MaxValue;
+ var samplingSize = 10_000;
+
+ var dict = Enumerable.Range(-99, 101).ToDictionary(x=>x, x=>0);
+ dict[lockTimeZero] = 0;
+
+ var curTip = 100_000;
+ foreach (var i in Enumerable.Range(0, samplingSize))
+ {
+ var lt = WalletService.InternalSelectLockTimeForTransaction(curTip).Height;
+ var diff = lt == 0 ? lockTimeZero : lt - curTip;
+ dict[diff]++;
+ }
+
+ Assert.InRange(dict[lockTimeZero], samplingSize * 0.85, samplingSize * 0.95); // around 90%
+ Assert.InRange(dict[0], samplingSize * 0.070, samplingSize * 0.080); // around 7.5%
+ Assert.InRange(dict[1], samplingSize * 0.003, samplingSize * 0.009); // around 0.65%
+
+ var rest = dict.Where(x=>x.Key < 0).Select(X=>X.Value);
+ Assert.DoesNotContain(rest, x=>x > samplingSize * 0.001);
+ }
+
[Fact]
public async Task IoManagerTestsAsync()
{

MODIFY
WalletWasabi/Services/WalletService.cs
WalletWasabi/Services/WalletService.cs
@@ -760,16 +760,31 @@ namespace WalletWasabi.Services
}
}
- private LockTime SelectLockTimeForTransaction()
+ internal static LockTime InternalSelectLockTimeForTransaction(int tipHeight)
{
try
{
- // discourage "fee sniping".
- var tipHeight = Synchronizer.BitcoinStore.HashChain.TipHeight;
- var rand = new Random();
+ // We use the timelock distribution observed in the bitcoin network
+ // in order to reduce the wasabi wallet transactions fingerprinting
+ // chances.
+ //
+ // Network observations:
+ // 90.0% uses locktime = 0
+ // 7.5% uses locktime = current tip
+ // 0.65% uses locktime = next tip (current tip + 1)
+ // 1.85% uses up to 5 blocks in the future (we don't do this)
+ // 0.65% uses an uniform random from -1 to -99
+
+ var randomGenerator = new Random();
// sometimes pick locktime a bit further back, to help privacy.
- var randomize = rand.Next(0, 10) == 0;
- return tipHeight - (randomize ? rand.Next(0, 100) : 0);
+ var randomValue = randomGenerator.NextDouble();
+ return randomValue switch
+ {
+ var r when r < (0.9) => LockTime.Zero,
+ var r when r < (0.9 + 0.075) => tipHeight,
+ var r when r < (0.9 + 0.075 + 0.0065) => tipHeight + 1,
+ _ => tipHeight - randomGenerator.Next(1, 100)
+ };
}
catch(Exception)
{
@@ -777,6 +792,13 @@ namespace WalletWasabi.Services
}
}
+ private LockTime SelectLockTimeForTransaction()
+ {
+ var currentTipHeight = Synchronizer.BitcoinStore.HashChain.TipHeight;
+
+ return InternalSelectLockTimeForTransaction(currentTipHeight);
+ }
+
private static long SendCount = 0;
private async Task BroadcastTransactionToNetworkNodeAsync(SmartTransaction transaction, Node node)

Use network traffic observations