/// <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(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); }
/// <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(string methodName) { var executionResult = new SmartContractExecutionResult { Exception = new SmartContractDoesNotExistException(methodName), GasConsumed = GasPriceList.ContractDoesNotExist() }; return(executionResult); }
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)); }
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("()"); // 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); }
/// <summary> /// Invokes a method on an existing smart contract /// </summary> public VmExecutionResult ExecuteMethod(byte[] contractCode, string contractMethodName, ISmartContractExecutionContext context, IGasMeter gasMeter, IPersistentState persistentState, IContractStateRepository repository) { this.logger.LogTrace("(){0}:{1}", nameof(contractMethodName), contractMethodName); ISmartContractExecutionResult executionResult = new SmartContractExecutionResult(); if (contractMethodName == null) { this.logger.LogTrace("(-)[CALLCONTRACT_METHODNAME_NOT_GIVEN]"); return(VmExecutionResult.Error(gasMeter.GasConsumed, null)); } byte[] gasInjectedCode = SmartContractGasInjector.AddGasCalculationToContractMethod(contractCode, contractMethodName); Type contractType = Load(gasInjectedCode); if (contractType == null) { this.logger.LogTrace("(-)[CALLCONTRACT_CONTRACTTYPE_NULL]"); return(VmExecutionResult.Error(gasMeter.GasConsumed, null)); } var internalTransferList = new List <TransferInfo>(); IInternalTransactionExecutor internalTransactionExecutor = this.internalTransactionExecutorFactory.Create(repository, internalTransferList); var balanceState = new BalanceState(repository, context.Message.Value, internalTransferList); var contractState = new SmartContractState( context.Block, context.Message, persistentState, gasMeter, internalTransactionExecutor, new InternalHashHelper(), () => balanceState.GetBalance(context.ContractAddress)); LifecycleResult result = SmartContractRestorer.Restore(contractType, contractState); if (!result.Success) { LogException(result.Exception); this.logger.LogTrace("(-)[CALLCONTRACT_INSTANTIATION_FAILED]:{0}={1}", nameof(gasMeter.GasConsumed), gasMeter.GasConsumed); executionResult.Exception = result.Exception.InnerException ?? result.Exception; executionResult.GasConsumed = gasMeter.GasConsumed; return(VmExecutionResult.Error(gasMeter.GasConsumed, result.Exception.InnerException ?? result.Exception)); } else { this.logger.LogTrace("[CALL_CONTRACT_INSTANTIATION_SUCCEEDED]"); } object methodResult = null; try { MethodInfo methodToInvoke = contractType.GetMethod(contractMethodName); if (methodToInvoke == null) { throw new ArgumentException(string.Format("[CALLCONTRACT_METHODTOINVOKE_NULL_DOESNOT_EXIST]:{0}={1}", nameof(contractMethodName), contractMethodName)); } if (methodToInvoke.IsConstructor) { throw new ConstructorInvocationException("[CALLCONTRACT_CANNOT_INVOKE_CTOR]"); } SmartContract smartContract = result.Object; methodResult = methodToInvoke.Invoke(smartContract, context.Parameters); } catch (ArgumentException argumentException) { LogException(argumentException); return(VmExecutionResult.Error(gasMeter.GasConsumed, argumentException)); } catch (TargetInvocationException targetException) { LogException(targetException); return(VmExecutionResult.Error(gasMeter.GasConsumed, targetException.InnerException ?? targetException)); } catch (TargetParameterCountException parameterException) { LogException(parameterException); } catch (ConstructorInvocationException constructorInvocationException) { LogException(constructorInvocationException); return(VmExecutionResult.Error(gasMeter.GasConsumed, constructorInvocationException)); } this.logger.LogTrace("(-):{0}={1}", nameof(gasMeter.GasConsumed), gasMeter.GasConsumed); return(VmExecutionResult.Success(internalTransferList, gasMeter.GasConsumed, methodResult)); }
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 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); }
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); }