private StateTransitionResult ApplyCreate(IState state, object[] parameters, byte[] code, BaseMessage message, uint160 address, IGasMeter gasMeter, string type = null) { state.ContractState.CreateAccount(address); ISmartContractState smartContractState = state.CreateSmartContractState(state, gasMeter, address, message, state.ContractState); VmExecutionResult result = this.Vm.Create(state.ContractState, smartContractState, code, parameters, type); bool revert = !result.IsSuccess; if (revert) { return(StateTransitionResult.Fail( gasMeter.GasConsumed, result.Error)); } return(StateTransitionResult.Ok( gasMeter.GasConsumed, address, result.Success.Result )); }
private StateTransitionResult ApplyCall(IState state, CallMessage message, byte[] contractCode, IGasMeter gasMeter) { // This needs to happen after the base fee is charged, which is why it's in here. // TODO - Remove this check. It isn't possible for the method name to be null. if (message.Method.Name == null) { return(StateTransitionResult.Fail(gasMeter.GasConsumed, StateTransitionErrorKind.NoMethodName)); } string type = state.ContractState.GetContractType(message.To); ISmartContractState smartContractState = state.CreateSmartContractState(state, gasMeter, message.To, message, state.ContractState); VmExecutionResult result = this.Vm.ExecuteMethod(smartContractState, message.Method, contractCode, type); bool revert = !result.IsSuccess; if (revert) { return(StateTransitionResult.Fail( gasMeter.GasConsumed, result.Error)); } return(StateTransitionResult.Ok( gasMeter.GasConsumed, message.To, result.Success.Result )); }
/// <summary> /// Invokes a method on an existing smart contract /// </summary> public VmExecutionResult ExecuteMethod(ISmartContractState contractState, RuntimeObserver.IGasMeter gasMeter, MethodCall methodCall, byte[] contractCode, string typeName) { ContractByteCode code; // Code we're loading from database - can assume it's valid. using (IContractModuleDefinition moduleDefinition = this.moduleDefinitionReader.Read(contractCode).Value) { var observer = new Observer(gasMeter, new MemoryMeter(MemoryUnitLimit)); var rewriter = new ObserverRewriter(observer); if (!this.Rewrite(moduleDefinition, rewriter)) { return(VmExecutionResult.Fail(VmExecutionErrorKind.RewriteFailed, "Rewrite module failed")); } Result <ContractByteCode> getCodeResult = this.GetByteCode(moduleDefinition); if (!getCodeResult.IsSuccess) { return(VmExecutionResult.Fail(VmExecutionErrorKind.RewriteFailed, "Serialize module failed")); } code = getCodeResult.Value; } Result <IContract> contractLoadResult = this.Load( code, typeName, contractState.Message.ContractAddress.ToUint160(), contractState); if (!contractLoadResult.IsSuccess) { return(VmExecutionResult.Fail(VmExecutionErrorKind.LoadFailed, contractLoadResult.Error)); } IContract contract = contractLoadResult.Value; this.LogExecutionContext(contract.State.Block, contract.State.Message, contract.Address); IContractInvocationResult invocationResult = contract.Invoke(methodCall); if (!invocationResult.IsSuccess) { this.logger.LogTrace("(-)[CALLCONTRACT_INSTANTIATION_FAILED]"); return(GetInvocationVmErrorResult(invocationResult)); } this.logger.LogTrace("[CALL_CONTRACT_INSTANTIATION_SUCCEEDED]"); return(VmExecutionResult.Ok(invocationResult.Return, typeName)); }
private static VmExecutionResult GetInvocationVmErrorResult(IContractInvocationResult invocationResult) { if (invocationResult.InvocationErrorType == ContractInvocationErrorType.OutOfGas) { return(VmExecutionResult.Fail(VmExecutionErrorKind.OutOfGas, invocationResult.ErrorMessage)); } if (invocationResult.InvocationErrorType == ContractInvocationErrorType.OverMemoryLimit) { return(VmExecutionResult.Fail(VmExecutionErrorKind.OutOfResources, invocationResult.ErrorMessage)); } return(VmExecutionResult.Fail(VmExecutionErrorKind.InvocationFailed, invocationResult.ErrorMessage)); }
/// <summary> /// Creates a new instance of a smart contract by invoking the contract's constructor /// </summary> public VmExecutionResult Create(IStateRepository repository, ISmartContractState contractState, RuntimeObserver.IGasMeter gasMeter, byte[] contractCode, object[] parameters, string typeName = null) { string typeToInstantiate; ContractByteCode code; // Decompile the contract execution code Result <IContractModuleDefinition> moduleResult = this.moduleDefinitionReader.Read(contractCode); if (moduleResult.IsFailure) { this.logger.LogTrace(moduleResult.Error); this.logger.LogTrace("(-)[CONTRACT_BYTECODE_INVALID]"); return(VmExecutionResult.Fail(VmExecutionErrorKind.LoadFailed, "Contract bytecode is not valid IL.")); } // Validate contract execution code using (IContractModuleDefinition moduleDefinition = moduleResult.Value) { SmartContractValidationResult validation = moduleDefinition.Validate(this.validator); // If validation failed, refund the sender any remaining gas. if (!validation.IsValid) { this.logger.LogTrace("(-)[CONTRACT_VALIDATION_FAILED]"); return(VmExecutionResult.Fail(VmExecutionErrorKind.ValidationFailed, new SmartContractValidationException(validation.Errors).ToString())); } typeToInstantiate = typeName ?? moduleDefinition.ContractType.Name; var observer = new Observer(gasMeter, new MemoryMeter(MemoryUnitLimit)); var rewriter = new ObserverRewriter(observer); if (!this.Rewrite(moduleDefinition, rewriter)) { return(VmExecutionResult.Fail(VmExecutionErrorKind.RewriteFailed, "Rewrite module failed")); } Result <ContractByteCode> getCodeResult = this.GetByteCode(moduleDefinition); if (!getCodeResult.IsSuccess) { return(VmExecutionResult.Fail(VmExecutionErrorKind.RewriteFailed, "Serialize module failed")); } code = getCodeResult.Value; } Result <IContract> contractLoadResult = this.Load( code, typeToInstantiate, contractState.Message.ContractAddress.ToUint160(), contractState); if (!contractLoadResult.IsSuccess) { return(VmExecutionResult.Fail(VmExecutionErrorKind.LoadFailed, contractLoadResult.Error)); } IContract contract = contractLoadResult.Value; this.LogExecutionContext(contract.State.Block, contract.State.Message, contract.Address); // Set the code and the Type before the method is invoked repository.SetCode(contract.Address, contractCode); repository.SetContractType(contract.Address, typeToInstantiate); // Invoke the constructor of the provided contract code IContractInvocationResult invocationResult = contract.InvokeConstructor(parameters); if (!invocationResult.IsSuccess) { this.logger.LogTrace("[CREATE_CONTRACT_INSTANTIATION_FAILED]"); return(GetInvocationVmErrorResult(invocationResult)); } this.logger.LogTrace("[CREATE_CONTRACT_INSTANTIATION_SUCCEEDED]"); return(VmExecutionResult.Ok(invocationResult.Return, typeToInstantiate)); }
/// <summary> /// Creates a new instance of a smart contract by invoking the contract's constructor /// </summary> public VmExecutionResult Create(IStateRepository repository, ISmartContractState contractState, ExecutionContext executionContext, byte[] contractCode, object[] parameters, string typeName = null) { // The type and code that will ultimately be executed. Assigned based on which method we use to rewrite contract code. string typeToInstantiate; IContract contract; Observer previousObserver = null; // Hash the code byte[] codeHash = HashHelper.Keccak256(contractCode); uint256 codeHashUint256 = new uint256(codeHash); // Lets see if we already have an assembly CachedAssemblyPackage assemblyPackage = this.assemblyCache.Retrieve(codeHashUint256); if (assemblyPackage != null) { // If the assembly is in the cache, keep a reference to its observer around. // We might be in a nested execution for the same assembly, // in which case we need to restore the previous observer later. previousObserver = assemblyPackage.Assembly.GetObserver(); typeToInstantiate = typeName ?? assemblyPackage.Assembly.DeployedType.Name; Type type = assemblyPackage.Assembly.GetType(typeToInstantiate); uint160 address = contractState.Message.ContractAddress.ToUint160(); contract = Contract.CreateUninitialized(type, contractState, address); // TODO: Type not found? // TODO: Setting observer error? // TODO: Error instantiating contract? } else { // Create from scratch // Validate then rewrite the entirety of the incoming code. Result <IContractModuleDefinition> moduleResult = this.moduleDefinitionReader.Read(contractCode); if (moduleResult.IsFailure) { this.logger.LogDebug(moduleResult.Error); this.logger.LogTrace("(-)[CONTRACT_BYTECODE_INVALID]"); return(VmExecutionResult.Fail(VmExecutionErrorKind.LoadFailed, "Contract bytecode is not valid IL.")); } using (IContractModuleDefinition moduleDefinition = moduleResult.Value) { SmartContractValidationResult validation = moduleDefinition.Validate(this.validator); // If validation failed, refund the sender any remaining gas. if (!validation.IsValid) { this.logger.LogTrace("(-)[CONTRACT_VALIDATION_FAILED]"); return(VmExecutionResult.Fail(VmExecutionErrorKind.ValidationFailed, new SmartContractValidationException(validation.Errors).ToString())); } var rewriter = new ObserverInstanceRewriter(); if (!this.Rewrite(moduleDefinition, rewriter)) { return(VmExecutionResult.Fail(VmExecutionErrorKind.RewriteFailed, "Rewrite module failed")); } Result <ContractByteCode> getCodeResult = this.GetByteCode(moduleDefinition); if (!getCodeResult.IsSuccess) { return(VmExecutionResult.Fail(VmExecutionErrorKind.RewriteFailed, "Serialize module failed")); } // Everything worked. Assign what will get executed. typeToInstantiate = typeName ?? moduleDefinition.ContractType.Name; ContractByteCode code = getCodeResult.Value; Result <IContract> contractLoadResult = this.Load( code, typeToInstantiate, contractState.Message.ContractAddress.ToUint160(), contractState); if (!contractLoadResult.IsSuccess) { return(VmExecutionResult.Fail(VmExecutionErrorKind.LoadFailed, contractLoadResult.Error)); } contract = contractLoadResult.Value; assemblyPackage = new CachedAssemblyPackage(new ContractAssembly(contract.Type.Assembly)); // Cache this completely validated and rewritten contract to reuse later. this.assemblyCache.Store(codeHashUint256, assemblyPackage); } } this.LogExecutionContext(contract.State.Block, contract.State.Message, contract.Address); // Set the code and the Type before the method is invoked repository.SetCode(contract.Address, contractCode); repository.SetContractType(contract.Address, typeToInstantiate); // Set Observer and load and execute. assemblyPackage.Assembly.SetObserver(executionContext.Observer); // Invoke the constructor of the provided contract code IContractInvocationResult invocationResult = contract.InvokeConstructor(parameters); // Always reset the observer, even if the previous was null. assemblyPackage.Assembly.SetObserver(previousObserver); if (!invocationResult.IsSuccess) { this.logger.LogDebug("CREATE_CONTRACT_INSTANTIATION_FAILED"); return(GetInvocationVmErrorResult(invocationResult)); } this.logger.LogDebug("CREATE_CONTRACT_INSTANTIATION_SUCCEEDED"); return(VmExecutionResult.Ok(invocationResult.Return, typeToInstantiate)); }
/// <summary> /// Invokes a method on an existing smart contract /// </summary> public VmExecutionResult ExecuteMethod(ISmartContractState contractState, ExecutionContext executionContext, MethodCall methodCall, byte[] contractCode, string typeName) { IContract contract; Observer previousObserver = null; // Hash the code byte[] codeHash = HashHelper.Keccak256(contractCode); uint256 codeHashUint256 = new uint256(codeHash); // Lets see if we already have an assembly CachedAssemblyPackage assemblyPackage = this.assemblyCache.Retrieve(codeHashUint256); if (assemblyPackage != null) { // If the assembly is in the cache, keep a reference to its observer around. // We might be in a nested execution for the same assembly, // in which case we need to restore the previous observer later. previousObserver = assemblyPackage.Assembly.GetObserver(); Type type = assemblyPackage.Assembly.GetType(typeName); uint160 address = contractState.Message.ContractAddress.ToUint160(); contract = Contract.CreateUninitialized(type, contractState, address); } else { // Rewrite from scratch. using (IContractModuleDefinition moduleDefinition = this.moduleDefinitionReader.Read(contractCode).Value) { var rewriter = new ObserverInstanceRewriter(); if (!this.Rewrite(moduleDefinition, rewriter)) { return(VmExecutionResult.Fail(VmExecutionErrorKind.RewriteFailed, "Rewrite module failed")); } Result <ContractByteCode> getCodeResult = this.GetByteCode(moduleDefinition); if (!getCodeResult.IsSuccess) { return(VmExecutionResult.Fail(VmExecutionErrorKind.RewriteFailed, "Serialize module failed")); } // Everything worked. Assign the code that will be executed. ContractByteCode code = getCodeResult.Value; // Creating a new observer instance here is necessary due to nesting. // If a nested call takes place it will use a new gas meter instance, // due to the fact that the nested call's gas limit may be specified by the user. // Because of that we can't reuse the same observer for a single execution. Result <IContract> contractLoadResult = this.Load( code, typeName, contractState.Message.ContractAddress.ToUint160(), contractState); if (!contractLoadResult.IsSuccess) { return(VmExecutionResult.Fail(VmExecutionErrorKind.LoadFailed, contractLoadResult.Error)); } contract = contractLoadResult.Value; assemblyPackage = new CachedAssemblyPackage(new ContractAssembly(contract.Type.Assembly)); // Cache this completely validated and rewritten contract to reuse later. this.assemblyCache.Store(codeHashUint256, assemblyPackage); } } this.LogExecutionContext(contract.State.Block, contract.State.Message, contract.Address); // Set new Observer and load and execute. assemblyPackage.Assembly.SetObserver(executionContext.Observer); IContractInvocationResult invocationResult = contract.Invoke(methodCall); // Always reset the observer, even if the previous was null. assemblyPackage.Assembly.SetObserver(previousObserver); if (!invocationResult.IsSuccess) { this.logger.LogTrace("(-)[CALLCONTRACT_INSTANTIATION_FAILED]"); return(GetInvocationVmErrorResult(invocationResult)); } this.logger.LogDebug("CALL_CONTRACT_INSTANTIATION_SUCCEEDED"); return(VmExecutionResult.Ok(invocationResult.Return, typeName)); }