/// <summary> /// Execute the contract and add all relevant fees and refunds to the block. /// </summary> /// <remarks>TODO: At some point we need to change height to a ulong.</remarks> private IContractExecutionResult ExecuteSmartContract(TxMempoolEntry mempoolEntry) { // This coinview object can be altered by consensus whilst we're mining. // If this occurred, we would be mining on top of the wrong tip anyway, so // it's okay to throw a ConsensusError which is handled by the miner, and continue. GetSenderResult getSenderResult = this.senderRetriever.GetSender(mempoolEntry.Transaction, this.coinView, this.inBlock.Select(x => x.Transaction).ToList()); if (!getSenderResult.Success) { throw new ConsensusErrorException(new ConsensusError("sc-block-assembler-addcontracttoblock", getSenderResult.Error)); } IContractTransactionContext transactionContext = new ContractTransactionContext((ulong)this.height, this.coinbaseAddress, mempoolEntry.Fee, getSenderResult.Sender, mempoolEntry.Transaction); IContractExecutor executor = this.executorFactory.CreateExecutor(this.stateSnapshot, transactionContext); IContractExecutionResult result = executor.Execute(transactionContext); this.blockGasConsumed += result.GasConsumed; // As we're not storing receipts, can use only consensus fields. var receipt = new Receipt( new uint256(this.stateSnapshot.Root), result.GasConsumed, result.Logs.ToArray() ); this.receipts.Add(receipt); return(result); }
public void SpentOutput_Returns_False() { // Construct transaction. Transaction transaction = this.network.CreateTransaction(); transaction.Inputs.Add(new TxIn(new OutPoint(uint256.One, 0))); // Setup coinview to return as if the PrevOut is spent. var unspentOutputs = new UnspentOutputs(); unspentOutputs.Outputs = new TxOut[] { null }; var unspentOutputArray = new UnspentOutputs[] { unspentOutputs }; this.coinView.Setup(x => x.FetchCoinsAsync(It.IsAny <uint256[]>(), default(CancellationToken))) .ReturnsAsync(new FetchCoinsResponse(unspentOutputArray, uint256.Zero)); var blockTxs = new List <Transaction>(); // Retriever fails but doesn't throw exception GetSenderResult result = this.senderRetriever.GetSender(transaction, this.coinView.Object, blockTxs); Assert.False(result.Success); Assert.Equal(result.Error, SenderRetriever.OutputAlreadySpent); }
public IActionResult GetBalancesOverAmount(int blockHeight, decimal amount) { Money thresholdBalance = Money.Coins(amount); CoinviewContext coinView = GetCoinviewAtHeight(blockHeight); Dictionary <Script, Money> balances = GetBalances(coinView); List <Script> addressesWithThreshold = balances .Where(x => x.Value >= thresholdBalance) .Select(x => x.Key) .ToList(); List <string> base58Addresses = new List <string>(); foreach (Script script in addressesWithThreshold) { GetSenderResult senderResult = this.GetAddressFromScript(script); if (senderResult.Success) { base58Addresses.Add(senderResult.Sender.ToBase58Address(this.network)); } else { this.logger.LogWarning($"{script} has a balance of {balances[script]} but is not a P2PK or P2PKH."); } } return(Json(base58Addresses)); }
public void InvalidPrevOutIndex_Returns_False() { // Construct transaction with a reference to prevout index of 2. Transaction transaction = this.network.CreateTransaction(); transaction.Inputs.Add(new TxIn(new OutPoint(uint256.One, 2))); // Setup coinview to return a prevout with only 1 output. AKA index 2 doesn't exist. var unspentOutputs = new UnspentOutputs(); unspentOutputs.Outputs = new TxOut[] { new TxOut(0, new Script()) }; var unspentOutputArray = new UnspentOutputs[] { unspentOutputs }; this.coinView.Setup(x => x.FetchCoinsAsync(It.IsAny <uint256[]>(), default(CancellationToken))) .ReturnsAsync(new FetchCoinsResponse(unspentOutputArray, uint256.Zero)); var blockTxs = new List <Transaction>(); // Retriever fails but doesn't throw IndexOutOfRangeException GetSenderResult result = this.senderRetriever.GetSender(transaction, this.coinView.Object, blockTxs); Assert.False(result.Success); Assert.Equal(result.Error, SenderRetriever.InvalidOutputIndex); }
public void SpentOutput_Returns_False() { // Construct transaction. Transaction transaction = this.network.CreateTransaction(); transaction.Inputs.Add(new TxIn(new OutPoint(uint256.One, 0))); // Setup coinview to return as if the PrevOut is spent (i.e. Coins is null). var unspentOutput = new UnspentOutput(transaction.Inputs[0].PrevOut, null); var unspentOutputArray = new UnspentOutput[] { unspentOutput }; var fetchResponse = new FetchCoinsResponse(); fetchResponse.UnspentOutputs.Add(unspentOutputArray[0].OutPoint, unspentOutput); this.coinView.Setup(x => x.FetchCoins(It.IsAny <OutPoint[]>())) .Returns(fetchResponse); var blockTxs = new List <Transaction>(); // Retriever fails but doesn't throw exception GetSenderResult result = this.senderRetriever.GetSender(transaction, this.coinView.Object, blockTxs); Assert.False(result.Success); Assert.Equal(SenderRetriever.OutputAlreadySpent, result.Error); }
/// <inheritdoc/> public override BlockTemplate Build(ChainedHeader chainTip, Script scriptPubKey) { this.logger.LogTrace("()"); GetSenderResult getSenderResult = this.senderRetriever.GetAddressFromScript(scriptPubKey); if (!getSenderResult.Success) { throw new ConsensusErrorException(new ConsensusError("sc-block-assembler-createnewblock", getSenderResult.Error)); } this.coinbaseAddress = getSenderResult.Sender; this.stateSnapshot = this.stateRoot.GetSnapshotTo(((SmartContractBlockHeader)this.ConsensusLoop.Tip.Header).HashStateRoot.ToBytes()); this.refundOutputs.Clear(); this.receipts.Clear(); base.OnBuild(chainTip, scriptPubKey); this.coinbase.Outputs.AddRange(this.refundOutputs); this.logger.LogTrace("(-)"); return(this.BlockTemplate); }
/// <summary> /// Execute the contract and add all relevant fees and refunds to the block. /// </summary> /// <remarks>TODO: At some point we need to change height to a ulong.</remarks> private ISmartContractExecutionResult ExecuteSmartContract(TxMempoolEntry mempoolEntry) { this.logger.LogTrace("()"); GetSenderResult getSenderResult = this.senderRetriever.GetSender(mempoolEntry.Transaction, this.coinView, this.inBlock.Select(x => x.Transaction).ToList()); if (!getSenderResult.Success) { throw new ConsensusErrorException(new ConsensusError("sc-block-assembler-addcontracttoblock", getSenderResult.Error)); } ISmartContractTransactionContext transactionContext = new SmartContractTransactionContext((ulong)this.height, this.coinbaseAddress, mempoolEntry.Fee, getSenderResult.Sender, mempoolEntry.Transaction); ISmartContractExecutor executor = this.executorFactory.CreateExecutor(this.stateSnapshot, transactionContext); ISmartContractExecutionResult result = executor.Execute(transactionContext); var receipt = new Receipt( new uint256(this.stateSnapshot.Root), result.GasConsumed, result.Logs.ToArray() ); this.receipts.Add(receipt); this.logger.LogTrace("(-)"); return(result); }
public void CheckTransaction(MempoolValidationContext context) { // If wanting to execute a contract, we must be able to get the sender. if (context.Transaction.Outputs.Any(x => x.ScriptPubKey.IsSmartContractExec())) { GetSenderResult result = this.senderRetriever.GetSender(context.Transaction, context.View); if (!result.Success) { new ConsensusError("cant-get-sender", "smart contract output without a P2PKH as the first input to the tx.").Throw(); } } }
private void CheckTransactionInsideBlock(Transaction transaction, ICoinView coinView, IList <Transaction> blockTxs) { // If wanting to execute a contract, we must be able to get the sender. if (transaction.Outputs.Any(x => x.ScriptPubKey.IsSmartContractExec())) { GetSenderResult result = this.senderRetriever.GetSender(transaction, coinView, blockTxs); if (!result.Success) { new ConsensusError("cant-get-sender", "smart contract output without a P2PKH as the first input to the tx.").Throw(); } } }
public void NoCoinViewOrTransactions_Returns_False() { // Construct transaction. Transaction transaction = this.network.CreateTransaction(); transaction.Inputs.Add(new TxIn()); // Retriever fails - no transactions to draw from GetSenderResult result = this.senderRetriever.GetSender(transaction, null, null); Assert.False(result.Success); Assert.Equal(result.Error, SenderRetriever.UnableToGetSender); }
/// <inheritdoc/> public override BlockTemplate Build(ChainedHeader chainTip, Script scriptPubKeyIn) { GetSenderResult getSenderResult = this.senderRetriever.GetAddressFromScript(scriptPubKeyIn); this.coinbaseAddress = (getSenderResult.Success) ? getSenderResult.Sender : uint160.Zero; this.stateSnapshot = this.stateRoot.GetSnapshotTo(((ISmartContractBlockHeader)this.ConsensusManager.Tip.Header).HashStateRoot.ToBytes()); this.refundOutputs.Clear(); this.receipts.Clear(); base.OnBuild(chainTip, scriptPubKeyIn); this.coinbase.Outputs.AddRange(this.refundOutputs); return(this.BlockTemplate); }
/// <summary> /// Retrieves the context object to be given to the contract executor. /// </summary> private IContractTransactionContext GetSmartContractTransactionContext(RuleContext context, Transaction transaction) { ulong blockHeight = Convert.ToUInt64(context.ValidationContext.ChainedHeaderToValidate.Height); GetSenderResult getSenderResult = this.ContractCoinviewRule.SenderRetriever.GetSender(transaction, ((PowConsensusRuleEngine)this.Parent).UtxoSet, this.blockTxsProcessed); if (!getSenderResult.Success) throw new ConsensusErrorException(new ConsensusError("sc-consensusvalidator-executecontracttransaction-sender", getSenderResult.Error)); Script coinbaseScriptPubKey = context.ValidationContext.BlockToValidate.Transactions[0].Outputs[0].ScriptPubKey; GetSenderResult getCoinbaseResult = this.ContractCoinviewRule.SenderRetriever.GetAddressFromScript(coinbaseScriptPubKey); if (!getCoinbaseResult.Success) throw new ConsensusErrorException(new ConsensusError("sc-consensusvalidator-executecontracttransaction-coinbase", getCoinbaseResult.Error)); Money mempoolFee = transaction.GetFee(((UtxoRuleContext)context).UnspentOutputSet); return new ContractTransactionContext(blockHeight, getCoinbaseResult.Sender, mempoolFee, getSenderResult.Sender, transaction); }
private GetSenderResult GetAddressFromScript(Script script) { // Borrowed from SenderRetriever PubKey payToPubKey = PayToPubkeyTemplate.Instance.ExtractScriptPubKeyParameters(script); if (payToPubKey != null) { var address = new uint160(payToPubKey.Hash.ToBytes()); return(GetSenderResult.CreateSuccess(address)); } if (PayToPubkeyHashTemplate.Instance.CheckScriptPubKey(script)) { var address = new uint160(PayToPubkeyHashTemplate.Instance.ExtractScriptPubKeyParameters(script).ToBytes()); return(GetSenderResult.CreateSuccess(address)); } return(GetSenderResult.CreateFailure("Addresses can only be retrieved from Pay to Pub Key or Pay to Pub Key Hash")); }
/// <summary> /// Retrieves the context object to be given to the contract executor. /// </summary> public IContractTransactionContext GetSmartContractTransactionContext(RuleContext context, Transaction transaction) { ulong blockHeight = Convert.ToUInt64(context.ValidationContext.ChainedHeaderToValidate.Height); GetSenderResult getSenderResult = this.senderRetriever.GetSender(transaction, this.coinView, this.blockTxsProcessed); if (!getSenderResult.Success) { throw new ConsensusErrorException(new ConsensusError("sc-consensusvalidator-executecontracttransaction-sender", getSenderResult.Error)); } Script coinbaseScriptPubKey = context.ValidationContext.BlockToValidate.Transactions[0].Outputs[0].ScriptPubKey; GetSenderResult getCoinbaseResult = this.senderRetriever.GetAddressFromScript(coinbaseScriptPubKey); uint160 coinbaseAddress = (getCoinbaseResult.Success) ? getCoinbaseResult.Sender : uint160.Zero; Money mempoolFee = transaction.GetFee(((UtxoRuleContext)context).UnspentOutputSet); return(new ContractTransactionContext(blockHeight, coinbaseAddress, mempoolFee, getSenderResult.Sender, transaction)); }
public void MissingOutput_Returns_False() { // Construct transaction. Transaction transaction = this.network.CreateTransaction(); transaction.Inputs.Add(new TxIn()); // Setup coinview to return as if the PrevOut does not exist. var fetchResponse = new FetchCoinsResponse(); this.coinView.Setup(x => x.FetchCoins(It.IsAny <OutPoint[]>())) .Returns(fetchResponse); var blockTxs = new List <Transaction>(); // Retriever fails but doesn't throw exception GetSenderResult result = this.senderRetriever.GetSender(transaction, this.coinView.Object, blockTxs); Assert.False(result.Success); Assert.Equal(SenderRetriever.OutputsNotInCoinView, result.Error); }
/// <summary> /// Execute the contract and add all relevant fees and refunds to the block. /// </summary> /// <remarks>TODO: At some point we need to change height to a ulong.</remarks> /// <param name="mempoolEntry">The mempool entry containing the smart contract transaction.</param> private IContractExecutionResult ExecuteSmartContract(TxMempoolEntry mempoolEntry) { // This coinview object can be altered by consensus whilst we're mining. // If this occurred, we would be mining on top of the wrong tip anyway, so // it's okay to throw a ConsensusError which is handled by the miner, and continue. GetSenderResult getSenderResult = this.senderRetriever.GetSender(mempoolEntry.Transaction, this.coinView, this.inBlock.Select(x => x.Transaction).ToList()); if (!getSenderResult.Success) { throw new ConsensusErrorException(new ConsensusError("sc-block-assembler-addcontracttoblock", getSenderResult.Error)); } IContractTransactionContext transactionContext = new ContractTransactionContext((ulong)this.height, this.coinbaseAddress, mempoolEntry.Fee, getSenderResult.Sender, mempoolEntry.Transaction); IContractExecutor executor = this.executorFactory.CreateExecutor(this.stateSnapshot, transactionContext); IContractExecutionResult result = executor.Execute(transactionContext); Result <ContractTxData> deserializedCallData = this.callDataSerializer.Deserialize(transactionContext.Data); this.blockGasConsumed += result.GasConsumed; // Store all fields. We will reuse these in CoinviewRule. var receipt = new Receipt( new uint256(this.stateSnapshot.Root), result.GasConsumed, result.Logs.ToArray(), transactionContext.TransactionHash, transactionContext.Sender, result.To, result.NewContractAddress, !result.Revert, result.Return?.ToString(), result.ErrorMessage, deserializedCallData.Value.GasPrice, transactionContext.TxOutValue, deserializedCallData.Value.IsCreateContract ? null : deserializedCallData.Value.MethodName); this.receipts.Add(receipt); return(result); }
public void InvalidPrevOutIndex_InsideBlock_Returns_False() { // Construct transaction with a reference to prevout index of 2. Transaction prevOutTransaction = this.network.CreateTransaction(); prevOutTransaction.AddOutput(0, new Script()); Transaction transaction = this.network.CreateTransaction(); transaction.Inputs.Add(new TxIn(new OutPoint(prevOutTransaction, 2))); // Put referenced PrevOut as if it was earlier in the block var blockTxs = new List <Transaction> { prevOutTransaction }; // Retriever fails but doesn't throw IndexOutOfRangeException GetSenderResult result = this.senderRetriever.GetSender(transaction, null, blockTxs); Assert.False(result.Success); Assert.Equal(result.Error, SenderRetriever.InvalidOutputIndex); }
public void InvalidPrevOutIndex_Returns_False() { // Construct transaction with a reference to prevout index of 2. Transaction transaction = this.network.CreateTransaction(); transaction.Inputs.Add(new TxIn(new OutPoint(uint256.One, 2))); // Here we emulate the output not being found within the UTXO set at all. Spent-ness would be indicated // by the Coins field being null. The UnspentOutputs dictionary being empty indicates that the UTXO was not found. // This looks very much like the 'missing output' test, but in practice it would be a distinct case, perhaps better left to an integration test. var fetchResponse = new FetchCoinsResponse(); this.coinView.Setup(x => x.FetchCoins(It.IsAny <OutPoint[]>())) .Returns(fetchResponse); var blockTxs = new List <Transaction>(); // Retriever fails but doesn't throw IndexOutOfRangeException GetSenderResult result = this.senderRetriever.GetSender(transaction, this.coinView.Object, blockTxs); Assert.False(result.Success); Assert.Equal(SenderRetriever.OutputsNotInCoinView, result.Error); }
/// <inheritdoc/> public override BlockTemplate Build(ChainedHeader chainTip, Script scriptPubKeyIn) { GetSenderResult getSenderResult = this.senderRetriever.GetAddressFromScript(scriptPubKeyIn); this.coinbaseAddress = (getSenderResult.Success) ? getSenderResult.Sender : uint160.Zero; this.stateSnapshot = this.stateRoot.GetSnapshotTo(((ISmartContractBlockHeader)this.ConsensusManager.Tip.Header).HashStateRoot.ToBytes()); this.refundOutputs.Clear(); this.receipts.Clear(); this.blockGasConsumed = 0; base.OnBuild(chainTip, scriptPubKeyIn); this.coinbase.Outputs.AddRange(this.refundOutputs); // Cache the results. We don't need to execute these again when validating. var cacheModel = new BlockExecutionResultModel(this.stateSnapshot, this.receipts); this.executionCache.StoreExecutionResult(this.BlockTemplate.Block.GetHash(), cacheModel); return(this.BlockTemplate); }
public void P2PKH_GetSender_Passes() { var successResult = GetSenderResult.CreateSuccess(new uint160(0)); this.senderRetriever.Setup(x => x.GetSender(It.IsAny <Transaction>(), It.IsAny <MempoolCoinView>())) .Returns(successResult); this.senderRetriever.Setup(x => x.GetSender(It.IsAny <Transaction>(), It.IsAny <ICoinView>(), It.IsAny <IList <Transaction> >())) .Returns(successResult); Transaction transaction = this.network.CreateTransaction(); transaction.Outputs.Add(new TxOut(100, new Script(new byte[] { (byte)ScOpcodeType.OP_CREATECONTRACT }))); // Mempool check works this.rule.CheckTransaction(new MempoolValidationContext(transaction, new MempoolValidationState(false))); // Block validation check works Block block = this.network.CreateBlock(); block.AddTransaction(transaction); this.rule.RunAsync(new RuleContext(new ValidationContext { BlockToValidate = block }, DateTimeOffset.Now)); }
public void P2PKH_GetSender_Fails() { var failResult = GetSenderResult.CreateFailure("String error"); this.senderRetriever.Setup(x => x.GetSender(It.IsAny <Transaction>(), It.IsAny <MempoolCoinView>())) .Returns(failResult); this.senderRetriever.Setup(x => x.GetSender(It.IsAny <Transaction>(), It.IsAny <ICoinView>(), It.IsAny <IList <Transaction> >())) .Returns(failResult); Transaction transaction = this.network.CreateTransaction(); transaction.Outputs.Add(new TxOut(100, new Script(new byte[] { (byte)ScOpcodeType.OP_CREATECONTRACT }))); // Mempool check fails Assert.ThrowsAny <ConsensusErrorException>(() => this.rule.CheckTransaction(new MempoolValidationContext(transaction, new MempoolValidationState(false)))); // Block validation check fails Block block = this.network.CreateBlock(); block.AddTransaction(transaction); Assert.ThrowsAnyAsync <ConsensusErrorException>(() => this.rule.RunAsync(new RuleContext(new ValidationContext { BlockToValidate = block }, DateTimeOffset.Now))); }