public static bool CheckExpectedHash(string filePath, string sourceFolderPath) { var fileHash = GetHashFile(filePath); try { var digests = File.ReadAllLines(Path.Combine(sourceFolderPath, "digests.txt")); foreach (var digest in digests) { var expectedHash = ByteHelpers.FromHex(digest); if (ByteHelpers.CompareFastUnsafe(fileHash, expectedHash)) { return(true); } } return(false); } catch (Exception) { return(false); } }
public static bool TryGetMasterFingerprintFromFile(string filePath, out HDFingerprint masterFingerprint) { masterFingerprint = default; filePath = Guard.NotNullOrEmptyOrWhitespace(nameof(filePath), filePath); if (!File.Exists(filePath)) { throw new FileNotFoundException($"Wallet file not found at: `{filePath}`."); } // Example text to handle: "ExtPubKey": "03BF8271268000000013B9013C881FE456DDF524764F6322F611B03CF6". var masterFpLine = File.ReadLines(filePath) // Enumerated read. .Take(21) // Limit reads to x lines. .FirstOrDefault(line => line.Contains("\"MasterFingerprint\": \"", StringComparison.OrdinalIgnoreCase)); if (string.IsNullOrEmpty(masterFpLine)) { return(false); } var parts = masterFpLine.Split("\"MasterFingerprint\": \""); if (parts.Length != 2) { throw new FormatException($"Could not split line: {masterFpLine}"); } var hex = parts[1].TrimEnd(',', '"'); if (string.IsNullOrEmpty(hex) || hex.Equals("null", StringComparison.OrdinalIgnoreCase)) { return(false); } var mfp = new HDFingerprint(ByteHelpers.FromHex(hex)); masterFingerprint = mfp; return(true); }
/// <summary> /// Transmits one tpdu and sends the NAK or ACK /// </summary> /// <param name="tpduData"></param> /// <returns></returns> private void SafeTransmit(byte[] tpduData) { int transmitCounter = 0; bool transmitSucceded = false; while (transmitCounter <= MAX_BLOCKREPEATS && !transmitSucceded) { _log.Debug("Sending transmitCounter={1}: {0}", ByteHelpers.ByteToString(tpduData), transmitCounter); //Write Data to the serial port _serialPort.SendData(tpduData, 0, tpduData.Length); //Read NAK or ACK or something else ;) var statusByte = _buffer.WaitForByte(RECEIVE_RESPONSE_TIMEOUT, true); if (statusByte != null && statusByte == ACK) { transmitSucceded = true; return; } else { _log.Debug("TPDU transmission failed, increasing transmitCounter to '{0}'", transmitCounter + 1); transmitCounter++; } } if (transmitSucceded == false) { _log.Debug("TPDU transmission failed {0}-times, reporting error to application layer", transmitCounter); throw new RS232TransportException($"TPDU transmission failed {transmitCounter}-times, reporting error to application layer"); } else { //Should never get here Debug.Assert(false); throw new InvalidOperationException("Transmission succeeded but no data"); } }
private static KeyManager GetKeyManagerByColdcardJson(WalletManager manager, JObject jsonWallet, string walletFullPath) { var xpubString = jsonWallet["ExtPubKey"].ToString(); var mfpString = jsonWallet["MasterFingerprint"].ToString(); // https://github.com/zkSNACKs/WalletWasabi/pull/1663#issuecomment-508073066 // Coldcard 2.1.0 improperly implemented Wasabi skeleton fingerprint at first, so we must reverse byte order. // The solution was to add a ColdCardFirmwareVersion json field from 2.1.1 and correct the one generated by 2.1.0. var coldCardVersionString = jsonWallet["ColdCardFirmwareVersion"]?.ToString(); var reverseByteOrder = false; if (coldCardVersionString is null) { reverseByteOrder = true; } else { Version coldCardVersion = new(coldCardVersionString); if (coldCardVersion == new Version("2.1.0")) // Should never happen though. { reverseByteOrder = true; } } var bytes = ByteHelpers.FromHex(Guard.NotNullOrEmptyOrWhitespace(nameof(mfpString), mfpString, trim: true)); HDFingerprint mfp = reverseByteOrder ? new HDFingerprint(bytes.Reverse().ToArray()) : new HDFingerprint(bytes); if (manager.WalletExists(mfp)) { throw new InvalidOperationException(WalletExistsErrorMessage); } ExtPubKey extPubKey = NBitcoinHelpers.BetterParseExtPubKey(xpubString); return(KeyManager.CreateNewHardwareWalletWatchOnly(mfp, extPubKey, walletFullPath)); }
public static BitcoinExtPubKey BetterParseExtPubKey(string extPubKeyString, Network network, bool ignoreInvalidNetwork) { extPubKeyString = Guard.NotNullOrEmptyOrWhitespace(nameof(extPubKeyString), extPubKeyString, trim: true); ExtPubKey epk; try { epk = ExtPubKey.Parse(extPubKeyString, network); // Starts with "ExtPubKey": "xpub... } catch { try { // Try hex, Old wallet format was like this. epk = new ExtPubKey(ByteHelpers.FromHex(extPubKeyString)); // Starts with "ExtPubKey": "hexbytes... } catch when(ignoreInvalidNetwork) { // Let's replace the version prefix var data = Encoders.Base58Check.DecodeData(extPubKeyString); var versionBytes = network.GetVersionBytes(Base58Type.EXT_PUBLIC_KEY, true); if (versionBytes.Length > data.Length) { throw; } for (int i = 0; i < versionBytes.Length; i++) { data[i] = versionBytes[i]; } extPubKeyString = Encoders.Base58Check.EncodeData(data); epk = ExtPubKey.Parse(extPubKeyString, network); } } return(epk.GetWif(network)); }
private static bool ConfirmationPassword(PasswordEntry passwordEntry) { using (var passwordEntry2 = new PasswordEntry()) { passwordEntry2.LabelText = "Please confirm your session password(s)."; if (passwordEntry2.ShowDialog() != DialogResult.OK) { return(false); } bool password1Match = ByteHelpers.ByteArrayCompare(passwordEntry.Password.Password1, passwordEntry2.Password.Password1); bool password2Match = ByteHelpers.ByteArrayCompare(passwordEntry.Password.Password2, passwordEntry2.Password.Password2); if ((password1Match == false) || (password2Match == false)) { MessageBox.Show(Resources.TextShredderMainForm_ConfirmationPassword_The_passwords_do_not_match_, Resources.TextShredderMainForm_ConfirmationPassword_The_passwords_do_not_match_, MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return(false); } return(true); } }
public async Task PostOutputAsync(string roundHash, Script activeOutput, byte[] unblindedSignature) { var request = new OutputRequest() { OutputScript = activeOutput.ToString(), SignatureHex = ByteHelpers.ToHex(unblindedSignature) }; using (var response = await TorClient.SendAsync(HttpMethod.Post, $"/api/v1/btc/chaumiancoinjoin/output?roundHash={roundHash}", request.ToHttpStringContent())) { if (response.StatusCode != HttpStatusCode.NoContent) { string error = await response.Content.ReadAsJsonAsync <string>(); if (error == null) { throw new HttpRequestException(response.StatusCode.ToReasonString()); } else { throw new HttpRequestException($"{response.StatusCode.ToReasonString()}\n{error}"); } } } }
private async Task ReorgOneAsync() { // 1. Rollback index using (await IndexLock.LockAsync()) { Logger.LogInfo($"REORG invalid block: {Index.Last().BlockHash}"); Index.RemoveLast(); } // 2. Serialize Index. (Remove last line.) var lines = await File.ReadAllLinesAsync(IndexFilePath); await File.WriteAllLinesAsync(IndexFilePath, lines.Take(lines.Length - 1).ToArray()); // 3. Rollback Bech32UtxoSet if (Bech32UtxoSetHistory.Count != 0) { Bech32UtxoSetHistory.Last().Rollback(Bech32UtxoSet); // The Bech32UtxoSet MUST be recovered to its previous state. Bech32UtxoSetHistory.RemoveLast(); // 4. Serialize Bech32UtxoSet. await File.WriteAllLinesAsync(Bech32UtxoSetFilePath, Bech32UtxoSet .Select(entry => entry.Key.Hash + ":" + entry.Key.N + ":" + ByteHelpers.ToHex(entry.Value.ToCompressedBytes()))); } }
private static void CreateDigests() { var tempDir = "DigestTempDir"; IoHelpers.DeleteRecursivelyWithMagicDustAsync(tempDir).GetAwaiter().GetResult(); Directory.CreateDirectory(tempDir); var torDaemonsDir = Path.Combine(LibraryProjectDirectory, "TorDaemons"); string torWinZip = Path.Combine(torDaemonsDir, "tor-win32.zip"); IoHelpers.BetterExtractZipToDirectoryAsync(torWinZip, tempDir).GetAwaiter().GetResult(); File.Move(Path.Combine(tempDir, "Tor", "tor.exe"), Path.Combine(tempDir, "TorWin")); string torLinuxZip = Path.Combine(torDaemonsDir, "tor-linux64.zip"); IoHelpers.BetterExtractZipToDirectoryAsync(torLinuxZip, tempDir).GetAwaiter().GetResult(); File.Move(Path.Combine(tempDir, "Tor", "tor"), Path.Combine(tempDir, "TorLin")); string torOsxZip = Path.Combine(torDaemonsDir, "tor-osx64.zip"); IoHelpers.BetterExtractZipToDirectoryAsync(torOsxZip, tempDir).GetAwaiter().GetResult(); File.Move(Path.Combine(tempDir, "Tor", "tor"), Path.Combine(tempDir, "TorOsx")); var tempDirInfo = new DirectoryInfo(tempDir); var binaries = tempDirInfo.GetFiles(); Console.WriteLine("Digests:"); foreach (var file in binaries) { var filePath = file.FullName; var hash = ByteHelpers.ToHex(IoHelpers.GetHashFile(filePath)).ToLowerInvariant(); Console.WriteLine($"{file.Name}: {hash}"); } IoHelpers.DeleteRecursivelyWithMagicDustAsync(tempDir).GetAwaiter().GetResult(); }
public static bool operator ==(ByteArraySerializableBase x, byte[] y) => ByteHelpers.CompareFastUnsafe(x?.ToBytes(), y);
public override byte[] ToBytes() => ByteHelpers.Combine(new byte[] { Ver.ToByte(), Rep.ToByte(), Rsv.ToByte(), Atyp.ToByte() }, BndAddr.ToBytes(), BndPort.ToBytes());
public bool Equals(byte[] other) => ByteHelpers.CompareFastUnsafe(ToBytes(), other);
public static bool operator ==(byte[] x, ByteArraySerializableBase y) => ByteHelpers.CompareFastUnsafe(x, y?.ToBytes());
public void CreateSpecialByteArrayThrowsInvalidOperationExceptionIfLengthIsZero() { ByteHelpers.CreateSpecialByteArray(0); }
private async Task TryRegisterCoinsAsync(CcjClientRound inputRegistrableRound) { try { List <(uint256 txid, uint index)> registrableCoins = State.GetRegistrableCoins( inputRegistrableRound.State.MaximumInputCountPerPeer, inputRegistrableRound.State.Denomination, inputRegistrableRound.State.FeePerInputs, inputRegistrableRound.State.FeePerOutputs).ToList(); if (registrableCoins.Any()) { BitcoinAddress changeAddress = null; BitcoinAddress activeAddress = null; lock (CustomChangeAddressesLock) { if (CustomChangeAddresses.Count > 0) { changeAddress = CustomChangeAddresses.First(); CustomChangeAddresses.RemoveFirst(); } } lock (CustomActiveAddressesLock) { if (CustomActiveAddresses.Count > 0) { activeAddress = CustomActiveAddresses.First(); CustomActiveAddresses.RemoveFirst(); } } if (changeAddress is null || activeAddress is null) { IEnumerable <HdPubKey> allUnusedInternalKeys = KeyManager.GetKeys(keyState: null, isInternal: true).Where(x => x.KeyState != KeyState.Used); if (changeAddress is null) { string changeLabel = "ZeroLink Change"; IEnumerable <HdPubKey> allChangeKeys = allUnusedInternalKeys.Where(x => x.Label == changeLabel); HdPubKey changeKey = null; KeyManager.AssertLockedInternalKeysIndexed(14); IEnumerable <HdPubKey> internalNotCachedLockedKeys = KeyManager.GetKeys(KeyState.Locked, isInternal: true).Except(AccessCache.Keys); if (allChangeKeys.Count() >= 7 || !internalNotCachedLockedKeys.Any()) // Then don't generate new keys, because it'd bloat the wallet. { // Find the first one that we did not try to register in the current session. changeKey = allChangeKeys.FirstOrDefault(x => !AccessCache.ContainsKey(x)); // If there is no such a key, then use the oldest. if (changeKey == default) { changeKey = AccessCache.Where(x => allChangeKeys.Contains(x.Key)).OrderBy(x => x.Value).First().Key; } changeKey.SetLabel(changeLabel); changeKey.SetKeyState(KeyState.Locked); } else { changeKey = internalNotCachedLockedKeys.RandomElement(); changeKey.SetLabel(changeLabel); } changeAddress = changeKey.GetP2wpkhAddress(Network); AccessCache.AddOrReplace(changeKey, DateTimeOffset.UtcNow); } if (activeAddress is null) { string activeLabel = "ZeroLink Mixed Coin"; IEnumerable <HdPubKey> allActiveKeys = allUnusedInternalKeys.Where(x => x.Label == activeLabel); HdPubKey activeKey = null; KeyManager.AssertLockedInternalKeysIndexed(14); IEnumerable <HdPubKey> internalNotCachedLockedKeys = KeyManager.GetKeys(KeyState.Locked, isInternal: true).Except(AccessCache.Keys); if (allActiveKeys.Count() >= 7 || !internalNotCachedLockedKeys.Any()) // Then don't generate new keys, because it'd bloat the wallet. { // Find the first one that we did not try to register in the current session. activeKey = allActiveKeys.FirstOrDefault(x => !AccessCache.ContainsKey(x)); // If there is no such a key, then use the oldest. if (activeKey == default) { activeKey = AccessCache.Where(x => allActiveKeys.Contains(x.Key)).OrderBy(x => x.Value).First().Key; } activeKey.SetLabel(activeLabel); activeKey.SetKeyState(KeyState.Locked); activeAddress = activeKey.GetP2wpkhAddress(Network); } else { activeKey = internalNotCachedLockedKeys.RandomElement(); activeKey.SetLabel(activeLabel); } activeAddress = activeKey.GetP2wpkhAddress(Network); AccessCache.AddOrReplace(activeKey, DateTimeOffset.UtcNow); } } KeyManager.ToFile(); var blind = CoordinatorPubKey.Blind(activeAddress.ScriptPubKey.ToBytes()); var inputProofs = new List <InputProofModel>(); foreach ((uint256 txid, uint index)coinReference in registrableCoins) { SmartCoin coin = State.GetSingleOrDefaultFromWaitingList(coinReference); if (coin is null) { throw new NotSupportedException("This is impossible."); } coin.Secret = coin.Secret ?? KeyManager.GetSecrets(OnePiece, coin.ScriptPubKey).Single(); var inputProof = new InputProofModel { Input = coin.GetTxoRef(), Proof = coin.Secret.PrivateKey.SignMessage(ByteHelpers.ToHex(blind.BlindedData)) }; inputProofs.Add(inputProof); } AliceClient aliceClient = await AliceClient.CreateNewAsync(Network, changeAddress, blind.BlindedData, inputProofs, CcjHostUri, TorSocks5EndPoint); byte[] unblindedSignature = CoordinatorPubKey.UnblindSignature(aliceClient.BlindedOutputSignature, blind.BlindingFactor); if (!CoordinatorPubKey.Verify(unblindedSignature, activeAddress.ScriptPubKey.ToBytes())) { throw new NotSupportedException("Coordinator did not sign the blinded output properly."); } CcjClientRound roundRegistered = State.GetSingleOrDefaultRound(aliceClient.RoundId); if (roundRegistered is null) { // If our SatoshiClient doesn't yet know about the round because of the dealy create it. // Make its state as it'd be the same as our assumed round was, except the roundId and registeredPeerCount, it'll be updated later. roundRegistered = new CcjClientRound(CcjRunningRoundState.CloneExcept(inputRegistrableRound.State, aliceClient.RoundId, registeredPeerCount: 1)); State.AddOrReplaceRound(roundRegistered); } foreach ((uint256 txid, uint index)coinReference in registrableCoins) { var coin = State.GetSingleOrDefaultFromWaitingList(coinReference); if (coin is null) { throw new NotSupportedException("This is impossible."); } roundRegistered.CoinsRegistered.Add(coin); State.RemoveCoinFromWaitingList(coin); } roundRegistered.ActiveOutputAddress = activeAddress; roundRegistered.ChangeOutputAddress = changeAddress; roundRegistered.UnblindedSignature = unblindedSignature; roundRegistered.AliceClient = aliceClient; } } catch (Exception ex) { Logger.LogError <CcjClient>(ex); } }
public static void FromHex(this IBitcoinSerializable me, string hex) { Guard.NotNullOrEmptyOrWhitespace(nameof(hex), hex); me.FromBytes(ByteHelpers.FromHex(hex)); }
public void GetStringThrowsArgumentNullExceptionIsInputArrayIsNull() { ByteHelpers.GetString(null); }
public override byte[] ToBytes() => ByteHelpers.Combine(new byte[] { Ver.ToByte(), Cmd.ToByte(), Rsv.ToByte(), Atyp.ToByte() }, DstAddr.ToBytes(), DstPort.ToBytes());
public void ByteArrayCompareThrowsArgumentNullExceptionIfFirstParameterIsNull() { ByteHelpers.ByteArrayCompare(null, null); }
public void CombineThrowsArgumentNullExceptionIfSecondParameterIsNull() { var test = new byte[5]; ByteHelpers.Combine(test, null); }
public void CombineThrowsArgumentNullExceptionIfFirstParameterIsNull() { ByteHelpers.Combine(null, null); }
public void CreateSpecialByteArrayCreateArayOfSize10() { byte[] array = ByteHelpers.CreateSpecialByteArray(10); Assert.AreEqual(10, array.Length); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var value = (string)reader.Value; return(OwnershipProof.FromBytes(ByteHelpers.FromHex(value))); }
public void GeneratorsArentChanged() { Assert.Equal("0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", ByteHelpers.ToHex(Generators.G.ToBytes())); Assert.Equal("03AB8F46084B4FA0FC8261328A5A71AF267B1D1F8FE229C63C751D02A2E996E0EC", ByteHelpers.ToHex(Generators.Ga.ToBytes())); Assert.Equal("02FB8868ACD9CBBD68964BAA1CFA6B893A6269E01569183474E6C1C4242A0071A9", ByteHelpers.ToHex(Generators.Gg.ToBytes())); Assert.Equal("023D11E10CE7A8C17671ED777886FC2B84E65A532FA0C411ABBE96E1206F9DFF80", ByteHelpers.ToHex(Generators.Gh.ToBytes())); Assert.Equal("031E7775ED62B79E9E83366198CFE69DFE7408AFF10C331CEE3B2C7F7A5F2EB0C8", ByteHelpers.ToHex(Generators.Gs.ToBytes())); Assert.Equal("03665E9B8468DCEDA16ED3E315FBD0A0E597F4AA3B4C6F2146437F53F3AF204C2C", ByteHelpers.ToHex(Generators.GV.ToBytes())); Assert.Equal("02B4DF49B623A8A0B245CCF2867134A5DAC12FE39ECEC08B3D361801D2C79DDC14", ByteHelpers.ToHex(Generators.Gw.ToBytes())); Assert.Equal("03F50265578FCE5E977162E662ED75D7224AE720FA79B72CF2B6FB86B2136E3B48", ByteHelpers.ToHex(Generators.Gwp.ToBytes())); Assert.Equal("02E33C9F3CBE6388A2D3C3ECB12153DB73499928541905D86AAA4FFC01F2763B54", ByteHelpers.ToHex(Generators.Gx0.ToBytes())); Assert.Equal("0246253CC926AAB789BAA278AB9A54EDEF455CA2014038E9F84DE312C05A8121CC", ByteHelpers.ToHex(Generators.Gx1.ToBytes())); }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var bytes = ((OwnershipProof)value).ToBytes(); writer.WriteValue(ByteHelpers.ToHex(bytes)); }
public async Task <IActionResult> PostInputsAsync([FromBody] InputsRequest request) { // Validate request. if (!ModelState.IsValid || request == null || string.IsNullOrWhiteSpace(request.BlindedOutputScriptHex) || string.IsNullOrWhiteSpace(request.ChangeOutputScript) || request.Inputs == null || request.Inputs.Count() == 0 || request.Inputs.Any(x => x.Input == null || x.Input.Hash == null || string.IsNullOrWhiteSpace(x.Proof))) { return(BadRequest("Invalid request.")); } if (request.Inputs.Count() > 7) { return(BadRequest("Maximum 7 inputs can be registered.")); } using (await InputsLock.LockAsync()) { CcjRound round = Coordinator.GetCurrentInputRegisterableRound(); // Do more checks. try { if (round.ContainsBlindedOutputScriptHex(request.BlindedOutputScriptHex, out _)) { return(BadRequest("Blinded output has already been registered.")); } var changeOutput = new Script(request.ChangeOutputScript); var inputs = new HashSet <(OutPoint OutPoint, TxOut Output)>(); var alicesToRemove = new HashSet <Guid>(); foreach (InputProofModel inputProof in request.Inputs) { if (inputs.Any(x => x.OutPoint == inputProof.Input)) { return(BadRequest("Cannot register an input twice.")); } if (round.ContainsInput(inputProof.Input, out List <Alice> tr)) { alicesToRemove.UnionWith(tr.Select(x => x.UniqueId)); // Input is already registered by this alice, remove it later if all the checks are completed fine. } if (Coordinator.AnyRunningRoundContainsInput(inputProof.Input, out List <Alice> tnr)) { if (tr.Union(tnr).Count() > tr.Count()) { return(BadRequest("Input is already registered in another round.")); } } var bannedElem = Coordinator.UtxoReferee.BannedUtxos.SingleOrDefault(x => x.Key == inputProof.Input); if (bannedElem.Key != default) { int maxBan = (int)TimeSpan.FromDays(30).TotalMinutes; int banLeft = maxBan - (int)((DateTimeOffset.UtcNow - bannedElem.Value.timeOfBan).TotalMinutes); if (banLeft > 0) { return(BadRequest($"Input is banned from participation for {banLeft} minutes: {inputProof.Input.N}:{inputProof.Input.Hash}.")); } else { await Coordinator.UtxoReferee.UnbanAsync(bannedElem.Key); } } GetTxOutResponse getTxOutResponse = await RpcClient.GetTxOutAsync(inputProof.Input.Hash, (int)inputProof.Input.N, includeMempool : true); // Check if inputs are unspent. if (getTxOutResponse == null) { return(BadRequest("Provided input is not unspent.")); } // Check if unconfirmed. if (getTxOutResponse.Confirmations <= 0) { // If it spends a CJ then it may be acceptable to register. if (!Coordinator.ContainsCoinJoin(inputProof.Input.Hash)) { return(BadRequest("Provided input is neither confirmed, nor is from an unconfirmed coinjoin.")); } // After 24 unconfirmed cj in the mempool dont't let unconfirmed coinjoin to be registered. if (await Coordinator.IsUnconfirmedCoinJoinLimitReachedAsync()) { return(BadRequest("Provided input is from an unconfirmed coinjoin, but the maximum number of unconfirmed coinjoins is reached.")); } } // Check if immature. if (getTxOutResponse.Confirmations <= 100) { if (getTxOutResponse.IsCoinBase) { return(BadRequest("Provided input is immature.")); } } // Check if inputs are native segwit. if (getTxOutResponse.ScriptPubKeyType != "witness_v0_keyhash") { return(BadRequest("Provided input must be witness_v0_keyhash.")); } TxOut txout = getTxOutResponse.TxOut; var address = (BitcoinWitPubKeyAddress)txout.ScriptPubKey.GetDestinationAddress(Network); // Check if proofs are valid. bool validProof; try { validProof = address.VerifyMessage(request.BlindedOutputScriptHex, inputProof.Proof); } catch (FormatException ex) { return(BadRequest($"Provided proof is invalid: {ex.Message}")); } if (!validProof) { return(BadRequest("Provided proof is invalid.")); } inputs.Add((inputProof.Input, txout)); } // Check if inputs have enough coins. Money inputSum = inputs.Sum(x => x.Output.Value); Money networkFeeToPay = (inputs.Count() * round.FeePerInputs + 2 * round.FeePerOutputs); Money changeAmount = inputSum - (round.Denomination + networkFeeToPay); if (changeAmount < Money.Zero) { return(BadRequest($"Not enough inputs are provided. Fee to pay: {networkFeeToPay.ToString(false, true)} BTC. Round denomination: {round.Denomination.ToString(false, true)} BTC. Only provided: {inputSum.ToString(false, true)} BTC.")); } // Make sure Alice checks work. var alice = new Alice(inputs, networkFeeToPay, new Script(request.ChangeOutputScript), request.BlindedOutputScriptHex); foreach (Guid aliceToRemove in alicesToRemove) { round.RemoveAlicesBy(aliceToRemove); } round.AddAlice(alice); // All checks are good. Sign. byte[] blindedData; try { blindedData = ByteHelpers.FromHex(request.BlindedOutputScriptHex); } catch { return(BadRequest("Invalid blinded output hex.")); } Logger.LogDebug <ChaumianCoinJoinController>($"Blinded data hex: {request.BlindedOutputScriptHex}"); Logger.LogDebug <ChaumianCoinJoinController>($"Blinded data array size: {blindedData.Length}"); byte[] signature = RsaKey.SignBlindedData(blindedData); // Check if phase changed since. if (round.Status != ChaumianCoinJoin.CcjRoundStatus.Running || round.Phase != CcjRoundPhase.InputRegistration) { return(base.StatusCode(StatusCodes.Status503ServiceUnavailable, "The state of the round changed while handling the request. Try again.")); } // Progress round if needed. if (round.CountAlices() >= round.AnonymitySet) { await round.RemoveAlicesIfInputsSpentAsync(); if (round.CountAlices() >= round.AnonymitySet) { await round.ExecuteNextPhaseAsync(CcjRoundPhase.ConnectionConfirmation); } } var resp = new InputsResponse { UniqueId = alice.UniqueId, BlindedOutputSignature = signature, RoundId = round.RoundId }; return(Ok(resp)); } catch (Exception ex) { Logger.LogDebug <ChaumianCoinJoinController>(ex); return(BadRequest(ex.Message)); } } }
public void Synchronize() { Interlocked.Exchange(ref _running, 1); Task.Run(async() => { try { var blockCount = await RpcClient.GetBlockCountAsync(); var isIIB = true; // Initial Index Building phase while (IsRunning) { try { // If stop was requested return. if (IsRunning == false) { return; } var height = StartingHeight; uint256 prevHash = null; using (await IndexLock.LockAsync()) { if (Index.Count != 0) { var lastIndex = Index.Last(); height = lastIndex.BlockHeight + 1; prevHash = lastIndex.BlockHash; } } if (blockCount - (int)height <= 100) { isIIB = false; } Block block = null; try { block = await RpcClient.GetBlockAsync(height); } catch (RPCException) // if the block didn't come yet { await Task.Delay(1000); continue; } if (prevHash != null) { // In case of reorg: if (prevHash != block.Header.HashPrevBlock && !isIIB) // There is no reorg in IIB { Logger.LogInfo <IndexBuilderService>($"REORG Invalid Block: {prevHash}"); // 1. Rollback index using (await IndexLock.LockAsync()) { Index.RemoveLast(); } // 2. Serialize Index. (Remove last line.) var lines = File.ReadAllLines(IndexFilePath); File.WriteAllLines(IndexFilePath, lines.Take(lines.Length - 1).ToArray()); // 3. Rollback Bech32UtxoSet if (Bech32UtxoSetHistory.Count != 0) { Bech32UtxoSetHistory.Last().Rollback(Bech32UtxoSet); // The Bech32UtxoSet MUST be recovered to its previous state. Bech32UtxoSetHistory.RemoveLast(); // 4. Serialize Bech32UtxoSet. await File.WriteAllLinesAsync(Bech32UtxoSetFilePath, Bech32UtxoSet .Select(entry => entry.Key.Hash + ":" + entry.Key.N + ":" + ByteHelpers.ToHex(entry.Value.ToCompressedBytes()))); } // 5. Skip the current block. continue; } } if (!isIIB) { if (Bech32UtxoSetHistory.Count >= 100) { Bech32UtxoSetHistory.RemoveFirst(); } Bech32UtxoSetHistory.Add(new ActionHistoryHelper()); } var scripts = new HashSet <Script>(); foreach (var tx in block.Transactions) { for (int i = 0; i < tx.Outputs.Count; i++) { var output = tx.Outputs[i]; if (!output.ScriptPubKey.IsPayToScriptHash && output.ScriptPubKey.IsWitness) { var outpoint = new OutPoint(tx.GetHash(), i); Bech32UtxoSet.Add(outpoint, output.ScriptPubKey); if (!isIIB) { Bech32UtxoSetHistory.Last().StoreAction(ActionHistoryHelper.Operation.Add, outpoint, output.ScriptPubKey); } scripts.Add(output.ScriptPubKey); } } foreach (var input in tx.Inputs) { var found = Bech32UtxoSet.SingleOrDefault(x => x.Key == input.PrevOut); if (found.Key != default) { Script val = Bech32UtxoSet[input.PrevOut]; Bech32UtxoSet.Remove(input.PrevOut); if (!isIIB) { Bech32UtxoSetHistory.Last().StoreAction(ActionHistoryHelper.Operation.Remove, input.PrevOut, val); } scripts.Add(found.Value); } } } // https://github.com/bitcoin/bips/blob/master/bip-0158.mediawiki // The parameter k MUST be set to the first 16 bytes of the hash of the block for which the filter // is constructed.This ensures the key is deterministic while still varying from block to block. var key = block.GetHash().ToBytes().Take(16).ToArray(); GolombRiceFilter filter = null; if (scripts.Count != 0) { filter = GolombRiceFilter.Build(key, scripts.Select(x => x.ToCompressedBytes())); } var filterModel = new FilterModel { BlockHash = block.GetHash(), BlockHeight = height, Filter = filter }; await File.AppendAllLinesAsync(IndexFilePath, new[] { filterModel.ToLine() }); using (await IndexLock.LockAsync()) { Index.Add(filterModel); } if (File.Exists(Bech32UtxoSetFilePath)) { File.Delete(Bech32UtxoSetFilePath); } await File.WriteAllLinesAsync(Bech32UtxoSetFilePath, Bech32UtxoSet .Select(entry => entry.Key.Hash + ":" + entry.Key.N + ":" + ByteHelpers.ToHex(entry.Value.ToCompressedBytes()))); if (blockCount - height <= 3 || height % 100 == 0) // If not close to the tip, just log debug. { Logger.LogInfo <IndexBuilderService>($"Created filter for block: {height}."); } else { Logger.LogDebug <IndexBuilderService>($"Created filter for block: {height}."); } } catch (Exception ex) { Logger.LogDebug <IndexBuilderService>(ex); } } } finally { if (IsStopping) { Interlocked.Exchange(ref _running, 3); } } }); }
public void ByteArrayCompareThrowsArgumentNullExceptionIfSecondParameterIsNull() { var test = new byte[5]; ByteHelpers.ByteArrayCompare(test, null); }
public void Synchronize() { Interlocked.Exchange(ref _running, 1); Task.Run(async() => { try { var blockCount = await RpcClient.GetBlockCountAsync(); var isIIB = true; // Initial Index Building phase while (IsRunning) { try { // If stop was requested return. if (IsRunning == false) { return; } Height height = StartingHeight; uint256 prevHash = null; using (await IndexLock.LockAsync()) { if (Index.Count != 0) { var lastIndex = Index.Last(); height = lastIndex.BlockHeight + 1; prevHash = lastIndex.BlockHash; } } if (blockCount - height <= 100) { isIIB = false; } Block block = null; try { block = await RpcClient.GetBlockAsync(height); } catch (RPCException) // if the block didn't come yet { await Task.Delay(1000); continue; } if (blockCount - height <= 2) { NewBlock?.Invoke(this, block); } if (!(prevHash is null)) { // In case of reorg: if (prevHash != block.Header.HashPrevBlock && !isIIB) // There is no reorg in IIB { Logger.LogInfo <IndexBuilderService>($"REORG Invalid Block: {prevHash}"); // 1. Rollback index using (await IndexLock.LockAsync()) { Index.RemoveLast(); } // 2. Serialize Index. (Remove last line.) var lines = File.ReadAllLines(IndexFilePath); File.WriteAllLines(IndexFilePath, lines.Take(lines.Length - 1).ToArray()); // 3. Rollback Bech32UtxoSet if (Bech32UtxoSetHistory.Count != 0) { Bech32UtxoSetHistory.Last().Rollback(Bech32UtxoSet); // The Bech32UtxoSet MUST be recovered to its previous state. Bech32UtxoSetHistory.RemoveLast(); // 4. Serialize Bech32UtxoSet. await File.WriteAllLinesAsync(Bech32UtxoSetFilePath, Bech32UtxoSet .Select(entry => entry.Key.Hash + ":" + entry.Key.N + ":" + ByteHelpers.ToHex(entry.Value.ToCompressedBytes()))); } // 5. Skip the current block. continue; } } if (!isIIB) { if (Bech32UtxoSetHistory.Count >= 100) { Bech32UtxoSetHistory.RemoveFirst(); } Bech32UtxoSetHistory.Add(new ActionHistoryHelper()); } var scripts = new HashSet <Script>(); foreach (var tx in block.Transactions) { // If stop was requested return. // Because this tx iteration can take even minutes // It doesn't need to be accessed with a thread safe fasion with Interlocked through IsRunning, this may have some performance benefit if (_running != 1) { return; } for (int i = 0; i < tx.Outputs.Count; i++) { var output = tx.Outputs[i]; if (!output.ScriptPubKey.IsPayToScriptHash && output.ScriptPubKey.IsWitness) { var outpoint = new OutPoint(tx.GetHash(), i); Bech32UtxoSet.Add(outpoint, output.ScriptPubKey); if (!isIIB) { Bech32UtxoSetHistory.Last().StoreAction(ActionHistoryHelper.Operation.Add, outpoint, output.ScriptPubKey); } scripts.Add(output.ScriptPubKey); } } foreach (var input in tx.Inputs) { OutPoint prevOut = input.PrevOut; if (Bech32UtxoSet.TryGetValue(prevOut, out Script foundScript)) { Bech32UtxoSet.Remove(prevOut); if (!isIIB) { Bech32UtxoSetHistory.Last().StoreAction(ActionHistoryHelper.Operation.Remove, prevOut, foundScript); } scripts.Add(foundScript); } } } GolombRiceFilter filter = null; if (scripts.Count != 0) { filter = new GolombRiceFilterBuilder() .SetKey(block.GetHash()) .SetP(20) .SetM(1 << 20) .AddEntries(scripts.Select(x => x.ToCompressedBytes())) .Build(); } var filterModel = new FilterModel { BlockHash = block.GetHash(), BlockHeight = height, Filter = filter }; await File.AppendAllLinesAsync(IndexFilePath, new[] { filterModel.ToLine() }); using (await IndexLock.LockAsync()) { Index.Add(filterModel); } if (File.Exists(Bech32UtxoSetFilePath)) { File.Delete(Bech32UtxoSetFilePath); } await File.WriteAllLinesAsync(Bech32UtxoSetFilePath, Bech32UtxoSet .Select(entry => entry.Key.Hash + ":" + entry.Key.N + ":" + ByteHelpers.ToHex(entry.Value.ToCompressedBytes()))); // If not close to the tip, just log debug. // Use height.Value instead of simply height, because it cannot be negative height. if (blockCount - height.Value <= 3 || height % 100 == 0) { Logger.LogInfo <IndexBuilderService>($"Created filter for block: {height}."); } else { Logger.LogDebug <IndexBuilderService>($"Created filter for block: {height}."); } } catch (Exception ex) { Logger.LogDebug <IndexBuilderService>(ex); } } } finally { if (IsStopping) { Interlocked.Exchange(ref _running, 3); } } }); }
public static string ToHex(this IBitcoinSerializable me) { return(ByteHelpers.ToHex(me.ToBytes())); }