/// <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);
        }
Пример #5
0
        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);
        }
Пример #6
0
        /// <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);
        }
Пример #7
0
        /// <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);
        }
Пример #8
0
 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();
         }
     }
 }
Пример #9
0
 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);
        }
Пример #12
0
        /// <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"));
        }
Пример #14
0
        /// <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));
        }
Пример #15
0
        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);
        }
Пример #18
0
        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);
        }
Пример #20
0
        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));
        }
Пример #21
0
        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)));
        }