public (Money, List <TxOut>) Process(SmartContractCarrier carrier, Money mempoolFee, Gas gasConsumed, Exception exception) { this.logger.LogTrace("(){0}:{1}", nameof(mempoolFee), mempoolFee); Money fee = mempoolFee; var refunds = new List <TxOut>(); if (exception is OutOfGasException) { this.logger.LogTrace("(-)[OUTOFGAS_EXCEPTION]"); return(fee, refunds); } var refund = new Money(carrier.GasCostBudget - (gasConsumed * carrier.CallData.GasPrice)); this.logger.LogTrace("{0}:{1},{2}:{3},{4}:{5},{6}:{7}", nameof(carrier.GasCostBudget), carrier.GasCostBudget, nameof(gasConsumed), gasConsumed, nameof(carrier.CallData.GasPrice), carrier.CallData.GasPrice, nameof(refund), refund); if (refund > 0) { fee -= refund; refunds.Add(CreateRefund(carrier.Sender, refund)); } this.logger.LogTrace("(-)"); return(fee, refunds); }
/// <summary> /// Contract validation failed, so set the gas units used to a value from the price list and set /// the validation errors in a <see cref="SmartContractValidationException"/>. /// </summary> public static SmartContractExecutionResult ValidationFailed(SmartContractCarrier carrier, SmartContractValidationResult validationResult) { var executionResult = new SmartContractExecutionResult { Exception = new SmartContractValidationException(validationResult.Errors), GasConsumed = GasPriceList.ContractValidationFailed() }; return(executionResult); }
/// <summary> /// Contract does not exist, so set the gas units used to a value from the price list and set /// a <see cref="SmartContractDoesNotExistException"/>. /// </summary> internal static ISmartContractExecutionResult ContractDoesNotExist(SmartContractCarrier carrier) { var executionResult = new SmartContractExecutionResult { Exception = new SmartContractDoesNotExistException(carrier.CallData.MethodName), GasConsumed = GasPriceList.ContractDoesNotExist() }; return(executionResult); }
/// <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()); }
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> /// Instantiates a <see cref="ScOpcodeType.OP_CALLCONTRACT"/> smart contract carrier. /// </summary> public static SmartContractCarrier CallContract(int vmVersion, uint160 contractAddress, string methodName, ulong gasPrice, Gas gasLimit, string[] methodParameters = null) { if (string.IsNullOrWhiteSpace(methodName)) { throw new SmartContractCarrierException(nameof(methodName) + " is null or empty"); } var serializer = new MethodParameterSerializer(); string methodParams = GetMethodParams(serializer, methodParameters); var carrier = new SmartContractCarrier(new MethodParameterSerializer()); carrier.CallData = new CallData((byte)ScOpcodeType.OP_CALLCONTRACT, vmVersion, gasPrice, gasLimit, contractAddress, methodName, methodParams); if (!string.IsNullOrWhiteSpace(methodParams)) { carrier.MethodParameters = serializer.ToObjects(methodParams); } return(carrier); }
/// <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); }
/// <summary> /// Instantiates a <see cref="ScOpcodeType.OP_CREATECONTRACT"/> smart contract carrier. /// </summary> public static SmartContractCarrier CreateContract(int vmVersion, byte[] contractExecutionCode, ulong gasPrice, Gas gasLimit, string[] methodParameters = null) { if (contractExecutionCode == null) { throw new SmartContractCarrierException(nameof(contractExecutionCode) + " is null"); } var serializer = new MethodParameterSerializer(); string methodParams = GetMethodParams(serializer, methodParameters); var callData = new CallData((byte)ScOpcodeType.OP_CREATECONTRACT, vmVersion, gasPrice, gasLimit, contractExecutionCode, methodParams); var carrier = new SmartContractCarrier(new MethodParameterSerializer()); carrier.CallData = callData; if (!string.IsNullOrWhiteSpace(methodParams)) { carrier.MethodParameters = serializer.ToObjects(methodParams); } return(carrier); }
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); }
internal void LogExecutionContext(ILogger logger, IBlock block, IMessage message, uint160 contractAddress, SmartContractCarrier carrier) { var builder = new StringBuilder(); builder.Append(string.Format("{0}:{1},{2}:{3},", nameof(block.Coinbase), block.Coinbase, nameof(block.Number), block.Number)); builder.Append(string.Format("{0}:{1},", nameof(contractAddress), contractAddress.ToAddress(this.network))); builder.Append(string.Format("{0}:{1},", nameof(carrier.CallData.GasPrice), carrier.CallData.GasPrice)); builder.Append(string.Format("{0}:{1},{2}:{3},{4}:{5},{6}:{7}", nameof(message.ContractAddress), message.ContractAddress, nameof(message.GasLimit), message.GasLimit, nameof(message.Sender), message.Sender, nameof(message.Value), message.Value)); if (carrier.MethodParameters != null && carrier.MethodParameters.Length > 0) { builder.Append(string.Format(",{0}:{1}", nameof(carrier.MethodParameters), carrier.MethodParameters)); } logger.LogTrace("{0}", builder.ToString()); }
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); }
/// <summary> /// Get the address for a newly deployed contract. /// </summary> /// <param name="transaction"></param> public static uint160 GetNewContractAddress(this SmartContractCarrier carrier) { return(Core.NewContractAddressExtension.GetContractAddressFromTransactionHash(carrier.TransactionHash)); }