Implement output registration checks
MODIFY
WalletWasabi.Tests/Helpers/WabiSabiFactory.cs
WalletWasabi.Tests/Helpers/WabiSabiFactory.cs
@@ -65,16 +65,16 @@ namespace WalletWasabi.Tests.Helpers
new FeeRate(100m),
new InsecureRandom());
- public static Alice CreateAlice(InputRoundSignaturePair inputSigPairs) => CreateAlice(new[] { inputSigPairs });
+ public static Alice CreateAlice(InputRoundSignaturePair inputSigPairs, Money? value = null) => CreateAlice(new[] { inputSigPairs }, value);
- public static Alice CreateAlice(IEnumerable<InputRoundSignaturePair>? inputSigPairs = null)
+ public static Alice CreateAlice(IEnumerable<InputRoundSignaturePair>? inputSigPairs = null, Money? value = null)
{
var pairs = inputSigPairs ?? CreateInputRoundSignaturePairs(1);
var myDic = new Dictionary<Coin, byte[]>();
foreach (var pair in pairs)
{
- var coin = new Coin(pair.Input, new TxOut(Money.Coins(1), BitcoinFactory.CreateScript()));
+ var coin = new Coin(pair.Input, new TxOut(value ?? Money.Coins(1), BitcoinFactory.CreateScript()));
myDic.Add(coin, pair.RoundSignature);
}
return new Alice(myDic);
@@ -120,18 +120,18 @@ namespace WalletWasabi.Tests.Helpers
{
var rnd = new InsecureRandom();
var ai = round?.AmountCredentialIssuer ?? new CredentialIssuer(new CredentialIssuerSecretKey(rnd), 2, rnd, 4300000000000);
- var wi = round?.WeightCredentialIssuer ?? new CredentialIssuer(new CredentialIssuerSecretKey(rnd), 2, rnd, 4300000000000);
+ var wi = round?.WeightCredentialIssuer ?? new CredentialIssuer(new CredentialIssuerSecretKey(rnd), 2, rnd, 2000ul);
var ac = new WabiSabiClient(
ai.CredentialIssuerSecretKey.ComputeCredentialIssuerParameters(),
ai.NumberOfCredentials,
rnd,
- round?.MaxRegistrableAmountByAlice ?? 4300000000000);
+ ai.MaxAmount);
var wc = new WabiSabiClient(
wi.CredentialIssuerSecretKey.ComputeCredentialIssuerParameters(),
wi.NumberOfCredentials,
rnd,
- round?.RegistrableWeightCredentials ?? 2000ul);
+ wi.MaxAmount);
return (ac, wc, ai, wi);
}
@@ -139,7 +139,11 @@ namespace WalletWasabi.Tests.Helpers
public static (IEnumerable<Credential> amountCredentials, IEnumerable<Credential> weightCredentials) CreateZeroCredentials(Round? round)
{
(var amClient, var weClient, var amIssuer, var weIssuer) = CreateWabiSabiClientsAndIssuers(round);
+ return CreateZeroCredentials(amClient, weClient, amIssuer, weIssuer);
+ }
+ private static (IEnumerable<Credential> amountCredentials, IEnumerable<Credential> weightCredentials) CreateZeroCredentials(WabiSabiClient amClient, WabiSabiClient weClient, CredentialIssuer amIssuer, CredentialIssuer weIssuer)
+ {
var (zeroAmountCredentialRequest, amVal) = amClient.CreateRequestForZeroAmount();
var (zeroWeightCredentialRequest, weVal) = weClient.CreateRequestForZeroAmount();
var amCredResp = amIssuer.HandleRequest(zeroAmountCredentialRequest);
@@ -169,9 +173,6 @@ namespace WalletWasabi.Tests.Helpers
{
(var amClient, var weClient, _, _) = CreateWabiSabiClientsAndIssuers(round);
- var (zeroAmountCredentialRequest, _) = amClient.CreateRequestForZeroAmount();
- var (zeroWeightCredentialRequest, _) = weClient.CreateRequestForZeroAmount();
-
var zeroPresentables = CreateZeroCredentials(round);
var alice = round?.Alices.FirstOrDefault();
var (realAmountCredentialRequest, _) = amClient.CreateRequest(
@@ -181,6 +182,9 @@ namespace WalletWasabi.Tests.Helpers
new[] { alice?.CalculateRemainingWeightCredentials(round!.RegistrableWeightCredentials) ?? 1000L },
zeroPresentables.weightCredentials);
+ var (zeroAmountCredentialRequest, _) = amClient.CreateRequestForZeroAmount();
+ var (zeroWeightCredentialRequest, _) = weClient.CreateRequestForZeroAmount();
+
return new ConnectionConfirmationRequest(
round?.Id ?? Guid.NewGuid(),
alice?.Id ?? Guid.NewGuid(),
@@ -189,5 +193,49 @@ namespace WalletWasabi.Tests.Helpers
zeroWeightCredentialRequest,
realWeightCredentialRequest);
}
+
+ public static OutputRegistrationRequest CreateOutputRegistrationRequest(Round? round = null, Script? script = null, int? weight = null)
+ {
+ (var amClient, var weClient, var amIssuer, var weIssuer) = CreateWabiSabiClientsAndIssuers(round);
+ var zeroPresentables = CreateZeroCredentials(amClient, weClient, amIssuer, weIssuer);
+
+ var alice = round?.Alices.FirstOrDefault();
+ var (amCredentialRequest, amValid) = amClient.CreateRequest(
+ new[] { alice?.CalculateRemainingAmountCredentials(round!.FeeRate).Satoshi ?? 1000L },
+ zeroPresentables.amountCredentials);
+ long startingWeightCredentialAmount = alice?.CalculateRemainingWeightCredentials(round!.RegistrableWeightCredentials) ?? 1000L;
+ var (weCredentialRequest, weValid) = weClient.CreateRequest(
+ new[] { startingWeightCredentialAmount },
+ zeroPresentables.weightCredentials);
+
+ var amResp = amIssuer.HandleRequest(amCredentialRequest);
+ var weResp = weIssuer.HandleRequest(weCredentialRequest);
+ amClient.HandleResponse(amResp, amValid);
+ weClient.HandleResponse(weResp, weValid);
+
+ script ??= BitcoinFactory.CreateScript();
+ var (realAmountCredentialRequest, _) = amClient.CreateRequest(
+ Array.Empty<long>(),
+ amClient.Credentials.Valuable);
+
+ try
+ {
+ weight ??= script.EstimateOutputVsize() * 4;
+ }
+ catch (NotImplementedException)
+ {
+ weight = 100;
+ }
+
+ var (realWeightCredentialRequest, _) = weClient.CreateRequest(
+ new[] { startingWeightCredentialAmount - (long)weight },
+ weClient.Credentials.Valuable);
+
+ return new OutputRegistrationRequest(
+ round?.Id ?? Guid.NewGuid(),
+ script,
+ realAmountCredentialRequest,
+ realWeightCredentialRequest);
+ }
}
}

MODIFY
WalletWasabi.Tests/UnitTests/Crypto/ProofSystemTests.cs
WalletWasabi.Tests/UnitTests/Crypto/ProofSystemTests.cs
@@ -203,7 +203,7 @@ namespace WalletWasabi.Tests.UnitTests.Crypto
var commitment = amountScalar * Generators.Gg + randomness * Generators.Gh;
var maskedScalar = new Scalar(amount & ((1ul << width) - 1));
- var (knowledge, bitCommitments) = ProofSystem.RangeProofKnowledge(maskedScalar, randomness, width, rnd, 42);
+ var (knowledge, bitCommitments) = ProofSystem.RangeProofKnowledge(maskedScalar, randomness, width, rnd);
var rangeProof = ProofSystemHelpers.Prove(knowledge, rnd);
@@ -211,7 +211,7 @@ namespace WalletWasabi.Tests.UnitTests.Crypto
if (!pass)
{
- Assert.Throws<ArgumentException>(() => ProofSystem.RangeProofKnowledge(amountScalar, randomness, width, rnd, 42));
+ Assert.Throws<ArgumentException>(() => ProofSystem.RangeProofKnowledge(amountScalar, randomness, width, rnd));
}
}

MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/RegisterOutputTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/RegisterOutputTests.cs
@@ -24,47 +24,98 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend
arena.OnTryGetRound = _ => null;
await using PostRequestHandler handler = new(new WabiSabiConfig(), new Prison(), arena, new MockRpcClient());
- var req = new OutputRegistrationRequest(Guid.NewGuid(), null!, null!, null!);
+ var req = WabiSabiFactory.CreateOutputRegistrationRequest();
var ex = Assert.Throws<WabiSabiProtocolException>(() => handler.RegisterOutput(req));
Assert.Equal(WabiSabiProtocolErrorCode.RoundNotFound, ex.ErrorCode);
}
[Fact]
- public async Task WrongPhaseAsync()
+ public async Task ScriptNotAllowedAsync()
{
MockArena arena = new();
WabiSabiConfig cfg = new();
var round = WabiSabiFactory.CreateRound(cfg);
+ round.Phase = Phase.OutputRegistration;
arena.OnTryGetRound = _ => round;
using Key key = new();
- var req = new OutputRegistrationRequest(Guid.NewGuid(), null!, null!, null!);
- foreach (Phase phase in Enum.GetValues(typeof(Phase)))
- {
- if (phase != Phase.OutputRegistration)
- {
- round.Phase = phase;
+ var req = WabiSabiFactory.CreateOutputRegistrationRequest(round, key.PubKey.GetAddress(ScriptPubKeyType.Legacy, Network.Main).ScriptPubKey);
await using PostRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient());
var ex = Assert.Throws<WabiSabiProtocolException>(() => handler.RegisterOutput(req));
- Assert.Equal(WabiSabiProtocolErrorCode.WrongPhase, ex.ErrorCode);
+ Assert.Equal(WabiSabiProtocolErrorCode.ScriptNotAllowed, ex.ErrorCode);
}
+
+ [Fact]
+ public async Task NotEnoughFundsAsync()
+ {
+ MockArena arena = new();
+ WabiSabiConfig cfg = new() { MinRegistrableAmount = Money.Coins(2) };
+ var round = WabiSabiFactory.CreateRound(cfg);
+ round.Phase = Phase.OutputRegistration;
+ round.Alices.Add(WabiSabiFactory.CreateAlice(value: Money.Coins(1)));
+ arena.OnTryGetRound = _ => round;
+ await using PostRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient());
+
+ var req = WabiSabiFactory.CreateOutputRegistrationRequest(round);
+
+ var ex = Assert.Throws<WabiSabiProtocolException>(() => handler.RegisterOutput(req));
+ Assert.Equal(WabiSabiProtocolErrorCode.NotEnoughFunds, ex.ErrorCode);
}
+
+ [Fact]
+ public async Task TooMuchFundsAsync()
+ {
+ MockArena arena = new();
+ WabiSabiConfig cfg = new() { MaxRegistrableAmount = Money.Coins(1.999m) };
+ var round = WabiSabiFactory.CreateRound(cfg);
+ round.Phase = Phase.OutputRegistration;
+ round.Alices.Add(WabiSabiFactory.CreateAlice(value: Money.Coins(2)));
+ arena.OnTryGetRound = _ => round;
+ await using PostRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient());
+
+ var req = WabiSabiFactory.CreateOutputRegistrationRequest(round);
+
+ var ex = Assert.Throws<WabiSabiProtocolException>(() => handler.RegisterOutput(req));
+ Assert.Equal(WabiSabiProtocolErrorCode.TooMuchFunds, ex.ErrorCode);
}
[Fact]
- public async Task ScriptNotAllowedAsync()
+ public async Task IncorrectRequestedWeightCredentialsAsync()
{
MockArena arena = new();
WabiSabiConfig cfg = new();
var round = WabiSabiFactory.CreateRound(cfg);
round.Phase = Phase.OutputRegistration;
+ round.Alices.Add(WabiSabiFactory.CreateAlice());
arena.OnTryGetRound = _ => round;
- using Key key = new();
+ await using PostRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient());
- var req = new OutputRegistrationRequest(Guid.NewGuid(), new TxOut(Money.Coins(1), key.PubKey.GetAddress(ScriptPubKeyType.Legacy, Network.Main)), null!, null!);
+ var req = WabiSabiFactory.CreateOutputRegistrationRequest(round, weight: 30);
+
+ var ex = Assert.Throws<WabiSabiProtocolException>(() => handler.RegisterOutput(req));
+ Assert.Equal(WabiSabiProtocolErrorCode.IncorrectRequestedWeightCredentials, ex.ErrorCode);
+ }
+
+ [Fact]
+ public async Task WrongPhaseAsync()
+ {
+ MockArena arena = new();
+ WabiSabiConfig cfg = new();
+ var round = WabiSabiFactory.CreateRound(cfg);
+ round.Alices.Add(WabiSabiFactory.CreateAlice());
+ arena.OnTryGetRound = _ => round;
await using PostRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient());
+
+ foreach (Phase phase in Enum.GetValues(typeof(Phase)))
+ {
+ if (phase != Phase.OutputRegistration)
+ {
+ var req = WabiSabiFactory.CreateOutputRegistrationRequest(round);
+ round.Phase = phase;
var ex = Assert.Throws<WabiSabiProtocolException>(() => handler.RegisterOutput(req));
- Assert.Equal(WabiSabiProtocolErrorCode.ScriptNotAllowed, ex.ErrorCode);
+ Assert.Equal(WabiSabiProtocolErrorCode.WrongPhase, ex.ErrorCode);
+ }
+ }
}
}
}

MODIFY
WalletWasabi/Crypto/ZeroKnowledge/ProofSystem.cs
WalletWasabi/Crypto/ZeroKnowledge/ProofSystem.cs
@@ -141,7 +141,7 @@ namespace WalletWasabi.Crypto.ZeroKnowledge
// TODO swap return value order, remove GroupElement argument
// expect nonce provider instead of WasabiRandom?
- public static (Knowledge knowledge, IEnumerable<GroupElement> bitCommitments) RangeProofKnowledge(Scalar a, Scalar r, int width, WasabiRandom rnd, int rangeProofWidth)
+ public static (Knowledge knowledge, IEnumerable<GroupElement> bitCommitments) RangeProofKnowledge(Scalar a, Scalar r, int width, WasabiRandom rnd)
{
var ma = PedersenCommitment(a, r);
var bits = Enumerable.Range(0, width).Select(i => a.GetBits(i, 1) == 0 ? Scalar.Zero : Scalar.One);
@@ -177,7 +177,7 @@ namespace WalletWasabi.Crypto.ZeroKnowledge
witness[ProductColumn(i)] = r_i * b_i;
}
- return (new Knowledge(RangeProofStatement(ma, bitCommitments, rangeProofWidth), new ScalarVector(witness)), bitCommitments);
+ return (new Knowledge(RangeProofStatement(ma, bitCommitments, width), new ScalarVector(witness)), bitCommitments);
}
// overload for bootstrap credential request proofs.

MODIFY
WalletWasabi/Extensions/NBitcoinExtensions.cs
WalletWasabi/Extensions/NBitcoinExtensions.cs
@@ -455,7 +455,19 @@ namespace NBitcoin
return sanityFee;
}
- public static int EstimateSpendVsize(this Script script)
+ public static int EstimateOutputVsize(this Script script)
+ {
+ if (script.IsScriptType(ScriptType.P2WPKH))
+ {
+ return Constants.OutputSizeInBytes;
+ }
+ else
+ {
+ throw new NotImplementedException($"Weight estimation isn't implemented for provided script type.");
+ }
+ }
+
+ public static int EstimateInputVsize(this Script script)
{
if (script.IsScriptType(ScriptType.P2WPKH))
{

MODIFY
WalletWasabi/WabiSabi/Backend/Models/Alice.cs
WalletWasabi/WabiSabi/Backend/Models/Alice.cs
@@ -21,7 +21,7 @@ namespace WalletWasabi.WabiSabi.Backend.Models
public IDictionary<Coin, byte[]> CoinRoundSignaturePairs { get; }
public Money TotalInputAmount => Coins.Sum(x => x.Amount);
public long TotalInputWeight => TotalInputVsize * 4;
- public int TotalInputVsize => Coins.Sum(x => x.ScriptPubKey.EstimateSpendVsize());
+ public int TotalInputVsize => Coins.Sum(x => x.ScriptPubKey.EstimateInputVsize());
public long CalculateRemainingWeightCredentials(uint maxRegistrableWeight) => maxRegistrableWeight - TotalInputWeight;

ADD
WalletWasabi/WabiSabi/Backend/Models/Bob.cs
+using NBitcoin;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WalletWasabi.WabiSabi.Backend.Models
+{
+ public class Bob
+ {
+ public Bob(Script script, long credentialAmount)
+ {
+ Script = script;
+ CredentialAmount = credentialAmount;
+ }
+
+ public Script Script { get; }
+
+ /// <summary>
+ /// This is slightly larger than the final TXO amount,
+ /// because the fees are coming down from this.
+ /// </summary>
+ public long CredentialAmount { get; }
+
+ public int OutputVsize => Script.EstimateOutputVsize();
+
+ public Money CalculateOutputAmount(FeeRate feeRate)
+ => CredentialAmount - feeRate.GetFee(OutputVsize);
+
+ public long CalculateWeight() => OutputVsize * 4;
+ }
+}

MODIFY
WalletWasabi/WabiSabi/Backend/Models/WabiSabiProtocolErrorCode.cs
WalletWasabi/WabiSabi/Backend/Models/WabiSabiProtocolErrorCode.cs
@@ -13,7 +13,6 @@ namespace WalletWasabi.WabiSabi.Backend.Models
InputSpent,
InputUnconfirmed,
InputImmature,
- ScriptNotAllowed,
WrongRoundSignature,
TooManyInputs,
NotEnoughFunds,
@@ -24,6 +23,7 @@ namespace WalletWasabi.WabiSabi.Backend.Models
AliceNotFound,
IncorrectRequestedWeightCredentials,
TooMuchWeight,
+ ScriptNotAllowed,
IncorrectRequestedAmountCredentials
}
}

MODIFY
WalletWasabi/WabiSabi/Backend/PostRequests/PostRequestHandler.cs
WalletWasabi/WabiSabi/Backend/PostRequests/PostRequestHandler.cs
@@ -129,16 +129,18 @@ namespace WalletWasabi.WabiSabi.Backend.PostRequests
{
throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.RoundNotFound);
}
- if (round.Phase != Phase.OutputRegistration)
- {
- throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.WrongPhase);
- }
- if (!request.Output.ScriptPubKey.IsScriptType(ScriptType.P2WPKH))
+ if (!request.Script.IsScriptType(ScriptType.P2WPKH))
{
throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.ScriptNotAllowed);
}
- throw new NotImplementedException();
+ var credentialAmount = -request.AmountCredentialRequests.Delta;
+ return round.RegisterBob(
+ new Bob(
+ request.Script,
+ credentialAmount),
+ request.AmountCredentialRequests,
+ request.WeightCredentialRequests);
}
}

MODIFY
WalletWasabi/WabiSabi/Backend/Rounds/Round.cs
WalletWasabi/WabiSabi/Backend/Rounds/Round.cs
@@ -20,8 +20,8 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
public Round(
Network network,
uint maxInputCountByAlice,
- Money minRegistrableAmountByAlice,
- Money maxRegistrableAmountByAlice,
+ Money minRegistrableAmount,
+ Money maxRegistrableAmount,
uint registrableWeightCredentials,
TimeSpan connectionConfirmationTimeout,
TimeSpan outputRegistrationTimeout,
@@ -31,29 +31,33 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
{
Network = network;
MaxInputCountByAlice = maxInputCountByAlice;
- MinRegistrableAmountByAlice = minRegistrableAmountByAlice;
- MaxRegistrableAmountByAlice = maxRegistrableAmountByAlice;
+ MinRegistrableAmount = minRegistrableAmount;
+ MaxRegistrableAmount = maxRegistrableAmount;
RegistrableWeightCredentials = registrableWeightCredentials;
ConnectionConfirmationTimeout = connectionConfirmationTimeout;
OutputRegistrationTimeout = outputRegistrationTimeout;
TransactionSigningTimeout = transactionSigningTimeout;
+
FeeRate = feeRate;
+
Random = random;
- AmountCredentialIssuer = new(new(Random), 2, random, MaxRegistrableAmountByAlice);
+ UnsignedTxSecret = Random.GetBytes(64);
+
+ AmountCredentialIssuer = new(new(Random), 2, random, MaxRegistrableAmount);
WeightCredentialIssuer = new(new(Random), 2, random, RegistrableWeightCredentials);
AmountCredentialIssuerParameters = AmountCredentialIssuer.CredentialIssuerSecretKey.ComputeCredentialIssuerParameters();
WeightCredentialIssuerParameters = WeightCredentialIssuer.CredentialIssuerSecretKey.ComputeCredentialIssuerParameters();
- Hash = new(HashHelpers.GenerateSha256Hash($"{Id}{MaxInputCountByAlice}{MinRegistrableAmountByAlice}{MaxRegistrableAmountByAlice}{RegistrableWeightCredentials}{AmountCredentialIssuerParameters}{WeightCredentialIssuerParameters}"));
+ Hash = new(HashHelpers.GenerateSha256Hash($"{Id}{MaxInputCountByAlice}{MinRegistrableAmount}{MaxRegistrableAmount}{RegistrableWeightCredentials}{AmountCredentialIssuerParameters}{WeightCredentialIssuerParameters}{FeeRate.SatoshiPerByte}"));
}
public Round(Round blameOf)
: this(
blameOf.Network,
blameOf.MaxInputCountByAlice,
- blameOf.MinRegistrableAmountByAlice,
- blameOf.MaxRegistrableAmountByAlice,
+ blameOf.MinRegistrableAmount,
+ blameOf.MaxRegistrableAmount,
blameOf.RegistrableWeightCredentials,
blameOf.ConnectionConfirmationTimeout,
blameOf.OutputRegistrationTimeout,
@@ -72,8 +76,8 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
public uint256 Hash { get; }
public Network Network { get; }
public uint MaxInputCountByAlice { get; }
- public Money MinRegistrableAmountByAlice { get; }
- public Money MaxRegistrableAmountByAlice { get; }
+ public Money MinRegistrableAmount { get; }
+ public Money MaxRegistrableAmount { get; }
public uint RegistrableWeightCredentials { get; }
public TimeSpan ConnectionConfirmationTimeout { get; }
public TimeSpan OutputRegistrationTimeout { get; }
@@ -91,6 +95,7 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
public bool IsBlameRound => BlameOf is not null;
public ISet<OutPoint> BlameWhitelist { get; } = new HashSet<OutPoint>();
private object Lock { get; } = new();
+ public byte[] UnsignedTxSecret { get; }
public bool TryGetAlice(Guid aliceId, [NotNullWhen(true)] out Alice? alice)
{
@@ -127,14 +132,14 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
inputValueSum += coin.TxOut.Value;
// Convert conservative P2WPKH size in virtual bytes to weight units.
- inputWeightSum += coin.TxOut.ScriptPubKey.EstimateSpendVsize() * 4;
+ inputWeightSum += coin.TxOut.ScriptPubKey.EstimateInputVsize() * 4;
}
- if (inputValueSum < MinRegistrableAmountByAlice)
+ if (inputValueSum < MinRegistrableAmount)
{
throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.NotEnoughFunds);
}
- if (inputValueSum > MaxRegistrableAmountByAlice)
+ if (inputValueSum > MaxRegistrableAmount)
{
throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.TooMuchFunds);
}
@@ -207,7 +212,7 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
}
}
- internal void RemoveAlice(Guid aliceId)
+ public void RemoveAlice(Guid aliceId)
{
lock (Lock)
{
@@ -218,5 +223,39 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
Alices.RemoveAll(x => x.Id == aliceId);
}
}
+
+ public OutputRegistrationResponse RegisterBob(Bob bob, RealCredentialsRequest amountCredentialRequests, RealCredentialsRequest weightCredentialRequests)
+ {
+ var outputValue = bob.CalculateOutputAmount(FeeRate);
+ if (outputValue < MinRegistrableAmount)
+ {
+ throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.NotEnoughFunds);
+ }
+ if (outputValue > MaxRegistrableAmount)
+ {
+ throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.TooMuchFunds);
+ }
+
+ var amountCredentialResponse = AmountCredentialIssuer.HandleRequest(amountCredentialRequests);
+ var weightCredentialResponse = WeightCredentialIssuer.HandleRequest(weightCredentialRequests);
+
+ if (-weightCredentialRequests.Delta != bob.CalculateWeight())
+ {
+ throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.IncorrectRequestedWeightCredentials);
+ }
+
+ lock (Lock)
+ {
+ if (Phase != Phase.OutputRegistration)
+ {
+ throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.WrongPhase);
+ }
+
+ return new(
+ UnsignedTxSecret,
+ amountCredentialResponse,
+ weightCredentialResponse);
+ }
+ }
}
}

MODIFY
WalletWasabi/WabiSabi/Crypto/WabiSabiClient.cs
WalletWasabi/WabiSabi/Crypto/WabiSabiClient.cs
@@ -153,7 +153,7 @@ namespace WalletWasabi.WabiSabi.Crypto
var randomness = RandomNumberGenerator.GetScalar(allowZero: false);
var ma = ProofSystem.PedersenCommitment(scalarAmount, randomness);
- var (rangeKnowledge, bitCommitments) = ProofSystem.RangeProofKnowledge(scalarAmount, randomness, RangeProofWidth, RandomNumberGenerator, RangeProofWidth);
+ var (rangeKnowledge, bitCommitments) = ProofSystem.RangeProofKnowledge(scalarAmount, randomness, RangeProofWidth, RandomNumberGenerator);
knowledgeToProve.Add(rangeKnowledge);
var credentialRequest = new IssuanceRequest(ma, bitCommitments);

MODIFY
WalletWasabi/WabiSabi/Models/OutputRegistrationRequest.cs
WalletWasabi/WabiSabi/Models/OutputRegistrationRequest.cs
@@ -11,17 +11,17 @@ namespace WalletWasabi.WabiSabi.Models
{
public class OutputRegistrationRequest
{
- public OutputRegistrationRequest(Guid roundId, TxOut output, RealCredentialsRequest amountCredentialRequests, RealCredentialsRequest weighCredentialRequests)
+ public OutputRegistrationRequest(Guid roundId, Script script, RealCredentialsRequest amountCredentialRequests, RealCredentialsRequest weightCredentialRequests)
{
RoundId = roundId;
- Output = output;
+ Script = script;
AmountCredentialRequests = amountCredentialRequests;
- WeighCredentialRequests = weighCredentialRequests;
+ WeightCredentialRequests = weightCredentialRequests;
}
public Guid RoundId { get; }
- public TxOut Output { get; }
+ public Script Script { get; }
public RealCredentialsRequest AmountCredentialRequests { get; }
- public RealCredentialsRequest WeighCredentialRequests { get; }
+ public RealCredentialsRequest WeightCredentialRequests { get; }
}
}

Implement output registration checks