public Input(OutPoint previousOutPoint, byte[] signatureScript, uint sequence) { if (signatureScript == null) throw new ArgumentNullException(nameof(signatureScript)); PreviousOutpoint = previousOutPoint; SignatureScript = signatureScript; Sequence = sequence; }
public void CanGetUTXOsMempool() { var client = CreateRestClient(); var txId = uint256.Parse("3a3422dfd155f1d2ffc3e46cf978a9c5698c17c187f04cfa1b93358699c4ed3f"); var outPoint = new OutPoint(txId, 0); var utxos = client.GetUnspectOutputsAsync(new []{ outPoint }, true).Result; Assert.Equal(1, utxos.Outputs.Length); Assert.Equal(1, (int)utxos.Outputs[0].Version); Assert.Equal(Money.Parse("0.1"), (int)utxos.Outputs[0].Output.Value); }
public void CanGetUTXOs() { var client = CreateRestClient(); var txId = uint256.Parse("3a3422dfd155f1d2ffc3e46cf978a9c5698c17c187f04cfa1b93358699c4ed3f"); var outPoint = new OutPoint(txId, 0); var utxos = client.GetUnspectOutputsAsync(new[] { outPoint }, false).Result; Assert.Equal(true, utxos.Bitmap[0]); Assert.Equal(false, utxos.Bitmap[1]); Assert.Equal(0, utxos.Outputs.Length); }
public UnspentCoin(JObject unspent) { OutPoint = new OutPoint(uint256.Parse((string)unspent["txid"]), (uint)unspent["vout"]); Address = Network.CreateFromBase58Data<BitcoinAddress>((string)unspent["address"]); Account = (string)unspent["account"]; ScriptPubKey = new Script(Encoders.Hex.DecodeData((string)unspent["scriptPubKey"])); var amount = (decimal)unspent["amount"]; Amount = new Money((long)(amount * Money.COIN)); Confirmations = (uint)unspent["confirmations"]; }
public void CanGetUTXOsMempool() { var client = CreateRestClient(); var txId = uint256.Parse("bd3052b71c4fdd8f49c54880db4a59aadcfff3c11e8f23e9ed7f7ab4a9bb5700"); var outPoint = new OutPoint(txId, 0); var utxos = client.GetUnspentOutputsAsync(new []{ outPoint }, true).Result; Assert.Equal(1, utxos.Outputs.Length); Assert.Equal(1, (int)utxos.Outputs[0].Version); Assert.Equal(Money.Parse("0.01"), (int)utxos.Outputs[0].Output.Value); }
public Input(OutPoint previousOutPoint, uint sequence, Amount inputAmount, uint blockHeight, uint blockIndex, byte[] signatureScript) { if (signatureScript == null) throw new ArgumentNullException(nameof(signatureScript)); PreviousOutpoint = previousOutPoint; Sequence = sequence; SignatureScript = signatureScript; InputAmount = inputAmount; BlockHeight = blockHeight; BlockIndex = blockIndex; }
public void OnConnectUp(GameObject pObject) { setPoint(pObject); if (choosedInPoint && choosedOutPoint) { choosedOutPoint.connect(choosedInPoint); choosedInPoint.showLine(); } choosedInPoint = null; choosedOutPoint = null; choosedPointTransform = null; connectLine.visible = false; }
public bool setPoint(GameObject pObject) { if (!pObject) return false; if (pObject.GetComponent<InPoint>()) choosedInPoint = pObject.GetComponent<InPoint>(); else if (pObject.GetComponent<OutPoint>()) choosedOutPoint = pObject.GetComponent<OutPoint>(); else return false; choosedPointTransform = pObject.transform; connectLine.visible = true; refreshLineShow(); return true; }
public async Task TestRewindAsync() { HashHeightPair tip = this.cachedCoinView.GetTipHash(); Assert.Equal(this.chainIndexer.Genesis.HashBlock, tip.Hash); int currentHeight = 0; // Create a lot of new coins. List <UnspentOutput> outputsList = this.CreateOutputsList(currentHeight + 1, 100); this.SaveChanges(outputsList, currentHeight + 1); currentHeight++; this.cachedCoinView.Flush(true); HashHeightPair tipAfterOriginalCoinsCreation = this.cachedCoinView.GetTipHash(); // Collection that will be used as a coinview that we will update in parallel. Needed to verify that actual coinview is ok. List <OutPoint> outPoints = this.ConvertToListOfOutputPoints(outputsList); // Copy of current state to later rewind and verify against it. List <OutPoint> copyOfOriginalOutPoints = new List <OutPoint>(outPoints); List <OutPoint> copyAfterHalfOfAdditions = new List <OutPoint>(); HashHeightPair coinviewTipAfterHalf = null; int addChangesTimes = 500; // Spend some coins in the next N saves. for (int i = 0; i < addChangesTimes; ++i) { OutPoint txId = outPoints[this.random.Next(0, outPoints.Count)]; List <OutPoint> txPoints = outPoints.Where(x => x.Hash == txId.Hash).ToList(); this.Shuffle(txPoints); List <OutPoint> txPointsToSpend = txPoints.Take(txPoints.Count / 2).ToList(); // First spend in cached coinview FetchCoinsResponse response = this.cachedCoinView.FetchCoins(txPoints.ToArray()); Assert.Equal(txPoints.Count, response.UnspentOutputs.Count); var toSpend = new List <UnspentOutput>(); foreach (OutPoint outPointToSpend in txPointsToSpend) { response.UnspentOutputs[outPointToSpend].Spend(); toSpend.Add(response.UnspentOutputs[outPointToSpend]); } // Spend from outPoints. outPoints.RemoveAll(x => txPointsToSpend.Contains(x)); // Save coinview this.SaveChanges(toSpend, currentHeight + 1); currentHeight++; if (i == addChangesTimes / 2) { copyAfterHalfOfAdditions = new List <OutPoint>(outPoints); coinviewTipAfterHalf = this.cachedCoinView.GetTipHash(); } } await this.ValidateCoinviewIntegrityAsync(outPoints); for (int i = 0; i < addChangesTimes; i++) { this.cachedCoinView.Rewind(); HashHeightPair currentTip = this.cachedCoinView.GetTipHash(); if (currentTip == coinviewTipAfterHalf) { await this.ValidateCoinviewIntegrityAsync(copyAfterHalfOfAdditions); } } Assert.Equal(tipAfterOriginalCoinsCreation, this.cachedCoinView.GetTipHash()); await this.ValidateCoinviewIntegrityAsync(copyOfOriginalOutPoints); }
public async Task <IActionResult> PostInputsAsync([FromBody, Required] InputsRequest request) { // Validate request. if (request.RoundId < 0 || !ModelState.IsValid) { return(BadRequest("Invalid request.")); } if (request.Inputs.Count() > 7) { return(BadRequest("Maximum 7 inputs can be registered.")); } using (await InputsLock.LockAsync()) { CcjRound round = Coordinator.TryGetRound(request.RoundId); if (round is null || round.Phase != CcjRoundPhase.InputRegistration) { return(NotFound("No such running round in InputRegistration. Try another round.")); } // Do more checks. try { uint256[] blindedOutputs = request.BlindedOutputScripts.ToArray(); int blindedOutputCount = blindedOutputs.Length; int maxBlindedOutputCount = round.MixingLevels.Count(); if (blindedOutputCount > maxBlindedOutputCount) { return(BadRequest($"Too many blinded output was provided: {blindedOutputCount}, maximum: {maxBlindedOutputCount}.")); } if (blindedOutputs.Distinct().Count() < blindedOutputs.Length) { return(BadRequest("Duplicate blinded output found.")); } if (round.ContainsAnyBlindedOutputScript(blindedOutputs)) { return(BadRequest("Blinded output has already been registered.")); } if (request.ChangeOutputAddress.Network != Network) { // RegTest and TestNet address formats are sometimes the same. if (Network == Network.Main) { return(BadRequest($"Invalid ChangeOutputAddress Network.")); } } var uniqueInputs = new HashSet <TxoRef>(); foreach (InputProofModel inputProof in request.Inputs) { if (uniqueInputs.Contains(inputProof.Input)) { return(BadRequest("Cannot register an input twice.")); } uniqueInputs.Add(inputProof.Input); } var alicesToRemove = new HashSet <Guid>(); var getTxOutResponses = new List <(InputProofModel inputModel, Task <GetTxOutResponse> getTxOutTask)>(); var batch = RpcClient.PrepareBatch(); foreach (InputProofModel inputProof in request.Inputs) { if (round.ContainsInput(inputProof.Input.ToOutPoint(), 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.ToOutPoint(), out List <Alice> tnr)) { if (tr.Union(tnr).Count() > tr.Count) { return(BadRequest("Input is already registered in another round.")); } } OutPoint outpoint = inputProof.Input.ToOutPoint(); var bannedElem = await Coordinator.UtxoReferee.TryGetBannedAsync(outpoint, notedToo : false); if (bannedElem != null) { return(BadRequest($"Input is banned from participation for {(int)bannedElem.BannedRemaining.TotalMinutes} minutes: {inputProof.Input.Index}:{inputProof.Input.TransactionId}.")); } var txOutResponseTask = batch.GetTxOutAsync(inputProof.Input.TransactionId, (int)inputProof.Input.Index, includeMempool: true); getTxOutResponses.Add((inputProof, txOutResponseTask)); } // Perform all RPC request at once var waiting = Task.WhenAll(getTxOutResponses.Select(x => x.getTxOutTask)); await batch.SendBatchAsync(); await waiting; byte[] blindedOutputScriptHashesByte = ByteHelpers.Combine(blindedOutputs.Select(x => x.ToBytes())); uint256 blindedOutputScriptsHash = new uint256(Hashes.SHA256(blindedOutputScriptHashesByte)); var inputs = new HashSet <Coin>(); foreach (var responses in getTxOutResponses) { var(inputProof, getTxOutResponseTask) = responses; var getTxOutResponse = await getTxOutResponseTask; // Check if inputs are unspent. if (getTxOutResponse is null) { return(BadRequest($"Provided input is not unspent: {inputProof.Input.Index}:{inputProof.Input.TransactionId}.")); } // Check if unconfirmed. if (getTxOutResponse.Confirmations <= 0) { // If it spends a CJ then it may be acceptable to register. if (!await Coordinator.ContainsCoinJoinAsync(inputProof.Input.TransactionId)) { return(BadRequest("Provided input is neither confirmed, nor is from an unconfirmed coinjoin.")); } // Check if mempool would accept a fake transaction created with the registered inputs. // This will catch ascendant/descendant count and size limits for example. var result = await RpcClient.TestMempoolAcceptAsync(new[] { new Coin(inputProof.Input.ToOutPoint(), getTxOutResponse.TxOut) }); if (!result.accept) { return(BadRequest($"Provided input is from an unconfirmed coinjoin, but a limit is reached: {result.rejectReason}")); } } // 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. if (!address.VerifyMessage(blindedOutputScriptsHash, inputProof.Proof)) { return(BadRequest("Provided proof is invalid.")); } inputs.Add(new Coin(inputProof.Input.ToOutPoint(), txOut)); } var acceptedBlindedOutputScripts = new List <uint256>(); // Calculate expected networkfee to pay after base denomination. int inputCount = inputs.Count; Money networkFeeToPayAfterBaseDenomination = (inputCount * round.FeePerInputs) + (2 * round.FeePerOutputs); // Check if inputs have enough coins. Money inputSum = inputs.Sum(x => x.Amount); Money changeAmount = (inputSum - (round.MixingLevels.GetBaseDenomination() + networkFeeToPayAfterBaseDenomination)); if (changeAmount < Money.Zero) { return(BadRequest($"Not enough inputs are provided. Fee to pay: {networkFeeToPayAfterBaseDenomination.ToString(false, true)} BTC. Round denomination: {round.MixingLevels.GetBaseDenomination().ToString(false, true)} BTC. Only provided: {inputSum.ToString(false, true)} BTC.")); } acceptedBlindedOutputScripts.Add(blindedOutputs.First()); Money networkFeeToPay = networkFeeToPayAfterBaseDenomination; // Make sure we sign the proper number of additional blinded outputs. var moneySoFar = Money.Zero; for (int i = 1; i < blindedOutputCount; i++) { if (!round.MixingLevels.TryGetDenomination(i, out Money denomination)) { break; } Money coordinatorFee = denomination.Percentage(round.CoordinatorFeePercent * round.AnonymitySet); // It should be the number of bobs, but we must make sure they'd have money to pay all. changeAmount -= (denomination + round.FeePerOutputs + coordinatorFee); networkFeeToPay += round.FeePerOutputs; if (changeAmount < Money.Zero) { break; } acceptedBlindedOutputScripts.Add(blindedOutputs[i]); } // Make sure Alice checks work. var alice = new Alice(inputs, networkFeeToPayAfterBaseDenomination, request.ChangeOutputAddress, acceptedBlindedOutputScripts); foreach (Guid aliceToRemove in alicesToRemove) { round.RemoveAlicesBy(aliceToRemove); } round.AddAlice(alice); // All checks are good. Sign. var blindSignatures = new List <uint256>(); for (int i = 0; i < acceptedBlindedOutputScripts.Count; i++) { var blindedOutput = acceptedBlindedOutputScripts[i]; var signer = round.MixingLevels.GetLevel(i).Signer; uint256 blindSignature = signer.Sign(blindedOutput); blindSignatures.Add(blindSignature); } alice.BlindedOutputSignatures = blindSignatures.ToArray(); // Check if phase changed since. if (round.Status != CcjRoundStatus.Running || round.Phase != CcjRoundPhase.InputRegistration) { return(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.RemoveAlicesIfAnInputRefusedByMempoolAsync(); if (round.CountAlices() >= round.AnonymitySet) { await round.ExecuteNextPhaseAsync(CcjRoundPhase.ConnectionConfirmation); } } var resp = new InputsResponse { UniqueId = alice.UniqueId, RoundId = round.RoundId }; return(Ok(resp)); } catch (Exception ex) { Logger.LogDebug <ChaumianCoinJoinController>(ex); return(BadRequest(ex.Message)); } } }
/// <summary> /// Sign a transaction /// </summary> /// <param name="request">The transaction to be signed</param> /// <returns>The signed transaction</returns> public async Task <SignRawTransactionResponse> SignRawTransactionWithKeyAsync(SignRawTransactionWithKeyRequest request) { Dictionary <string, object> values = new Dictionary <string, object>(); values.Add("hexstring", request.Transaction.ToHex()); JArray keys = new JArray(); foreach (var k in request.PrivateKeys ?? new Key[0]) { keys.Add(k.GetBitcoinSecret(Network).ToString()); } values.Add("privkeys", keys); if (request.PreviousTransactions != null) { JArray prevs = new JArray(); foreach (var prev in request.PreviousTransactions) { JObject prevObj = new JObject(); prevObj.Add(new JProperty("txid", prev.OutPoint.Hash.ToString())); prevObj.Add(new JProperty("vout", prev.OutPoint.N)); prevObj.Add(new JProperty("scriptPubKey", prev.ScriptPubKey.ToHex())); if (prev.RedeemScript != null) { prevObj.Add(new JProperty("redeemScript", prev.RedeemScript.ToHex())); } prevObj.Add(new JProperty("amount", prev.Amount.ToDecimal(MoneyUnit.BTC).ToString())); prevs.Add(prevObj); } values.Add("prevtxs", prevs); if (request.SigHash.HasValue) { values.Add("sighashtype", SigHashToString(request.SigHash.Value)); } } var result = await SendCommandWithNamedArgsAsync("signrawtransactionwithkey", values).ConfigureAwait(false); var response = new SignRawTransactionResponse(); response.SignedTransaction = ParseTxHex(result.Result["hex"].Value <string>()); response.Complete = result.Result["complete"].Value <bool>(); var errors = result.Result["errors"] as JArray; var errorList = new List <SignRawTransactionResponse.ScriptError>(); if (errors != null) { foreach (var error in errors) { var scriptError = new SignRawTransactionResponse.ScriptError(); scriptError.OutPoint = OutPoint.Parse($"{error["txid"].Value<string>()}-{(int)error["vout"].Value<long>()}"); scriptError.ScriptSig = Script.FromBytesUnsafe(Encoders.Hex.DecodeData(error["scriptSig"].Value <string>())); scriptError.Sequence = new Sequence((uint)error["sequence"].Value <long>()); scriptError.Error = error["error"].Value <string>(); errorList.Add(scriptError); } } response.Errors = errorList.ToArray(); return(response); }
public async Task NotingTestsAsync() { (_, IRPCClient rpc, Network network, Coordinator coordinator, _, _, _) = await Common.InitializeTestEnvironmentAsync(RegTestFixture, 1); Money denomination = Money.Coins(1m); decimal coordinatorFeePercent = 0.1m; int anonymitySet = 2; int connectionConfirmationTimeout = 1; bool doesNoteBeforeBan = true; CoordinatorRoundConfig roundConfig = RegTestFixture.CreateRoundConfig(denomination, 140, 0.7, coordinatorFeePercent, anonymitySet, 240, connectionConfirmationTimeout, 1, 1, 1, 24, doesNoteBeforeBan, 11); coordinator.RoundConfig.UpdateOrDefault(roundConfig, toFile: true); coordinator.AbortAllRoundsInInputRegistration(""); Uri baseUri = new(RegTestFixture.BackendEndPoint); List <(BitcoinAddress changeOutputAddress, BlindedOutputWithNonceIndex blindedData, InputProofModel[] inputsProofs)> registerRequests = new(); AliceClient4?aliceClientBackup = null; Assert.True(coordinator.TryGetCurrentInputRegisterableRound(out CoordinatorRound? round)); for (int i = 0; i < roundConfig.AnonymitySet; i++) { var activeOutputAddress = new Key().GetAddress(ScriptPubKeyType.Segwit, network); var changeOutputAddress = new Key().GetAddress(ScriptPubKeyType.Segwit, network); Key inputKey = new(); var inputAddress = inputKey.PubKey.GetAddress(ScriptPubKeyType.Segwit, network); Requester requester = new(); var nonce = round !.NonceProvider.GetNextNonce(); BlindedOutputWithNonceIndex blinded = new(nonce.N, requester.BlindScript(round.MixingLevels.GetBaseLevel().SignerKey.PubKey, nonce.R, activeOutputAddress.ScriptPubKey)); uint256 blindedOutputScriptsHash = new(Hashes.SHA256(blinded.BlindedOutput.ToBytes())); uint256 txHash = await rpc.SendToAddressAsync(inputAddress, Money.Coins(2)); await rpc.GenerateAsync(1); Transaction transaction = await rpc.GetRawTransactionAsync(txHash); Coin coin = transaction.Outputs.GetCoins(inputAddress.ScriptPubKey).Single(); OutPoint input = coin.Outpoint; InputProofModel inputProof = new() { Input = input, Proof = inputKey.SignCompact(blindedOutputScriptsHash) }; InputProofModel[] inputsProofs = new InputProofModel[] { inputProof }; registerRequests.Add((changeOutputAddress, blinded, inputsProofs)); aliceClientBackup = await AliceClientBase.CreateNewAsync(round.RoundId, new[] { activeOutputAddress }, new[] { round.MixingLevels.GetBaseLevel().SignerKey.PubKey }, new[] { requester }, network, changeOutputAddress, new[] { blinded }, inputsProofs, BackendHttpClient); } await WaitForTimeoutAsync(); int bannedCount = coordinator.UtxoReferee.CountBanned(false); Assert.Equal(0, bannedCount); int notedCount = coordinator.UtxoReferee.CountBanned(true); Assert.Equal(anonymitySet, notedCount); Assert.True(coordinator.TryGetCurrentInputRegisterableRound(out round)); foreach (var registerRequest in registerRequests) { await AliceClientBase.CreateNewAsync(round !.RoundId, aliceClientBackup.RegisteredAddresses, round.MixingLevels.GetAllLevels().Select(x => x.SignerKey.PubKey), aliceClientBackup.Requesters, network, registerRequest.changeOutputAddress, new[] { registerRequest.blindedData }, registerRequest.inputsProofs, BackendHttpClient); } await WaitForTimeoutAsync(); bannedCount = coordinator.UtxoReferee.CountBanned(false); Assert.Equal(anonymitySet, bannedCount); notedCount = coordinator.UtxoReferee.CountBanned(true); Assert.Equal(anonymitySet, notedCount); } }
public SmartCoin GetByOutPoint(OutPoint outpoint) => Coins.FirstOrDefault(x => x.GetOutPoint() == outpoint);
public static Transaction Deserialize(byte[] rawTransaction) { if (rawTransaction == null) throw new ArgumentNullException(nameof(rawTransaction)); int version; Input[] inputs; Output[] outputs; uint lockTime; uint expiry; var cursor = new ByteCursor(rawTransaction); try { version = cursor.ReadInt32(); if (version != SupportedVersion) { var reason = $"Unsupported transaction version `{version}`"; throw new EncodingException(typeof(Transaction), cursor, reason); } // Prefix deserialization var prefixInputCount = cursor.ReadCompact(); if (prefixInputCount > TransactionRules.MaxInputs) { var reason = $"Input count {prefixInputCount} exceeds maximum value {TransactionRules.MaxInputs}"; throw new EncodingException(typeof(Transaction), cursor, reason); } inputs = new Input[prefixInputCount]; for (int i = 0; i < (int)prefixInputCount; i++) { var previousHash = new Blake256Hash(cursor.ReadBytes(Blake256Hash.Length)); var previousIndex = cursor.ReadUInt32(); var previousTree = cursor.ReadByte(); var previousOutPoint = new OutPoint(previousHash, previousIndex, previousTree); var sequence = cursor.ReadUInt32(); inputs[i] = Input.CreateFromPrefix(previousOutPoint, sequence); } var outputCount = cursor.ReadCompact(); if (outputCount > TransactionRules.MaxOutputs) { var reason = $"Output count {outputCount} exceeds maximum value {TransactionRules.MaxOutputs}"; throw new EncodingException(typeof(Transaction), cursor, reason); } outputs = new Output[outputCount]; for (int i = 0; i < (int)outputCount; i++) { var amount = (Amount)cursor.ReadInt64(); var outputVersion = cursor.ReadUInt16(); var pkScript = cursor.ReadVarBytes(TransactionRules.MaxPayload); outputs[i] = new Output(amount, outputVersion, pkScript); } lockTime = cursor.ReadUInt32(); expiry = cursor.ReadUInt32(); // Witness deserialization var witnessInputCount = cursor.ReadCompact(); if (witnessInputCount != prefixInputCount) { var reason = $"Input counts in prefix and witness do not match ({prefixInputCount} != {witnessInputCount})"; throw new EncodingException(typeof(Transaction), cursor, reason); } for (int i = 0; i < (int)witnessInputCount; i++) { var inputAmount = (Amount)cursor.ReadInt64(); var blockHeight = cursor.ReadUInt32(); var blockIndex = cursor.ReadUInt32(); var signatureScript = cursor.ReadVarBytes(TransactionRules.MaxPayload); inputs[i] = Input.AddWitness(inputs[i], inputAmount, blockHeight, blockIndex, signatureScript); } } catch (Exception ex) when (!(ex is EncodingException)) { throw new EncodingException(typeof(Transaction), cursor, ex); } return new Transaction(version, inputs, outputs, lockTime, expiry); }
public void Synchronize() { Task.Run(async() => { try { if (Interlocked.Read(ref _runner) >= 2) { return; } Interlocked.Increment(ref _runner); while (Interlocked.Read(ref _runner) != 1) { await Task.Delay(100); } if (Interlocked.Read(ref _running) >= 2) { return; } try { Interlocked.Exchange(ref _running, 1); var isImmature = false; // The last 100 blocks are reorgable. (Assume it is mature at first.) SyncInfo syncInfo = null; while (IsRunning) { try { // If we didn't yet initialized syncInfo, do so. if (syncInfo is null) { syncInfo = await GetSyncInfoAsync(); } Height heightToRequest = StartingHeight; uint256 currentHash = null; using (await IndexLock.LockAsync()) { if (Index.Count != 0) { var lastIndex = Index.Last(); heightToRequest = lastIndex.BlockHeight + 1; currentHash = lastIndex.BlockHash; } } // If not synchronized or already 5 min passed since last update, get the latest blockchain info. if (!syncInfo.IsCoreSynchornized || (syncInfo.BlockchainInfoUpdated - DateTimeOffset.UtcNow) > TimeSpan.FromMinutes(5)) { syncInfo = await GetSyncInfoAsync(); } if (syncInfo.BlockCount - heightToRequest <= 100) { // Both Wasabi and our Core node is in sync. Start doing stuff through P2P from now on. if (syncInfo.IsCoreSynchornized && syncInfo.BlockCount == heightToRequest - 1) { syncInfo = await GetSyncInfoAsync(); // Double it to make sure not to accidentally miss any notification. if (syncInfo.IsCoreSynchornized && syncInfo.BlockCount == heightToRequest - 1) { // Mark the process notstarted, so it can be started again and finally block can mark it is stopped. Interlocked.Exchange(ref _running, 0); return; } } // Mark the synchronizing process is working with immature blocks from now on. isImmature = true; } Block block = await RpcClient.GetBlockAsync(heightToRequest); // Reorg check, except if we're requesting the starting height, because then the "currentHash" wouldn't exist. if (heightToRequest != StartingHeight && currentHash != block.Header.HashPrevBlock) { // Reorg can happen only when immature. (If it'd not be immature, that'd be a huge issue.) if (isImmature) { await ReorgOneAsync(); } else { Logger.LogCritical <IndexBuilderService>("This is something serious! Over 100 block reorg is noticed! We cannot handle that!"); } // Skip the current block. continue; } if (isImmature) { PrepareBech32UtxoSetHistory(); } 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 (isImmature) { 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 (isImmature) { 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 = heightToRequest, Filter = filter }; await File.AppendAllLinesAsync(IndexFilePath, new[] { filterModel.ToHeightlessLine() }); 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 (syncInfo.BlockCount - heightToRequest.Value <= 3 || heightToRequest % 100 == 0) { Logger.LogInfo <IndexBuilderService>($"Created filter for block: {heightToRequest}."); } else { Logger.LogDebug <IndexBuilderService>($"Created filter for block: {heightToRequest}."); } } catch (Exception ex) { Logger.LogDebug <IndexBuilderService>(ex); } } } finally { Interlocked.CompareExchange(ref _running, 3, 2); // If IsStopping, make it stopped. Interlocked.Decrement(ref _runner); } } catch (Exception ex) { Logger.LogError <IndexBuilderService>($"Synchronization attempt failed to start: {ex}"); } }); }
public ScriptCoin(OutPoint fromOutpoint, TxOut fromTxOut, Script redeem) : base(fromOutpoint, fromTxOut) { Redeem = redeem; AssertCoherent(); }
public IssuanceCoin(OutPoint outpoint, TxOut txout) { Bearer = new Coin(outpoint, txout); }
public Coin(uint256 fromTxHash, uint fromOutputIndex, Money amount, Script scriptPubKey) { Outpoint = new OutPoint(fromTxHash, fromOutputIndex); TxOut = new TxOut(amount, scriptPubKey); }
public Coin(IndexedTxOut txOut) { Outpoint = new OutPoint(txOut.Transaction.GetHash(), txOut.N); TxOut = txOut.TxOut; }
public Coin(OutPoint fromOutpoint, TxOut fromTxOut) { Outpoint = fromOutpoint; TxOut = fromTxOut; }
public async Task <BroadcastResponse> Send(string to, Money amount, Money minerfee, string message = "") { QBitNinjaClient client = new QBitNinjaClient(Network.TestNet); var xxx = client.GetBalance(privateKey.ScriptPubKey, true).Result; OutPoint outPointToSpend = null; var trans = xxx.Operations.First().ReceivedCoins; foreach (var coin in trans) { if (coin.TxOut.ScriptPubKey == privateKey.ScriptPubKey) { outPointToSpend = coin.Outpoint; } } Money txInAmount = null; if (trans.Count == 1) { txInAmount = (Money)trans.First().Amount; } else { txInAmount = (Money)trans[(int)outPointToSpend.N].Amount; } var changeAmount = txInAmount - amount - minerfee; var hallOfTheMakersAddress = BitcoinAddress.Create(to, network); TxOut hallOfTheMakersTxOut = new TxOut() { Value = amount, ScriptPubKey = hallOfTheMakersAddress.ScriptPubKey }; TxOut changeTxOut = new TxOut() { Value = changeAmount, ScriptPubKey = privateKey.ScriptPubKey }; var transaction = new Transaction(); transaction.Inputs.Add(new TxIn() { PrevOut = outPointToSpend }); transaction.Outputs.Add(hallOfTheMakersTxOut); transaction.Outputs.Add(changeTxOut); var bytes = Encoding.UTF8.GetBytes(message); transaction.Outputs.Add(new TxOut() { Value = Money.Zero, ScriptPubKey = TxNullDataTemplate.Instance.GenerateScriptPubKey(bytes) }); transaction.Inputs[0].ScriptSig = secret.ScriptPubKey; transaction.Sign(privateKey, trans.ToArray()); BroadcastResponse broadcastResponse = await client.Broadcast(transaction); return(broadcastResponse); }
public IEnumerable<WalletRule> GetMatchedRules(OutPoint outPoint) { if(outPoint.Hash == TransactionId) return GetMatchedRules((int)outPoint.N, MatchLocation.Output); else { var index = SpentOutpoints.IndexOf(outPoint); if(index == -1) return new WalletRule[0]; return GetMatchedRules((int)SpentIndices[index], MatchLocation.Input); } }
public static string GetId(OutPoint outPoint) { return "o-" + Encoders.Hex.EncodeData(outPoint.ToBytes()); }
public ActionItem(Operation action, OutPoint outPoint, Script script) { Action = action; OutPoint = outPoint; Script = script; }
public UnspentOutput AccessCoins(OutPoint outpoint) { return(this.unspents.TryGet(outpoint)); }
public void StoreAction(Operation action, OutPoint outpoint, Script script) { StoreAction(new ActionItem(action, outpoint, script)); }
public void SaveOffer(Script scriptPubKey, OutPoint outpoint) { _Repo.UpdateOrInsert("1", scriptPubKey.Hash.ToString(), outpoint, (o, n) => n); }
public async Task CanUsePayjoin() { using (var tester = ServerTester.Create()) { await tester.StartAsync(); ////var payJoinStateProvider = tester.PayTester.GetService<PayJoinStateProvider>(); var btcPayNetwork = tester.NetworkProvider.GetNetwork <BTCPayNetwork>("BTC"); var btcPayWallet = tester.PayTester.GetService <BTCPayWalletProvider>().GetWallet(btcPayNetwork); var cashCow = tester.ExplorerNode; cashCow.Generate(2); // get some money in case var senderUser = tester.NewAccount(); senderUser.GrantAccess(true); senderUser.RegisterDerivationScheme("BTC", ScriptPubKeyType.Segwit, true); var invoice = senderUser.BitPay.CreateInvoice( new Invoice() { Price = 100, Currency = "USD", FullNotifications = true }); //payjoin is not enabled by default. Assert.DoesNotContain($"{PayjoinClient.BIP21EndpointKey}", invoice.CryptoInfo.First().PaymentUrls.BIP21); cashCow.SendToAddress(BitcoinAddress.Create(invoice.BitcoinAddress, cashCow.Network), Money.Coins(0.06m)); var receiverUser = tester.NewAccount(); receiverUser.GrantAccess(true); receiverUser.RegisterDerivationScheme("BTC", ScriptPubKeyType.Segwit, true); await receiverUser.EnablePayJoin(); // payjoin is enabled, with a segwit wallet, and the keys are available in nbxplorer invoice = receiverUser.BitPay.CreateInvoice( new Invoice() { Price = 0.02m, Currency = "BTC", FullNotifications = true }); cashCow.SendToAddress(BitcoinAddress.Create(invoice.BitcoinAddress, cashCow.Network), Money.Coins(0.06m)); var receiverWalletId = new WalletId(receiverUser.StoreId, "BTC"); //give the cow some cash await cashCow.GenerateAsync(1); //let's get some more utxos first await receiverUser.ReceiveUTXO(Money.Coins(0.011m), btcPayNetwork); await receiverUser.ReceiveUTXO(Money.Coins(0.012m), btcPayNetwork); await receiverUser.ReceiveUTXO(Money.Coins(0.013m), btcPayNetwork); await receiverUser.ReceiveUTXO(Money.Coins(0.014m), btcPayNetwork); await senderUser.ReceiveUTXO(Money.Coins(0.021m), btcPayNetwork); await senderUser.ReceiveUTXO(Money.Coins(0.022m), btcPayNetwork); await senderUser.ReceiveUTXO(Money.Coins(0.023m), btcPayNetwork); await senderUser.ReceiveUTXO(Money.Coins(0.024m), btcPayNetwork); await senderUser.ReceiveUTXO(Money.Coins(0.025m), btcPayNetwork); await senderUser.ReceiveUTXO(Money.Coins(0.026m), btcPayNetwork); var senderChange = await senderUser.GetNewAddress(btcPayNetwork); //Let's start the harassment invoice = receiverUser.BitPay.CreateInvoice( new Invoice() { Price = 0.02m, Currency = "BTC", FullNotifications = true }); var parsedBip21 = new BitcoinUrlBuilder(invoice.CryptoInfo.First().PaymentUrls.BIP21, tester.ExplorerClient.Network.NBitcoinNetwork); var invoice2 = receiverUser.BitPay.CreateInvoice( new Invoice() { Price = 0.02m, Currency = "BTC", FullNotifications = true }); var secondInvoiceParsedBip21 = new BitcoinUrlBuilder(invoice2.CryptoInfo.First().PaymentUrls.BIP21, tester.ExplorerClient.Network.NBitcoinNetwork); var senderStore = await tester.PayTester.StoreRepository.FindStore(senderUser.StoreId); var paymentMethodId = new PaymentMethodId("BTC", PaymentTypes.BTCLike); var derivationSchemeSettings = senderStore.GetSupportedPaymentMethods(tester.NetworkProvider) .OfType <DerivationSchemeSettings>().SingleOrDefault(settings => settings.PaymentId == paymentMethodId); ReceivedCoin[] senderCoins = null; await TestUtils.EventuallyAsync(async() => { senderCoins = await btcPayWallet.GetUnspentCoins(senderUser.DerivationScheme); Assert.Contains(senderCoins, coin => coin.Value.GetValue(btcPayNetwork) == 0.026m); }); var coin = senderCoins.Single(coin => coin.Value.GetValue(btcPayNetwork) == 0.021m); var coin2 = senderCoins.Single(coin => coin.Value.GetValue(btcPayNetwork) == 0.022m); var coin3 = senderCoins.Single(coin => coin.Value.GetValue(btcPayNetwork) == 0.023m); var coin4 = senderCoins.Single(coin => coin.Value.GetValue(btcPayNetwork) == 0.024m); var coin5 = senderCoins.Single(coin => coin.Value.GetValue(btcPayNetwork) == 0.025m); var coin6 = senderCoins.Single(coin => coin.Value.GetValue(btcPayNetwork) == 0.026m); var signingKeySettings = derivationSchemeSettings.GetSigningAccountKeySettings(); signingKeySettings.RootFingerprint = senderUser.GenerateWalletResponseV.MasterHDKey.GetPublicKey().GetHDFingerPrint(); var extKey = senderUser.GenerateWalletResponseV.MasterHDKey.Derive(signingKeySettings.GetRootedKeyPath() .KeyPath); var n = tester.ExplorerClient.Network.NBitcoinNetwork; var Invoice1Coin1 = tester.ExplorerClient.Network.NBitcoinNetwork.CreateTransactionBuilder() .SetChange(senderChange) .Send(parsedBip21.Address, parsedBip21.Amount) .AddCoins(coin.Coin) .AddKeys(extKey.Derive(coin.KeyPath)) .SendEstimatedFees(new FeeRate(100m)) .BuildTransaction(true); var Invoice1Coin2 = tester.ExplorerClient.Network.NBitcoinNetwork.CreateTransactionBuilder() .SetChange(senderChange) .Send(parsedBip21.Address, parsedBip21.Amount) .AddCoins(coin2.Coin) .AddKeys(extKey.Derive(coin2.KeyPath)) .SendEstimatedFees(new FeeRate(100m)) .BuildTransaction(true); var Invoice2Coin1 = tester.ExplorerClient.Network.NBitcoinNetwork.CreateTransactionBuilder() .SetChange(senderChange) .Send(secondInvoiceParsedBip21.Address, secondInvoiceParsedBip21.Amount) .AddCoins(coin.Coin) .AddKeys(extKey.Derive(coin.KeyPath)) .SendEstimatedFees(new FeeRate(100m)) .BuildTransaction(true); var Invoice2Coin2 = tester.ExplorerClient.Network.NBitcoinNetwork.CreateTransactionBuilder() .SetChange(senderChange) .Send(secondInvoiceParsedBip21.Address, secondInvoiceParsedBip21.Amount) .AddCoins(coin2.Coin) .AddKeys(extKey.Derive(coin2.KeyPath)) .SendEstimatedFees(new FeeRate(100m)) .BuildTransaction(true); //Attempt 1: Send a signed tx to invoice 1 that does not pay the invoice at all //Result: reject // Assert.False((await tester.PayTester.HttpClient.PostAsync(endpoint, // new StringContent(Invoice2Coin1.ToHex(), Encoding.UTF8, "text/plain"))).IsSuccessStatusCode); //Attempt 2: Create two transactions using different inputs and send them to the same invoice. //Result: Second Tx should be rejected. var Invoice1Coin1ResponseTx = await senderUser.SubmitPayjoin(invoice, Invoice1Coin1, btcPayNetwork); await senderUser.SubmitPayjoin(invoice, Invoice1Coin1, btcPayNetwork, "already-paid"); var contributedInputsInvoice1Coin1ResponseTx = Invoice1Coin1ResponseTx.Inputs.Where(txin => coin.OutPoint != txin.PrevOut); Assert.Single(contributedInputsInvoice1Coin1ResponseTx); //Attempt 3: Send the same inputs from invoice 1 to invoice 2 while invoice 1 tx has not been broadcasted //Result: Reject Tx1 but accept tx 2 as its inputs were never accepted by invoice 1 await senderUser.SubmitPayjoin(invoice2, Invoice2Coin1, btcPayNetwork, "inputs-already-used"); var Invoice2Coin2ResponseTx = await senderUser.SubmitPayjoin(invoice2, Invoice2Coin2, btcPayNetwork); var contributedInputsInvoice2Coin2ResponseTx = Invoice2Coin2ResponseTx.Inputs.Where(txin => coin2.OutPoint != txin.PrevOut); Assert.Single(contributedInputsInvoice2Coin2ResponseTx); //Attempt 4: Make tx that pays invoice 3 and 4 and submit to both //Result: reject on 4: the protocol should not worry about this complexity var invoice3 = receiverUser.BitPay.CreateInvoice( new Invoice() { Price = 0.01m, Currency = "BTC", FullNotifications = true }); var invoice3ParsedBip21 = new BitcoinUrlBuilder(invoice3.CryptoInfo.First().PaymentUrls.BIP21, tester.ExplorerClient.Network.NBitcoinNetwork); var invoice4 = receiverUser.BitPay.CreateInvoice( new Invoice() { Price = 0.01m, Currency = "BTC", FullNotifications = true }); var invoice4ParsedBip21 = new BitcoinUrlBuilder(invoice4.CryptoInfo.First().PaymentUrls.BIP21, tester.ExplorerClient.Network.NBitcoinNetwork); var Invoice3AndInvoice4Coin3 = tester.ExplorerClient.Network.NBitcoinNetwork.CreateTransactionBuilder() .SetChange(senderChange) .Send(invoice3ParsedBip21.Address, invoice3ParsedBip21.Amount) .Send(invoice4ParsedBip21.Address, invoice4ParsedBip21.Amount) .AddCoins(coin3.Coin) .AddKeys(extKey.Derive(coin3.KeyPath)) .SendEstimatedFees(new FeeRate(100m)) .BuildTransaction(true); await senderUser.SubmitPayjoin(invoice3, Invoice3AndInvoice4Coin3, btcPayNetwork); await senderUser.SubmitPayjoin(invoice4, Invoice3AndInvoice4Coin3, btcPayNetwork, "already-paid"); //Attempt 5: Make tx that pays invoice 5 with 2 outputs //Result: proposed tx consolidates the outputs var invoice5 = receiverUser.BitPay.CreateInvoice( new Invoice() { Price = 0.01m, Currency = "BTC", FullNotifications = true }); var invoice5ParsedBip21 = new BitcoinUrlBuilder(invoice5.CryptoInfo.First().PaymentUrls.BIP21, tester.ExplorerClient.Network.NBitcoinNetwork); var Invoice5Coin4TxBuilder = tester.ExplorerClient.Network.NBitcoinNetwork.CreateTransactionBuilder() .SetChange(senderChange) .Send(invoice5ParsedBip21.Address, invoice5ParsedBip21.Amount / 2) .Send(invoice5ParsedBip21.Address, invoice5ParsedBip21.Amount / 2) .AddCoins(coin4.Coin) .AddKeys(extKey.Derive(coin4.KeyPath)) .SendEstimatedFees(new FeeRate(100m)); var Invoice5Coin4 = Invoice5Coin4TxBuilder.BuildTransaction(true); var Invoice5Coin4ResponseTx = await senderUser.SubmitPayjoin(invoice5, Invoice5Coin4, btcPayNetwork); Assert.Single(Invoice5Coin4ResponseTx.Outputs.To(invoice5ParsedBip21.Address)); //Attempt 10: send tx with rbf, broadcast payjoin tx, bump the rbf payjoin , attempt to submit tx again //Result: same tx gets sent back //give the receiver some more utxos Assert.NotNull(await tester.ExplorerNode.SendToAddressAsync( (await btcPayWallet.ReserveAddressAsync(receiverUser.DerivationScheme)).Address, new Money(0.1m, MoneyUnit.BTC))); var invoice6 = receiverUser.BitPay.CreateInvoice( new Invoice() { Price = 0.01m, Currency = "BTC", FullNotifications = true }); var invoice6ParsedBip21 = new BitcoinUrlBuilder(invoice6.CryptoInfo.First().PaymentUrls.BIP21, tester.ExplorerClient.Network.NBitcoinNetwork); var invoice6Coin5TxBuilder = tester.ExplorerClient.Network.NBitcoinNetwork.CreateTransactionBuilder() .SetChange(senderChange) .Send(invoice6ParsedBip21.Address, invoice6ParsedBip21.Amount) .AddCoins(coin5.Coin) .AddKeys(extKey.Derive(coin5.KeyPath)) .SendEstimatedFees(new FeeRate(100m)) .SetLockTime(0); var invoice6Coin5 = invoice6Coin5TxBuilder .BuildTransaction(true); var Invoice6Coin5Response1Tx = await senderUser.SubmitPayjoin(invoice6, invoice6Coin5, btcPayNetwork); var Invoice6Coin5Response1TxSigned = invoice6Coin5TxBuilder.SignTransaction(Invoice6Coin5Response1Tx); //broadcast the first payjoin await tester.ExplorerClient.BroadcastAsync(Invoice6Coin5Response1TxSigned); // invoice6Coin5TxBuilder = invoice6Coin5TxBuilder.SendEstimatedFees(new FeeRate(100m)); // var invoice6Coin5Bumpedfee = invoice6Coin5TxBuilder // .BuildTransaction(true); // // var Invoice6Coin5Response3 = await tester.PayTester.HttpClient.PostAsync(invoice6Endpoint, // new StringContent(invoice6Coin5Bumpedfee.ToHex(), Encoding.UTF8, "text/plain")); // Assert.True(Invoice6Coin5Response3.IsSuccessStatusCode); // var Invoice6Coin5Response3Tx = // Transaction.Parse(await Invoice6Coin5Response3.Content.ReadAsStringAsync(), n); // Assert.True(invoice6Coin5Bumpedfee.Inputs.All(txin => // Invoice6Coin5Response3Tx.Inputs.Any(txin2 => txin2.PrevOut == txin.PrevOut))); //Attempt 11: //send tx with rbt, broadcast payjoin, //create tx spending the original tx inputs with rbf to self, //Result: the exposed utxos are priorized in the next p2ep //give the receiver some more utxos Assert.NotNull(await tester.ExplorerNode.SendToAddressAsync( (await btcPayWallet.ReserveAddressAsync(receiverUser.DerivationScheme)).Address, new Money(0.1m, MoneyUnit.BTC))); var invoice7 = receiverUser.BitPay.CreateInvoice( new Invoice() { Price = 0.01m, Currency = "BTC", FullNotifications = true }); var invoice7ParsedBip21 = new BitcoinUrlBuilder(invoice7.CryptoInfo.First().PaymentUrls.BIP21, tester.ExplorerClient.Network.NBitcoinNetwork); var invoice7Coin6TxBuilder = tester.ExplorerClient.Network.NBitcoinNetwork.CreateTransactionBuilder() .SetChange(senderChange) .Send(invoice7ParsedBip21.Address, invoice7ParsedBip21.Amount) .AddCoins(coin6.Coin) .AddKeys(extKey.Derive(coin6.KeyPath)) .SendEstimatedFees(new FeeRate(100m)) .SetLockTime(0); var invoice7Coin6Tx = invoice7Coin6TxBuilder .BuildTransaction(true); var invoice7Coin6Response1Tx = await senderUser.SubmitPayjoin(invoice7, invoice7Coin6Tx, btcPayNetwork); var Invoice7Coin6Response1TxSigned = invoice7Coin6TxBuilder.SignTransaction(invoice7Coin6Response1Tx); var contributedInputsInvoice7Coin6Response1TxSigned = Invoice7Coin6Response1TxSigned.Inputs.Single(txin => coin6.OutPoint != txin.PrevOut); ////var receiverWalletPayJoinState = payJoinStateProvider.Get(receiverWalletId); ////Assert.Contains(receiverWalletPayJoinState.GetRecords(), item => item.InvoiceId == invoice7.Id); //broadcast the payjoin var res = (await tester.ExplorerClient.BroadcastAsync(Invoice7Coin6Response1TxSigned)); Assert.True(res.Success); // Paid with coinjoin await TestUtils.EventuallyAsync(async() => { var invoiceEntity = await tester.PayTester.GetService <InvoiceRepository>().GetInvoice(invoice7.Id); Assert.Equal(InvoiceStatus.Paid, invoiceEntity.Status); Assert.Contains(invoiceEntity.GetPayments(), p => p.Accounted && ((BitcoinLikePaymentData)p.GetCryptoPaymentData()).PayjoinInformation is null); }); ////Assert.Contains(receiverWalletPayJoinState.GetRecords(), item => item.InvoiceId == invoice7.Id && item.TxSeen); var invoice7Coin6Tx2 = tester.ExplorerClient.Network.NBitcoinNetwork.CreateTransactionBuilder() .SetChange(senderChange) .AddCoins(coin6.Coin) .SendAll(senderChange) .SubtractFees() .AddKeys(extKey.Derive(coin6.KeyPath)) .SendEstimatedFees(new FeeRate(200m)) .SetLockTime(0) .BuildTransaction(true); //broadcast the "rbf cancel" tx res = (await tester.ExplorerClient.BroadcastAsync(invoice7Coin6Tx2)); Assert.True(res.Success); // Make a block, this should put back the invoice to new var blockhash = tester.ExplorerNode.Generate(1)[0]; Assert.NotNull(await tester.ExplorerNode.GetRawTransactionAsync(invoice7Coin6Tx2.GetHash(), blockhash)); Assert.Null(await tester.ExplorerNode.GetRawTransactionAsync(Invoice7Coin6Response1TxSigned.GetHash(), blockhash, false)); // Now we should return to New OutPoint ourOutpoint = null; await TestUtils.EventuallyAsync(async() => { var invoiceEntity = await tester.PayTester.GetService <InvoiceRepository>().GetInvoice(invoice7.Id); Assert.Equal(InvoiceStatus.New, invoiceEntity.Status); Assert.True(invoiceEntity.GetPayments().All(p => !p.Accounted)); ourOutpoint = invoiceEntity.GetAllBitcoinPaymentData().First().PayjoinInformation.ContributedOutPoints[0]; }); var payjoinRepository = tester.PayTester.GetService <PayJoinRepository>(); // The outpoint should now be available for next pj selection Assert.False(await payjoinRepository.TryUnlock(ourOutpoint)); } }
public async Task BanningTestsAsync() { (_, IRPCClient rpc, Network network, Coordinator coordinator, _, _, _) = await Common.InitializeTestEnvironmentAsync(RegTestFixture, 1); Money denomination = Money.Coins(0.1m); decimal coordinatorFeePercent = 0.1m; int anonymitySet = 3; int connectionConfirmationTimeout = 120; var roundConfig = RegTestFixture.CreateRoundConfig(denomination, 140, 0.7, coordinatorFeePercent, anonymitySet, 240, connectionConfirmationTimeout, 1, 1, 1, 24, true, 11); coordinator.RoundConfig.UpdateOrDefault(roundConfig, toFile: true); coordinator.AbortAllRoundsInInputRegistration(""); await rpc.GenerateAsync(3); // So to make sure we have enough money. Uri baseUri = new(RegTestFixture.BackendEndPoint); var fundingTxCount = 0; List <(Requester requester, BlindedOutputWithNonceIndex blinded, BitcoinAddress activeOutputAddress, BitcoinAddress changeOutputAddress, IEnumerable <InputProofModel> inputProofModels, List <(Key key, BitcoinAddress address, uint256 txHash, Transaction tx, OutPoint input)> userInputData)> inputRegistrationUsers = new(); CoordinatorRound?round = null; for (int i = 0; i < roundConfig.AnonymitySet; i++) { List <(Key key, BitcoinAddress inputAddress, uint256 txHash, Transaction tx, OutPoint input)> userInputData = new(); var activeOutputAddress = new Key().GetAddress(ScriptPubKeyType.Segwit, network); var changeOutputAddress = new Key().GetAddress(ScriptPubKeyType.Segwit, network); Assert.True(coordinator.TryGetCurrentInputRegisterableRound(out round)); Requester requester = new(); var nonce = round !.NonceProvider.GetNextNonce(); BlindedOutputWithNonceIndex blinded = new(nonce.N, requester.BlindScript(round.MixingLevels.GetBaseLevel().SignerKey.PubKey, nonce.R, activeOutputAddress.ScriptPubKey)); uint256 blindedOutputScriptsHash = new(Hashes.SHA256(blinded.BlindedOutput.ToBytes())); List <InputProofModel> inputProofModels = new(); int numberOfInputs = CryptoHelpers.RandomInt(1, 6); var receiveSatoshiSum = 0; for (int j = 0; j < numberOfInputs; j++) { Key key = new(); var receiveSatoshi = CryptoHelpers.RandomInt(1000, 100000000); receiveSatoshiSum += receiveSatoshi; if (j == numberOfInputs - 1) { receiveSatoshi = 100000000; } var inputAddress = key.PubKey.GetAddress(ScriptPubKeyType.Segwit, network); uint256 txHash = await rpc.SendToAddressAsync(inputAddress, Money.Satoshis(receiveSatoshi)); fundingTxCount++; Assert.NotNull(txHash); Transaction transaction = await rpc.GetRawTransactionAsync(txHash); var coin = transaction.Outputs.GetCoins(inputAddress.ScriptPubKey).Single(); OutPoint input = coin.Outpoint; var inputProof = new InputProofModel { Input = input, Proof = key.SignCompact(blindedOutputScriptsHash) }; inputProofModels.Add(inputProof); GetTxOutResponse?getTxOutResponse = await rpc.GetTxOutAsync(input.Hash, (int)input.N, includeMempool : true); // Check if inputs are unspent. Assert.NotNull(getTxOutResponse); userInputData.Add((key, inputAddress, txHash, transaction, input)); } inputRegistrationUsers.Add((requester, blinded, activeOutputAddress, changeOutputAddress, inputProofModels, userInputData)); } var mempool = await rpc.GetRawMempoolAsync(); Assert.Equal(inputRegistrationUsers.SelectMany(x => x.userInputData).Count(), mempool.Length); while ((await rpc.GetRawMempoolAsync()).Length != 0) { await rpc.GenerateAsync(1); } List <Task <AliceClient4> > aliceClients = new(); foreach (var user in inputRegistrationUsers) { aliceClients.Add(AliceClientBase.CreateNewAsync(round.RoundId, new[] { user.activeOutputAddress }, new[] { round.MixingLevels.GetBaseLevel().SignerKey.PubKey }, new[] { user.requester }, network, user.changeOutputAddress, new[] { user.blinded }, user.inputProofModels, BackendHttpClient)); } long roundId = 0; List <(Requester requester, BlindedOutputWithNonceIndex blinded, BitcoinAddress activeOutputAddress, BitcoinAddress changeOutputAddress, IEnumerable <InputProofModel> inputProofModels, List <(Key key, BitcoinAddress address, uint256 txHash, Transaction tx, OutPoint input)> userInputData, AliceClient4 aliceClient, UnblindedSignature unblindedSignature)> users = new(); for (int i = 0; i < inputRegistrationUsers.Count; i++) { var user = inputRegistrationUsers[i]; var request = aliceClients[i]; var aliceClient = await request; if (roundId == 0) { roundId = aliceClient.RoundId; } else { Assert.Equal(roundId, aliceClient.RoundId); } // Because it's a value tuple. users.Add((user.requester, user.blinded, user.activeOutputAddress, user.changeOutputAddress, user.inputProofModels, user.userInputData, aliceClient, null)); } Assert.Equal(users.Count, roundConfig.AnonymitySet); List <Task <(RoundPhase currentPhase, IEnumerable <ActiveOutput>)> > confirmationRequests = new(); foreach (var user in users) { confirmationRequests.Add(user.aliceClient.PostConfirmationAsync()); } RoundPhase roundPhase = RoundPhase.InputRegistration; int k = 0; foreach (var request in confirmationRequests) { var resp = await request; if (roundPhase == RoundPhase.InputRegistration) { roundPhase = resp.currentPhase; } else { Assert.Equal(roundPhase, resp.currentPhase); } var user = users.ElementAt(k); user.unblindedSignature = resp.Item2.First().Signature; } { var times = 0; while (!(await SatoshiClient.GetAllRoundStatesAsync()).All(x => x.Phase == RoundPhase.InputRegistration)) { await Task.Delay(100); if (times > 50) // 5 sec, 3 should be enough { throw new TimeoutException("Not all rounds were in InputRegistration."); } times++; } } int bannedCount = coordinator.UtxoReferee.CountBanned(false); Assert.Equal(0, bannedCount); aliceClients.Clear(); Assert.True(coordinator.TryGetCurrentInputRegisterableRound(out round)); foreach (var user in inputRegistrationUsers) { aliceClients.Add(AliceClientBase.CreateNewAsync(round !.RoundId, new[] { user.activeOutputAddress }, new[] { round.MixingLevels.GetBaseLevel().SignerKey.PubKey }, new[] { user.requester }, network, user.changeOutputAddress, new[] { user.blinded }, user.inputProofModels, BackendHttpClient)); } roundId = 0; users = new List <(Requester requester, BlindedOutputWithNonceIndex blinded, BitcoinAddress activeOutputAddress, BitcoinAddress changeOutputAddress, IEnumerable <InputProofModel> inputProofModels, List <(Key key, BitcoinAddress address, uint256 txHash, Transaction tx, OutPoint input)> userInputData, AliceClient4 aliceClient, UnblindedSignature unblindedSignature)>(); for (int i = 0; i < inputRegistrationUsers.Count; i++) { var user = inputRegistrationUsers[i]; var request = aliceClients[i]; var aliceClient = await request; if (roundId == 0) { roundId = aliceClient.RoundId; } else { Assert.Equal(roundId, aliceClient.RoundId); } // Because it's a value tuple. users.Add((user.requester, user.blinded, user.activeOutputAddress, user.changeOutputAddress, user.inputProofModels, user.userInputData, aliceClient, null)); } Assert.Equal(users.Count, roundConfig.AnonymitySet); confirmationRequests = new List <Task <(RoundPhase currentPhase, IEnumerable <ActiveOutput>)> >(); foreach (var user in users) { confirmationRequests.Add(user.aliceClient.PostConfirmationAsync()); } { var times = 0; while (!(await SatoshiClient.GetAllRoundStatesAsync()).All(x => x.Phase == RoundPhase.InputRegistration)) { await Task.Delay(100); if (times > 50) // 5 sec, 3 should be enough { throw new TimeoutException("Not all rounds were in InputRegistration."); } times++; } } bannedCount = coordinator.UtxoReferee.CountBanned(false); Assert.True(bannedCount >= roundConfig.AnonymitySet); foreach (var aliceClient in aliceClients) { aliceClient?.Dispose(); } }
public SmartCoin GetByOutPoint(OutPoint outpoint) { return(AsCoinsView().GetByOutPoint(outpoint)); }
public void CanSerializeInJson() { Key k = new Key(); CanSerializeInJsonCore(DateTimeOffset.UtcNow); CanSerializeInJsonCore(new byte[] { 1, 2, 3 }); CanSerializeInJsonCore(k); CanSerializeInJsonCore(Money.Coins(5.0m)); CanSerializeInJsonCore(k.PubKey.GetAddress(ScriptPubKeyType.Legacy, Network.Main)); CanSerializeInJsonCore(new KeyPath("1/2")); CanSerializeInJsonCore(RootedKeyPath.Parse("7b09d780/0'/0'/2'")); CanSerializeInJsonCore(Network.Main); CanSerializeInJsonCore(new uint256(RandomUtils.GetBytes(32))); CanSerializeInJsonCore(new uint160(RandomUtils.GetBytes(20))); CanSerializeInJsonCore(new AssetId(k.PubKey)); CanSerializeInJsonCore(k.PubKey.ScriptPubKey); CanSerializeInJsonCore(new Key().PubKey.WitHash.GetAddress(Network.Main)); CanSerializeInJsonCore(new Key().PubKey.WitHash.ScriptPubKey.GetWitScriptAddress(Network.Main)); var sig = k.Sign(new uint256(RandomUtils.GetBytes(32))); CanSerializeInJsonCore(sig); CanSerializeInJsonCore(new TransactionSignature(sig, SigHash.All)); CanSerializeInJsonCore(k.PubKey.Hash); CanSerializeInJsonCore(k.PubKey.ScriptPubKey.Hash); CanSerializeInJsonCore(k.PubKey.WitHash); CanSerializeInJsonCore(k); CanSerializeInJsonCore(k.PubKey); CanSerializeInJsonCore(new WitScript(new Script(Op.GetPushOp(sig.ToDER()), Op.GetPushOp(sig.ToDER())))); CanSerializeInJsonCore(new LockTime(1)); CanSerializeInJsonCore(new LockTime(130), out var str); Assert.Equal("130", str); CanSerializeInJsonCore(new LockTime(DateTime.UtcNow)); CanSerializeInJsonCore(new FeeRate(Money.Satoshis(1), 1000)); CanSerializeInJsonCore(new FeeRate(Money.Satoshis(1000), 1000)); CanSerializeInJsonCore(new FeeRate(0.5m)); CanSerializeInJsonCore(new HDFingerprint(0x0a), out str); Assert.Equal("\"0a000000\"", str); var print = Serializer.ToObject <HDFingerprint>("\"0a000000\""); var print2 = Serializer.ToObject <HDFingerprint>("10"); Assert.Equal(print, print2); var printn = Serializer.ToObject <HDFingerprint?>("\"0a000000\""); var print2n = Serializer.ToObject <HDFingerprint?>("10"); Assert.Equal(printn, print2n); Assert.Null(Serializer.ToObject <HDFingerprint?>("")); var psbt = PSBT.Parse("70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000002202029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01010304010000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e887220203089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f010103040100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000", Network.Main); var psbtJson = Serializer.ToString(psbt, Network.Main); var psbt2 = Serializer.ToObject <PSBT>(psbtJson, Network.Main); Assert.Equal(psbt, psbt2); var expectedOutpoint = OutPoint.Parse("44f69ca74088d6d88e30156da85aae54543a87f67cdfdabbe9b53a92d6d7027c01000000"); var actualOutpoint = Serializer.ToObject <OutPoint>("\"44f69ca74088d6d88e30156da85aae54543a87f67cdfdabbe9b53a92d6d7027c01000000\"", Network.Main); Assert.Equal(expectedOutpoint, actualOutpoint); actualOutpoint = Serializer.ToObject <OutPoint>("\"7c02d7d6923ab5e9bbdadf7cf6873a5454ae5aa86d15308ed8d68840a79cf644-1\"", Network.Main); Assert.Equal(expectedOutpoint, actualOutpoint); CanSerializeInJsonCore(expectedOutpoint, out str); Assert.Equal("\"44f69ca74088d6d88e30156da85aae54543a87f67cdfdabbe9b53a92d6d7027c01000000\"", str); }
public TxoRef(OutPoint outPoint) { Guard.NotNull(nameof(outPoint), outPoint); TransactionId = outPoint.Hash; Index = outPoint.N; }
/// <inheritdoc/> public void CheckKernel(PosRuleContext context, ChainedHeader prevChainedHeader, uint headerBits, long transactionTime, OutPoint prevout) { FetchCoinsResponse coins = this.coinView.FetchCoinsAsync(new[] { prevout.Hash }).GetAwaiter().GetResult(); if ((coins == null) || (coins.UnspentOutputs.Length != 1)) { this.logger.LogTrace("(-)[READ_PREV_TX_FAILED]"); ConsensusErrors.ReadTxPrevFailed.Throw(); } ChainedHeader prevBlock = this.chain.GetBlock(coins.BlockHash); if (prevBlock == null) { this.logger.LogTrace("(-)[REORG]"); ConsensusErrors.ReadTxPrevFailed.Throw(); } UnspentOutputs prevUtxo = coins.UnspentOutputs[0]; if (this.IsConfirmedInNPrevBlocks(prevUtxo, prevChainedHeader, ((PosConsensusOptions)this.network.Consensus.Options).GetStakeMinConfirmations(prevChainedHeader.Height + 1, this.network) - 1)) { this.logger.LogTrace("(-)[LOW_COIN_AGE]"); ConsensusErrors.InvalidStakeDepth.Throw(); } BlockStake prevBlockStake = this.stakeChain.Get(prevChainedHeader.HashBlock); if (prevBlockStake == null) { this.logger.LogTrace("(-)[BAD_STAKE_BLOCK]"); ConsensusErrors.BadStakeBlock.Throw(); } this.CheckStakeKernelHash(context, headerBits, prevBlockStake.StakeModifierV2, prevUtxo, prevout, (uint)transactionTime); }
public TransactionOutPointWithValue(OutPoint outpoint, Money value, Script scriptPubKey) { this.outpoint = outpoint; this.value = value; this.scriptPubKey = scriptPubKey; }
public bool Remove(OutPoint outPoint) { var ret = this.sqliteConnection.ExecuteScalar <int>("DELETE FROM TransactionData WHERE OutPoint = @outPoint", new { outPoint }); return(ret > 0); }
public static Input CreateFromPrefix(OutPoint previousOutPoint, uint sequence) => new Input(previousOutPoint, sequence, 0, 0, 0, new byte[0]);
public void UpdateToScriptCoins() { foreach(var match in MatchedRules) { var scriptRule = match.Rule as ScriptRule; if(scriptRule != null && scriptRule.RedeemScript != null) { if(match.MatchType == MatchLocation.Output) { var outpoint = new OutPoint(TransactionId, match.Index); var coin = ReceivedCoins[outpoint] as Coin; if(coin != null) { ReceivedCoins[outpoint] = coin.ToScriptCoin(scriptRule.RedeemScript); } } else { if(SpentCoins == null) continue; var n = this.SpentIndices.IndexOf(match.Index); var coin = SpentCoins[n] as Coin; if(coin != null) { this.SpentCoins[n] = coin.ToScriptCoin(scriptRule.RedeemScript); } } } } }
private async Task LockUnspentCoreAsync(bool unlock, OutPoint[] outpoints) { if (outpoints == null || outpoints.Length == 0) return; var parameters = new List<object>(); parameters.Add(unlock); var array = new JArray(); parameters.Add(array); foreach (var outp in outpoints) { var obj = new JObject(); obj["txid"] = outp.Hash.ToString(); obj["vout"] = outp.N; array.Add(obj); } await SendCommandAsync("lockunspent", parameters.ToArray()).ConfigureAwait(false); }
internal static Coin FromJsonCoin(JToken obj) { OutPoint outpoint = new OutPoint(); outpoint.FromBytes(Encoders.Hex.DecodeData((string)obj["Outpoint"])); TxOut txout = new TxOut(); txout.FromBytes(Encoders.Hex.DecodeData((string)obj["TxOut"])); return new Coin(outpoint, txout); }
public static Transaction Deserialize(byte[] rawTransaction) { if (rawTransaction == null) throw new ArgumentNullException(nameof(rawTransaction)); int version; Input[] inputs; Output[] outputs; uint lockTime; var cursor = new ByteCursor(rawTransaction); try { version = cursor.ReadInt32(); var inputCount = cursor.ReadCompact(); if (inputCount > TransactionRules.MaxInputs) { var reason = $"Input count {inputCount} exceeds maximum value {TransactionRules.MaxInputs}"; throw new EncodingException(typeof(Transaction), cursor, reason); } inputs = new Input[inputCount]; for (int i = 0; i < (int)inputCount; i++) { var previousHash = new Sha256Hash(cursor.ReadBytes(Sha256Hash.Length)); var previousIndex = cursor.ReadUInt32(); var previousOutPoint = new OutPoint(previousHash, previousIndex); var signatureScript = cursor.ReadVarBytes(TransactionRules.MaxPayload); var sequence = cursor.ReadUInt32(); inputs[i] = new Input(previousOutPoint, signatureScript, sequence); } var outputCount = cursor.ReadCompact(); if (outputCount > TransactionRules.MaxOutputs) { var reason = $"Output count {outputCount} exceeds maximum value {TransactionRules.MaxOutputs}"; throw new EncodingException(typeof(Transaction), cursor, reason); } outputs = new Output[outputCount]; for (int i = 0; i < (int)outputCount; i++) { var amount = (Amount)cursor.ReadInt64(); var pkScript = cursor.ReadVarBytes(TransactionRules.MaxPayload); outputs[i] = new Output(amount, pkScript); } lockTime = cursor.ReadUInt32(); } catch (Exception ex) when (!(ex is EncodingException)) { throw new EncodingException(typeof(Transaction), cursor, ex); } return new Transaction(version, inputs, outputs, lockTime); }
private void LockUnspentCore(bool unlock, OutPoint[] outpoints) { if(outpoints == null || outpoints.Length == 0) return; List<object> parameters = new List<object>(); parameters.Add(unlock); JArray array = new JArray(); parameters.Add(array); foreach(var outp in outpoints) { var obj = new JObject(); obj["txid"] = outp.Hash.ToString(); obj["vout"] = outp.N; array.Add(obj); } SendCommand("lockunspent", parameters.ToArray()); }
/// <summary> /// Checks that UTXO is valid for staking and then checks kernel hash. /// </summary> /// <param name="context">Staking context.</param> /// <param name="prevChainedBlock">Previous chained block.</param> /// <param name="headerBits">Chained block's header bits, which define the difficulty target.</param> /// <param name="transactionTime">Transaction time.</param> /// <param name="prevout">Information about transaction id and index.</param> /// <param name="prevBlockTime">The previous block time.</param> public void CheckKernel(ContextStakeInformation context, ChainedBlock prevChainedBlock, uint headerBits, long transactionTime, OutPoint prevout) { this.logger.LogTrace("({0}:'{1}',{2}:0x{3:X},{4}:{5},{6}:'{7}.{8}')", nameof(prevChainedBlock), prevChainedBlock, nameof(headerBits), headerBits, nameof(transactionTime), transactionTime, nameof(prevout), prevout.Hash, prevout.N); FetchCoinsResponse coins = this.coinView.FetchCoinsAsync(new[] { prevout.Hash }).GetAwaiter().GetResult(); if ((coins == null) || (coins.UnspentOutputs.Length != 1)) { this.logger.LogTrace("(-)[READ_PREV_TX_FAILED]"); ConsensusErrors.ReadTxPrevFailed.Throw(); } ChainedBlock prevBlock = this.chain.GetBlock(coins.BlockHash); if (prevBlock == null) { this.logger.LogTrace("(-)[REORG]"); ConsensusErrors.ReadTxPrevFailed.Throw(); } UnspentOutputs prevUtxo = coins.UnspentOutputs[0]; if (this.IsConfirmedInNPrevBlocks(prevUtxo, prevChainedBlock, this.consensusOptions.StakeMinConfirmations - 1)) { this.logger.LogTrace("(-)[LOW_COIN_AGE]"); ConsensusErrors.InvalidStakeDepth.Throw(); } BlockStake prevBlockStake = this.stakeChain.Get(prevChainedBlock.HashBlock); if (prevBlockStake == null) { this.logger.LogTrace("(-)[BAD_STAKE_BLOCK]"); ConsensusErrors.BadStakeBlock.Throw(); } this.CheckStakeKernelHash(context, prevChainedBlock, headerBits, prevBlock.Header.Time, prevBlockStake, prevUtxo, prevout, (uint)transactionTime); }
public VerboseInputInfo(OutPoint outPoint, VerboseOutputInfo prevOutput) { OutPoint = outPoint; PrevOutput = prevOutput; }
/// <summary> /// Checks that the stake kernel hash satisfies the target difficulty. /// </summary> /// <param name="context">Staking context.</param> /// <param name="prevChainedBlock">Previous chained block.</param> /// <param name="headerBits">Chained block's header bits, which define the difficulty target.</param> /// <param name="prevBlockTime">The previous block time.</param> /// <param name="prevBlockStake">Information about previous staked block.</param> /// <param name="stakingCoins">Coins that participate in staking.</param> /// <param name="prevout">Information about transaction id and index.</param> /// <param name="transactionTime">Transaction time.</param> /// <remarks> /// Coinstake must meet hash target according to the protocol: /// kernel (input 0) must meet the formula /// <c>hash(stakeModifierV2 + stakingCoins.Time + prevout.Hash + prevout.N + transactionTime) < target * weight</c>. /// This ensures that the chance of getting a coinstake is proportional to the amount of coins one owns. /// <para> /// The reason this hash is chosen is the following: /// <list type="number"> /// <item><paramref name="prevBlockStake.StakeModifierV2"/>: Scrambles computation to make it very difficult to precompute future proof-of-stake.</item> /// <item><paramref name="stakingCoins.Time"/>: Time of the coinstake UTXO. Slightly scrambles computation.</item> /// <item><paramref name="prevout.Hash"/> Hash of stakingCoins UTXO, to reduce the chance of nodes generating coinstake at the same time.</item> /// <item><paramref name="prevout.N"/>: Output number of stakingCoins UTXO, to reduce the chance of nodes generating coinstake at the same time.</item> /// <item><paramref name="transactionTime"/>: Timestamp of the coinstake transaction.</item> /// </list> /// Block or transaction tx hash should not be used here as they can be generated in vast /// quantities so as to generate blocks faster, degrading the system back into a proof-of-work situation. /// </para> /// </remarks> /// <exception cref="ConsensusErrors.StakeTimeViolation">Thrown in case transaction time is lower than it's own UTXO timestamp.</exception> /// <exception cref="ConsensusErrors.StakeHashInvalidTarget">Thrown in case PoS hash doesn't meet target protocol.</exception> private void CheckStakeKernelHash(ContextStakeInformation context, ChainedBlock prevChainedBlock, uint headerBits, uint prevBlockTime, BlockStake prevBlockStake, UnspentOutputs stakingCoins, OutPoint prevout, uint transactionTime) { this.logger.LogTrace("({0}:'{1}/{2}',{3}:{4:X},{5}:{6},{7}.{8}:'{9}',{10}:'{11}/{12}',{13}:'{14}',{15}:{16})", nameof(prevChainedBlock), prevChainedBlock.HashBlock, prevChainedBlock.Height, nameof(headerBits), headerBits, nameof(prevBlockTime), prevBlockTime, nameof(prevBlockStake), nameof(prevBlockStake.HashProof), prevBlockStake.HashProof, nameof(stakingCoins), stakingCoins.TransactionId, stakingCoins.Height, nameof(prevout), prevout, nameof(transactionTime), transactionTime); if (transactionTime < stakingCoins.Time) { this.logger.LogTrace("Coinstake transaction timestamp {0} is lower than it's own UTXO timestamp {1}.", transactionTime, stakingCoins.Time); this.logger.LogTrace("(-)[BAD_STAKE_TIME]"); ConsensusErrors.StakeTimeViolation.Throw(); } // Base target. BigInteger target = new Target(headerBits).ToBigInteger(); // TODO: Investigate: // The POS protocol should probably put a limit on the max amount that can be staked // not a hard limit but a limit that allow any amount to be staked with a max weight value. // the max weight should not exceed the max uint256 array size (array size = 32). // Weighted target. long valueIn = stakingCoins.Outputs[prevout.N].Value.Satoshi; BigInteger weight = BigInteger.ValueOf(valueIn); BigInteger weightedTarget = target.Multiply(weight); context.TargetProofOfStake = ToUInt256(weightedTarget); this.logger.LogTrace("POS target is '{0}', weighted target for {1} coins is '{2}'.", ToUInt256(target), valueIn, context.TargetProofOfStake); uint256 stakeModifierV2 = prevBlockStake.StakeModifierV2; // Calculate hash. using (var ms = new MemoryStream()) { var serializer = new BitcoinStream(ms, true); serializer.ReadWrite(stakeModifierV2); serializer.ReadWrite(stakingCoins.Time); serializer.ReadWrite(prevout.Hash); serializer.ReadWrite(prevout.N); serializer.ReadWrite(transactionTime); context.HashProofOfStake = Hashes.Hash256(ms.ToArray()); } this.logger.LogTrace("Stake modifier V2 is '{0}', hash POS is '{1}'.", stakeModifierV2, context.HashProofOfStake); // Now check if proof-of-stake hash meets target protocol. BigInteger hashProofOfStakeTarget = new BigInteger(1, context.HashProofOfStake.ToBytes(false)); if (hashProofOfStakeTarget.CompareTo(weightedTarget) > 0) { this.logger.LogTrace("(-)[TARGET_MISSED]"); ConsensusErrors.StakeHashInvalidTarget.Throw(); } this.logger.LogTrace("(-)[OK]"); }
private void LockUnspentCore(bool unlock, OutPoint[] outpoints) { try { LockUnspentCoreAsync(unlock, outpoints).Wait(); } catch (AggregateException ex) { ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); } }
public CycleProgressInfo Update() { int height = Services.BlockExplorerService.GetCurrentHeight(); CycleParameters cycle; CyclePhase phase; CyclePeriod period; bool isSafetyPeriod = false; if (ClientChannelNegotiation == null) { cycle = Parameters.CycleGenerator.GetRegisteringCycle(height); phase = CyclePhase.Registration; period = cycle.GetPeriods().GetPeriod(phase); } else { cycle = ClientChannelNegotiation.GetCycle(); var phases = new CyclePhase[] { CyclePhase.Registration, CyclePhase.ClientChannelEstablishment, CyclePhase.TumblerChannelEstablishment, CyclePhase.PaymentPhase, CyclePhase.TumblerCashoutPhase, CyclePhase.ClientCashoutPhase }; if (cycle.IsComplete(height)) { return(null); } //If we are not in any phase we are in the SaftyPeriod. if (!phases.Any(p => cycle.IsInPhase(p, height))) { //Find last CyclePhase phase = CyclePhase.Registration; for (int i = height - 1; i >= cycle.Start; i--) { if (phases.Any(p => cycle.IsInPhase(p, i))) { phase = phases.First(p => cycle.IsInPhase(p, i)); break; } } period = cycle.GetPeriods().GetPeriod(phase); period.End += cycle.SafetyPeriodDuration; isSafetyPeriod = true; } else { phase = phases.First(p => cycle.IsInPhase(p, height)); period = cycle.GetPeriods().GetPeriod(phase); isSafetyPeriod = false; } } var blocksLeft = period.End - height; Logs.Client.LogInformation($"Cycle {cycle.Start} ({Status})"); Logs.Client.LogInformation($"{cycle.ToString(height)} in phase {phase} ({blocksLeft} more blocks)"); var previousState = Status; var progressInfo = new CycleProgressInfo(period, height, blocksLeft, cycle.Start, Status, phase, isSafetyPeriod, $"{cycle.ToString(height)} in phase {phase} ({blocksLeft} more blocks)"); TumblerClient bob = null, alice = null; try { var correlation = SolverClientSession == null ? CorrelationId.Zero : new CorrelationId(SolverClientSession.Id); FeeRate feeRate = null; switch (phase) { case CyclePhase.Registration: if (Status == PaymentStateMachineStatus.New) { bob = Runtime.CreateTumblerClient(cycle.Start, Identity.Bob); //Client asks for voucher var voucherResponse = bob.AskUnsignedVoucher(); NeedSave = true; //Client ensures he is in the same cycle as the tumbler (would fail if one tumbler or client's chain isn't sync) var tumblerCycle = Parameters.CycleGenerator.GetCycle(voucherResponse.CycleStart); Assert(tumblerCycle.Start == cycle.Start, "invalid-phase"); //Saving the voucher for later StartCycle = cycle.Start; ClientChannelNegotiation = new ClientChannelNegotiation(Parameters, cycle.Start); ClientChannelNegotiation.ReceiveUnsignedVoucher(voucherResponse); Status = PaymentStateMachineStatus.Registered; } break; case CyclePhase.ClientChannelEstablishment: if (Status == PaymentStateMachineStatus.Registered) { alice = Runtime.CreateTumblerClient(cycle.Start, Identity.Alice); var key = alice.RequestTumblerEscrowKey(); ClientChannelNegotiation.ReceiveTumblerEscrowKey(key.PubKey, key.KeyIndex); //Client create the escrow var escrowTxOut = ClientChannelNegotiation.BuildClientEscrowTxOut(); feeRate = GetFeeRate(); Transaction clientEscrowTx = null; try { clientEscrowTx = Services.WalletService.FundTransactionAsync(escrowTxOut, feeRate).GetAwaiter().GetResult(); } catch (NotEnoughFundsException ex) { Logs.Client.LogInformation($"Not enough funds in the wallet to tumble. Missing about {ex.Missing}. Denomination is {Parameters.Denomination}."); break; } NeedSave = true; var redeemDestination = Services.WalletService.GenerateAddressAsync().GetAwaiter().GetResult().ScriptPubKey; var channelId = new uint160(RandomUtils.GetBytes(20)); SolverClientSession = ClientChannelNegotiation.SetClientSignedTransaction(channelId, clientEscrowTx, redeemDestination); correlation = new CorrelationId(SolverClientSession.Id); Tracker.AddressCreated(cycle.Start, TransactionType.ClientEscrow, escrowTxOut.ScriptPubKey, correlation); Tracker.TransactionCreated(cycle.Start, TransactionType.ClientEscrow, clientEscrowTx.GetHash(), correlation); Services.BlockExplorerService.TrackAsync(escrowTxOut.ScriptPubKey).GetAwaiter().GetResult(); var redeemTx = SolverClientSession.CreateRedeemTransaction(feeRate); Tracker.AddressCreated(cycle.Start, TransactionType.ClientRedeem, redeemDestination, correlation); //redeemTx does not be to be recorded to the tracker, this is TrustedBroadcastService job Services.BroadcastService.BroadcastAsync(clientEscrowTx).GetAwaiter().GetResult(); Services.TrustedBroadcastService.Broadcast(cycle.Start, TransactionType.ClientRedeem, correlation, redeemTx); Status = PaymentStateMachineStatus.ClientChannelBroadcasted; } else if (Status == PaymentStateMachineStatus.ClientChannelBroadcasted) { alice = Runtime.CreateTumblerClient(cycle.Start, Identity.Alice); TransactionInformation clientTx = GetTransactionInformation(SolverClientSession.EscrowedCoin, true); var state = ClientChannelNegotiation.GetInternalState(); if (clientTx != null && clientTx.Confirmations >= cycle.SafetyPeriodDuration) { Logs.Client.LogInformation($"Client escrow reached {cycle.SafetyPeriodDuration} confirmations"); //Client asks the public key of the Tumbler and sends its own alice.BeginSignVoucher(new SignVoucherRequest { MerkleProof = clientTx.MerkleProof, Transaction = clientTx.Transaction, KeyReference = state.TumblerEscrowKeyReference, UnsignedVoucher = state.BlindedVoucher, Cycle = cycle.Start, ClientEscrowKey = state.ClientEscrowKey.PubKey, ChannelId = SolverClientSession.Id }); NeedSave = true; Status = PaymentStateMachineStatus.TumblerVoucherSigning; } } else if (Status == PaymentStateMachineStatus.TumblerVoucherSigning) { alice = Runtime.CreateTumblerClient(cycle.Start, Identity.Alice); var voucher = alice.EndSignVoucher(SolverClientSession.Id); if (voucher != null) { ClientChannelNegotiation.CheckVoucherSolution(voucher); NeedSave = true; Status = PaymentStateMachineStatus.TumblerVoucherObtained; } } break; case CyclePhase.TumblerChannelEstablishment: if (Status == PaymentStateMachineStatus.TumblerVoucherObtained) { bob = Runtime.CreateTumblerClient(cycle.Start, Identity.Bob); Logs.Client.LogInformation("Begin ask to open the channel..."); //Client asks the Tumbler to make a channel var bobEscrowInformation = ClientChannelNegotiation.GetOpenChannelRequest(); uint160 channelId = null; try { channelId = bob.BeginOpenChannel(bobEscrowInformation); NeedSave = true; } catch (Exception ex) { if (ex.Message.Contains("tumbler-insufficient-funds")) { Logs.Client.LogWarning("The tumbler server has not enough funds and can't open a channel for now"); break; } throw; } ClientChannelNegotiation.SetChannelId(channelId); Status = PaymentStateMachineStatus.TumblerChannelCreating; } else if (Status == PaymentStateMachineStatus.TumblerChannelCreating) { bob = Runtime.CreateTumblerClient(cycle.Start, Identity.Bob); var tumblerEscrow = bob.EndOpenChannel(cycle.Start, ClientChannelNegotiation.GetInternalState().ChannelId); if (tumblerEscrow == null) { Logs.Client.LogInformation("Tumbler escrow still creating..."); break; } NeedSave = true; if (tumblerEscrow.OutputIndex >= tumblerEscrow.Transaction.Outputs.Count) { Logs.Client.LogError("Tumbler escrow output out-of-bound"); Status = PaymentStateMachineStatus.Wasted; break; } var txOut = tumblerEscrow.Transaction.Outputs[tumblerEscrow.OutputIndex]; var outpoint = new OutPoint(tumblerEscrow.Transaction.GetHash(), tumblerEscrow.OutputIndex); var escrowCoin = new Coin(outpoint, txOut).ToScriptCoin(ClientChannelNegotiation.GetTumblerEscrowParameters(tumblerEscrow.EscrowInitiatorKey).ToScript()); Console.WriteLine("TumblerEscrow hex: " + tumblerEscrow.Transaction.ToHex()); PromiseClientSession = ClientChannelNegotiation.ReceiveTumblerEscrowedCoin(escrowCoin); Logs.Client.LogInformation("Tumbler expected escrowed coin received"); //Tell to the block explorer we need to track that address (for checking if it is confirmed in payment phase) Services.BlockExplorerService.TrackAsync(PromiseClientSession.EscrowedCoin.ScriptPubKey).GetAwaiter().GetResult(); Services.BlockExplorerService.TrackPrunedTransactionAsync(tumblerEscrow.Transaction, tumblerEscrow.MerkleProof).GetAwaiter().GetResult(); Tracker.AddressCreated(cycle.Start, TransactionType.TumblerEscrow, PromiseClientSession.EscrowedCoin.ScriptPubKey, correlation); Tracker.TransactionCreated(cycle.Start, TransactionType.TumblerEscrow, PromiseClientSession.EscrowedCoin.Outpoint.Hash, correlation); Services.BroadcastService.BroadcastAsync(tumblerEscrow.Transaction).GetAwaiter().GetResult(); //Channel is done, now need to run the promise protocol to get valid puzzle var cashoutDestination = DestinationWallet.GetNewDestination(); Tracker.AddressCreated(cycle.Start, TransactionType.TumblerCashout, cashoutDestination, correlation); feeRate = GetFeeRate(); var sigReq = PromiseClientSession.CreateSignatureRequest(cashoutDestination, feeRate); var commitments = bob.SignHashes(PromiseClientSession.Id, sigReq); var revelation = PromiseClientSession.Reveal(commitments); var proof = bob.CheckRevelation(PromiseClientSession.Id, revelation); var puzzle = PromiseClientSession.CheckCommitmentProof(proof); SolverClientSession.AcceptPuzzle(puzzle); Status = PaymentStateMachineStatus.TumblerChannelCreated; } else if (Status == PaymentStateMachineStatus.TumblerChannelCreated) { CheckTumblerChannelSecured(cycle); } break; case CyclePhase.PaymentPhase: //Could have confirmed during safe period //Only check for the first block when period start, //else Tumbler can know deanonymize you based on the timing of first Alice request if the transaction was not confirmed previously if (Status == PaymentStateMachineStatus.TumblerChannelCreated && height == period.Start) { CheckTumblerChannelSecured(cycle); } //No "else if" intended if (Status == PaymentStateMachineStatus.TumblerChannelSecured) { alice = Runtime.CreateTumblerClient(cycle.Start, Identity.Alice); Logs.Client.LogDebug("Starting the puzzle solver protocol..."); var puzzles = SolverClientSession.GeneratePuzzles(); alice.BeginSolvePuzzles(SolverClientSession.Id, puzzles); NeedSave = true; Status = PaymentStateMachineStatus.ProcessingPayment; } else if (Status == PaymentStateMachineStatus.ProcessingPayment) { feeRate = GetFeeRate(); alice = Runtime.CreateTumblerClient(cycle.Start, Identity.Alice); var commitments = alice.EndSolvePuzzles(SolverClientSession.Id); NeedSave = true; if (commitments == null) { Logs.Client.LogDebug("Still solving puzzles..."); break; } var revelation2 = SolverClientSession.Reveal(commitments); var solutionKeys = alice.CheckRevelation(SolverClientSession.Id, revelation2); var blindFactors = SolverClientSession.GetBlindFactors(solutionKeys); var offerInformation = alice.CheckBlindFactors(SolverClientSession.Id, blindFactors); var offerSignature = SolverClientSession.SignOffer(offerInformation); var offerRedeem = SolverClientSession.CreateOfferRedeemTransaction(feeRate); Logs.Client.LogDebug("Puzzle solver protocol ended..."); //May need to find solution in the fulfillment transaction Services.BlockExplorerService.TrackAsync(offerRedeem.PreviousScriptPubKey).GetAwaiter().GetResult(); Tracker.AddressCreated(cycle.Start, TransactionType.ClientOfferRedeem, SolverClientSession.GetInternalState().RedeemDestination, correlation); Services.TrustedBroadcastService.Broadcast(cycle.Start, TransactionType.ClientOfferRedeem, correlation, offerRedeem); try { solutionKeys = alice.FulfillOffer(SolverClientSession.Id, offerSignature); SolverClientSession.CheckSolutions(solutionKeys); var tumblingSolution = SolverClientSession.GetSolution(); var transaction = PromiseClientSession.GetSignedTransaction(tumblingSolution); Logs.Client.LogDebug("Got puzzle solution cooperatively from the tumbler"); Logs.Client.LogDebug("TumblerCashOut hex: {0}", transaction.ToHex()); Status = PaymentStateMachineStatus.PuzzleSolutionObtained; Services.TrustedBroadcastService.Broadcast(cycle.Start, TransactionType.TumblerCashout, correlation, new TrustedBroadcastRequest() { BroadcastAt = cycle.GetPeriods().ClientCashout.Start, Transaction = transaction }); if (Cooperative) { try { // No need to await for it, it is a just nice for the tumbler (we don't want the underlying socks connection cut before the escape key is sent) var signature = SolverClientSession.SignEscape(); alice.GiveEscapeKeyAsync(SolverClientSession.Id, signature).GetAwaiter().GetResult(); } catch (Exception ex) { Logs.Client.LogDebug(new EventId(), ex, "Exception while giving the escape key"); } Logs.Client.LogInformation("Gave escape signature to the tumbler"); } } catch (Exception ex) { Status = PaymentStateMachineStatus.UncooperativeTumbler; Logs.Client.LogWarning("The tumbler did not gave puzzle solution cooperatively"); Logs.Client.LogWarning(ex.ToString()); } } break; case CyclePhase.ClientCashoutPhase: //If the tumbler is uncooperative, he published solutions on the blockchain if (Status == PaymentStateMachineStatus.UncooperativeTumbler) { var transactions = Services.BlockExplorerService.GetTransactionsAsync(SolverClientSession.GetInternalState().OfferCoin.ScriptPubKey, false).GetAwaiter().GetResult(); if (transactions.Count != 0) { SolverClientSession.CheckSolutions(transactions.Select(t => t.Transaction).ToArray()); Logs.Client.LogInformation("Puzzle solution recovered from tumbler's fulfill transaction"); NeedSave = true; Status = PaymentStateMachineStatus.PuzzleSolutionObtained; var tumblingSolution = SolverClientSession.GetSolution(); var transaction = PromiseClientSession.GetSignedTransaction(tumblingSolution); Tracker.TransactionCreated(cycle.Start, TransactionType.TumblerCashout, transaction.GetHash(), correlation); Services.BroadcastService.BroadcastAsync(transaction).GetAwaiter().GetResult(); } } break; } } catch (InvalidStateException ex) { Logs.Client.LogDebug(new EventId(), ex, "Client side Invalid State, the payment is wasted"); Status = PaymentStateMachineStatus.Wasted; } catch (Exception ex) when(ex.Message.IndexOf("invalid-state", StringComparison.OrdinalIgnoreCase) >= 0) { Logs.Client.LogDebug(new EventId(), ex, "Tumbler side Invalid State, the payment is wasted"); Status = PaymentStateMachineStatus.Wasted; } finally { if (previousState != Status) { Logs.Client.LogInformation($"Status changed {previousState} => {Status}"); } if (alice != null && bob != null) { throw new InvalidOperationException("Bob and Alice have been both initialized, please report the bug to NTumbleBit developers"); } if (alice != null) { alice.Dispose(); } if (bob != null) { bob.Dispose(); } } return(progressInfo); }
private void FindTxBtn_ClickAsync(object sender, RoutedEventArgs e) { ListTxSummary.Items.Clear(); primaryVM.RecievedCoins = null; primaryVM.SpentCoins = null; primaryVM.Block = 0; try { if (MainTxBtn.IsChecked == true) { primaryVM.TxQueryMain(TxInputBox.Text); } else { primaryVM.TxQueryTestNet(TxInputBox.Text); } if (primaryVM.RecievedCoins != null) { primaryVM.RecievedAmount = Money.Zero; ListTxSummary.Items.Add("All Recieved Coins"); } //Sum: Total Recieved foreach (var recievedCoin in primaryVM.RecievedCoins) { primaryVM.RecievedAmount = (Money)recievedCoin.Amount.Add(primaryVM.RecievedAmount); } //List: Recieved Coins if (MainTxBtn.IsChecked == true) { foreach (Coin coin in primaryVM.RecievedCoins) { var amount = coin.Amount; var paymentScript = coin.ScriptPubKey; var address = paymentScript.GetDestinationAddress(Network.Main); ListTxSummary.Items.Add($"Recieved +{amount.ToDecimal(MoneyUnit.BTC)}BTC To: {address} : ScriptPubKey: {paymentScript}"); } } else { foreach (Coin coin in primaryVM.RecievedCoins) { var amount = coin.Amount; var paymentScript = coin.ScriptPubKey; var address = paymentScript.GetDestinationAddress(Network.TestNet); ListTxSummary.Items.Add($"Recieved +{amount.ToDecimal(MoneyUnit.BTC)}BTC To: {address} : ScriptPubKey: {paymentScript}"); } } primaryVM.SpentAmount = Money.Zero; ListTxSummary.Items.Add("All Spent Coins"); //Sum: Total Spent foreach (var spentCoin in primaryVM.SpentCoins) { primaryVM.SpentAmount = (Money)spentCoin.Amount.Add(primaryVM.SpentAmount); } //List: Total Spent if (MainTxBtn.IsChecked == true) { foreach (Coin coin in primaryVM.SpentCoins) { Money amount = coin.Amount; var paymentScript = coin.ScriptPubKey; var address = paymentScript.GetDestinationAddress(Network.Main); ListTxSummary.Items.Add($"Spent -{amount.ToDecimal(MoneyUnit.BTC)}BTC From: {address} : ScriptPubKey: {paymentScript}"); } } else { foreach (Coin coin in primaryVM.SpentCoins) { Money amount = coin.Amount; var paymentScript = coin.ScriptPubKey; var address = paymentScript.GetDestinationAddress(Network.TestNet); ListTxSummary.Items.Add($"Spent -{amount.ToDecimal(MoneyUnit.BTC)}BTC From: {address} : ScriptPubKey: {paymentScript}"); } } // Previous Outpoints var inputs = primaryVM.Transaction.Inputs; primaryVM.OutpointCount = inputs.Count; var firstOutpoint = primaryVM.RecievedCoins.First().Outpoint; var firstTransaction = primaryVM.Client.GetTransaction(firstOutpoint.Hash).Result.Transaction; ListTxSummary.Items.Add("All Outpoints"); ListTxSummary.Items.Add($"#({primaryVM.OutpointCount + 1}) Current Outpoint = {firstOutpoint.Hash} , {firstOutpoint.N} , {firstTransaction.IsCoinBase.ToString()}"); var countOrder = primaryVM.OutpointCount; foreach (TxIn input in inputs) { OutPoint previousOutpoint = input.PrevOut; var previousTransaction = primaryVM.Client.GetTransaction(previousOutpoint.Hash).Result.Transaction; ListTxSummary.Items.Add($"#({countOrder}) Prev Outpoint = {previousOutpoint.Hash} , {previousOutpoint.N} , {previousTransaction.IsCoinBase.ToString()}"); countOrder -= 1; } } catch (Exception ex) { TxInputBox.Text = ex.ToString(); } }
public static IBitcoinWriter Add(this IBitcoinWriter w, OutPoint op) => op.AddTo(w);
public void bloom_match() { // Random real transaction (b4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b) Transaction tx = new Transaction(); tx.ReadWrite(ParseHex("01000000010b26e9b7735eb6aabdf358bab62f9816a21ba9ebdb719d5299e88607d722c190000000008b4830450220070aca44506c5cef3a16ed519d7c3c39f8aab192c4e1c90d065f37b8a4af6141022100a8e160b856c2d43d27d8fba71e5aef6405b8643ac4cb7cb3c462aced7f14711a0141046d11fee51b0e60666d5049a9101a72741df480b96ee26488a4d3466b95c9a40ac5eeef87e10a5cd336c19a84565f80fa6c547957b7700ff4dfbdefe76036c339ffffffff021bff3d11000000001976a91404943fdd508053c75000106d3bc6e2754dbcff1988ac2f15de00000000001976a914a266436d2965547608b9e15d9032a7b9d64fa43188ac00000000")); // and one which spends it (e2769b09e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436) var ch = new byte[] { 0x01, 0x00, 0x00, 0x00, 0x01, 0x6b, 0xff, 0x7f, 0xcd, 0x4f, 0x85, 0x65, 0xef, 0x40, 0x6d, 0xd5, 0xd6, 0x3d, 0x4f, 0xf9, 0x4f, 0x31, 0x8f, 0xe8, 0x20, 0x27, 0xfd, 0x4d, 0xc4, 0x51, 0xb0, 0x44, 0x74, 0x01, 0x9f, 0x74, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x49, 0x30, 0x46, 0x02, 0x21, 0x00, 0xda, 0x0d, 0xc6, 0xae, 0xce, 0xfe, 0x1e, 0x06, 0xef, 0xdf, 0x05, 0x77, 0x37, 0x57, 0xde, 0xb1, 0x68, 0x82, 0x09, 0x30, 0xe3, 0xb0, 0xd0, 0x3f, 0x46, 0xf5, 0xfc, 0xf1, 0x50, 0xbf, 0x99, 0x0c, 0x02, 0x21, 0x00, 0xd2, 0x5b, 0x5c, 0x87, 0x04, 0x00, 0x76, 0xe4, 0xf2, 0x53, 0xf8, 0x26, 0x2e, 0x76, 0x3e, 0x2d, 0xd5, 0x1e, 0x7f, 0xf0, 0xbe, 0x15, 0x77, 0x27, 0xc4, 0xbc, 0x42, 0x80, 0x7f, 0x17, 0xbd, 0x39, 0x01, 0x41, 0x04, 0xe6, 0xc2, 0x6e, 0xf6, 0x7d, 0xc6, 0x10, 0xd2, 0xcd, 0x19, 0x24, 0x84, 0x78, 0x9a, 0x6c, 0xf9, 0xae, 0xa9, 0x93, 0x0b, 0x94, 0x4b, 0x7e, 0x2d, 0xb5, 0x34, 0x2b, 0x9d, 0x9e, 0x5b, 0x9f, 0xf7, 0x9a, 0xff, 0x9a, 0x2e, 0xe1, 0x97, 0x8d, 0xd7, 0xfd, 0x01, 0xdf, 0xc5, 0x22, 0xee, 0x02, 0x28, 0x3d, 0x3b, 0x06, 0xa9, 0xd0, 0x3a, 0xcf, 0x80, 0x96, 0x96, 0x8d, 0x7d, 0xbb, 0x0f, 0x91, 0x78, 0xff, 0xff, 0xff, 0xff, 0x02, 0x8b, 0xa7, 0x94, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xba, 0xde, 0xec, 0xfd, 0xef, 0x05, 0x07, 0x24, 0x7f, 0xc8, 0xf7, 0x42, 0x41, 0xd7, 0x3b, 0xc0, 0x39, 0x97, 0x2d, 0x7b, 0x88, 0xac, 0x40, 0x94, 0xa8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xc1, 0x09, 0x32, 0x48, 0x3f, 0xec, 0x93, 0xed, 0x51, 0xf5, 0xfe, 0x95, 0xe7, 0x25, 0x59, 0xf2, 0xcc, 0x70, 0x43, 0xf9, 0x88, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00 }; var vch = ch.Take(ch.Length - 1).ToArray(); Transaction spendingTx = new Transaction(); spendingTx.ReadWrite(vch); BloomFilter filter = new BloomFilter(10, 0.000001, 0, BloomFlags.UPDATE_ALL); filter.Insert(uint256.Parse("0xb4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b")); Assert.True(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match tx hash"); filter = new BloomFilter(10, 0.000001, 0, BloomFlags.UPDATE_ALL); // byte-reversed tx hash filter.Insert(ParseHex("6bff7fcd4f8565ef406dd5d63d4ff94f318fe82027fd4dc451b04474019f74b4")); Assert.True(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match manually serialized tx hash"); filter = new BloomFilter(10, 0.000001, 0, BloomFlags.UPDATE_ALL); filter.Insert(ParseHex("30450220070aca44506c5cef3a16ed519d7c3c39f8aab192c4e1c90d065f37b8a4af6141022100a8e160b856c2d43d27d8fba71e5aef6405b8643ac4cb7cb3c462aced7f14711a01")); Assert.True(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match input signature"); filter = new BloomFilter(10, 0.000001, 0, BloomFlags.UPDATE_ALL); filter.Insert(ParseHex("046d11fee51b0e60666d5049a9101a72741df480b96ee26488a4d3466b95c9a40ac5eeef87e10a5cd336c19a84565f80fa6c547957b7700ff4dfbdefe76036c339")); Assert.True(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match input pub key"); filter = new BloomFilter(10, 0.000001, 0, BloomFlags.UPDATE_ALL); filter.Insert(ParseHex("04943fdd508053c75000106d3bc6e2754dbcff19")); Assert.True(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match output address"); Assert.True(filter.IsRelevantAndUpdate(spendingTx), "Simple Bloom filter didn't add output"); filter = new BloomFilter(10, 0.000001, 0, BloomFlags.UPDATE_ALL); filter.Insert(ParseHex("a266436d2965547608b9e15d9032a7b9d64fa431")); Assert.True(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match output address"); filter = new BloomFilter(10, 0.000001, 0, BloomFlags.UPDATE_ALL); filter.Insert(new OutPoint(uint256.Parse("0x90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 0)); Assert.True(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match COutPoint"); filter = new BloomFilter(10, 0.000001, 0, BloomFlags.UPDATE_ALL); OutPoint prevOutPoint = new OutPoint(uint256.Parse("0x90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 0); { var data = prevOutPoint.ToBytes(); filter.Insert(data); } Assert.True(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match manually serialized COutPoint"); filter = new BloomFilter(10, 0.000001, 0, BloomFlags.UPDATE_ALL); filter.Insert(uint256.Parse("00000009e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436")); Assert.True(!filter.IsRelevantAndUpdate(tx), "Simple Bloom filter matched random tx hash"); filter = new BloomFilter(10, 0.000001, 0, BloomFlags.UPDATE_ALL); filter.Insert(ParseHex("0000006d2965547608b9e15d9032a7b9d64fa431")); Assert.True(!filter.IsRelevantAndUpdate(tx), "Simple Bloom filter matched random address"); filter = new BloomFilter(10, 0.000001, 0, BloomFlags.UPDATE_ALL); filter.Insert(new OutPoint(uint256.Parse("0x90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 1)); Assert.True(!filter.IsRelevantAndUpdate(tx), "Simple Bloom filter matched COutPoint for an output we didn't care about"); filter = new BloomFilter(10, 0.000001, 0, BloomFlags.UPDATE_ALL); filter.Insert(new OutPoint(uint256.Parse("0x000000d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 0)); Assert.True(!filter.IsRelevantAndUpdate(tx), "Simple Bloom filter matched COutPoint for an output we didn't care about"); }