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);
            });
        }
예제 #6
0
        public bool AddToStratisMempool(Transaction trx)
        {
            var state = new MempoolValidationState(true);

            return(this.runner.FullNode.MempoolManager().Validator.AcceptToMemoryPool(state, trx).Result);
        }