Progress input tests
MODIFY
WalletWasabi.Backend/Controllers/ChaumianCoinJoinController.cs
WalletWasabi.Backend/Controllers/ChaumianCoinJoinController.cs
@@ -78,15 +78,13 @@ namespace WalletWasabi.Backend.Controllers
// Validate request.
if (!ModelState.IsValid
|| request == null
- || request.BlindedOutput == null
- || request.BlindedOutput.Length == 0
+ || string.IsNullOrWhiteSpace(request.BlindedOutput)
|| string.IsNullOrWhiteSpace(request.ChangeOutputScript)
|| request.Inputs == null
|| request.Inputs.Count() == 0
|| request.Inputs.Any(x=> x.Input == null
|| x.Input.Hash == null
- || x.Proof == null
- || x.Proof.Length == 0))
+ || string.IsNullOrWhiteSpace(x.Proof)))
{
return BadRequest("Invalid request.");
}
@@ -157,10 +155,10 @@ namespace WalletWasabi.Backend.Controllers
var address = (BitcoinWitPubKeyAddress)txout.ScriptPubKey.GetDestinationAddress(Network);
// Check if proofs are valid.
- bool validProof = address.VerifyMessage(ByteHelpers.ToHex(request.BlindedOutput), ByteHelpers.ToHex(inputProof.Proof));
+ bool validProof = address.VerifyMessage(request.BlindedOutput, inputProof.Proof);
if (!validProof)
{
- return BadRequest("Provided proof is invalid");
+ return BadRequest("Provided proof is invalid.");
}
inputs.Add((inputProof.Input, txout));
@@ -185,7 +183,7 @@ namespace WalletWasabi.Backend.Controllers
round.AddAlice(alice);
// All checks are good. Sign.
- byte[] signature = RsaKey.SignBlindedData(request.BlindedOutput);
+ byte[] signature = RsaKey.SignBlindedData(ByteHelpers.FromHex(request.BlindedOutput));
// Check if phase changed since.
if (round.Status != CcjRoundStatus.Running || round.Phase != CcjRoundPhase.InputRegistration)

MODIFY
WalletWasabi.Tests/RegTests.cs
WalletWasabi.Tests/RegTests.cs
@@ -1479,9 +1479,9 @@ namespace WalletWasabi.Tests
Assert.Equal("\"Invalid request.\"", message);
}
- request.BlindedOutput = new byte[] { };
+ request.BlindedOutput = "";
request.ChangeOutputScript = "";
- request.Inputs = new List<InputProofModel> { new InputProofModel { Input = new OutPoint(), Proof = new byte[] { } } };
+ request.Inputs = new List<InputProofModel> { new InputProofModel { Input = new OutPoint(), Proof = "" } };
using (var response = await client.SendAsync(HttpMethod.Post, "/api/v1/btc/chaumiancoinjoin/inputs/", request.ToHttpStringContent()))
{
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
@@ -1489,9 +1489,9 @@ namespace WalletWasabi.Tests
Assert.Equal("\"Invalid request.\"", message);
}
- request.BlindedOutput = new byte[] { 1 };
+ request.BlindedOutput = "c";
request.ChangeOutputScript = "a";
- request.Inputs = new List<InputProofModel> { new InputProofModel { Input = new OutPoint(uint256.One, 0), Proof = new byte[] { 1 } } };
+ request.Inputs = new List<InputProofModel> { new InputProofModel { Input = new OutPoint(uint256.One, 0), Proof = "b" } };
using (var response = await client.SendAsync(HttpMethod.Post, "/api/v1/btc/chaumiancoinjoin/inputs/", request.ToHttpStringContent()))
{
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
@@ -1512,7 +1512,7 @@ namespace WalletWasabi.Tests
}
}
- request.Inputs = new List<InputProofModel> { new InputProofModel { Input = new OutPoint(hash, index), Proof = new byte[] { 1 } } };
+ request.Inputs = new List<InputProofModel> { new InputProofModel { Input = new OutPoint(hash, index), Proof = "b" } };
using (var response = await client.SendAsync(HttpMethod.Post, "/api/v1/btc/chaumiancoinjoin/inputs/", request.ToHttpStringContent()))
{
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
@@ -1520,13 +1520,62 @@ namespace WalletWasabi.Tests
Assert.Equal("\"Provided input is unconfirmed.\"", message);
}
- await rpc.GenerateAsync(1);
+ var blocks = await rpc.GenerateAsync(1);
using (var response = await client.SendAsync(HttpMethod.Post, "/api/v1/btc/chaumiancoinjoin/inputs/", request.ToHttpStringContent()))
{
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
string message = await response.Content.ReadAsStringAsync();
Assert.Equal("\"Provided input must be witness_v0_keyhash.\"", message);
}
+
+ var blockHash = blocks.Single();
+ var block = await rpc.GetBlockAsync(blockHash);
+ var coinbase = block.Transactions.First();
+ request.Inputs = new List<InputProofModel> { new InputProofModel { Input = new OutPoint(coinbase.GetHash(), 0), Proof = "b" } };
+ using (var response = await client.SendAsync(HttpMethod.Post, "/api/v1/btc/chaumiancoinjoin/inputs/", request.ToHttpStringContent()))
+ {
+ Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
+ string message = await response.Content.ReadAsStringAsync();
+ Assert.Equal("\"Provided input is immature.\"", message);
+ }
+
+ var key = new Key();
+ var witnessAddress = key.PubKey.GetSegwitAddress(network);
+ hash = await rpc.SendToAddressAsync(witnessAddress, new Money(0.01m, MoneyUnit.BTC));
+ await rpc.GenerateAsync(1);
+ tx = await rpc.GetRawTransactionAsync(hash);
+ index = 0;
+ for (int i = 0; i < tx.Outputs.Count; i++)
+ {
+ var output = tx.Outputs[i];
+ if (output.ScriptPubKey == witnessAddress.ScriptPubKey)
+ {
+ index = i;
+ }
+ }
+ request.Inputs = new List<InputProofModel> { new InputProofModel { Input = new OutPoint(hash, index), Proof = "b" } };
+ using (var response = await client.SendAsync(HttpMethod.Post, "/api/v1/btc/chaumiancoinjoin/inputs/", request.ToHttpStringContent()))
+ {
+ Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
+ }
+ var proof = key.SignMessage("foo");
+ request.Inputs.First().Proof = proof;
+ using (var response = await client.SendAsync(HttpMethod.Post, "/api/v1/btc/chaumiancoinjoin/inputs/", request.ToHttpStringContent()))
+ {
+ Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
+ string message = await response.Content.ReadAsStringAsync();
+ Assert.Equal("\"Provided proof is invalid.\"", message);
+ }
+
+ request.BlindedOutput = new Transaction().ToHex();
+ proof = key.SignMessage(request.BlindedOutput);
+ request.Inputs.First().Proof = proof;
+ using (var response = await client.SendAsync(HttpMethod.Post, "/api/v1/btc/chaumiancoinjoin/inputs/", request.ToHttpStringContent()))
+ {
+ Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
+ string message = await response.Content.ReadAsStringAsync();
+ Assert.StartsWith("\"Not enough inputs are provided. Fee to pay:", message);
+ }
}
}

MODIFY
WalletWasabi/Backend/Models/InputProofModel.cs
WalletWasabi/Backend/Models/InputProofModel.cs
@@ -15,7 +15,6 @@ namespace WalletWasabi.Backend.Models
public OutPoint Input { get; set; }
[Required]
- [JsonConverter(typeof(ByteArrayJsonConverter))]
- public byte[] Proof { get; set; }
+ public string Proof { get; set; }
}
}

MODIFY
WalletWasabi/Backend/Models/Requests/InputsRequest.cs
WalletWasabi/Backend/Models/Requests/InputsRequest.cs
@@ -15,8 +15,7 @@ namespace WalletWasabi.Backend.Models.Requests
public IEnumerable<InputProofModel> Inputs { get; set; }
[Required]
- [JsonConverter(typeof(ByteArrayJsonConverter))]
- public byte[] BlindedOutput { get; set; }
+ public string BlindedOutput { get; set; }
[Required]
public string ChangeOutputScript { get; set; }

MODIFY
WalletWasabi/ChaumianCoinJoin/Alice.cs
WalletWasabi/ChaumianCoinJoin/Alice.cs
@@ -23,17 +23,17 @@ namespace WalletWasabi.ChaumianCoinJoin
public Script ChangeOutputScript { get; }
- public byte[] BlindedOutput { get; }
+ public string BlindedOutput { get; }
public Money GetChangeAmount(Money denomination, Money coordinatorFee) => OutputSumWithoutCoordinatorFeeAndDenomination - denomination - coordinatorFee;
public AliceState State { get; set; }
- public Alice(IEnumerable<(OutPoint OutPoint, TxOut Output)> inputs, Money networkFeeToPay, Script changeOutputScript, byte[] blindedOutput)
+ public Alice(IEnumerable<(OutPoint OutPoint, TxOut Output)> inputs, Money networkFeeToPay, Script changeOutputScript, string blindedOutput)
{
Inputs = Guard.NotNullOrEmpty(nameof(inputs), inputs);
NetworkFeeToPay = Guard.NotNull(nameof(networkFeeToPay), networkFeeToPay);
- BlindedOutput = Guard.NotNullOrEmpty(nameof(blindedOutput), blindedOutput);
+ BlindedOutput = Guard.NotNullOrEmptyOrWhitespace(nameof(blindedOutput), blindedOutput);
Guard.NotNull(nameof(changeOutputScript), changeOutputScript);
// 33 bytes maximum: https://bitcoin.stackexchange.com/a/46379/26859

MODIFY
WalletWasabi/ChaumianCoinJoin/CcjRound.cs
WalletWasabi/ChaumianCoinJoin/CcjRound.cs
@@ -395,7 +395,7 @@ namespace WalletWasabi.ChaumianCoinJoin
}
}
- public bool ContainsBlindedOutput(byte[] blindedOutput, out List<Alice> alices)
+ public bool ContainsBlindedOutput(string blindedOutput, out List<Alice> alices)
{
alices = new List<Alice>();
@@ -403,7 +403,7 @@ namespace WalletWasabi.ChaumianCoinJoin
{
foreach (Alice alice in Alices)
{
- if (ByteHelpers.CompareFastUnsafe(alice.BlindedOutput, blindedOutput))
+ if (alice.BlindedOutput == blindedOutput)
{
alices.Add(alice);
}

Progress input tests