/// <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)); }
/// <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; }
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>(); }
/// <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); }
/// <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); }
/// <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; } }
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); }