public async Task AcceptToMemoryPool_WithP2SHValidTxns_IsSuccessfullAsync() { string dataDir = GetTestDirectoryPath(this); BitcoinSecret miner = new BitcoinSecret(new Key(), Network.RegTest); ITestChainContext context = await TestChainFactory.CreateAsync(Network.RegTest, miner.PubKey.Hash.ScriptPubKey, dataDir); IMempoolValidator validator = context.MempoolValidator; Assert.NotNull(validator); BitcoinSecret alice = new BitcoinSecret(new Key(), Network.RegTest); BitcoinSecret bob = new BitcoinSecret(new Key(), Network.RegTest); BitcoinSecret satoshi = new BitcoinSecret(new Key(), Network.RegTest); BitcoinSecret nico = new BitcoinSecret(new Key(), Network.RegTest); // corp needs two out of three of alice, bob, nico Script corpMultiSig = PayToMultiSigTemplate .Instance .GenerateScriptPubKey(2, new[] { alice.PubKey, bob.PubKey, nico.PubKey }); // P2SH address for corp multi-sig BitcoinScriptAddress corpRedeemAddress = corpMultiSig.GetScriptAddress(Network.RegTest); // Fund corp // 50 Coins come from first tx on chain - send corp 42 and change back to miner Coin coin = new Coin(context.SrcTxs[0].GetHash(), 0, context.SrcTxs[0].TotalOut, miner.ScriptPubKey); TransactionBuilder txBuilder = new TransactionBuilder(Network.RegTest); Transaction fundP2shTx = txBuilder .AddCoins(new List <Coin> { coin }) .AddKeys(miner) .Send(corpRedeemAddress, "42.00") .SendFees("0.001") .SetChange(miner.GetAddress()) .BuildTransaction(true); Assert.True(txBuilder.Verify(fundP2shTx)); //check fully signed MempoolValidationState state = new MempoolValidationState(false); Assert.True(await validator.AcceptToMemoryPool(state, fundP2shTx), $"Transaction: {nameof(fundP2shTx)} failed mempool validation."); // AliceBobNico corp. send 20 to Satoshi Coin[] corpCoins = fundP2shTx.Outputs .Where(o => o.ScriptPubKey == corpRedeemAddress.ScriptPubKey) .Select(o => ScriptCoin.Create(Network.RegTest, new OutPoint(fundP2shTx.GetHash(), fundP2shTx.Outputs.IndexOf(o)), o, corpMultiSig)) .ToArray(); txBuilder = new TransactionBuilder(Network.RegTest); Transaction p2shSpendTx = txBuilder .AddCoins(corpCoins) .AddKeys(alice, bob) .Send(satoshi.GetAddress(), "20") .SendFees("0.001") .SetChange(corpRedeemAddress) .BuildTransaction(true); Assert.True(txBuilder.Verify(p2shSpendTx)); Assert.True(await validator.AcceptToMemoryPool(state, p2shSpendTx), $"Transaction: {nameof(p2shSpendTx)} failed mempool validation."); }
public async Task AcceptToMemoryPool_WithMultiSigValidTxns_IsSuccessfullAsync() { string dataDir = GetTestDirectoryPath(this); BitcoinSecret miner = new BitcoinSecret(new Key(), Network.RegTest); ITestChainContext context = await TestChainFactory.CreateAsync(Network.RegTest, miner.PubKey.Hash.ScriptPubKey, dataDir); IMempoolValidator validator = context.MempoolValidator; Assert.NotNull(validator); BitcoinSecret alice = new BitcoinSecret(new Key(), Network.RegTest); BitcoinSecret bob = new BitcoinSecret(new Key(), Network.RegTest); BitcoinSecret satoshi = new BitcoinSecret(new Key(), Network.RegTest); BitcoinSecret nico = new BitcoinSecret(new Key(), Network.RegTest); // corp needs two out of three of alice, bob, nico Script corpMultiSig = PayToMultiSigTemplate .Instance .GenerateScriptPubKey(2, new[] { alice.PubKey, bob.PubKey, nico.PubKey }); // Fund corp // 50 Coins come from first tx on chain - send corp 42 and change back to miner Coin coin = new Coin(context.SrcTxs[0].GetHash(), 0, context.SrcTxs[0].TotalOut, miner.ScriptPubKey); TransactionBuilder txBuilder = new TransactionBuilder(Network.RegTest); Transaction sendToMultiSigTx = txBuilder .AddCoins(new List <Coin> { coin }) .AddKeys(miner) .Send(corpMultiSig, "42.00") .SendFees("0.001") .SetChange(miner.GetAddress()) .BuildTransaction(true); Assert.True(txBuilder.Verify(sendToMultiSigTx)); //check fully signed MempoolValidationState state = new MempoolValidationState(false); Assert.True(await validator.AcceptToMemoryPool(state, sendToMultiSigTx), $"Transaction: {nameof(sendToMultiSigTx)} failed mempool validation."); // AliceBobNico corp. send to Satoshi Coin[] corpCoins = sendToMultiSigTx.Outputs .Where(o => o.ScriptPubKey == corpMultiSig) .Select(o => new Coin(new OutPoint(sendToMultiSigTx.GetHash(), sendToMultiSigTx.Outputs.IndexOf(o)), o)) .ToArray(); // Alice initiates the transaction txBuilder = new TransactionBuilder(Network.RegTest); Transaction multiSigTx = txBuilder .AddCoins(corpCoins) .AddKeys(alice) .Send(satoshi.GetAddress(), "4.5") .SendFees("0.001") .SetChange(corpMultiSig) .BuildTransaction(true); Assert.True(!txBuilder.Verify(multiSigTx)); //Well, only one signature on the two required... // Nico completes the transaction txBuilder = new TransactionBuilder(Network.RegTest); multiSigTx = txBuilder .AddCoins(corpCoins) .AddKeys(nico) .SignTransaction(multiSigTx); Assert.True(txBuilder.Verify(multiSigTx)); Assert.True(await validator.AcceptToMemoryPool(state, multiSigTx), $"Transaction: {nameof(multiSigTx)} failed mempool validation."); }
public async Task AcceptToMemoryPool_WithMultiInOutValidTxns_IsSuccessfullAsync() { string dataDir = GetTestDirectoryPath(this); BitcoinSecret miner = new BitcoinSecret(new Key(), Network.RegTest); ITestChainContext context = await TestChainFactory.CreateAsync(Network.RegTest, miner.PubKey.Hash.ScriptPubKey, dataDir); IMempoolValidator validator = context.MempoolValidator; Assert.NotNull(validator); BitcoinSecret alice = new BitcoinSecret(new Key(), Network.RegTest); BitcoinSecret bob = new BitcoinSecret(new Key(), Network.RegTest); BitcoinSecret satoshi = new BitcoinSecret(new Key(), Network.RegTest); // Fund Alice, Bob, Satoshi // 50 Coins come from first tx on chain - send satoshi 1, bob 2, Alice 1.5 and change back to miner Coin coin = new Coin(context.SrcTxs[0].GetHash(), 0, context.SrcTxs[0].TotalOut, miner.ScriptPubKey); TransactionBuilder txBuilder = new TransactionBuilder(Network.RegTest); Transaction multiOutputTx = txBuilder .AddCoins(new List <Coin> { coin }) .AddKeys(miner) .Send(satoshi.GetAddress(), "1.00") .Send(bob.GetAddress(), "2.00") .Send(alice.GetAddress(), "1.50") .SendFees("0.001") .SetChange(miner.GetAddress()) .BuildTransaction(true); Assert.True(txBuilder.Verify(multiOutputTx)); //check fully signed MempoolValidationState state = new MempoolValidationState(false); Assert.True(await validator.AcceptToMemoryPool(state, multiOutputTx), $"Transaction: {nameof(multiOutputTx)} failed mempool validation."); // Alice then Bob sends to Satoshi Coin[] aliceCoins = multiOutputTx.Outputs .Where(o => o.ScriptPubKey == alice.ScriptPubKey) .Select(o => new Coin(new OutPoint(multiOutputTx.GetHash(), multiOutputTx.Outputs.IndexOf(o)), o)) .ToArray(); Coin[] bobCoins = multiOutputTx.Outputs .Where(o => o.ScriptPubKey == bob.ScriptPubKey) .Select(o => new Coin(new OutPoint(multiOutputTx.GetHash(), multiOutputTx.Outputs.IndexOf(o)), o)) .ToArray(); txBuilder = new TransactionBuilder(Network.RegTest); Transaction multiInputTx = txBuilder .AddCoins(aliceCoins) .AddKeys(alice) .Send(satoshi.GetAddress(), "0.8") .SetChange(alice.GetAddress()) .SendFees("0.0005") .Then() .AddCoins(bobCoins) .AddKeys(bob) .Send(satoshi.GetAddress(), "0.2") .SetChange(bob.GetAddress()) .SendFees("0.0005") .BuildTransaction(true); Assert.True(txBuilder.Verify(multiInputTx)); //check fully signed Assert.True(await validator.AcceptToMemoryPool(state, multiInputTx), $"Transaction: {nameof(multiInputTx)} failed mempool validation."); }
/// <inheritdoc /> public Task <bool> AcceptToMemoryPool(MempoolValidationState state, Transaction tx) { state.AcceptTime = this.dateTimeProvider.GetTime(); return(AcceptToMemoryPoolWithTime(state, tx)); }
/// <summary> /// Validates and then adds a transaction to memory pool. /// </summary> /// <param name="state">Validation state for creating the validation context.</param> /// <param name="tx">The transaction to validate.</param> private async Task AcceptToMemoryPoolWorkerAsync(MempoolValidationState state, Transaction tx) { var context = new MempoolValidationContext(tx, state); context.MinRelayTxFee = this.minRelayTxFee; // TODO: Convert these into rules too // this.PreMempoolChecks(context); - done! // Create the MemPoolCoinView and load relevant utxoset context.View = new MempoolCoinView(this.coinView, this.txMemPool, this.mempoolLock, this); // adding to the mem pool can only be done sequentially // use the sequential scheduler for that. await this.mempoolLock.WriteAsync(() => { context.View.LoadViewLocked(context.Transaction); // If the transaction already exists in the mempool, // we only record the state but do not throw an exception. // This is because the caller will check if the state is invalid // and if so return false, meaning that the transaction should not be relayed. if (this.txMemPool.Exists(context.TransactionHash)) { state.Invalid(MempoolErrors.InPool); this.logger.LogTrace("(-)[INVALID_TX_ALREADY_EXISTS]"); return; } foreach (IMempoolRule rule in this.mempoolRules) { rule.CheckTransaction(context); } // Remove conflicting transactions from the mempool foreach (TxMempoolEntry it in context.AllConflicting) { this.logger.LogInformation($"Replacing tx {it.TransactionHash} with {context.TransactionHash} for {context.ModifiedFees - context.ConflictingFees} BTC additional fees, {context.EntrySize - context.ConflictingSize} delta bytes"); } this.txMemPool.RemoveStaged(context.AllConflicting, false); // This transaction should only count for fee estimation if // the node is not behind and it is not dependent on any other // transactions in the mempool bool validForFeeEstimation = IsCurrentForFeeEstimation() && this.txMemPool.HasNoInputsOf(tx); // Store transaction in memory this.txMemPool.AddUnchecked(context.TransactionHash, context.Entry, context.SetAncestors, validForFeeEstimation); // trim mempool and check if tx was trimmed if (!state.OverrideMempoolLimit) { LimitMempoolSize(this.mempoolSettings.MaxMempool * 1000000, this.mempoolSettings.MempoolExpiry * 60 * 60); if (!this.txMemPool.Exists(context.TransactionHash)) { this.logger.LogTrace("(-)[FAIL_MEMPOOL_FULL]"); state.Fail(MempoolErrors.Full).Throw(); } } // do this here inside the exclusive scheduler for better accuracy // and to avoid springing more concurrent tasks later state.MempoolSize = this.txMemPool.Size; state.MempoolDynamicSize = this.txMemPool.DynamicMemoryUsage(); this.PerformanceCounter.SetMempoolSize(state.MempoolSize); this.PerformanceCounter.SetMempoolDynamicSize(state.MempoolDynamicSize); this.PerformanceCounter.AddHitCount(1); }); }
public bool AddToStratisMempool(Transaction trx) { var state = new MempoolValidationState(true); return(this.runner.FullNode.MempoolManager().Validator.AcceptToMemoryPool(state, trx).Result); }