/// <summary>
        /// Sets up a new <see cref="ISmartContractState"/> based on the current state.
        /// </summary>
        public ISmartContractState Create(IState state, IGasMeter gasMeter, uint160 address, BaseMessage message, IStateRepository repository)
        {
            IPersistenceStrategy persistenceStrategy = new MeteredPersistenceStrategy(repository, gasMeter, new BasicKeyEncodingStrategy());

            var persistentState = new PersistentState(persistenceStrategy, this.Serializer, address);

            var contractLogger = new MeteredContractLogger(gasMeter, state.LogHolder, this.Network, this.Serializer);

            var contractState = new SmartContractState(
                state.Block,
                new Message(
                    address.ToAddress(this.Network),
                    message.From.ToAddress(this.Network),
                    message.Amount
                    ),
                persistentState,
                this.Serializer,
                gasMeter,
                contractLogger,
                this.InternalTransactionExecutorFactory.Create(state),
                new InternalHashHelper(),
                () => state.GetBalance(address));

            return(contractState);
        }
        /// <summary>
        /// Creates a new instance of a smart contract by invoking the contract's constructor
        /// </summary>
        public VmExecutionResult Create(byte[] contractCode,
                                        ISmartContractExecutionContext context,
                                        IGasMeter gasMeter,
                                        IPersistentState persistentState,
                                        IContractStateRepository repository)
        {
            this.logger.LogTrace("()");

            byte[] gasInjectedCode = SmartContractGasInjector.AddGasCalculationToConstructor(contractCode);

            Type contractType = Load(gasInjectedCode);

            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));

            // Invoke the constructor of the provided contract code
            LifecycleResult result = SmartContractConstructor.Construct(contractType, contractState, context.Parameters);

            if (!result.Success)
            {
                LogException(result.Exception);

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

                return(VmExecutionResult.Error(gasMeter.GasConsumed, result.Exception.InnerException ?? result.Exception));
            }
            else
            {
                this.logger.LogTrace("[CREATE_CONTRACT_INSTANTIATION_SUCCEEDED]");
            }

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

            return(VmExecutionResult.Success(internalTransferList, gasMeter.GasConsumed, result.Object));
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Sets up a new <see cref="ISmartContractState"/> based on the current state.
        /// </summary>
        private ISmartContractState CreateSmartContractState(IGasMeter gasMeter, uint160 address, BaseMessage message, IContractState repository)
        {
            IPersistenceStrategy persistenceStrategy = new MeteredPersistenceStrategy(repository, gasMeter, new BasicKeyEncodingStrategy());

            var persistentState = new PersistentState(persistenceStrategy, new ContractPrimitiveSerializer(this.Network), address);

            var contractState = new SmartContractState(
                this.Block,
                new Message(
                    address.ToAddress(this.Network),
                    message.From.ToAddress(this.Network),
                    message.Amount
                    ),
                persistentState,
                this.Serializer,
                gasMeter,
                this.LogHolder,
                this.InternalTransactionExecutorFactory.Create(this),
                new InternalHashHelper(),
                () => this.BalanceState.GetBalance(address));

            return(contractState);
        }
        /// <summary>
        /// Sets up the state object for the contract execution
        /// </summary>
        private ISmartContractState SetupState(
            IContractLogHolder contractLogger,
            List <TransferInfo> internalTransferList,
            IGasMeter gasMeter,
            IContractState repository,
            ITransactionContext transactionContext,
            uint160 contractAddress)
        {
            IPersistenceStrategy persistenceStrategy =
                new MeteredPersistenceStrategy(repository, gasMeter, new BasicKeyEncodingStrategy());

            var persistentState = new PersistentState(persistenceStrategy, this.contractPrimitiveSerializer, contractAddress);

            IInternalTransactionExecutor internalTransactionExecutor = this.internalTransactionExecutorFactory.Create(this, contractLogger, repository, internalTransferList, transactionContext);

            var balanceState = new BalanceState(repository, transactionContext.Amount, internalTransferList);

            var contractState = new SmartContractState(
                new Block(
                    transactionContext.BlockHeight,
                    transactionContext.Coinbase.ToAddress(this.network)
                    ),
                new Message(
                    contractAddress.ToAddress(this.network),
                    transactionContext.From.ToAddress(this.network),
                    transactionContext.Amount
                    ),
                persistentState,
                this.contractPrimitiveSerializer,
                gasMeter,
                contractLogger,
                internalTransactionExecutor,
                new InternalHashHelper(),
                () => balanceState.GetBalance(contractAddress));

            return(contractState);
        }
        /// <summary>
        /// Creates a new instance of a smart contract by invoking the contract's constructor
        /// </summary>
        public VmExecutionResult Create(IGasMeter gasMeter,
                                        IContractStateRepository repository,
                                        ICreateData createData,
                                        ITransactionContext transactionContext)
        {
            this.logger.LogTrace("()");

            // TODO: Spend Validation + Creation Fee here.

            // Decompile the contract execution code and validate it.
            SmartContractDecompilation decompilation = SmartContractDecompiler.GetModuleDefinition(createData.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(VmExecutionResult.Error(gasMeter.GasConsumed, new SmartContractValidationException(validation.Errors)));
            }

            byte[] gasInjectedCode = SmartContractGasInjector.AddGasCalculationToConstructor(createData.ContractExecutionCode, decompilation.ContractType.Name);

            Type contractType = Load(gasInjectedCode, decompilation.ContractType.Name);

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

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

            IPersistenceStrategy persistenceStrategy = new MeteredPersistenceStrategy(repository, gasMeter, new BasicKeyEncodingStrategy());

            var persistentState = new PersistentState(persistenceStrategy, contractAddress, this.network);

            var internalTransferList = new List <TransferInfo>();

            IInternalTransactionExecutor internalTransactionExecutor = this.internalTransactionExecutorFactory.Create(this, repository, internalTransferList, transactionContext);

            var balanceState = new BalanceState(repository, transactionContext.Amount, internalTransferList);

            var contractState = new SmartContractState(
                new Block(
                    transactionContext.BlockHeight,
                    transactionContext.Coinbase.ToAddress(this.network)
                    ),
                new Message(
                    contractAddress.ToAddress(this.network),
                    transactionContext.From.ToAddress(this.network),
                    transactionContext.Amount
                    ),
                persistentState,
                gasMeter,
                internalTransactionExecutor,
                new InternalHashHelper(),
                () => balanceState.GetBalance(contractAddress));

            LogExecutionContext(this.logger, contractState.Block, contractState.Message, contractAddress, createData);

            // Invoke the constructor of the provided contract code
            LifecycleResult result = SmartContractConstructor.Construct(contractType, contractState, createData.MethodParameters);

            if (!result.Success)
            {
                LogException(result.Exception);

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

                return(VmExecutionResult.Error(gasMeter.GasConsumed, result.Exception.InnerException ?? result.Exception));
            }

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

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

            repository.SetCode(contractAddress, createData.ContractExecutionCode);
            repository.SetContractType(contractAddress, contractType.Name);

            return(VmExecutionResult.CreationSuccess(contractAddress, internalTransferList, gasMeter.GasConsumed, result.Object));
        }
        /// <summary>
        /// Invokes a method on an existing smart contract
        /// </summary>
        public VmExecutionResult ExecuteMethod(
            IGasMeter gasMeter,
            IContractStateRepository repository,
            ICallData callData,
            ITransactionContext transactionContext)
        {
            this.logger.LogTrace("(){0}:{1}", nameof(callData.MethodName), callData.MethodName);

            if (callData.MethodName == null)
            {
                this.logger.LogTrace("(-)[CALLCONTRACT_METHODNAME_NOT_GIVEN]");
                return(VmExecutionResult.Error(gasMeter.GasConsumed, null));
            }

            byte[] contractExecutionCode = repository.GetCode(callData.ContractAddress);
            string typeName = repository.GetContractType(callData.ContractAddress);

            if (contractExecutionCode == null)
            {
                return(VmExecutionResult.Error(gasMeter.GasConsumed, new SmartContractDoesNotExistException(callData.MethodName)));
            }

            byte[] gasInjectedCode = SmartContractGasInjector.AddGasCalculationToContractMethod(contractExecutionCode, typeName, callData.MethodName);

            Type contractType = Load(gasInjectedCode, typeName);

            if (contractType == null)
            {
                this.logger.LogTrace("(-)[CALLCONTRACT_CONTRACTTYPE_NULL]");
                return(VmExecutionResult.Error(gasMeter.GasConsumed, null));
            }

            uint160 contractAddress = callData.ContractAddress;

            IPersistenceStrategy persistenceStrategy = new MeteredPersistenceStrategy(repository, gasMeter, new BasicKeyEncodingStrategy());

            IPersistentState persistentState = new PersistentState(persistenceStrategy, contractAddress, this.network);

            var internalTransferList = new List <TransferInfo>();

            IInternalTransactionExecutor internalTransactionExecutor = this.internalTransactionExecutorFactory.Create(this, repository, internalTransferList, transactionContext);

            var balanceState = new BalanceState(repository, transactionContext.Amount, internalTransferList);

            var contractState = new SmartContractState(
                new Block(
                    transactionContext.BlockHeight,
                    transactionContext.Coinbase.ToAddress(this.network)
                    ),
                new Message(
                    callData.ContractAddress.ToAddress(this.network),
                    transactionContext.From.ToAddress(this.network),
                    transactionContext.Amount
                    ),
                persistentState,
                gasMeter,
                internalTransactionExecutor,
                new InternalHashHelper(),
                () => balanceState.GetBalance(callData.ContractAddress));

            LogExecutionContext(this.logger, contractState.Block, contractState.Message, contractAddress, callData);

            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);

                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(callData.MethodName);
                if (methodToInvoke == null)
                {
                    throw new ArgumentException(string.Format("[CALLCONTRACT_METHODTOINVOKE_NULL_DOESNOT_EXIST]:{0}={1}", nameof(callData.MethodName), callData.MethodName));
                }

                if (methodToInvoke.IsConstructor)
                {
                    throw new ConstructorInvocationException("[CALLCONTRACT_CANNOT_INVOKE_CTOR]");
                }

                SmartContract smartContract = result.Object;
                methodResult = methodToInvoke.Invoke(smartContract, callData.MethodParameters);
            }
            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));
        }
        /// <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));
        }