/// <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)); }
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>(); }
/// <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); } }
/// <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()); }
/// <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)); }
/// <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); }
/// <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); }
/// <inheritdoc /> public void SaveReceipt(ISmartContractTransactionContext txContext, ISmartContractExecutionResult result) { SaveReceipt(txContext.TransactionHash, txContext.BlockHeight, result.NewContractAddress, result.GasConsumed, !result.Revert, result.Exception, result.Return); }
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); }
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); }
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); }
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); }