/// <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));
        }
Esempio n. 4
0
        /// <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));
        }
Esempio n. 5
0
        /// <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));
        }