Commit 0e5c86ab authored by Sergei Zubov's avatar Sergei Zubov

Modify consensus to ignore pointer input

Input with pointer to change output conflicts with consensus rules as
there is no underlying transaction with funds for that input. Consensus
modified to ignore that input in funds checks.
parent e0951510
......@@ -40,7 +40,7 @@ namespace DeStream.DeStreamD
.UseNodeSettings(nodeSettings)
.UseBlockStore()
.UseDeStreamPosConsensus()
.UseMempool()
.UseDeStreamMempool()
.UseDeStreamWallet()
.AddDeStreamPowPosMining()
.UseApi()
......
using System.Linq;
namespace NBitcoin
{
public static class DeStreamTxInListExtensions
{
public static TxInList RemoveChangePointer(this TxInList txInList)
{
return (TxInList) txInList.Where(p => p.PrevOut.Hash != uint256.Zero);
}
}
}
\ No newline at end of file
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Stratis.Bitcoin.BlockPulling;
using Stratis.Bitcoin.Builder;
using Stratis.Bitcoin.Configuration.Logging;
......@@ -8,6 +9,7 @@ using Stratis.Bitcoin.Consensus.Rules;
using Stratis.Bitcoin.Features.Consensus.CoinViews;
using Stratis.Bitcoin.Features.Consensus.Interfaces;
using Stratis.Bitcoin.Features.Consensus.Rules;
using Stratis.Bitcoin.Features.Consensus.Rules.CommonRules;
using Stratis.Bitcoin.Interfaces;
namespace Stratis.Bitcoin.Features.Consensus
......@@ -51,7 +53,7 @@ namespace Stratis.Bitcoin.Features.Consensus
services.AddSingleton<IConsensusRules, PowConsensusRules>();
services
.AddSingleton<IRuleRegistration,
FullNodeBuilderConsensusExtension.PowConsensusRulesRegistration>();
DeStreamPowConsensusRulesRegistration>();
});
});
......@@ -94,11 +96,90 @@ namespace Stratis.Bitcoin.Features.Consensus
services.AddSingleton<IConsensusRules, PosConsensusRules>();
services
.AddSingleton<IRuleRegistration,
FullNodeBuilderConsensusExtension.PosConsensusRulesRegistration>();
DeStreamPosConsensusRulesRegistration>();
});
});
return fullNodeBuilder;
}
private class DeStreamPowConsensusRulesRegistration : IRuleRegistration
{
public IEnumerable<ConsensusRule> GetRules()
{
return new List<ConsensusRule>
{
new BlockHeaderRule(),
// rules that are inside the method CheckBlockHeader
new CalculateWorkRule(),
// rules that are inside the method ContextualCheckBlockHeader
new CheckpointsRule(),
new AssumeValidRule(),
new BlockHeaderPowContextualRule(),
// rules that are inside the method ContextualCheckBlock
new TransactionLocktimeActivationRule(), // implements BIP113
new CoinbaseHeightActivationRule(), // implements BIP34
new WitnessCommitmentsRule(), // BIP141, BIP144
new BlockSizeRule(),
// rules that are inside the method CheckBlock
new BlockMerkleRootRule(),
new EnsureCoinbaseRule(),
new CheckPowTransactionRule(),
new CheckSigOpsRule(),
// rules that require the store to be loaded (coinview)
new DeStreamLoadCoinviewRule(),
new TransactionDuplicationActivationRule(), // implements BIP30
new PowCoinviewRule() // implements BIP68, MaxSigOps and BlockReward calculation
};
}
}
private class DeStreamPosConsensusRulesRegistration : IRuleRegistration
{
public IEnumerable<ConsensusRule> GetRules()
{
return new List<ConsensusRule>
{
new BlockHeaderRule(),
// rules that are inside the method CheckBlockHeader
new CalculateStakeRule(),
// rules that are inside the method ContextualCheckBlockHeader
new CheckpointsRule(),
new AssumeValidRule(),
new BlockHeaderPowContextualRule(),
new BlockHeaderPosContextualRule(),
// rules that are inside the method ContextualCheckBlock
new TransactionLocktimeActivationRule(), // implements BIP113
new CoinbaseHeightActivationRule(), // implements BIP34
new WitnessCommitmentsRule(), // BIP141, BIP144
new BlockSizeRule(),
new PosBlockContextRule(), // TODO: this rule needs to be implemented
// rules that are inside the method CheckBlock
new BlockMerkleRootRule(),
new EnsureCoinbaseRule(),
new CheckPowTransactionRule(),
new CheckPosTransactionRule(),
new CheckSigOpsRule(),
new PosFutureDriftRule(),
new PosCoinstakeRule(),
new PosBlockSignatureRule(),
// rules that require the store to be loaded (coinview)
new DeStreamLoadCoinviewRule(),
new TransactionDuplicationActivationRule(), // implements BIP30
new PosCoinviewRule() // implements BIP68, MaxSigOps and BlockReward calculation
};
}
}
}
}
\ No newline at end of file
using System.Linq;
using NBitcoin;
using Stratis.Bitcoin.Utilities;
namespace Stratis.Bitcoin.Features.Consensus
{
public class DeStreamUnspentOutputSet : UnspentOutputSet
{
public override bool HaveInputs(Transaction tx)
{
return tx.Inputs.RemoveChangePointer().All(txin => this.GetOutputFor(txin) != null);
}
public override void Update(Transaction transaction, int height)
{
if (!transaction.IsCoinBase)
{
foreach (TxIn input in transaction.Inputs.RemoveChangePointer())
{
UnspentOutputs c = this.AccessCoins(input.PrevOut.Hash);
c.Spend(input.PrevOut.N);
}
}
this.unspents.AddOrReplace(transaction.GetHash(), new UnspentOutputs((uint) height, transaction));
}
public override Money GetValueIn(Transaction tx)
{
return tx.Inputs.RemoveChangePointer().Select(txin => this.GetOutputFor(txin).Value)
.Sum();
}
}
}
\ No newline at end of file
......@@ -227,7 +227,7 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
/// <exception cref="ConsensusErrors.BadTransactionInBelowOut">Thrown if transaction inputs are less then outputs.</exception>
/// <exception cref="ConsensusErrors.BadTransactionNegativeFee">Thrown if fees sum is negative.</exception>
/// <exception cref="ConsensusErrors.BadTransactionFeeOutOfRange">Thrown if fees value is out of range.</exception>
public void CheckInputs(Transaction transaction, UnspentOutputSet inputs, int spendHeight)
public virtual void CheckInputs(Transaction transaction, UnspentOutputSet inputs, int spendHeight)
{
this.Logger.LogTrace("({0}:{1})", nameof(spendHeight), spendHeight);
......@@ -290,7 +290,7 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
/// <param name="inputs">Map of previous transactions that have outputs we're spending.</param>
/// <param name="flags">Script verification flags.</param>
/// <returns>Signature operation cost for all transaction's inputs.</returns>
public long GetTransactionSignatureOperationCost(Transaction transaction, UnspentOutputSet inputs, DeploymentFlags flags)
public virtual long GetTransactionSignatureOperationCost(Transaction transaction, UnspentOutputSet inputs, DeploymentFlags flags)
{
long signatureOperationCost = this.GetLegacySignatureOperationsCount(transaction) * this.PowConsensusOptions.WitnessScaleFactor;
......@@ -318,7 +318,7 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
/// <param name="witness">Witness script.</param>
/// <param name="flags">Script verification flags.</param>
/// <returns>Signature operation cost for single transaction input.</returns>
private long CountWitnessSignatureOperation(Script scriptPubKey, WitScript witness, DeploymentFlags flags)
protected long CountWitnessSignatureOperation(Script scriptPubKey, WitScript witness, DeploymentFlags flags)
{
witness = witness ?? WitScript.Empty;
if (!flags.ScriptFlags.HasFlag(ScriptVerify.Witness))
......@@ -347,7 +347,7 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
/// <param name="transaction">Transaction for which we are computing the cost.</param>
/// <param name="inputs">Map of previous transactions that have outputs we're spending.</param>
/// <returns>Signature operation cost for transaction.</returns>
private uint GetP2SHSignatureOperationsCount(Transaction transaction, UnspentOutputSet inputs)
protected virtual uint GetP2SHSignatureOperationsCount(Transaction transaction, UnspentOutputSet inputs)
{
if (transaction.IsCoinBase)
return 0;
......@@ -368,7 +368,7 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
/// </summary>
/// <param name="transaction">Transaction for which we are computing the cost.</param>
/// <returns>Legacy signature operation cost for transaction.</returns>
private long GetLegacySignatureOperationsCount(Transaction transaction)
protected long GetLegacySignatureOperationsCount(Transaction transaction)
{
long sigOps = 0;
foreach (TxIn txin in transaction.Inputs)
......@@ -385,7 +385,7 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
/// </summary>
/// <param name="value">The value to be checked.</param>
/// <returns><c>true</c> if the value is in range. Otherwise <c>false</c>.</returns>
private bool MoneyRange(long value)
protected bool MoneyRange(long value)
{
return ((value >= 0) && (value <= this.Consensus.MaxMoney));
}
......
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using NBitcoin;
using Stratis.Bitcoin.Base.Deployments;
using Stratis.Bitcoin.Consensus.Rules;
using Stratis.Bitcoin.Utilities;
namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
{
public abstract class DeStreamCoinViewRule : CoinViewRule
{
public override async Task RunAsync(RuleContext context)
{
this.Logger.LogTrace("()");
Block block = context.ValidationContext.Block;
ChainedHeader index = context.ValidationContext.ChainedHeader;
DeploymentFlags flags = context.Flags;
UnspentOutputSet view = (context as UtxoRuleContext).UnspentOutputSet;
this.Parent.PerformanceCounter.AddProcessedBlocks(1);
long sigOpsCost = 0;
Money fees = Money.Zero;
var checkInputs = new List<Task<bool>>();
for (int txIndex = 0; txIndex < block.Transactions.Count; txIndex++)
{
this.Parent.PerformanceCounter.AddProcessedTransactions(1);
Transaction tx = block.Transactions[txIndex];
if (!context.SkipValidation)
{
if (!this.IsProtocolTransaction(tx))
{
if (!view.HaveInputs(tx))
{
this.Logger.LogTrace("(-)[BAD_TX_NO_INPUT]");
ConsensusErrors.BadTransactionMissingInput.Throw();
}
var prevheights = new int[tx.Inputs.Count];
// Check that transaction is BIP68 final.
// BIP68 lock checks (as opposed to nLockTime checks) must
// be in ConnectBlock because they require the UTXO set.
for (int j = 0; j < tx.Inputs.Count; j++)
{
prevheights[j] = tx.Inputs[j].PrevOut.Hash == uint256.Zero
? 0
: (int) view.AccessCoins(tx.Inputs[j].PrevOut.Hash).Height;
}
if (!tx.CheckSequenceLocks(prevheights, index, flags.LockTimeFlags))
{
this.Logger.LogTrace("(-)[BAD_TX_NON_FINAL]");
ConsensusErrors.BadTransactionNonFinal.Throw();
}
}
// GetTransactionSignatureOperationCost counts 3 types of sigops:
// * legacy (always),
// * p2sh (when P2SH enabled in flags and excludes coinbase),
// * witness (when witness enabled in flags and excludes coinbase).
sigOpsCost += this.GetTransactionSignatureOperationCost(tx, view, flags);
if (sigOpsCost > this.PowConsensusOptions.MaxBlockSigopsCost)
{
this.Logger.LogTrace("(-)[BAD_BLOCK_SIG_OPS]");
ConsensusErrors.BadBlockSigOps.Throw();
}
if (!this.IsProtocolTransaction(tx))
{
this.CheckInputs(tx, view, index.Height);
fees += view.GetValueIn(tx) - tx.TotalOut;
var txData = new PrecomputedTransactionData(tx);
for (int inputIndex = 0; inputIndex < tx.Inputs.Count; inputIndex++)
{
if(tx.Inputs[inputIndex].PrevOut.Hash == uint256.Zero)
continue;
this.Parent.PerformanceCounter.AddProcessedInputs(1);
TxIn input = tx.Inputs[inputIndex];
int inputIndexCopy = inputIndex;
TxOut txout = view.GetOutputFor(input);
var checkInput = new Task<bool>(() =>
{
var checker = new TransactionChecker(tx, inputIndexCopy, txout.Value, txData);
var ctx = new ScriptEvaluationContext(this.Parent.Network);
ctx.ScriptVerify = flags.ScriptFlags;
bool verifyScriptResult = ctx.VerifyScript(input.ScriptSig, txout.ScriptPubKey, checker);
if (verifyScriptResult == false)
{
this.Logger.LogTrace("Verify script for transaction '{0}' failed, ScriptSig = '{1}', ScriptPubKey = '{2}', script evaluation error = '{3}'", tx.GetHash(), input.ScriptSig, txout.ScriptPubKey, ctx.Error);
}
return verifyScriptResult;
});
checkInput.Start();
checkInputs.Add(checkInput);
}
}
}
this.UpdateCoinView(context, tx);
}
if (!context.SkipValidation)
{
this.CheckBlockReward(context, fees, index.Height, block);
foreach (Task<bool> checkInput in checkInputs)
{
if (await checkInput.ConfigureAwait(false))
continue;
this.Logger.LogTrace("(-)[BAD_TX_SCRIPT]");
ConsensusErrors.BadTransactionScriptError.Throw();
}
}
else this.Logger.LogTrace("BIP68, SigOp cost, and block reward validation skipped for block at height {0}.", index.Height);
this.Logger.LogTrace("(-)");
}
public override void CheckInputs(Transaction transaction, UnspentOutputSet inputs, int spendHeight)
{
this.Logger.LogTrace("({0}:{1})", nameof(spendHeight), spendHeight);
if (!inputs.HaveInputs(transaction))
ConsensusErrors.BadTransactionMissingInput.Throw();
Money valueIn = Money.Zero;
Money fees = Money.Zero;
foreach (TxIn txIn in transaction.Inputs.RemoveChangePointer())
{
OutPoint prevout = txIn.PrevOut;
UnspentOutputs coins = inputs.AccessCoins(prevout.Hash);
this.CheckMaturity(coins, spendHeight);
// Check for negative or overflow input values.
valueIn += coins.TryGetOutput(prevout.N).Value;
if (!this.MoneyRange(coins.TryGetOutput(prevout.N).Value) || !this.MoneyRange(valueIn))
{
this.Logger.LogTrace("(-)[BAD_TX_INPUT_VALUE]");
ConsensusErrors.BadTransactionInputValueOutOfRange.Throw();
}
}
if (valueIn < transaction.TotalOut)
{
this.Logger.LogTrace("(-)[TX_IN_BELOW_OUT]");
ConsensusErrors.BadTransactionInBelowOut.Throw();
}
// Tally transaction fees.
Money txFee = valueIn - transaction.TotalOut;
if (txFee < 0)
{
this.Logger.LogTrace("(-)[NEGATIVE_FEE]");
ConsensusErrors.BadTransactionNegativeFee.Throw();
}
fees += txFee;
if (!this.MoneyRange(fees))
{
this.Logger.LogTrace("(-)[BAD_FEE]");
ConsensusErrors.BadTransactionFeeOutOfRange.Throw();
}
this.Logger.LogTrace("(-)");
}
public override long GetTransactionSignatureOperationCost(Transaction transaction, UnspentOutputSet inputs, DeploymentFlags flags)
{
long signatureOperationCost = this.GetLegacySignatureOperationsCount(transaction) * this.PowConsensusOptions.WitnessScaleFactor;
if (transaction.IsCoinBase)
return signatureOperationCost;
if (flags.ScriptFlags.HasFlag(ScriptVerify.P2SH))
{
signatureOperationCost += this.GetP2SHSignatureOperationsCount(transaction, inputs) * this.PowConsensusOptions.WitnessScaleFactor;
}
signatureOperationCost += (from t in transaction.Inputs.RemoveChangePointer()
let prevout = inputs.GetOutputFor(t)
select this.CountWitnessSignatureOperation(prevout.ScriptPubKey, t.WitScript, flags)).Sum();
return signatureOperationCost;
}
protected override uint GetP2SHSignatureOperationsCount(Transaction transaction, UnspentOutputSet inputs)
{
if (transaction.IsCoinBase)
return 0;
uint sigOps = 0;
foreach (TxIn txIn in transaction.Inputs.RemoveChangePointer())
{
TxOut prevout = inputs.GetOutputFor(txIn);
if (prevout.ScriptPubKey.IsPayToScriptHash(this.Parent.Network))
sigOps += prevout.ScriptPubKey.GetSigOpCount(this.Parent.Network, txIn.ScriptSig);
}
return sigOps;
}
}
}
\ No newline at end of file
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using NBitcoin;
using Stratis.Bitcoin.Consensus.Rules;
using Stratis.Bitcoin.Features.Consensus.CoinViews;
using Stratis.Bitcoin.Utilities;
namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
{
[ExecutionRule]
public class DeStreamLoadCoinviewRule : LoadCoinviewRule
{
public override async Task RunAsync(RuleContext context)
{
var utxoRuleContext = context as UtxoRuleContext;
// Load the UTXO set of the current block. UTXO may be loaded from cache or from disk.
// The UTXO set is stored in the context.
this.Logger.LogTrace("Loading UTXO set of the new block.");
utxoRuleContext.UnspentOutputSet = new DeStreamUnspentOutputSet();
using (new StopwatchDisposable(o => this.Parent.PerformanceCounter.AddUTXOFetchingTime(o)))
{
uint256[] ids = this.GetIdsToFetch(context.ValidationContext.Block, context.Flags.EnforceBIP30);
FetchCoinsResponse coins = await this.PowParent.UtxoSet.FetchCoinsAsync(ids).ConfigureAwait(false);
utxoRuleContext.UnspentOutputSet.SetCoins(coins.UnspentOutputs);
}
// Attempt to load into the cache the next set of UTXO to be validated.
// The task is not awaited so will not stall main validation process.
this.TryPrefetchAsync(context.Flags);
}
}
}
\ No newline at end of file
......@@ -38,7 +38,7 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
/// This method tries to load from cache the UTXO of the next block in a background task.
/// </summary>
/// <param name="flags">Information about activated features.</param>
private async void TryPrefetchAsync(DeploymentFlags flags)
protected async void TryPrefetchAsync(DeploymentFlags flags)
{
this.Logger.LogTrace("({0}:{1})", nameof(flags), flags);
......@@ -58,7 +58,7 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
/// <param name="block">The block with the transactions.</param>
/// <param name="enforceBIP30">Whether to enforce look up of the transaction id itself and not only the reference to previous transaction id.</param>
/// <returns>A list of transaction ids to fetch from store</returns>
private uint256[] GetIdsToFetch(Block block, bool enforceBIP30)
protected uint256[] GetIdsToFetch(Block block, bool enforceBIP30)
{
this.Logger.LogTrace("({0}:'{1}',{2}:{3})", nameof(block), block.GetHash(), nameof(enforceBIP30), enforceBIP30);
......
......@@ -11,7 +11,7 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
/// Proof of stake override for the coinview rules - BIP68, MaxSigOps and BlockReward checks.
/// </summary>
[ExecutionRule]
public sealed class PosCoinviewRule : CoinViewRule
public sealed class PosCoinviewRule : DeStreamCoinViewRule
{
/// <summary>Provides functionality for checking validity of PoS blocks.</summary>
private IStakeValidator stakeValidator;
......
......@@ -8,7 +8,7 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
{
/// <inheritdoc />
[ExecutionRule]
public sealed class PowCoinviewRule : CoinViewRule
public sealed class PowCoinviewRule : DeStreamCoinViewRule
{
/// <summary>Consensus parameters.</summary>
private NBitcoin.Consensus consensus;
......
......@@ -8,7 +8,7 @@ namespace Stratis.Bitcoin.Features.Consensus
{
public class UnspentOutputSet
{
private Dictionary<uint256, UnspentOutputs> unspents;
protected Dictionary<uint256, UnspentOutputs> unspents;
public TxOut GetOutputFor(TxIn txIn)
{
......@@ -19,7 +19,7 @@ namespace Stratis.Bitcoin.Features.Consensus
return unspent.TryGetOutput(txIn.PrevOut.N);
}
public bool HaveInputs(Transaction tx)
public virtual bool HaveInputs(Transaction tx)
{
return tx.Inputs.All(txin => this.GetOutputFor(txin) != null);
}
......@@ -29,7 +29,7 @@ namespace Stratis.Bitcoin.Features.Consensus
return this.unspents.TryGet(uint256);
}
public Money GetValueIn(Transaction tx)
public virtual Money GetValueIn(Transaction tx)
{
return tx.Inputs.Select(txin => this.GetOutputFor(txin).Value).Sum();
}
......@@ -39,7 +39,7 @@ namespace Stratis.Bitcoin.Features.Consensus
/// </summary>
/// <param name="transaction">Transaction which inputs and outputs are used for updating unspent coins list.</param>
/// <param name="height">Height of a block that contains target transaction.</param>
public void Update(Transaction transaction, int height)
public virtual void Update(Transaction transaction, int height)
{
if (!transaction.IsCoinBase)
{
......
using Microsoft.Extensions.DependencyInjection;
using Stratis.Bitcoin.Builder;
using Stratis.Bitcoin.Configuration.Logging;
using Stratis.Bitcoin.Features.Consensus;
using Stratis.Bitcoin.Features.MemoryPool.Fee;
using Stratis.Bitcoin.Features.MemoryPool.Interfaces;
using Stratis.Bitcoin.Interfaces;
namespace Stratis.Bitcoin.Features.MemoryPool
{
/// <summary>
/// A class providing extension methods for <see cref="IFullNodeBuilder" />.
/// </summary>
public static class DeStreamFullNodeBuilderMemoryPoolExtension
{
/// <summary>
/// Include the memory pool feature and related services in the full node.
/// </summary>
/// <param name="fullNodeBuilder">Full node builder.</param>
/// <returns>Full node builder.</returns>
public static IFullNodeBuilder UseDeStreamMempool(this IFullNodeBuilder fullNodeBuilder)
{
LoggingConfiguration.RegisterFeatureNamespace<MempoolFeature>("mempool");
LoggingConfiguration.RegisterFeatureNamespace<BlockPolicyEstimator>("estimatefee");
fullNodeBuilder.ConfigureFeature(features =>
{
features
.AddFeature<MempoolFeature>()
.DependOn<ConsensusFeature>()
.FeatureServices(services =>
{
services.AddSingleton<MempoolSchedulerLock>();
services.AddSingleton<ITxMempool, TxMempool>();
services.AddSingleton<BlockPolicyEstimator>();
services.AddSingleton<IMempoolValidator, DeStreamMempoolValidator>();
services.AddSingleton<MempoolOrphans>();
services.AddSingleton<DeStreamMempoolManager>();
services.AddSingleton<IPooledTransaction, DeStreamMempoolManager>();
services.AddSingleton<IPooledGetUnspentTransaction, DeStreamMempoolManager>();
services.AddSingleton<MempoolBehavior>();
services.AddSingleton<MempoolSignaled>();
services.AddSingleton<IMempoolPersistence, MempoolPersistence>();
services.AddSingleton<MempoolController>();
services.AddSingleton<MempoolSettings>();
});
});
return fullNodeBuilder;
}
}
}
\ No newline at end of file
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using NBitcoin;
using NBitcoin.BitcoinCore;
using Stratis.Bitcoin.Features.Consensus;
using Stratis.Bitcoin.Features.Consensus.CoinViews;
using Stratis.Bitcoin.Features.MemoryPool.Interfaces;
using Stratis.Bitcoin.Utilities;
namespace Stratis.Bitcoin.Features.MemoryPool
{
public class DeStreamMempoolCoinView : MempoolCoinView
{
public DeStreamMempoolCoinView(CoinView inner, ITxMempool memPool, SchedulerLock mempoolLock, IMempoolValidator mempoolValidator) : base(inner, memPool, mempoolLock, mempoolValidator)
{
this.Set = new DeStreamUnspentOutputSet();
}
public override async Task LoadViewAsync(Transaction trx)
{
// lookup all ids (duplicate ids are ignored in case a trx spends outputs from the same parent).
List<uint256> ids = trx.Inputs.Select(n => n.PrevOut.Hash).Distinct().Concat(new[] { trx.GetHash() }).ToList();
FetchCoinsResponse coins = await this.Inner.FetchCoinsAsync(ids.ToArray());
// find coins currently in the mempool
List<Transaction> mempoolcoins = await this.mempoolLock.ReadAsync(() =>
{
return this.memPool.MapTx.Values.Where(t => ids.Contains(t.TransactionHash)).Select(s => s.Transaction).ToList();
});
IEnumerable<UnspentOutputs> memOutputs = mempoolcoins.Select(s => new UnspentOutputs(TxMempool.MempoolHeight, s));
coins = new FetchCoinsResponse(coins.UnspentOutputs.Concat(memOutputs).Append(new UnspentOutputs(uint256.Zero, new Coins(new Transaction(), 0))).ToArray(), coins.BlockHash);
// the UTXO set might have been updated with a recently received block
// but the block has not yet arrived to the mempool and remove the pending trx
// from the pool (a race condition), block validation doesn't lock the mempool.
// its safe to ignore duplicats on the UTXO set as duplicates mean a trx is in
// a block and the block will soon remove the trx from the pool.
this.Set.TrySetCoins(coins.UnspentOutputs);
}
}
}
\ No newline at end of file
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using NBitcoin;
using Stratis.Bitcoin.Features.Consensus.CoinViews;
using Stratis.Bitcoin.Features.MemoryPool.Interfaces;
using Stratis.Bitcoin.Utilities;
namespace Stratis.Bitcoin.Features.MemoryPool
{
public class DeStreamMempoolManager : MempoolManager
{
public DeStreamMempoolManager(MempoolSchedulerLock mempoolLock, ITxMempool memPool, IMempoolValidator validator, IDateTimeProvider dateTimeProvider, MempoolSettings mempoolSettings, IMempoolPersistence mempoolPersistence, CoinView coinView, ILoggerFactory loggerFactory, Network network) : base(mempoolLock, memPool, validator, dateTimeProvider, mempoolSettings, mempoolPersistence, coinView, loggerFactory, network)
{
}
public override async Task<UnspentOutputs> GetUnspentTransactionAsync(uint256 trxid)
{
TxMempoolInfo txInfo = await this.InfoAsync(trxid);
if (txInfo == null)
{
return null;
}
var memPoolCoinView = new DeStreamMempoolCoinView(this.coinView, this.memPool, this.MempoolLock, this.Validator);
await memPoolCoinView.LoadViewAsync(txInfo.Trx);
return memPoolCoinView.GetCoins(trxid);
}
}
}
\ No newline at end of file
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using NBitcoin;
using Stratis.Bitcoin.Base.Deployments;
using Stratis.Bitcoin.Configuration;
using Stratis.Bitcoin.Consensus.Rules;
using Stratis.Bitcoin.Features.Consensus.CoinViews;
using Stratis.Bitcoin.Features.Consensus.Rules.CommonRules;
using Stratis.Bitcoin.Features.MemoryPool.Interfaces;
using Stratis.Bitcoin.Utilities;
namespace Stratis.Bitcoin.Features.MemoryPool
{
public class DeStreamMempoolValidator : MempoolValidator
{
public DeStreamMempoolValidator(ITxMempool memPool, MempoolSchedulerLock mempoolLock,
IDateTimeProvider dateTimeProvider, MempoolSettings mempoolSettings, ConcurrentChain chain,
CoinView coinView, ILoggerFactory loggerFactory, NodeSettings nodeSettings, IConsensusRules consensusRules)
: base(memPool, mempoolLock, dateTimeProvider, mempoolSettings, chain, coinView, loggerFactory,
nodeSettings, consensusRules)
{
}
protected override async Task AcceptToMemoryPoolWorkerAsync(MempoolValidationState state, Transaction tx,
List<uint256> vHashTxnToUncache)
{
var context = new MempoolValidationContext(tx, state);
this.PreMempoolChecks(context);
// create the MemPoolCoinView and load relevant utxoset
context.View = new DeStreamMempoolCoinView(this.coinView, this.memPool, this.mempoolLock, this);
await context.View.LoadViewAsync(context.Transaction).ConfigureAwait(false);
// adding to the mem pool can only be done sequentially
// use the sequential scheduler for that.
await this.mempoolLock.WriteAsync(() =>
{
// is it already in the memory pool?
if (this.memPool.Exists(context.TransactionHash))
state.Invalid(MempoolErrors.InPool).Throw();
// Check for conflicts with in-memory transactions
this.CheckConflicts(context);
this.CheckMempoolCoinView(context);
this.CreateMempoolEntry(context, state.AcceptTime);
this.CheckSigOps(context);
this.CheckFee(context);
this.CheckRateLimit(context, state.LimitFree);
this.CheckAncestors(context);
this.CheckReplacment(context);
this.CheckAllInputs(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.memPool.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 = this.IsCurrentForFeeEstimation() && this.memPool.HasNoInputsOf(tx);
// Store transaction in memory
this.memPool.AddUnchecked(context.TransactionHash, context.Entry, context.SetAncestors,
validForFeeEstimation);
// trim mempool and check if tx was trimmed
if (!state.OverrideMempoolLimit)
{
this.LimitMempoolSize(this.mempoolSettings.MaxMempool * 1000000,
this.mempoolSettings.MempoolExpiry * 60 * 60);
if (!this.memPool.Exists(context.TransactionHash))
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.memPool.Size;
state.MempoolDynamicSize = this.memPool.DynamicMemoryUsage();
this.PerformanceCounter.SetMempoolSize(state.MempoolSize);
this.PerformanceCounter.SetMempoolDynamicSize(state.MempoolDynamicSize);
this.PerformanceCounter.AddHitCount(1);
});
}
protected override void CheckMempoolCoinView(MempoolValidationContext context)
{
Guard.Assert(context.View != null);
context.LockPoints = new LockPoints();
// do we already have it?
if (context.View.HaveCoins(context.TransactionHash))
context.State.Invalid(MempoolErrors.AlreadyKnown).Throw();
// do all inputs exist?
// Note that this does not check for the presence of actual outputs (see the next check for that),
// and only helps with filling in pfMissingInputs (to determine missing vs spent).
foreach (TxIn txin in context.Transaction.Inputs.RemoveChangePointer())
{
if (context.View.HaveCoins(txin.PrevOut.Hash)) continue;
context.State.MissingInputs = true;
context.State.Fail(new MempoolError())
.Throw(); // fMissingInputs and !state.IsInvalid() is used to detect this condition, don't set state.Invalid()
}
// are the actual inputs available?
if (!context.View.HaveInputs(context.Transaction))
context.State.Invalid(MempoolErrors.BadInputsSpent).Throw();
}
}
}
\ No newline at end of file
......@@ -18,10 +18,10 @@ namespace Stratis.Bitcoin.Features.MemoryPool
{
/// <summary>Transaction memory pool for managing transactions in the memory pool.</summary>
/// <remarks>All access to this object has to be protected by <see cref="mempoolLock"/>.</remarks>
private readonly ITxMempool memPool;
protected readonly ITxMempool memPool;
/// <summary>A lock for protecting access to <see cref="memPool"/>.</summary>
private readonly SchedulerLock mempoolLock;
protected readonly SchedulerLock mempoolLock;
/// <summary>Memory pool validator for validating transactions.</summary>
private readonly IMempoolValidator mempoolValidator;
......@@ -45,7 +45,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
/// <summary>
/// Gets the unspent transaction output set.
/// </summary>
public UnspentOutputSet Set { get; private set; }
public UnspentOutputSet Set { get; protected set; }
/// <summary>
/// Backing coin view instance.
......@@ -75,7 +75,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
/// Load the coin view for a memory pool transaction.
/// </summary>
/// <param name="trx">Memory pool transaction.</param>
public async Task LoadViewAsync(Transaction trx)
public virtual async Task LoadViewAsync(Transaction trx)
{
// lookup all ids (duplicate ids are ignored in case a trx spends outputs from the same parent).
List<uint256> ids = trx.Inputs.Select(n => n.PrevOut.Hash).Distinct().Concat(new[] { trx.GetHash() }).ToList();
......
......@@ -32,10 +32,10 @@ namespace Stratis.Bitcoin.Features.MemoryPool
private readonly ILogger mempoolLogger;
/// <summary>Transaction memory pool for managing transactions in the memory pool.</summary>
private readonly ITxMempool memPool;
protected readonly ITxMempool memPool;
/// <summary>Coin view of the memory pool.</summary>
private readonly CoinView coinView;
protected readonly CoinView coinView;
private readonly Network network;
......@@ -208,7 +208,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
}
/// <inheritdoc />
public async Task<UnspentOutputs> GetUnspentTransactionAsync(uint256 trxid)
public virtual async Task<UnspentOutputs> GetUnspentTransactionAsync(uint256 trxid)
{
TxMempoolInfo txInfo = await this.InfoAsync(trxid);
if (txInfo == null)
......
......@@ -115,13 +115,13 @@ namespace Stratis.Bitcoin.Features.MemoryPool
private const int MaxFeeEstimationTipAge = 3 * 60 * 60;
/// <summary>A lock for managing asynchronous access to memory pool.</summary>
private readonly MempoolSchedulerLock mempoolLock;
protected readonly MempoolSchedulerLock mempoolLock;
/// <summary>Date and time information provider.</summary>
private readonly IDateTimeProvider dateTimeProvider;
/// <summary>Settings from the memory pool.</summary>
private readonly MempoolSettings mempoolSettings;
protected readonly MempoolSettings mempoolSettings;
/// <summary>Thread safe access to the best chain of block headers (that the node is aware of) from genesis.</summary>
private readonly ConcurrentChain chain;
......@@ -133,10 +133,10 @@ namespace Stratis.Bitcoin.Features.MemoryPool
private readonly IConsensusRules consensusRules;
/// <summary>Transaction memory pool for managing transactions in the memory pool.</summary>
private readonly ITxMempool memPool;
protected readonly ITxMempool memPool;
/// <summary>Instance logger for memory pool validator.</summary>
private readonly ILogger logger;
protected readonly ILogger logger;
/// <summary>Minimum fee rate for a relay transaction.</summary>
private readonly FeeRate minRelayTxFee;
......@@ -427,7 +427,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
/// <param name="state">Validation state for creating the validation context.</param>
/// <param name="tx">The transaction to validate.</param>
/// <param name="vHashTxnToUncache">Not currently used</param>
private async Task AcceptToMemoryPoolWorkerAsync(MempoolValidationState state, Transaction tx, List<uint256> vHashTxnToUncache)
protected virtual async Task AcceptToMemoryPoolWorkerAsync(MempoolValidationState state, Transaction tx, List<uint256> vHashTxnToUncache)
{
var context = new MempoolValidationContext(tx, state);
......@@ -499,7 +499,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
/// If a conflict is found it is added to the validation context.
/// </summary>
/// <param name="context">Current validation context.</param>
private void CheckConflicts(MempoolValidationContext context)
protected void CheckConflicts(MempoolValidationContext context)
{
context.SetConflicts = new List<uint256>();
foreach (TxIn txin in context.Transaction.Inputs)
......@@ -549,7 +549,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
/// These checks don't need to run under the memory pool lock.
/// </summary>
/// <param name="context">Current validation context.</param>
private void PreMempoolChecks(MempoolValidationContext context)
protected void PreMempoolChecks(MempoolValidationContext context)
{
// TODO: fix this to use dedicated mempool rules.
new CheckPowTransactionRule { Logger = this.logger }.CheckTransaction(this.network, this.ConsensusOptions, context.Transaction);
......@@ -662,7 +662,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
/// Checks if already in coin view, and missing and unavailable inputs.
/// </summary>
/// <param name="context">Validation context.</param>
private void CheckMempoolCoinView(MempoolValidationContext context)
protected virtual void CheckMempoolCoinView(MempoolValidationContext context)
{
Guard.Assert(context.View != null);
......@@ -697,7 +697,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
/// and absurdly high fees.
/// </summary>
/// <param name="context">Current validation context.</param>
private void CheckFee(MempoolValidationContext context)
protected void CheckFee(MempoolValidationContext context)
{
Money mempoolRejectFee = this.memPool.GetMinFee(this.mempoolSettings.MaxMempool * 1000000).GetFee(context.EntrySize);
if (mempoolRejectFee > 0 && context.ModifiedFees < mempoolRejectFee)
......@@ -719,7 +719,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
/// Check that the transaction doesn't have an excessive number of sigops.
/// </summary>
/// <param name="context">Current validation context.</param>
private void CheckSigOps(MempoolValidationContext context)
protected void CheckSigOps(MempoolValidationContext context)
{
// Check that the transaction doesn't have an excessive number of
// sigops, making it impossible to mine. Since the coinbase transaction
......@@ -737,7 +737,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
/// </summary>
/// <param name="context">Current validation context.</param>
/// <param name="acceptTime">The accept time to use for the entry.</param>
private void CreateMempoolEntry(MempoolValidationContext context, long acceptTime)
protected void CreateMempoolEntry(MempoolValidationContext context, long acceptTime)
{
// Only accept BIP68 sequence locked transactions that can be mined in the next
// block; we don't want our mempool filled up with transactions that can't
......@@ -787,7 +787,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
/// The new transaction must have sufficient fees to pay for it's bandwidth.
/// </summary>
/// <param name="context">Current validation context.</param>
private void CheckReplacment(MempoolValidationContext context)
protected void CheckReplacment(MempoolValidationContext context)
{
// Check if it's economically rational to mine this transaction rather
// than the ones it replaces.
......@@ -913,7 +913,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
/// </summary>
/// <param name="context">Current validation context</param>
/// <param name="limitFree">Whether to limit free transactioins</param>
private void CheckRateLimit(MempoolValidationContext context, bool limitFree)
protected void CheckRateLimit(MempoolValidationContext context, bool limitFree)
{
// TODO: sort this logic
return;
......@@ -925,7 +925,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
/// Checks for a transaction that spends outputs that would be replaced by it.
/// </summary>
/// <param name="context">Current validation context.</param>
private void CheckAncestors(MempoolValidationContext context)
protected void CheckAncestors(MempoolValidationContext context)
{
// Calculate in-mempool ancestors, up to a limit.
context.SetAncestors = new TxMempool.SetEntries();
......@@ -962,7 +962,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
/// </summary>
/// <param name="limit">New size.</param>
/// <param name="age">AAge to use for calculating expired transactions.</param>
private void LimitMempoolSize(long limit, long age)
protected void LimitMempoolSize(long limit, long age)
{
int expired = this.memPool.Expire(this.dateTimeProvider.GetTime() - age);
if (expired != 0)
......@@ -977,7 +977,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
/// It should only count for fee estimation if the node is not behind.
/// </summary>
/// <returns>Whether current for fee estimation.</returns>
private bool IsCurrentForFeeEstimation()
protected bool IsCurrentForFeeEstimation()
{
// TODO: implement method (find a way to know if in IBD)
......@@ -995,7 +995,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
/// Checks against <see cref="ScriptVerify.Standard"/> and <see cref="ScriptVerify.P2SH"/>
/// </summary>
/// <param name="context">Current validation context.</param>
private void CheckAllInputs(MempoolValidationContext context)
protected void CheckAllInputs(MempoolValidationContext context)
{
var scriptVerifyFlags = ScriptVerify.Standard;
if (!this.mempoolSettings.RequireStandard)
......
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Stratis Bitcoin Features MemoryPool</Description>
<AssemblyTitle>Stratis.Bitcoin.Features.MemoryPool</AssemblyTitle>
......@@ -14,7 +13,7 @@
<GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
<Version>1.1.12-beta</Version>
<Version>1.1.12-beta</Version>
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
<ApplicationIcon />
<OutputTypeEx>library</OutputTypeEx>
......@@ -27,24 +26,20 @@
<PackageReference Include="Microsoft.Extensions.Logging.Filter" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.1.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\NBitcoin\NBitcoin.csproj" />
<ProjectReference Include="..\Stratis.Bitcoin.Features.Consensus\Stratis.Bitcoin.Features.Consensus.csproj" />
<ProjectReference Include="..\Stratis.Bitcoin\Stratis.Bitcoin.csproj" />
</ItemGroup>
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<OutputType>Library</OutputType>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<DefineConstants>$(DefineConstants);NETCORE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<NoWarn>1701;1702;1705;IDE0008;</NoWarn>
<DocumentationFile></DocumentationFile>
<DocumentationFile>
</DocumentationFile>
</PropertyGroup>
</Project>
</Project>
\ No newline at end of file
......@@ -56,7 +56,7 @@ namespace Stratis.Bitcoin.Features.Miner
services.AddSingleton<IBlockProvider, BlockProvider>();
services.AddSingleton<BlockDefinition, DeStreamPowBlockDefinition>();
services.AddSingleton<BlockDefinition, DeStreamPosPowBlockDefinition>();
services.AddSingleton<BlockDefinition, DeStreamPosBlockDefinition>();
services.AddSingleton<BlockDefinition, PosBlockDefinition>();
services.AddSingleton<MinerController>();
services.AddSingleton<MiningRPCController>();
services.AddSingleton<MinerSettings>();
......
using System.Linq;
using Microsoft.Extensions.Logging;
using NBitcoin;
using NBitcoin.DataEncoders;
using Stratis.Bitcoin.Features.Consensus;
using Stratis.Bitcoin.Features.Consensus.Interfaces;
using Stratis.Bitcoin.Features.Consensus.Rules.CommonRules;
using Stratis.Bitcoin.Features.MemoryPool;
using Stratis.Bitcoin.Features.MemoryPool.Interfaces;
using Stratis.Bitcoin.Utilities;
namespace Stratis.Bitcoin.Features.Miner
{
public class DeStreamPosBlockDefinition : PosBlockDefinition
{
public DeStreamPosBlockDefinition(IConsensusLoop consensusLoop, IDateTimeProvider dateTimeProvider,
ILoggerFactory loggerFactory, ITxMempool mempool, MempoolSchedulerLock mempoolLock, Network network,
IStakeChain stakeChain, IStakeValidator stakeValidator) : base(consensusLoop, dateTimeProvider,
loggerFactory, mempool, mempoolLock, network, stakeChain, stakeValidator)
{
}
protected override void OnBuild(ChainedHeader chainTip, Script scriptPubKey)
{
this.Configure();
this.ChainTip = chainTip;
this.block = this.BlockTemplate.Block;
this.scriptPubKey = scriptPubKey;
this.CreateCoinbase();
this.ComputeBlockVersion();
// TODO: MineBlocksOnDemand
// -regtest only: allow overriding block.nVersion with
// -blockversion=N to test forking scenarios
//if (this.network. chainparams.MineBlocksOnDemand())
// pblock->nVersion = GetArg("-blockversion", pblock->nVersion);
this.MedianTimePast = Utils.DateTimeToUnixTime(this.ChainTip.GetMedianTimePast());
this.LockTimeCutoff =
MempoolValidator.StandardLocktimeVerifyFlags.HasFlag(Transaction.LockTimeFlags.MedianTimePast)
? this.MedianTimePast
: this.block.Header.Time;
// TODO: Implement Witness Code
// Decide whether to include witness transactions
// This is only needed in case the witness softfork activation is reverted
// (which would require a very deep reorganization) or when
// -promiscuousmempoolflags is used.
// TODO: replace this with a call to main to assess validity of a mempool
// transaction (which in most cases can be a no-op).
this.IncludeWitness = false; //IsWitnessEnabled(pindexPrev, chainparams.GetConsensus()) && fMineWitnessTx;
// add transactions from the mempool
int nPackagesSelected;
int nDescendantsUpdated;
this.AddTransactions(out nPackagesSelected, out nDescendantsUpdated);
this.LastBlockTx = this.BlockTx;
this.LastBlockSize = this.BlockSize;
this.LastBlockWeight = this.BlockWeight;
// TODO: Implement Witness Code
// pblocktemplate->CoinbaseCommitment = GenerateCoinbaseCommitment(*pblock, pindexPrev, chainparams.GetConsensus());
var coinviewRule = this.ConsensusLoop.ConsensusRules.GetRule<CoinViewRule>();
this.coinbase.Outputs[0].Value = (long) (this.fees.Satoshi * (1 - this.Network.DeStreamFeePart)) +
coinviewRule.GetProofOfWorkReward(this.height);
this.coinbase.Outputs[1].Value = (long) (this.fees.Satoshi * this.Network.DeStreamFeePart);
this.BlockTemplate.TotalFee = this.fees;
int nSerializeSize = this.block.GetSerializedSize();
this.logger.LogDebug(
"Serialized size is {0} bytes, block weight is {1}, number of txs is {2}, tx fees are {3}, number of sigops is {4}.",
nSerializeSize, coinviewRule.GetBlockWeight(this.block), this.BlockTx, this.fees, this.BlockSigOpsCost);
this.UpdateHeaders();
}
protected override void CreateCoinbase()
{
base.CreateCoinbase();
Script deStreamAddressKey = new KeyId(new uint160(Encoders.Base58Check
.DecodeData(this.Network.DeStreamWallets.First())
.Skip(this.Network.Base58Prefixes[(int) Base58Type.PUBKEY_ADDRESS].Length).ToArray())).ScriptPubKey;
this.coinbase.AddOutput(new TxOut(Money.Zero, deStreamAddressKey));
}
}
}
\ No newline at end of file
......@@ -178,7 +178,7 @@ namespace Stratis.Bitcoin.Features.Miner
long coinstakeInputValue = coinstakeInput.TxOut.Value + reward;
// Set output amount.
this.SetOutputAmount(coinstakeContext.CoinstakeTx.Outputs, coinstakeInputValue, fees);
this.SetOutputAmount(coinstakeContext.CoinstakeTx.Outputs, coinstakeInput.TxOut.Value, fees, reward);
// Sign.
if (!this.SignTransactionInput(coinstakeInput, coinstakeContext.CoinstakeTx))
......@@ -204,20 +204,20 @@ namespace Stratis.Bitcoin.Features.Miner
return true;
}
private void SetOutputAmount(TxOutList outputs, long coinstakeInputValue, long fees)
private void SetOutputAmount(TxOutList outputs, long totalOut, long fees, long reward)
{
if (outputs.Count == 4)
{
outputs[1].Value = coinstakeInputValue / 2 / Money.CENT * Money.CENT;
outputs[2].Value = coinstakeInputValue - outputs[1].Value;
outputs[3].Value = (long) (fees * this.network.DeStreamFeePart);
outputs[1].Value = (totalOut + reward) / 2 / Money.CENT * Money.CENT;
outputs[2].Value = totalOut + reward - outputs[1].Value;
outputs[3].Value = fees - reward;
this.logger.LogTrace("Coinstake first output value is {0}, second is {1}, third is {3}.",
outputs[1].Value, outputs[2].Value, outputs[3].Value);
}
else
{
outputs[1].Value = coinstakeInputValue;
outputs[2].Value = (long) (fees * this.network.DeStreamFeePart);
outputs[1].Value = totalOut + reward;
outputs[2].Value = fees - reward;
this.logger.LogTrace("Coinstake first output value is {0}, second is {1} .", outputs[1].Value,
outputs[2].Value);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment