Blame on coordinator
MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepOutputRegistrationTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepOutputRegistrationTests.cs
@@ -70,7 +70,8 @@ public class StepOutputRegistrationTests
{
MaxInputCountByRound = 2,
MinInputCountByRoundMultiplier = 0.5,
- OutputRegistrationTimeout = TimeSpan.Zero
+ OutputRegistrationTimeout = TimeSpan.Zero,
+ CoordinationFeeRate = CoordinationFeeRate.Zero
};
var (keyChain, coin1, coin2) = WabiSabiFactory.CreateCoinKeyPairs();
@@ -93,8 +94,8 @@ public class StepOutputRegistrationTests
Assert.Equal(Phase.TransactionSigning, round.Phase);
var tx = round.Assert<SigningState>().CreateTransaction();
Assert.Equal(2, tx.Inputs.Count);
- Assert.Equal(2 + 1, tx.Outputs.Count); // +1 for the coordinator fee
- Assert.Contains(cfg.BlameScript, tx.Outputs.Select(x => x.ScriptPubKey));
+ Assert.Equal(2, tx.Outputs.Count);
+ Assert.Contains(round.CoordinatorScript, tx.Outputs.Select(x => x.ScriptPubKey));
await arena.StopAsync(CancellationToken.None);
}
@@ -107,6 +108,7 @@ public class StepOutputRegistrationTests
MaxInputCountByRound = 2,
MinInputCountByRoundMultiplier = 0.5,
OutputRegistrationTimeout = TimeSpan.Zero,
+ CoordinationFeeRate = CoordinationFeeRate.Zero
};
var (keyChain, coin1, coin2) = WabiSabiFactory.CreateCoinKeyPairs();
@@ -144,8 +146,8 @@ public class StepOutputRegistrationTests
Assert.Equal(Phase.TransactionSigning, round.Phase);
var tx = round.Assert<SigningState>().CreateTransaction();
Assert.Equal(3, tx.Inputs.Count);
- Assert.Equal(2 + 1, tx.Outputs.Count); // +1 for the coordinator fee
- Assert.DoesNotContain(cfg.BlameScript, tx.Outputs.Select(x => x.ScriptPubKey));
+ Assert.Equal(2, tx.Outputs.Count);
+ Assert.DoesNotContain(round.CoordinatorScript, tx.Outputs.Select(x => x.ScriptPubKey));
await arena.StopAsync(CancellationToken.None);
}

MODIFY
WalletWasabi/WabiSabi/Backend/Rounds/Arena.cs
WalletWasabi/WabiSabi/Backend/Rounds/Arena.cs
@@ -179,10 +179,10 @@ public partial class Arena : PeriodicRunner
round.LogInfo($"{coinjoin.Inputs.Count()} inputs were added.");
round.LogInfo($"{coinjoin.Outputs.Count()} outputs were added.");
- Script coordinatorScriptPubKey = GetCoordinatorScriptPreventReuse(round);
- coinjoin = AddCoordinationFee(round, coinjoin, coordinatorScriptPubKey);
+ round.CoordinatorScript = GetCoordinatorScriptPreventReuse(round);
+ coinjoin = AddCoordinationFee(round, coinjoin, round.CoordinatorScript);
- coinjoin = AddBlameScript(round, coinjoin, allReady, coordinatorScriptPubKey);
+ coinjoin = AddBlameScript(round, coinjoin, allReady, round.CoordinatorScript);
round.CoinjoinState = coinjoin.Finalize();
@@ -355,7 +355,7 @@ public partial class Arena : PeriodicRunner
}
}
- private ConstructionState AddBlameScript(Round round, ConstructionState coinjoin, bool allReady, Script coordinatorScriptPubKey)
+ private ConstructionState AddBlameScript(Round round, ConstructionState coinjoin, bool allReady, Script blameScript)
{
long aliceSum = round.Alices.Sum(x => x.CalculateRemainingAmountCredentials(round.FeeRate, round.CoordinationFeeRate));
long bobSum = round.Bobs.Sum(x => x.CredentialAmount);
@@ -363,18 +363,18 @@ public partial class Arena : PeriodicRunner
// If timeout we must fill up the outputs to build a reasonable transaction.
// This won't be signed by the alice who failed to provide output, so we know who to ban.
- var diffMoney = Money.Satoshis(diff) - coinjoin.Parameters.FeeRate.GetFee(Config.BlameScript.EstimateOutputVsize());
+ var diffMoney = Money.Satoshis(diff) - coinjoin.Parameters.FeeRate.GetFee(blameScript.EstimateOutputVsize());
if (diffMoney > coinjoin.Parameters.AllowedOutputAmounts.Min)
{
+ coinjoin = coinjoin.AddOutput(new TxOut(diffMoney, blameScript));
+
if (allReady)
{
- coinjoin = coinjoin.AddOutput(new TxOut(diffMoney, coordinatorScriptPubKey));
- round.LogInfo($"Filled up the outputs with coordinator script to build a reasonable transaction, all Alices signalled ready. Added amount: '{diffMoney}'.");
+ round.LogInfo($"Filled up the outputs to build a reasonable transaction, all Alices signalled ready. Added amount: '{diffMoney}'.");
}
else
{
- coinjoin = coinjoin.AddOutput(new TxOut(diffMoney, Config.BlameScript));
- round.LogWarning($"Filled up the outputs with blame script to build a reasonable transaction because some alice failed to provide its output. Added amount: '{diffMoney}'.");
+ round.LogWarning($"Filled up the outputs to build a reasonable transaction because some alice failed to provide its output. Added amount: '{diffMoney}'.");
}
}
else if (!allReady)

MODIFY
WalletWasabi/WabiSabi/Backend/Rounds/Round.cs
WalletWasabi/WabiSabi/Backend/Rounds/Round.cs
@@ -63,6 +63,7 @@ public class Round
public int RemainingInputVsizeAllocation => InitialInputVsizeAllocation - (InputCount * MaxVsizeAllocationPerAlice);
protected RoundParameters RoundParameters { get; }
+ public Script CoordinatorScript { get; set; }
public TState Assert<TState>() where TState : MultipartyTransactionState =>
CoinjoinState switch

MODIFY
WalletWasabi/WabiSabi/Backend/WabiSabiConfig.cs
WalletWasabi/WabiSabi/Backend/WabiSabiConfig.cs
@@ -91,12 +91,6 @@ public class WabiSabiConfig : ConfigBase
[JsonProperty(PropertyName = "CoordinatorExtPubKeyCurrentDepth", DefaultValueHandling = DefaultValueHandling.Populate)]
public int CoordinatorExtPubKeyCurrentDepth { get; private set; } = 1;
- /// <summary>
- /// If money comes to the blame script, then either an attacker lost money or there's a client bug.
- /// </summary>
- [JsonIgnore]
- public Script BlameScript => DeriveCoordinatorScript(0);
-
public Script GetNextCleanCoordinatorScript() => DeriveCoordinatorScript(CoordinatorExtPubKeyCurrentDepth);
public Script DeriveCoordinatorScript(int index) => CoordinatorExtPubKey.Derive(0, false).Derive(index, false).PubKey.WitHash.ScriptPubKey;

Blame on coordinator