Example #1
0
 /// <summary>
 /// Initialize a smart contract executor for the block assembler or consensus validator.
 /// <para>
 /// After the contract has been executed, it will process any fees and/or refunds.
 /// </para>
 /// </summary>
 public IContractExecutor CreateExecutor(
     IStateRepositoryRoot stateRepository,
     IContractTransactionContext transactionContext)
 {
     return(new ContractExecutor(this.loggerFactory, this.serializer,
                                 stateRepository, this.refundProcessor, this.transferProcessor, this.network, this.stateFactory, this.stateProcessor, this.contractPrimitiveSerializer));
 }
Example #2
0
        /// <summary>
        /// Executes the smart contract part of a transaction
        /// </summary>
        protected void ExecuteContractTransaction(RuleContext context, Transaction transaction)
        {
            IContractTransactionContext txContext = GetSmartContractTransactionContext(context, transaction);
            IContractExecutor executor = this.ContractCoinviewRule.ExecutorFactory.CreateExecutor(this.ContractCoinviewRule.OriginalStateRoot, txContext);

            IContractExecutionResult result = executor.Execute(txContext);

            var receipt = new Receipt(
                new uint256(this.ContractCoinviewRule.OriginalStateRoot.Root),
                result.GasConsumed,
                result.Logs.ToArray(),
                txContext.TransactionHash,
                txContext.Sender,
                result.To,
                result.NewContractAddress,
                !result.Revert,
                result.ErrorMessage
            )
            {
                BlockHash = context.ValidationContext.BlockToValidate.GetHash()
            };

            this.receipts.Add(receipt);

            ValidateRefunds(result.Refund, context.ValidationContext.BlockToValidate.Transactions[0]);

            if (result.InternalTransaction != null)
                this.generatedTransaction = result.InternalTransaction;
        }
Example #3
0
        public ILocalExecutionResult Execute(IContractTransactionContext transactionContext)
        {
            Result <ContractTxData> callDataDeserializationResult = this.serializer.Deserialize(transactionContext.Data);

            ContractTxData callData = callDataDeserializationResult.Value;

            bool creation = callData.IsCreateContract;

            var block = new Block(
                transactionContext.BlockHeight,
                transactionContext.CoinbaseAddress.ToAddress()
                );

            IState state = this.stateFactory.Create(
                this.stateRoot.StartTracking(),
                block,
                transactionContext.TxOutValue,
                transactionContext.TransactionHash);

            StateTransitionResult result;
            IState newState = state.Snapshot();

            if (creation)
            {
                var message = new ExternalCreateMessage(
                    transactionContext.Sender,
                    transactionContext.TxOutValue,
                    callData.GasLimit,
                    callData.ContractExecutionCode,
                    callData.MethodParameters
                    );

                result = this.stateProcessor.Apply(newState, message);
            }
            else
            {
                var message = new ExternalCallMessage(
                    callData.ContractAddress,
                    transactionContext.Sender,
                    transactionContext.TxOutValue,
                    callData.GasLimit,
                    new MethodCall(callData.MethodName, callData.MethodParameters)
                    );

                result = this.stateProcessor.Apply(newState, message);
            }

            var executionResult = new LocalExecutionResult
            {
                ErrorMessage      = result.Error?.GetErrorMessage(),
                Revert            = result.IsFailure,
                GasConsumed       = result.GasConsumed,
                Return            = result.Success?.ExecutionResult,
                InternalTransfers = state.InternalTransfers.ToList(),
                Logs = state.GetLogs(this.contractPrimitiveSerializer)
            };

            return(executionResult);
        }
 public TransactionCondenser(uint160 contractAddress, ILoggerFactory loggerFactory, IReadOnlyList <TransferInfo> transfers, IStateRepository stateRepository, Network network, IContractTransactionContext transactionContext)
 {
     this.contractAddress    = contractAddress;
     this.logger             = loggerFactory.CreateLogger(this.GetType().Name);
     this.network            = network;
     this.transactionContext = transactionContext;
     this.stateRepository    = stateRepository;
     this.transfers          = transfers;
     this.nVouts             = new Dictionary <uint160, uint>();
     this.txBalances         = new Dictionary <uint160, ulong>();
     this.unspents           = new List <ContractUnspentOutput>();
 }
Example #5
0
        /// <summary>
        /// Should contract execution fail, we need to send the money, that was
        /// sent to contract, back to the contract's sender.
        /// </summary>
        private Transaction CreateRefundTransaction(IContractTransactionContext transactionContext)
        {
            Transaction tx = this.network.CreateTransaction();

            // Input from contract call
            var outpoint = new OutPoint(transactionContext.TransactionHash, transactionContext.Nvout);

            tx.AddInput(new TxIn(outpoint, new Script(new[] { (byte)ScOpcodeType.OP_SPEND })));

            // Refund output
            Script script = PayToPubkeyHashTemplate.Instance.GenerateScriptPubKey(new KeyId(transactionContext.Sender));
            var    txOut  = new TxOut(new Money(transactionContext.TxOutValue), script);

            tx.Outputs.Add(txOut);
            return(tx);
        }
Example #6
0
        /// <summary>
        /// Executes the smart contract part of a transaction
        /// </summary>
        public void ExecuteContractTransaction(RuleContext context, Transaction transaction)
        {
            IContractTransactionContext txContext = this.GetSmartContractTransactionContext(context, transaction);

            this.CheckFeeAccountsForGas(txContext.Data, txContext.MempoolFee);
            IContractExecutor       executor             = this.executorFactory.CreateExecutor(this.mutableStateRepository, txContext);
            Result <ContractTxData> deserializedCallData = this.callDataSerializer.Deserialize(txContext.Data);

            IContractExecutionResult result = executor.Execute(txContext);

            var receipt = new Receipt(
                new uint256(this.mutableStateRepository.Root),
                result.GasConsumed,
                result.Logs.ToArray(),
                txContext.TransactionHash,
                txContext.Sender,
                result.To,
                result.NewContractAddress,
                !result.Revert,
                result.Return?.ToString(),
                result.ErrorMessage,
                deserializedCallData.Value.GasPrice,
                txContext.TxOutValue,
                deserializedCallData.Value.IsCreateContract ? null : deserializedCallData.Value.MethodName,
                txContext.BlockHeight)
            {
                BlockHash = context.ValidationContext.BlockToValidate.GetHash()
            };

            this.receipts.Add(receipt);

            if (result.Refund != null)
            {
                this.ValidateRefunds(result.Refund, context.ValidationContext.BlockToValidate.Transactions[0]);
            }

            if (result.InternalTransaction != null)
            {
                this.generatedTransaction = result.InternalTransaction;
            }

            this.CheckBlockGasLimit(result.GasConsumed);
        }
Example #7
0
        /// <inheritdoc />
        public Transaction Process(IStateRepository stateSnapshot,
                                   uint160 contractAddress,
                                   IContractTransactionContext transactionContext,
                                   IReadOnlyList <TransferInfo> internalTransfers,
                                   bool reversionRequired)
        {
            if (reversionRequired)
            {
                // Send back funds
                if (transactionContext.TxOutValue > 0)
                {
                    return(this.CreateRefundTransaction(transactionContext));
                }

                return(null);
            }

            // If contract received no funds and made no transfers, do nothing.
            if (transactionContext.TxOutValue == 0 && !internalTransfers.Any(x => x.Value > 0))
            {
                return(null);
            }

            // If contract had no balance, received funds, but made no transfers, assign the current UTXO.
            if (stateSnapshot.GetUnspent(contractAddress) == null && transactionContext.TxOutValue > 0 && !internalTransfers.Any())
            {
                stateSnapshot.SetUnspent(contractAddress, new ContractUnspentOutput
                {
                    Value = transactionContext.TxOutValue,
                    Hash  = transactionContext.TransactionHash,
                    Nvout = transactionContext.Nvout
                });

                return(null);
            }

            // All other cases we need a condensing transaction
            var transactionCondenser = new TransactionCondenser(contractAddress, this.loggerFactory, internalTransfers, stateSnapshot, this.network, transactionContext);

            return(transactionCondenser.CreateCondensingTransaction());
        }
        /// <summary>
        /// Executes the smart contract part of a transaction
        /// </summary>
        public void ExecuteContractTransaction(RuleContext context, Transaction transaction)
        {
            IContractTransactionContext txContext = GetSmartContractTransactionContext(context, transaction);

            this.CheckFeeAccountsForGas(txContext.Data, txContext.MempoolFee);
            IContractExecutor executor = this.ContractCoinviewRule.ExecutorFactory.CreateExecutor(this.mutableStateRepository, txContext);

            IContractExecutionResult result = executor.Execute(txContext);

            var receipt = new Receipt(
                new uint256(this.mutableStateRepository.Root),
                result.GasConsumed,
                result.Logs.ToArray(),
                txContext.TransactionHash,
                txContext.Sender,
                result.To,
                result.NewContractAddress,
                !result.Revert,
                result.Return?.ToString(),
                result.ErrorMessage
                )
            {
                BlockHash = context.ValidationContext.BlockToValidate.GetHash()
            };

            this.receipts.Add(receipt);

            if (result.Refund != null)
            {
                ValidateRefunds(result.Refund, context.ValidationContext.BlockToValidate.Transactions[0]);
            }

            if (result.InternalTransaction != null)
            {
                this.generatedTransaction = result.InternalTransaction;
            }
        }
Example #9
0
        public IContractExecutionResult Execute(IContractTransactionContext transactionContext)
        {
            // Deserialization can't fail because this has already been through SmartContractFormatRule.
            Result <ContractTxData> callDataDeserializationResult = this.serializer.Deserialize(transactionContext.Data);
            ContractTxData          callData = callDataDeserializationResult.Value;

            bool creation = callData.IsCreateContract;

            var block = new Block(
                transactionContext.BlockHeight,
                transactionContext.CoinbaseAddress.ToAddress(this.network)
                );

            IState state = this.stateFactory.Create(
                this.stateRoot,
                block,
                transactionContext.TxOutValue,
                transactionContext.TransactionHash);

            StateTransitionResult result;
            IState newState = state.Snapshot();

            if (creation)
            {
                var message = new ExternalCreateMessage(
                    transactionContext.Sender,
                    transactionContext.TxOutValue,
                    callData.GasLimit,
                    callData.ContractExecutionCode,
                    callData.MethodParameters
                    );


                result = this.stateProcessor.Apply(newState, message);
            }
            else
            {
                var message = new ExternalCallMessage(
                    callData.ContractAddress,
                    transactionContext.Sender,
                    transactionContext.TxOutValue,
                    callData.GasLimit,
                    new MethodCall(callData.MethodName, callData.MethodParameters)
                    );

                result = this.stateProcessor.Apply(newState, message);
            }

            if (result.IsSuccess)
            {
                state.TransitionTo(newState);
            }

            bool revert = !result.IsSuccess;

            Transaction internalTransaction = this.transferProcessor.Process(
                this.stateRoot,
                result.Success?.ContractAddress,
                transactionContext,
                state.InternalTransfers,
                revert);

            bool outOfGas = result.IsFailure && result.Error.Kind == StateTransitionErrorKind.OutOfGas;

            (Money fee, TxOut refundTxOut) = this.refundProcessor.Process(
                callData,
                transactionContext.MempoolFee,
                transactionContext.Sender,
                result.GasConsumed,
                outOfGas);

            var executionResult = new SmartContractExecutionResult
            {
                To = !callData.IsCreateContract ? callData.ContractAddress : null,
                NewContractAddress  = !revert && creation ? result.Success?.ContractAddress : null,
                ErrorMessage        = result.Error?.GetErrorMessage(),
                Revert              = revert,
                GasConsumed         = result.GasConsumed,
                Return              = result.Success?.ExecutionResult,
                InternalTransaction = internalTransaction,
                Fee    = fee,
                Refund = refundTxOut,
                Logs   = state.GetLogs(this.contractPrimitiveSerializer)
            };

            return(executionResult);
        }