Пример #1
0
        /// <summary>
        /// Creates a new instance of a smart contract by invoking the contract's constructor
        /// </summary>
        public VmExecutionResult Create(IStateRepository repository, ISmartContractState contractState, byte[] contractCode, object[] parameters, string typeName = null)
        {
            this.logger.LogTrace("()");

            string           typeToInstantiate;
            ContractByteCode code;

            // Decompile the contract execution code
            Result <IContractModuleDefinition> moduleResult = this.moduleDefinitionReader.Read(contractCode);

            if (moduleResult.IsFailure)
            {
                this.logger.LogTrace("(-)[CONTRACT_BYTECODE_INVALID]");
                return(VmExecutionResult.Error(new ContractErrorMessage("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]");
                    // TODO: List errors by string.
                    return(VmExecutionResult.Error(new ContractErrorMessage(new SmartContractValidationException(validation.Errors).ToString())));
                }

                typeToInstantiate = typeName ?? moduleDefinition.ContractType.Name;

                var observer = new Observer(contractState.GasMeter, MemoryUnitLimit);
                var rewriter = new ObserverRewriter(observer);
                moduleDefinition.Rewrite(rewriter);

                code = moduleDefinition.ToByteCode();
            }

            Result <IContract> contractLoadResult = this.Load(
                code,
                typeToInstantiate,
                contractState.Message.ContractAddress.ToUint160(this.network),
                contractState);

            if (!contractLoadResult.IsSuccess)
            {
                LogErrorMessage(contractLoadResult.Error);

                this.logger.LogTrace("(-)[LOAD_CONTRACT_FAILED]");

                return(VmExecutionResult.Error(new ContractErrorMessage(contractLoadResult.Error)));
            }

            IContract contract = contractLoadResult.Value;

            LogExecutionContext(this.logger, 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(VmExecutionResult.Error(invocationResult.ErrorMessage));
            }

            this.logger.LogTrace("[CREATE_CONTRACT_INSTANTIATION_SUCCEEDED]");

            this.logger.LogTrace("(-):{0}={1}", nameof(contract.Address), contract.Address);

            return(VmExecutionResult.Success(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,
                                        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(IGasMeter gasMeter,
                                        IContractState repository,
                                        ICreateData createData,
                                        ITransactionContext transactionContext,
                                        string typeName = null)
        {
            this.logger.LogTrace("()");

            // TODO: Spend Validation + Creation Fee here.

            string           typeToInstantiate;
            ContractByteCode code;

            // Decompile the contract execution code and validate it.
            using (IContractModuleDefinition moduleDefinition = this.moduleDefinitionReader.Read(createData.ContractExecutionCode))
            {
                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.Error(gasMeter.GasConsumed, new SmartContractValidationException(validation.Errors)));
                }

                typeToInstantiate = typeName ?? moduleDefinition.ContractType.Name;

                moduleDefinition.InjectConstructorGas();

                code = moduleDefinition.ToByteCode();
            }

            var internalTransferList = new List <TransferInfo>();

            uint160 address = this.addressGenerator.GenerateAddress(transactionContext.TransactionHash, transactionContext.GetNonceAndIncrement());

            var contractLogger = new ContractLogHolder(this.network);

            ISmartContractState contractState = this.SetupState(contractLogger, internalTransferList, gasMeter, repository, transactionContext, address);

            Result <IContract> contractLoadResult = this.Load(
                code,
                typeToInstantiate,
                address,
                contractState);

            if (!contractLoadResult.IsSuccess)
            {
                // TODO this is temporary until we improve error handling overloads
                var exception = new Exception(contractLoadResult.Error);

                LogException(exception);

                this.logger.LogTrace("(-)[LOAD_CONTRACT_FAILED]:{0}={1}", nameof(gasMeter.GasConsumed), gasMeter.GasConsumed);

                return(VmExecutionResult.Error(gasMeter.GasConsumed, exception));
            }

            IContract contract = contractLoadResult.Value;

            LogExecutionContext(this.logger, contract.State.Block, contract.State.Message, contract.Address, createData);

            // Create an account for the contract in the state repository.
            repository.CreateAccount(contract.Address);

            // Invoke the constructor of the provided contract code
            IContractInvocationResult invocationResult = contract.InvokeConstructor(createData.MethodParameters);

            if (!invocationResult.IsSuccess)
            {
                this.logger.LogTrace("[CREATE_CONTRACT_INSTANTIATION_FAILED]");
                return(VmExecutionResult.Error(gasMeter.GasConsumed, new Exception("Constructor invocation failed!")));
            }

            this.logger.LogTrace("[CREATE_CONTRACT_INSTANTIATION_SUCCEEDED]");

            this.logger.LogTrace("(-):{0}={1}, {2}={3}", nameof(contract.Address), contract.Address, nameof(gasMeter.GasConsumed), gasMeter.GasConsumed);

            repository.SetCode(contract.Address, createData.ContractExecutionCode);
            repository.SetContractType(contract.Address, contract.Type.Name);

            return(VmExecutionResult.CreationSuccess(contract.Address, internalTransferList, gasMeter.GasConsumed, invocationResult.Return, contractLogger.GetRawLogs()));
        }
Пример #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));
        }
        /// <summary>
        /// Creates a new instance of a smart contract by invoking the contract's constructor
        /// </summary>
        public VmExecutionResult Create(IContractState repository, ISmartContractState contractState, byte[] contractCode, object[] parameters, string typeName = null)
        {
            this.logger.LogTrace("()");

            string           typeToInstantiate;
            ContractByteCode code;

            // Decompile the contract execution code and validate it.
            using (IContractModuleDefinition moduleDefinition = this.moduleDefinitionReader.Read(contractCode))
            {
                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.Error(new SmartContractValidationException(validation.Errors)));
                }

                typeToInstantiate = typeName ?? moduleDefinition.ContractType.Name;

                moduleDefinition.InjectConstructorGas();

                code = moduleDefinition.ToByteCode();
            }

            Result <IContract> contractLoadResult = this.Load(
                code,
                typeToInstantiate,
                contractState.Message.ContractAddress.ToUint160(this.network),
                contractState);

            if (!contractLoadResult.IsSuccess)
            {
                // TODO this is temporary until we improve error handling overloads
                var exception = new Exception(contractLoadResult.Error);

                LogException(exception);

                this.logger.LogTrace("(-)[LOAD_CONTRACT_FAILED]");

                return(VmExecutionResult.Error(exception));
            }

            IContract contract = contractLoadResult.Value;

            LogExecutionContext(this.logger, 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(VmExecutionResult.Error(new Exception("Constructor invocation failed!")));
            }

            this.logger.LogTrace("[CREATE_CONTRACT_INSTANTIATION_SUCCEEDED]");

            this.logger.LogTrace("(-):{0}={1}", nameof(contract.Address), contract.Address);

            return(VmExecutionResult.Success(invocationResult.Return, typeToInstantiate));
        }