Fix rpc response handling
MODIFY
HiddenWallet.ChaumianTumbler/Configuration/Config.cs
HiddenWallet.ChaumianTumbler/Configuration/Config.cs
@@ -71,7 +71,7 @@ namespace HiddenWallet.ChaumianTumbler.Configuration
public async Task ToFileAsync(string path, CancellationToken cancel)
{
- if (path == null) throw new ArgumentNullException(nameof(path));
+ if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException(nameof(path));
string jsonString = JsonConvert.SerializeObject(this, Formatting.Indented);
await File.WriteAllTextAsync(path,
@@ -82,7 +82,7 @@ namespace HiddenWallet.ChaumianTumbler.Configuration
public async Task LoadOrCreateDefaultFileAsync(string path, CancellationToken cancel)
{
- if (path == null) throw new ArgumentNullException(nameof(path));
+ if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException(nameof(path));
Network = Network.Main;
BitcoinRpcUser = "user";

MODIFY
HiddenWallet.ChaumianTumbler/Controllers/TumblerController.cs
HiddenWallet.ChaumianTumbler/Controllers/TumblerController.cs
@@ -64,8 +64,8 @@ namespace HiddenWallet.ChaumianTumbler.Controllers
}
// Check not nulls
- if (request.BlindedOutput == null) return new BadRequestResult();
- if (request.ChangeOutput == null) return new BadRequestResult();
+ if (string.IsNullOrWhiteSpace(request.BlindedOutput)) return new BadRequestResult();
+ if (string.IsNullOrWhiteSpace(request.ChangeOutput)) return new BadRequestResult();
if (request.Inputs == null || request.Inputs.Count() == 0) return new BadRequestResult();
// Check format (parse everyting))
@@ -100,7 +100,7 @@ namespace HiddenWallet.ChaumianTumbler.Controllers
var txOutResponse = await Global.RpcClient.SendCommandAsync(RPCOperations.gettxout, op.Hash.ToString(), op.N, true);
// Check if inputs are unspent
- if (txOutResponse.Result == null)
+ if (string.IsNullOrWhiteSpace(txOutResponse?.ResultString))
{
throw new ArgumentException("Provided input is not unspent");
}
@@ -220,7 +220,7 @@ namespace HiddenWallet.ChaumianTumbler.Controllers
return new ObjectResult(new FailureResponse { Message = "Wrong phase", Details = "" });
}
- if (request.UniqueId == null) return new BadRequestResult();
+ if (string.IsNullOrWhiteSpace(request.UniqueId)) return new BadRequestResult();
Alice alice = Global.StateMachine.FindAlice(request.UniqueId, throwException: true);
if (alice.State == AliceState.ConnectionConfirmed)
@@ -259,8 +259,8 @@ namespace HiddenWallet.ChaumianTumbler.Controllers
return new ObjectResult(new FailureResponse { Message = "Wrong phase", Details = "" });
}
- if (request.Output == null) return new BadRequestResult();
- if (request.Signature == null) return new BadRequestResult();
+ if (string.IsNullOrWhiteSpace(request.Output)) return new BadRequestResult();
+ if (string.IsNullOrWhiteSpace(request.Signature)) return new BadRequestResult();
var output = new BitcoinWitPubKeyAddress(request.Output, expectedNetwork: Global.Config.Network);
@@ -301,7 +301,7 @@ namespace HiddenWallet.ChaumianTumbler.Controllers
return new ObjectResult(new FailureResponse { Message = "Wrong phase", Details = "" });
}
- if (request.UniqueId == null) return new BadRequestResult();
+ if (string.IsNullOrWhiteSpace(request.UniqueId)) return new BadRequestResult();
Alice alice = Global.StateMachine.FindAlice(request.UniqueId, throwException: true);
if (alice.State == AliceState.AskedForCoinJoin)
@@ -334,7 +334,7 @@ namespace HiddenWallet.ChaumianTumbler.Controllers
return new ObjectResult(new FailureResponse { Message = "Wrong phase", Details = "" });
}
- if (request.UniqueId == null) return new BadRequestResult();
+ if (string.IsNullOrWhiteSpace(request.UniqueId)) return new BadRequestResult();
if (request.Signatures == null) return new BadRequestResult();
if (request.Signatures.Count() == 0) return new BadRequestResult();
Alice alice = Global.StateMachine.FindAlice(request.UniqueId, throwException: true);
@@ -377,7 +377,7 @@ namespace HiddenWallet.ChaumianTumbler.Controllers
{
var res = await Global.RpcClient.SendCommandAsync(RPCOperations.sendrawtransaction, Global.StateMachine.CoinJoin.ToHex(), true);
var state = CoinJoinTransactionState.Failed;
- if (Global.StateMachine.CoinJoin.GetHash().ToString() == res.ToString())
+ if (Global.StateMachine.CoinJoin.GetHash().ToString() == res.ResultString)
{
failed = false;
state = CoinJoinTransactionState.Succeeded;

MODIFY
HiddenWallet.ChaumianTumbler/Global.cs
HiddenWallet.ChaumianTumbler/Global.cs
@@ -82,9 +82,9 @@ namespace HiddenWallet.ChaumianTumbler
{
throw new NotSupportedException("blockchainInfo.Error != null");
}
- if (blockchainInfo.Result == null)
+ if (string.IsNullOrWhiteSpace(blockchainInfo?.ResultString))
{
- throw new NotSupportedException("blockchainInfo.Result == null");
+ throw new NotSupportedException("string.IsNullOrWhiteSpace(blockchainInfo?.ResultString) == true");
}
int blocks = blockchainInfo.Result.Value<int>("blocks");
if (blocks == 0)
@@ -101,7 +101,7 @@ namespace HiddenWallet.ChaumianTumbler
throw new NotSupportedException("blocks != headers");
}
- if (await RpcClient.SendCommandAsync("estimatesmartfee", 1, "ECONOMICAL") == null)
+ if (string.IsNullOrWhiteSpace((await RpcClient.SendCommandAsync("estimatesmartfee", 1, "ECONOMICAL"))?.ResultString))
{
throw new NotSupportedException("estimatesmartfee 1 ECONOMICAL == null");
}

MODIFY
HiddenWallet.ChaumianTumbler/Referee/UtxoReferee.cs
HiddenWallet.ChaumianTumbler/Referee/UtxoReferee.cs
@@ -97,7 +97,7 @@ namespace HiddenWallet.ChaumianTumbler.Referee
public async Task ToFileAsync(string filePath)
{
- if (filePath == null) throw new ArgumentNullException(nameof(filePath));
+ if (string.IsNullOrWhiteSpace(filePath)) throw new ArgumentException(nameof(filePath));
string jsonString = JsonConvert.SerializeObject(this, Formatting.Indented);
await File.WriteAllTextAsync(
@@ -108,7 +108,7 @@ namespace HiddenWallet.ChaumianTumbler.Referee
public async static Task<UtxoReferee> CreateFromFileAsync(string filePath)
{
- if (filePath == null) throw new ArgumentNullException(nameof(filePath));
+ if (string.IsNullOrWhiteSpace(filePath)) throw new ArgumentException(nameof(filePath));
string jsonString = await File.ReadAllTextAsync(filePath, Encoding.UTF8);
return JsonConvert.DeserializeObject<UtxoReferee>(jsonString);

MODIFY
HiddenWallet.ChaumianTumbler/Store/CoinJoinStore.cs
HiddenWallet.ChaumianTumbler/Store/CoinJoinStore.cs
@@ -22,7 +22,7 @@ namespace HiddenWallet.ChaumianTumbler.Store
public async Task ToFileAsync(string filePath)
{
- if (filePath == null) throw new ArgumentNullException(nameof(filePath));
+ if (string.IsNullOrWhiteSpace(filePath)) throw new ArgumentException(nameof(filePath));
string jsonString = JsonConvert.SerializeObject(this, Formatting.Indented);
await File.WriteAllTextAsync(
@@ -33,7 +33,7 @@ namespace HiddenWallet.ChaumianTumbler.Store
public async static Task<CoinJoinStore> CreateFromFileAsync(string filePath)
{
- if (filePath == null) throw new ArgumentNullException(nameof(filePath));
+ if (string.IsNullOrWhiteSpace(filePath)) throw new ArgumentException(nameof(filePath));
string jsonString = await File.ReadAllTextAsync(filePath, Encoding.UTF8);
return JsonConvert.DeserializeObject<CoinJoinStore>(jsonString);

MODIFY
HiddenWallet.ChaumianTumbler/TumblerPhaseBroadcaster.cs
HiddenWallet.ChaumianTumbler/TumblerPhaseBroadcaster.cs
@@ -36,11 +36,15 @@ namespace HiddenWallet.ChaumianTumbler
public int ConnectedClientCount() => _connectedClients.Count();
- public void Broadcast(PhaseChangeBroadcast broadcast)
+ public async Task BroadcastAsync(PhaseChangeBroadcast broadcast)
{
+ while (_context == null)
+ {
+ await Task.Delay(100);
+ }
IClientProxy proxy = _context.Clients.All;
string json = JsonConvert.SerializeObject(broadcast);
- proxy.InvokeAsync("PhaseChange", json);
+ await proxy.InvokeAsync("PhaseChange", json);
}
}
}

MODIFY
HiddenWallet.ChaumianTumbler/TumblerStateMachine.cs
HiddenWallet.ChaumianTumbler/TumblerStateMachine.cs
@@ -44,11 +44,11 @@ namespace HiddenWallet.ChaumianTumbler
}
- public void BroadcastPhaseChange()
+ public async Task BroadcastPhaseChangeAsync()
{
AcceptRequest = true;
var broadcast = new PhaseChangeBroadcast { NewPhase = Phase.ToString(), Message = "" };
- _broadcaster.Broadcast(broadcast);
+ await _broadcaster.BroadcastAsync(broadcast);
Console.WriteLine($"NEW PHASE: {Phase}");
}
@@ -76,12 +76,11 @@ namespace HiddenWallet.ChaumianTumbler
{
Alices = new ConcurrentHashSet<Alice>();
Bobs = new ConcurrentHashSet<Bob>();
- var denominationTask = SetDenominationAsync(cancel);
- var feeTask = SetFeesAsync(cancel);
+ await SetDenominationAsync(cancel);
+ await SetFeesAsync(cancel);
CalculateAnonymitySet();
- await Task.WhenAll(denominationTask, feeTask);
- BroadcastPhaseChange();
+ await BroadcastPhaseChangeAsync();
InputRegistrationStopwatch = new Stopwatch();
InputRegistrationStopwatch.Start();
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancel, _ctsPhaseCancel.Token))
@@ -99,7 +98,7 @@ namespace HiddenWallet.ChaumianTumbler
}
case TumblerPhase.ConnectionConfirmation:
{
- BroadcastPhaseChange();
+ await BroadcastPhaseChangeAsync();
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancel, _ctsPhaseCancel.Token))
{
await Task.Delay(TimeSpan.FromSeconds((int)Global.Config.ConnectionConfirmationPhaseTimeoutInSeconds), cts.Token).ContinueWith(t => { });
@@ -117,7 +116,7 @@ namespace HiddenWallet.ChaumianTumbler
}
case TumblerPhase.OutputRegistration:
{
- BroadcastPhaseChange();
+ await BroadcastPhaseChangeAsync();
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancel, _ctsPhaseCancel.Token))
{
await Task.Delay(TimeSpan.FromSeconds((int)Global.Config.OutputRegistrationPhaseTimeoutInSeconds), cts.Token).ContinueWith(t => { });
@@ -132,7 +131,7 @@ namespace HiddenWallet.ChaumianTumbler
{
BuildCoinJoin();
- BroadcastPhaseChange();
+ await BroadcastPhaseChangeAsync();
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancel, _ctsPhaseCancel.Token))
{
await Task.Delay(TimeSpan.FromSeconds((int)Global.Config.SigningPhaseTimeoutInSeconds), cts.Token).ContinueWith(t => { });
@@ -153,7 +152,9 @@ namespace HiddenWallet.ChaumianTumbler
}
catch (Exception ex)
{
- Console.WriteLine($"Ignoring {nameof(TumblerStateMachine)} exception: {ex}");
+ FallBackRound = true;
+ UpdatePhase(TumblerPhase.InputRegistration);
+ Console.WriteLine($"Fallback to InputRegistration, Reason: {nameof(TumblerStateMachine)} exception: {ex}");
}
}
}
@@ -242,9 +243,10 @@ namespace HiddenWallet.ChaumianTumbler
int outputSizeInBytes = Constants.OutputSizeInBytes;
try
{
- var result = (await Global.RpcClient.SendCommandAsync("estimatesmartfee", 1, "ECONOMICAL")).Result;
- if (result == null) throw new ArgumentNullException();
- var feeRateDecimal = result.Value<decimal>("feerate");
+ RPCResponse result = (await Global.RpcClient.SendCommandAsync("estimatesmartfee", 1, "ECONOMICAL"));
+ if (string.IsNullOrWhiteSpace(result?.ResultString)) throw new ArgumentException(nameof(result));
+ var resultJToken = result.Result;
+ var feeRateDecimal = resultJToken.Value<decimal>("feerate");
var feeRate = new FeeRate(new Money(feeRateDecimal, MoneyUnit.BTC));
Money feePerBytes = (feeRate.FeePerK / 1000);

MODIFY
HiddenWallet.Daemon/Wrappers/WalletWrapper.cs
HiddenWallet.Daemon/Wrappers/WalletWrapper.cs
@@ -277,7 +277,7 @@ namespace HiddenWallet.Daemon.Wrappers
public async Task<BaseResponse> BuildTransactionAsync(string password, SafeAccount safeAccount, BitcoinAddress address, Money amount, FeeType feeType)
{
// todo: delete this code comment, it was just a test, if you see it, I forgot to delete it
- // await _walletJob.CoinJoinService.TumbleAsync(AliceAccount, BobAccount, CancellationToken.None);
+ await _walletJob.CoinJoinService.TumbleAsync(AliceAccount, BobAccount, CancellationToken.None);
if (password != _password) throw new InvalidOperationException("Wrong password");
var result = await _walletJob.BuildTransactionAsync(address.ScriptPubKey, amount, feeType, safeAccount, (bool)Global.Config.CanSpendUnconfirmed);

Fix rpc response handling