/// <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 ISmartContractExecutor CreateExecutor(
     IContractStateRoot stateRepository,
     ISmartContractTransactionContext transactionContext)
 {
     return(new Executor(this.loggerFactory, this.serializer,
                         stateRepository, this.refundProcessor, this.transferProcessor, this.network, this.stateFactory));
 }
        /// <summary>
        /// Executes the smart contract part of a transaction
        /// </summary>
        protected void ExecuteContractTransaction(RuleContext context, Transaction transaction)
        {
            ISmartContractTransactionContext txContext = GetSmartContractTransactionContext(context, transaction);
            ISmartContractExecutor           executor  = this.ContractCoinviewRule.ExecutorFactory.CreateExecutor(this.ContractCoinviewRule.OriginalStateRoot, txContext);

            ISmartContractExecutionResult 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
                )
            {
                BlockHash = context.ValidationContext.Block.GetHash()
            };

            this.receipts.Add(receipt);

            ValidateRefunds(result.Refunds, context.ValidationContext.Block.Transactions[0]);

            if (result.InternalTransaction != null)
            {
                this.generatedTransaction = result.InternalTransaction;
            }
        }
 /// <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 ISmartContractExecutor CreateExecutor(
     IContractState stateRepository,
     ISmartContractTransactionContext transactionContext)
 {
     return(new Executor(this.loggerFactory, this.contractPrimitiveSerializer, this.serializer,
                         stateRepository, this.refundProcessor, this.transferProcessor, this.vm));
 }
Exemplo n.º 4
0
 public TransactionCondenser(uint160 contractAddress, ILoggerFactory loggerFactory, IList <TransferInfo> transfers, IContractStateRepository stateRepository, Network network, ISmartContractTransactionContext transactionContext)
 {
     this.contractAddress    = contractAddress;
     this.logger             = loggerFactory.CreateLogger(this.GetType());
     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>();
 }
Exemplo n.º 5
0
 /// <summary>
 /// Saves receipt in a database following execution.
 /// TODO: When we have a receipt root, ensure that this is deterministic, and validated. i.e. block receipt roots match!
 /// TODO: Also put it inside the block assembly then.
 /// </summary>
 private void SaveReceipt(ISmartContractTransactionContext txContext, ISmartContractExecutionResult result)
 {
     // For now we don't want it to interrupt execution so put it in a silly large try catch.
     try
     {
         this.Logger.LogTrace("Save Receipt : {0}:{1}", nameof(txContext.TransactionHash), txContext.TransactionHash);
         this.ContractCoinviewRule.ReceiptStorage.SaveReceipt(txContext, result);
     }
     catch (Exception e)
     {
         this.Logger.LogError("Exception occurred saving contract receipt: {0}", e.Message);
     }
 }
Exemplo n.º 6
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 ISmartContractExecutor CreateExecutor(
            IContractStateRepository stateRepository,
            ISmartContractTransactionContext transactionContext)
        {
            if (transactionContext.IsCreate)
            {
                return(new CreateSmartContract(this.keyEncodingStrategy, this.loggerFactory, this.network,
                                               stateRepository, this.validator, this.refundProcessor, this.transferProcessor, this.vm));
            }

            return(new CallSmartContract(this.keyEncodingStrategy, this.loggerFactory, this.network,
                                         stateRepository, this.refundProcessor, this.transferProcessor, this.vm));
        }
        /// <inheritdoc />
        public Transaction Process(SmartContractCarrier carrier,
                                   IContractStateRepository stateSnapshot,
                                   ISmartContractTransactionContext transactionContext,
                                   IList <TransferInfo> internalTransfers,
                                   bool reversionRequired)
        {
            if (reversionRequired)
            {
                // Send back funds
                if (carrier.Value > 0)
                {
                    return(CreateRefundTransaction(transactionContext));
                }
            }

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

            // TODO we should not be generating addresses in here!
            uint160 contractAddress = null;

            if (carrier.CallData.ContractAddress == uint160.Zero)
            {
                contractAddress = carrier.GetNewContractAddress();
            }
            else
            {
                contractAddress = carrier.CallData.ContractAddress;
            }

            // If contract had no balance, received funds, but made no transfers, assign the current UTXO.
            if (stateSnapshot.GetUnspent(contractAddress) == null && carrier.Value > 0 && !internalTransfers.Any())
            {
                stateSnapshot.SetUnspent(contractAddress, new ContractUnspentOutput
                {
                    Value = carrier.Value,
                    Hash  = carrier.TransactionHash,
                    Nvout = carrier.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());
        }
Exemplo n.º 8
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(ISmartContractTransactionContext transactionContext)
        {
            Transaction tx = this.network.Consensus.ConsensusFactory.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);
        }
        public void ReceiptStorage_General_Use()
        {
            // Test that we can save and retrieve a receipt, even if everything but the transaction hash is null.
            var txContextMock = new Mock <ISmartContractTransactionContext>();

            txContextMock.Setup(x => x.TransactionHash).Returns(() => new uint256(0));
            ISmartContractTransactionContext txContext = txContextMock.Object;
            ISmartContractExecutionResult    result    = new Mock <ISmartContractExecutionResult>().Object;

            this.receiptStorage.SaveReceipt(txContext, result);
            SmartContractReceipt receipt = this.receiptStorage.GetReceipt(txContext.TransactionHash);

            Assert.NotNull(receipt);
        }
        public ISmartContractExecutionResult Execute(ISmartContractTransactionContext transactionContext)
        {
            this.logger.LogTrace("()");

            var carrier = SmartContractCarrier.Deserialize(transactionContext);

            // Get the contract code (dll) from the repository.
            byte[] contractExecutionCode = this.stateSnapshot.GetCode(carrier.CallData.ContractAddress);
            if (contractExecutionCode == null)
            {
                return(SmartContractExecutionResult.ContractDoesNotExist(carrier));
            }

            // Execute the call to the contract.
            return(this.CreateContextAndExecute(carrier.CallData.ContractAddress, contractExecutionCode, carrier.CallData.MethodName, transactionContext, carrier));
        }
Exemplo n.º 11
0
        /// <summary>
        /// Executes the smart contract part of a transaction
        /// </summary>
        protected void ExecuteContractTransaction(RuleContext context, Transaction transaction)
        {
            ISmartContractTransactionContext txContext = GetSmartContractTransactionContext(context, transaction);
            ISmartContractExecutor           executor  = this.ContractCoinviewRule.ExecutorFactory.CreateExecutor(this.ContractCoinviewRule.OriginalStateRoot, txContext);

            ISmartContractExecutionResult result = executor.Execute(txContext);

            ValidateRefunds(result.Refunds, context.ValidationContext.Block.Transactions[0]);

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

            SaveReceipt(txContext, result);
        }
Exemplo n.º 12
0
        /// <inheritdoc />
        public Transaction Process(IContractStateRepository stateSnapshot,
                                   uint160 contractAddress,
                                   ISmartContractTransactionContext transactionContext,
                                   IList <TransferInfo> internalTransfers,
                                   bool reversionRequired)
        {
            if (reversionRequired)
            {
                // Send back funds
                if (transactionContext.TxOutValue > 0)
                {
                    return(CreateRefundTransaction(transactionContext));
                }
            }

            // If contract received no funds and made no transfers, do nothing.
            if (transactionContext.TxOutValue == 0 && !internalTransfers.Any())
            {
                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>
        /// Deserializes the smart contract execution code and other related information.
        /// </summary>
        public static SmartContractCarrier Deserialize(ISmartContractTransactionContext transactionContext)
        {
            var byteCursor = 0;
            var takeLength = 0;

            var callData = Deserialize(transactionContext.ScriptPubKey);

            var carrier = new SmartContractCarrier(new MethodParameterSerializer());

            carrier.CallData        = callData;
            carrier.Nvout           = transactionContext.Nvout;
            carrier.Sender          = transactionContext.Sender;
            carrier.TransactionHash = transactionContext.TransactionHash;
            carrier.Value           = transactionContext.TxOutValue;

            if (!string.IsNullOrWhiteSpace(callData.MethodParameters))
            {
                carrier.MethodParameters = carrier.serializer.ToObjects(callData.MethodParameters);
            }

            return(carrier);
        }
Exemplo n.º 14
0
 /// <inheritdoc />
 public void SaveReceipt(ISmartContractTransactionContext txContext, ISmartContractExecutionResult result)
 {
     SaveReceipt(txContext.TransactionHash, txContext.BlockHeight, result.NewContractAddress, result.GasConsumed, !result.Revert, result.Exception, result.Return);
 }
Exemplo n.º 15
0
        public ISmartContractExecutionResult Execute(ISmartContractTransactionContext transactionContext)
        {
            this.logger.LogTrace("()");

            // Deserialization can't fail because this has already been through SmartContractFormatRule.
            Result <ContractTxData> callDataDeserializationResult = this.serializer.Deserialize(transactionContext.ScriptPubKey.ToBytes());
            ContractTxData          callData = callDataDeserializationResult.Value;

            var gasMeter = new GasMeter(callData.GasLimit);

            gasMeter.Spend((Gas)GasPriceList.BaseCost);

            var context = new TransactionContext(
                transactionContext.TransactionHash,
                transactionContext.BlockHeight,
                transactionContext.CoinbaseAddress,
                transactionContext.Sender,
                transactionContext.TxOutValue);

            var creation = IsCreateContract(callData);

            VmExecutionResult result = creation
                ? this.vm.Create(gasMeter, this.stateSnapshot, callData, context)
                : this.vm.ExecuteMethod(gasMeter, this.stateSnapshot, callData, context);

            var revert = result.ExecutionException != null;

            Transaction internalTransaction = this.transferProcessor.Process(
                this.stateSnapshot,
                creation ? result.NewContractAddress : callData.ContractAddress,
                transactionContext,
                result.InternalTransfers,
                revert);

            (Money fee, List <TxOut> refundTxOuts) = this.refundProcessor.Process(
                callData,
                transactionContext.MempoolFee,
                transactionContext.Sender,
                result.GasConsumed,
                result.ExecutionException);

            var executionResult = new SmartContractExecutionResult
            {
                NewContractAddress  = !revert && creation ? result.NewContractAddress : null,
                Exception           = result.ExecutionException,
                GasConsumed         = result.GasConsumed,
                Return              = result.Result,
                InternalTransaction = internalTransaction,
                Fee     = fee,
                Refunds = refundTxOuts,
                Logs    = result.RawLogs.ToLogs(this.contractPrimitiveSerializer)
            };

            if (revert)
            {
                this.logger.LogTrace("(-)[CONTRACT_EXECUTION_FAILED]");

                this.stateSnapshot.Rollback();
            }
            else
            {
                this.logger.LogTrace("(-)[CONTRACT_EXECUTION_SUCCEEDED]");

                this.stateSnapshot.Commit();
            }

            return(executionResult);
        }
Exemplo n.º 16
0
        public void Create_Contract_Success()
        {
            var         network            = new SmartContractsRegTest();
            uint160     newContractAddress = uint160.One;
            var         gasConsumed        = (Gas)100;
            var         code           = new byte[] { 0xAA, 0xBB, 0xCC };
            var         contractTxData = new ContractTxData(1, 1, (Gas)1000, code);
            var         refund         = new Money(0);
            const ulong mempoolFee     = 2UL; // MOQ doesn't like it when you use a type with implicit conversions (Money)
            ISmartContractTransactionContext context = Mock.Of <ISmartContractTransactionContext>(c =>
                                                                                                  c.Data == code &&
                                                                                                  c.MempoolFee == mempoolFee &&
                                                                                                  c.Sender == uint160.One &&
                                                                                                  c.CoinbaseAddress == uint160.Zero);

            var            logger        = new Mock <ILogger>();
            ILoggerFactory loggerFactory = Mock.Of <ILoggerFactory>
                                               (l => l.CreateLogger(It.IsAny <string>()) == logger.Object);

            var callDataSerializer = new Mock <ICallDataSerializer>();

            callDataSerializer
            .Setup(s => s.Deserialize(It.IsAny <byte[]>()))
            .Returns(Result.Ok(contractTxData));

            var vmExecutionResult = VmExecutionResult.Success(null, null);

            var contractStateRoot = new Mock <IContractStateRoot>();
            var transferProcessor = new Mock <ISmartContractResultTransferProcessor>();

            (Money refund, TxOut)refundResult = (refund, null);
            var refundProcessor = new Mock <ISmartContractResultRefundProcessor>();

            refundProcessor
            .Setup(r => r.Process(
                       contractTxData,
                       mempoolFee,
                       context.Sender,
                       It.IsAny <Gas>(),
                       false))
            .Returns(refundResult);

            var stateTransitionResult = StateTransitionResult.Ok(gasConsumed, newContractAddress, vmExecutionResult.Result);

            var internalTransfers = new List <TransferInfo>().AsReadOnly();
            var stateMock         = new Mock <IState>();

            stateMock.Setup(s => s.Apply(It.IsAny <ExternalCreateMessage>()))
            .Returns(stateTransitionResult);
            stateMock.SetupGet(p => p.InternalTransfers).Returns(internalTransfers);

            var stateFactory = new Mock <IStateFactory>();

            stateFactory.Setup(sf => sf.Create(
                                   contractStateRoot.Object,
                                   It.IsAny <IBlock>(),
                                   context.TxOutValue,
                                   context.TransactionHash,
                                   contractTxData.GasLimit))
            .Returns(stateMock.Object);

            var sut = new Executor(
                loggerFactory,
                callDataSerializer.Object,
                contractStateRoot.Object,
                refundProcessor.Object,
                transferProcessor.Object,
                network,
                stateFactory.Object);

            sut.Execute(context);

            callDataSerializer.Verify(s => s.Deserialize(code), Times.Once);

            stateFactory.Verify(sf => sf
                                .Create(
                                    contractStateRoot.Object,
                                    It.IsAny <IBlock>(),
                                    context.TxOutValue,
                                    context.TransactionHash,
                                    contractTxData.GasLimit),
                                Times.Once);

            stateMock.Verify(sm => sm
                             .Apply(It.IsAny <ExternalCreateMessage>()), Times.Once);

            transferProcessor.Verify(t => t
                                     .Process(
                                         contractStateRoot.Object,
                                         newContractAddress,
                                         context,
                                         internalTransfers,
                                         false),
                                     Times.Once);

            refundProcessor.Verify(t => t
                                   .Process(
                                       contractTxData,
                                       mempoolFee,
                                       context.Sender,
                                       It.IsAny <Gas>(),
                                       false),
                                   Times.Once);
        }
Exemplo n.º 17
0
        public ISmartContractExecutionResult Execute(ISmartContractTransactionContext transactionContext)
        {
            this.logger.LogTrace("()");

            // 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,
                callData.GasLimit);

            StateTransitionResult result;

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

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

                result = state.Apply(message);
            }

            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,
                Exception           = result.Error?.VmException,
                Revert              = revert,
                GasConsumed         = result.GasConsumed,
                Return              = result.Success?.ExecutionResult,
                InternalTransaction = internalTransaction,
                Fee    = fee,
                Refund = refundTxOut,
                Logs   = state.GetLogs()
            };

            return(executionResult);
        }
        private ISmartContractExecutionResult CreateContextAndExecute(uint160 contractAddress, byte[] contractCode,
                                                                      string methodName, ISmartContractTransactionContext transactionContext, SmartContractCarrier carrier)
        {
            this.logger.LogTrace("()");

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

            var executionContext = new SmartContractExecutionContext
                                   (
                block,
                new Message(
                    contractAddress.ToAddress(this.network),
                    carrier.Sender.ToAddress(this.network),
                    carrier.Value,
                    carrier.CallData.GasLimit
                    ),
                contractAddress,
                carrier.CallData.GasPrice,
                carrier.MethodParameters
                                   );

            LogExecutionContext(this.logger, block, executionContext.Message, contractAddress, carrier);

            var gasMeter = new GasMeter(carrier.CallData.GasLimit);

            IPersistenceStrategy persistenceStrategy =
                new MeteredPersistenceStrategy(this.stateSnapshot, gasMeter, this.keyEncodingStrategy);

            var persistentState = new PersistentState(persistenceStrategy, contractAddress, this.network);

            gasMeter.Spend((Gas)GasPriceList.BaseCost);

            var result = this.vm.ExecuteMethod(
                contractCode,
                methodName,
                executionContext,
                gasMeter,
                persistentState,
                this.stateSnapshot);

            var revert = result.ExecutionException != null;

            this.logger.LogTrace("(-)");

            var internalTransaction = this.transferProcessor.Process(
                carrier,
                this.stateSnapshot,
                transactionContext,
                result.InternalTransfers,
                revert);

            (var fee, var refundTxOuts) = this.refundProcessor.Process(
                carrier,
                transactionContext.MempoolFee,
                result.GasConsumed,
                result.ExecutionException);

            var executionResult = new SmartContractExecutionResult
            {
                Exception           = result.ExecutionException,
                GasConsumed         = result.GasConsumed,
                Return              = result.Result,
                InternalTransaction = internalTransaction,
                Fee     = fee,
                Refunds = refundTxOuts
            };

            if (revert)
            {
                this.stateSnapshot.Rollback();
            }
            else
            {
                this.stateSnapshot.Commit();
            }

            return(executionResult);
        }
        public ISmartContractExecutionResult Execute(ISmartContractTransactionContext transactionContext)
        {
            this.logger.LogTrace("()");

            var carrier = SmartContractCarrier.Deserialize(transactionContext);

            // Create a new address for the contract.
            uint160 newContractAddress = carrier.GetNewContractAddress();

            // Create an account for the contract in the state repository.
            this.stateSnapshot.CreateAccount(newContractAddress);

            // Decompile the contract execution code and validate it.
            SmartContractDecompilation    decompilation = SmartContractDecompiler.GetModuleDefinition(carrier.CallData.ContractExecutionCode);
            SmartContractValidationResult validation    = this.validator.Validate(decompilation);

            // If validation failed, refund the sender any remaining gas.
            if (!validation.IsValid)
            {
                this.logger.LogTrace("(-)[CONTRACT_VALIDATION_FAILED]");
                return(SmartContractExecutionResult.ValidationFailed(carrier, validation));
            }

            var block            = new Block(transactionContext.BlockHeight, transactionContext.CoinbaseAddress.ToAddress(this.network));
            var executionContext = new SmartContractExecutionContext
                                   (
                block,
                new Message(
                    newContractAddress.ToAddress(this.network),
                    carrier.Sender.ToAddress(this.network),
                    carrier.Value,
                    carrier.CallData.GasLimit
                    ),
                newContractAddress,
                carrier.CallData.GasPrice,
                carrier.MethodParameters
                                   );

            LogExecutionContext(this.logger, block, executionContext.Message, newContractAddress, carrier);

            var gasMeter = new GasMeter(carrier.CallData.GasLimit);

            IPersistenceStrategy persistenceStrategy = new MeteredPersistenceStrategy(this.stateSnapshot, gasMeter, new BasicKeyEncodingStrategy());
            var persistentState = new PersistentState(persistenceStrategy, newContractAddress, this.network);

            gasMeter.Spend((Gas)GasPriceList.BaseCost);

            var result = this.vm.Create(carrier.CallData.ContractExecutionCode, executionContext, gasMeter, persistentState, this.stateSnapshot);

            var revert = result.ExecutionException != null;

            var internalTransaction = this.transferProcessor.Process(
                carrier,
                this.stateSnapshot,
                transactionContext,
                result.InternalTransfers,
                revert);

            (var fee, var refundTxOuts) = this.refundProcessor.Process(
                carrier,
                transactionContext.MempoolFee,
                result.GasConsumed,
                result.ExecutionException);

            var executionResult = new SmartContractExecutionResult
            {
                NewContractAddress  = revert ? null : newContractAddress,
                Exception           = result.ExecutionException,
                GasConsumed         = result.GasConsumed,
                Return              = result.Result,
                InternalTransaction = internalTransaction,
                Fee     = fee,
                Refunds = refundTxOuts
            };

            if (revert)
            {
                this.logger.LogTrace("(-)[CONTRACT_EXECUTION_FAILED]");

                this.stateSnapshot.Rollback();
            }
            else
            {
                this.logger.LogTrace("(-):{0}={1}", nameof(newContractAddress), newContractAddress);

                this.stateSnapshot.SetCode(newContractAddress, carrier.CallData.ContractExecutionCode);

                this.stateSnapshot.Commit();
            }

            return(executionResult);
        }
        public void Create_Contract_Success()
        {
            uint160     newContractAddress = uint160.One;
            var         gasConsumed        = (Gas)100;
            var         code           = new byte[] { 0xAA, 0xBB, 0xCC };
            var         contractTxData = new ContractTxData(1, 1, (Gas)1000, code);
            var         refund         = new Money(0);
            const ulong mempoolFee     = 2UL; // MOQ doesn't like it when you use a type with implicit conversions (Money)
            ISmartContractTransactionContext context = Mock.Of <ISmartContractTransactionContext>(c =>
                                                                                                  c.Data == code &&
                                                                                                  c.MempoolFee == mempoolFee &&
                                                                                                  c.Sender == uint160.One);

            var            logger        = new Mock <ILogger>();
            ILoggerFactory loggerFactory = Mock.Of <ILoggerFactory>
                                               (l => l.CreateLogger(It.IsAny <string>()) == logger.Object);

            var serializer = new Mock <ICallDataSerializer>();

            serializer
            .Setup(s => s.Deserialize(It.IsAny <byte[]>()))
            .Returns(Result.Ok(contractTxData));

            var contractPrimitiveSerializer = new Mock <IContractPrimitiveSerializer>();

            var vmExecutionResult =
                VmExecutionResult.CreationSuccess(
                    newContractAddress,
                    new List <TransferInfo>(),
                    gasConsumed,
                    null,
                    null);

            var state             = new Mock <IContractState>();
            var transferProcessor = new Mock <ISmartContractResultTransferProcessor>();

            (Money refund, List <TxOut>)refundResult = (refund, new List <TxOut>());
            var refundProcessor = new Mock <ISmartContractResultRefundProcessor>();

            refundProcessor
            .Setup(r => r.Process(
                       contractTxData,
                       mempoolFee,
                       context.Sender,
                       vmExecutionResult.GasConsumed,
                       vmExecutionResult.ExecutionException))
            .Returns(refundResult);

            var vm = new Mock <ISmartContractVirtualMachine>();

            vm.Setup(v => v.Create(It.Is <IGasMeter>(x => x.GasConsumed == GasPriceList.BaseCost),
                                   It.IsAny <IContractState>(),
                                   It.IsAny <ICreateData>(),
                                   It.IsAny <ITransactionContext>(),
                                   It.IsAny <string>()))
            .Returns(vmExecutionResult);

            var sut = new Executor(
                loggerFactory,
                contractPrimitiveSerializer.Object,
                serializer.Object,
                state.Object,
                refundProcessor.Object,
                transferProcessor.Object,
                vm.Object
                );

            sut.Execute(context);

            serializer.Verify(s => s.Deserialize(code), Times.Once);

            vm.Verify(v =>
                      v.Create(
                          It.IsAny <IGasMeter>(),
                          state.Object,
                          contractTxData,
                          It.IsAny <TransactionContext>(),
                          It.IsAny <string>()),
                      Times.Once);

            transferProcessor.Verify(t => t
                                     .Process(
                                         state.Object,
                                         vmExecutionResult.NewContractAddress,
                                         It.IsAny <ISmartContractTransactionContext>(),
                                         vmExecutionResult.InternalTransfers,
                                         false),
                                     Times.Once);

            refundProcessor.Verify(t => t
                                   .Process(
                                       contractTxData,
                                       mempoolFee,
                                       context.Sender,
                                       vmExecutionResult.GasConsumed,
                                       vmExecutionResult.ExecutionException),
                                   Times.Once);

            state.Verify(s => s.Commit(), Times.Once);
            state.Verify(s => s.Rollback(), Times.Never);
        }
Exemplo n.º 21
0
        public ISmartContractExecutionResult Execute(ISmartContractTransactionContext transactionContext)
        {
            this.logger.LogTrace("()");

            var callDataDeserializationResult = this.serializer.Deserialize(transactionContext.ScriptPubKey.ToBytes());

            // TODO Handle deserialization failure

            var callData = callDataDeserializationResult.Value;

            var gasMeter = new GasMeter(callData.GasLimit);

            var context = new TransactionContext(
                transactionContext.TransactionHash,
                transactionContext.BlockHeight,
                transactionContext.CoinbaseAddress,
                transactionContext.Sender,
                transactionContext.TxOutValue);

            var creation = IsCreateContract(callData);

            var result = creation
                ? this.vm.Create(gasMeter, this.stateSnapshot, callData, context)
                : this.vm.ExecuteMethod(gasMeter, this.stateSnapshot, callData, context);

            var revert = result.ExecutionException != null;

            var internalTransaction = this.transferProcessor.Process(
                this.stateSnapshot,
                creation ? result.NewContractAddress : callData.ContractAddress,
                transactionContext,
                result.InternalTransfers,
                revert);

            (var fee, var refundTxOuts) = this.refundProcessor.Process(
                callData,
                transactionContext.MempoolFee,
                transactionContext.Sender,
                result.GasConsumed,
                result.ExecutionException);

            var executionResult = new SmartContractExecutionResult
            {
                NewContractAddress  = !revert && creation ? result.NewContractAddress : null,
                Exception           = result.ExecutionException,
                GasConsumed         = result.GasConsumed,
                Return              = result.Result,
                InternalTransaction = internalTransaction,
                Fee     = fee,
                Refunds = refundTxOuts
            };

            if (revert)
            {
                this.logger.LogTrace("(-)[CONTRACT_EXECUTION_FAILED]");

                this.stateSnapshot.Rollback();
            }
            else
            {
                this.logger.LogTrace("(-)[CONTRACT_EXECUTION_SUCCEEDED]");

                this.stateSnapshot.Commit();
            }

            return(executionResult);
        }