Merging non-test changes
MODIFY
WalletWasabi.Backend/Controllers/ChaumianCoinJoinController.cs
WalletWasabi.Backend/Controllers/ChaumianCoinJoinController.cs
@@ -60,21 +60,22 @@ namespace WalletWasabi.Backend.Controllers
[ProducesResponseType(200)]
public IActionResult GetStates()
{
- IEnumerable<RoundStateResponse> response = GetStatesCollection();
+ IEnumerable<RoundStateResponse4> response = GetStatesCollection();
return Ok(response);
}
- internal IEnumerable<RoundStateResponse> GetStatesCollection()
+ internal IEnumerable<RoundStateResponse4> GetStatesCollection()
{
- var response = new List<RoundStateResponse>();
+ var response = new List<RoundStateResponse4>();
foreach (CoordinatorRound round in Coordinator.GetRunningRounds())
{
- var state = new RoundStateResponse
+ var state = new RoundStateResponse4
{
Phase = round.Phase,
- SchnorrPubKeys = round.MixingLevels.SchnorrPubKeys,
+ SignerPubKeys = round.MixingLevels.SignerPubKeys,
+ RPubKeys = round.GetNextNoncesForMixingLevels(),
Denomination = round.MixingLevels.GetBaseDenomination(),
InputRegistrationTimesout = round.InputRegistrationTimesout,
RegisteredPeerCount = round.CountAlices(syncLock: false),
@@ -105,7 +106,7 @@ namespace WalletWasabi.Backend.Controllers
[ProducesResponseType(200)]
[ProducesResponseType(400)]
[ProducesResponseType(404)]
- public async Task<IActionResult> PostInputsAsync([FromBody, Required]InputsRequest request)
+ public async Task<IActionResult> PostInputsAsync([FromBody, Required] InputsRequest4 request)
{
// Validate request.
if (request.RoundId < 0 || !ModelState.IsValid)
@@ -130,7 +131,7 @@ namespace WalletWasabi.Backend.Controllers
// Do more checks.
try
{
- uint256[] blindedOutputs = request.BlindedOutputScripts.ToArray();
+ var blindedOutputs = request.BlindedOutputScripts.ToArray();
int blindedOutputCount = blindedOutputs.Length;
int maxBlindedOutputCount = round.MixingLevels.Count();
if (blindedOutputCount > maxBlindedOutputCount)
@@ -143,7 +144,7 @@ namespace WalletWasabi.Backend.Controllers
return BadRequest("Duplicate blinded output found.");
}
- if (round.ContainsAnyBlindedOutputScript(blindedOutputs))
+ if (round.ContainsAnyBlindedOutputScript(blindedOutputs.Select(x => x.BlindedOutput)))
{
return BadRequest("Blinded output has already been registered.");
}
@@ -201,7 +202,7 @@ namespace WalletWasabi.Backend.Controllers
// Perform all RPC request at once
await batch.SendBatchAsync();
- byte[] blindedOutputScriptHashesByte = ByteHelpers.Combine(blindedOutputs.Select(x => x.ToBytes()));
+ byte[] blindedOutputScriptHashesByte = ByteHelpers.Combine(blindedOutputs.Select(x => x.BlindedOutput.ToBytes()));
uint256 blindedOutputScriptsHash = new uint256(Hashes.SHA256(blindedOutputScriptHashesByte));
var inputs = new HashSet<Coin>();
@@ -264,7 +265,7 @@ namespace WalletWasabi.Backend.Controllers
}
}
- var acceptedBlindedOutputScripts = new List<uint256>();
+ var acceptedBlindedOutputScripts = new List<BlindedOutputWithNonceIndex>();
// Calculate expected networkfee to pay after base denomination.
int inputCount = inputs.Count;
@@ -302,7 +303,7 @@ namespace WalletWasabi.Backend.Controllers
}
// Make sure Alice checks work.
- var alice = new Alice(inputs, networkFeeToPayAfterBaseDenomination, request.ChangeOutputAddress, acceptedBlindedOutputScripts);
+ var alice = new Alice(inputs, networkFeeToPayAfterBaseDenomination, request.ChangeOutputAddress, acceptedBlindedOutputScripts.Select(x => x.BlindedOutput));
foreach (Guid aliceToRemove in alicesToRemove)
{
@@ -316,7 +317,7 @@ namespace WalletWasabi.Backend.Controllers
{
var blindedOutput = acceptedBlindedOutputScripts[i];
var signer = round.MixingLevels.GetLevel(i).Signer;
- uint256 blindSignature = signer.Sign(blindedOutput);
+ uint256 blindSignature = signer.Sign(blindedOutput.BlindedOutput, round.GetNextNonceKey(blindedOutput.N));
blindSignatures.Add(blindSignature);
}
alice.BlindedOutputSignatures = blindSignatures.ToArray();

MODIFY
WalletWasabi/CoinJoin/Common/Crypto/SchnorrPubKey.cs
WalletWasabi/CoinJoin/Common/Crypto/SchnorrPubKey.cs
@@ -25,15 +25,6 @@ namespace WalletWasabi.CoinJoin.Common.Crypto
RpubKey = Guard.NotNull(nameof(rKey), rKey?.PubKey);
}
- public SchnorrPubKey(Signer signer)
- {
- signer = Guard.NotNull(nameof(signer), signer);
- var signerKey = signer?.Key;
- var rKey = signer?.R;
- SignerPubKey = Guard.NotNull(nameof(signerKey), signerKey?.PubKey);
- RpubKey = Guard.NotNull(nameof(rKey), rKey?.PubKey);
- }
-
[JsonProperty]
[JsonConverter(typeof(PubKeyJsonConverter))]
public PubKey SignerPubKey { get; }

MODIFY
WalletWasabi/CoinJoin/Coordinator/MixingLevels/MixingLevel.cs
WalletWasabi/CoinJoin/Coordinator/MixingLevels/MixingLevel.cs
@@ -18,28 +18,17 @@ namespace WalletWasabi.CoinJoin.Coordinator.MixingLevels
Denomination = Guard.NotNull(nameof(denomination), denomination);
Signer = Guard.NotNull(nameof(signer), signer);
var signerKey = Guard.NotNull(nameof(signer.Key), signer.Key);
- var rKey = Guard.NotNull(nameof(signer.R), signer.R);
- SchnorrKey = new SchnorrKey(signerKey, rKey);
- }
-
- public MixingLevel(Money denomination, Key signerKey, Key rKey)
- {
- Denomination = Guard.NotNull(nameof(denomination), denomination);
- signerKey = Guard.NotNull(nameof(signerKey), signerKey);
- rKey = Guard.NotNull(nameof(rKey), rKey);
- SchnorrKey = new SchnorrKey(signerKey, rKey);
-
- Signer = SchnorrKey.CreateSigner();
+ SignerKey = signer.Key;
}
[JsonConstructor]
- public MixingLevel(Money denomination, SchnorrKey schnorrKey)
+ public MixingLevel(Money denomination, Key signerKey)
{
Denomination = Guard.NotNull(nameof(denomination), denomination);
- SchnorrKey = Guard.NotNull(nameof(schnorrKey), schnorrKey);
+ SignerKey = Guard.NotNull(nameof(signerKey), signerKey);
- Signer = SchnorrKey.CreateSigner();
+ Signer = new Signer(signerKey);
}
public Signer Signer { get; }
@@ -49,7 +38,7 @@ namespace WalletWasabi.CoinJoin.Coordinator.MixingLevels
public Money Denomination { get; }
[JsonProperty]
- public SchnorrKey SchnorrKey { get; }
+ public Key SignerKey { get; }
#region EqualityAndComparison
@@ -57,9 +46,9 @@ namespace WalletWasabi.CoinJoin.Coordinator.MixingLevels
public bool Equals(MixingLevel other) => this == other;
- public override int GetHashCode() => (Denomination, SchnorrKey).GetHashCode();
+ public override int GetHashCode() => (Denomination, SignerKey).GetHashCode();
- public static bool operator ==(MixingLevel x, MixingLevel y) => y?.Denomination == x?.Denomination && y?.SchnorrKey == x?.SchnorrKey;
+ public static bool operator ==(MixingLevel x, MixingLevel y) => y?.Denomination == x?.Denomination && y?.SignerKey == x?.SignerKey;
public static bool operator !=(MixingLevel x, MixingLevel y) => !(x == y);

MODIFY
WalletWasabi/CoinJoin/Coordinator/MixingLevels/MixingLevelCollection.cs
WalletWasabi/CoinJoin/Coordinator/MixingLevels/MixingLevelCollection.cs
@@ -13,7 +13,7 @@ namespace WalletWasabi.CoinJoin.Coordinator.MixingLevels
[JsonObject(MemberSerialization.OptIn)]
public class MixingLevelCollection
{
- private IEnumerable<SchnorrPubKey> _schnorrPubKeys;
+ private IEnumerable<PubKey> _signerPubKeys;
[JsonConstructor]
public MixingLevelCollection(IEnumerable<MixingLevel> levels)
@@ -26,7 +26,6 @@ namespace WalletWasabi.CoinJoin.Coordinator.MixingLevels
baseDenomination = Guard.MinimumAndNotNull(nameof(baseDenomination), baseDenomination, Money.Zero);
signer = Guard.NotNull(nameof(signer), signer);
Guard.NotNull(nameof(signer.Key), signer.Key);
- Guard.NotNull(nameof(signer.R), signer.R);
Create(new List<MixingLevel> { new MixingLevel(baseDenomination, signer) });
}
@@ -34,17 +33,17 @@ namespace WalletWasabi.CoinJoin.Coordinator.MixingLevels
[JsonProperty]
public List<MixingLevel> Levels { get; private set; }
- public IEnumerable<SchnorrPubKey> SchnorrPubKeys
+ public IEnumerable<PubKey> SignerPubKeys
{
get
{
- if (_schnorrPubKeys?.Count() != Levels?.Count) // Signing keys do not change, but more levels may be added. (Although even that's unlikely.)
+ if (_signerPubKeys?.Count() != Levels?.Count) // Signing keys do not change, but more levels may be added. (Although even that's unlikely.)
{
- _schnorrPubKeys = Levels.Select(x => x.Signer.GetSchnorrPubKey());
+ _signerPubKeys = Levels.Select(x => x.Signer.Key.PubKey);
}
- return _schnorrPubKeys;
+ return _signerPubKeys;
}
- set => _schnorrPubKeys = value;
+ set => _signerPubKeys = value;
}
private void Create(IEnumerable<MixingLevel> levels)

MODIFY
WalletWasabi/CoinJoin/Coordinator/Rounds/CoordinatorRound.cs
WalletWasabi/CoinJoin/Coordinator/Rounds/CoordinatorRound.cs
@@ -6,6 +6,7 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
+using System.Security;
using System.Threading;
using System.Threading.Tasks;
using WalletWasabi.BitcoinCore;
@@ -25,6 +26,9 @@ namespace WalletWasabi.CoinJoin.Coordinator.Rounds
public static long RoundCount;
private RoundPhase _phase;
private CoordinatorRoundStatus _status;
+ private ExtKey _nonceGenerator;
+ private int _lastNonceIndex;
+ private HashSet<int> _alreadyUsedNonceIndexes;
public CoordinatorRound(IRPCClient rpc, UtxoReferee utxoReferee, CoordinatorRoundConfig config, int adjustedConfirmationTarget, int configuredConfirmationTarget, double configuredConfirmationTargetReductionRate)
{
@@ -55,6 +59,10 @@ namespace WalletWasabi.CoinJoin.Coordinator.Rounds
RegisteredUnblindedSignatures = new List<UnblindedSignature>();
RegisteredUnblindedSignaturesLock = new object();
+ _nonceGenerator = new ExtKey();
+ _lastNonceIndex = -1;
+ _alreadyUsedNonceIndexes = new HashSet<int>();
+
MixingLevels = new MixingLevelCollection(config.Denomination, new Signer(new Key()));
for (int i = 0; i < config.MaximumMixingLevelCount - 1; i++)
{
@@ -1346,5 +1354,37 @@ namespace WalletWasabi.CoinJoin.Coordinator.Rounds
}
#endregion Modifiers
+
+ #region Nonce management
+
+ public PublicNonceWithIndex GetNextNonce()
+ {
+ var n = Interlocked.Increment(ref _lastNonceIndex);
+ var extKey = _nonceGenerator.Derive(n, hardened: true);
+ return new PublicNonceWithIndex(n, extKey.GetPublicKey());
+ }
+
+ public PublicNonceWithIndex[] GetNextNoncesForMixingLevels()
+ {
+ var mixingLevels = MixingLevels.Count();
+ var nonces = new PublicNonceWithIndex[mixingLevels];
+ for (var i = 0; i < mixingLevels; i++)
+ {
+ nonces[i] = GetNextNonce();
+ }
+ return nonces;
+ }
+
+ public Key GetNextNonceKey(int n)
+ {
+ if (!_alreadyUsedNonceIndexes.Add(n))
+ {
+ throw new SecurityException($"Nonce {n} was already used.");
+ }
+ var extKey = _nonceGenerator.Derive(n, hardened: true);
+ return extKey.PrivateKey;
+ }
+
+ #endregion Nonce management
}
}

MODIFY
WalletWasabi/Crypto/SchnorrBlindingSignature.cs
WalletWasabi/Crypto/SchnorrBlindingSignature.cs
@@ -120,26 +120,15 @@ namespace WalletWasabi.Crypto
public class Signer
{
- public Signer(Key key)
- : this(key, new Key())
- {
- }
+ // The signer key used for signing
+ public Key Key { get; }
- public Signer(Key key, Key r)
+ public Signer(Key key)
{
- R = r;
Key = key;
}
- // The random generated r value. It is used to derivate an R point where
- // R = r*G that has to be sent to the requester in order to allow him to
- // blind the message to be signed.
- public Key R { get; }
-
- // The signer key used for signing
- public Key Key { get; }
-
- public uint256 Sign(uint256 blindedMessage)
+ public uint256 Sign(uint256 blindedMessage, Key rKey)
{
Span<byte> tmp = stackalloc byte[32];
blindedMessage.ToBytes(tmp);
@@ -149,7 +138,7 @@ namespace WalletWasabi.Crypto
throw new ArgumentException("Invalid blinded message.", nameof(blindedMessage));
}
- if (!Context.Instance.TryCreateECPrivKey(R.ToBytes(), out var r))
+ if (!Context.Instance.TryCreateECPrivKey(rKey.ToBytes(), out var r))
{
throw new FormatException("Invalid key.");
}

MODIFY
WalletWasabi/Extensions/NBitcoinExtensions.cs
WalletWasabi/Extensions/NBitcoinExtensions.cs
@@ -164,13 +164,6 @@ namespace NBitcoin
return pubKey.WitHash == address.Hash;
}
- public static Signer CreateSigner(this SchnorrKey schnorrKey)
- {
- var k = Guard.NotNull(nameof(schnorrKey.SignerKey), schnorrKey.SignerKey);
- var r = Guard.NotNull(nameof(schnorrKey.Rkey), schnorrKey.Rkey);
- return new Signer(k, r);
- }
-
/// <summary>
/// If scriptpubkey is already present, just add the value.
/// </summary>
@@ -214,8 +207,6 @@ namespace NBitcoin
}
}
- public static SchnorrPubKey GetSchnorrPubKey(this Signer signer) => new SchnorrPubKey(signer);
-
public static uint256 BlindMessage(this Requester requester, uint256 messageHash, SchnorrPubKey schnorrPubKey) => requester.BlindMessage(messageHash, schnorrPubKey.RpubKey, schnorrPubKey.SignerPubKey);
public static string ToZpub(this ExtPubKey extPubKey, Network network)

MODIFY
WalletWasabi/Helpers/Constants.cs
WalletWasabi/Helpers/Constants.cs
@@ -11,7 +11,7 @@ namespace WalletWasabi.Helpers
public static readonly Version ClientVersion = new Version(1, 1, 11, 1);
public const string ClientSupportBackendVersionMin = "3";
public const string ClientSupportBackendVersionMax = "4";
- public const string BackendMajorVersion = "3";
+ public const string BackendMajorVersion = "4";
public static readonly Version HwiVersion = new Version("1.1.0");
public static readonly Version BitcoinCoreVersion = new Version("0.19.1");
public static readonly Version LegalDocumentsVersion = new Version(2, 0);

Merging non-test changes