Esempio n. 1
0
        /// <summary>
        /// Applies an internally generated contract creation message to the current state.
        /// </summary>
        public StateTransitionResult Apply(InternalCreateMessage message)
        {
            bool enoughBalance = this.EnsureContractHasEnoughBalance(message.From, message.Amount);

            if (!enoughBalance)
            {
                throw new InsufficientBalanceException();
            }

            byte[] contractCode = this.intermediateState.GetCode(message.From);

            StateTransitionResult result = this.ApplyCreate(message.Parameters, contractCode, message, message.Type);

            // For successful internal creates we need to add the transfer to the internal transfer list.
            // For external creates we do not need to do this.
            if (result.IsSuccess)
            {
                this.internalTransfers.Add(new TransferInfo
                {
                    From  = message.From,
                    To    = result.Success.ContractAddress,
                    Value = message.Amount
                });
            }

            return(result);
        }
Esempio n. 2
0
        /// <summary>
        /// Applies an internally generated contract creation message to the current state.
        /// </summary>
        public StateTransitionResult Apply(IState state, InternalCreateMessage message)
        {
            bool enoughBalance = this.EnsureSenderHasEnoughBalance(state, message.From, message.Amount);

            if (!enoughBalance)
            {
                return(StateTransitionResult.Fail((Gas)0, StateTransitionErrorKind.InsufficientBalance)); // Trivial - just return and let the MethodCall gas account for it.
            }
            var gasMeter = new GasMeter(message.GasLimit);

            gasMeter.Spend((Gas)GasPriceList.CreateCost);

            byte[] contractCode = state.ContractState.GetCode(message.From);

            uint160 address = state.GenerateAddress(this.AddressGenerator);

            // For internal creates we need to add the value contained in the contract invocation transaction
            // to the internal transfer list. This must occur before we apply the message to the state.
            // For external creates we do not need to do this.
            state.AddInternalTransfer(new TransferInfo(message.From, address, message.Amount));

            StateTransitionResult result = this.ApplyCreate(state, message.Parameters, contractCode, message, address, gasMeter, message.Type);

            return(result);
        }
Esempio n. 3
0
        /// <summary>
        /// Applies an internally generated contract funds transfer message to the current state.
        /// </summary>
        public StateTransitionResult Apply(IState state, ContractTransferMessage message)
        {
            bool enoughBalance = this.EnsureSenderHasEnoughBalance(state, message.From, message.Amount);

            if (!enoughBalance)
            {
                return(StateTransitionResult.Fail((Gas)0, StateTransitionErrorKind.InsufficientBalance));
            }

            var gasMeter = new GasMeter(message.GasLimit);

            // If it's not a contract, create a regular P2PKH tx
            // If it is a contract, do a regular contract call
            byte[] contractCode = state.ContractState.GetCode(message.To);

            if (contractCode == null || contractCode.Length == 0)
            {
                gasMeter.Spend((Gas)GasPriceList.TransferCost);

                // No contract at this address, create a regular P2PKH xfer
                state.AddInternalTransfer(new TransferInfo(message.From, message.To, message.Amount));

                return(StateTransitionResult.Ok(gasMeter.GasConsumed, message.To));
            }

            // For internal contract-contract transfers we need to add the value contained in the contract invocation transaction
            // to the internal transfer list. This must occur before we apply the message to the state.
            state.AddInternalTransfer(new TransferInfo(message.From, message.To, message.Amount));

            gasMeter.Spend((Gas)GasPriceList.BaseCost);
            StateTransitionResult result = this.ApplyCall(state, message, contractCode, gasMeter);

            return(result);
        }
Esempio n. 4
0
        /// <summary>
        /// Applies an internally generated contract method call message to the current state.
        /// </summary>
        public StateTransitionResult Apply(InternalCallMessage message)
        {
            bool enoughBalance = this.EnsureContractHasEnoughBalance(message.From, message.Amount);

            if (!enoughBalance)
            {
                return(StateTransitionResult.Fail((Gas)0, StateTransitionErrorKind.InsufficientBalance));
            }

            byte[] contractCode = this.intermediateState.GetCode(message.To);

            if (contractCode == null || contractCode.Length == 0)
            {
                return(StateTransitionResult.Fail((Gas)0, StateTransitionErrorKind.NoCode));
            }

            StateTransitionResult result = this.ApplyCall(message, contractCode);

            // For successful internal calls we need to add the transfer to the internal transfer list.
            // For external calls we do not need to do this.
            if (result.IsSuccess)
            {
                this.internalTransfers.Add(new TransferInfo
                {
                    From  = message.From,
                    To    = message.To,
                    Value = message.Amount
                });
            }

            return(result);
        }
Esempio n. 5
0
        /// <summary>
        /// Applies an internally generated contract method call message to the current state.
        /// </summary>
        public StateTransitionResult Apply(IState state, InternalCallMessage message)
        {
            bool enoughBalance = this.EnsureSenderHasEnoughBalance(state, message.From, message.Amount);

            if (!enoughBalance)
            {
                return(StateTransitionResult.Fail((Gas)0, StateTransitionErrorKind.InsufficientBalance)); // Trivial - just return and let the MethodCall gas account for it.
            }
            var gasMeter = new GasMeter(message.GasLimit);

            gasMeter.Spend((Gas)GasPriceList.BaseCost);

            byte[] contractCode = state.ContractState.GetCode(message.To);

            if (contractCode == null || contractCode.Length == 0)
            {
                return(StateTransitionResult.Fail(gasMeter.GasConsumed, StateTransitionErrorKind.NoCode));
            }

            // For internal calls we need to add the value contained in the contract invocation transaction
            // to the internal transfer list. This must occur before we apply the message to the state.
            // For external calls we do not need to do this.
            state.AddInternalTransfer(new TransferInfo(message.From, message.To, message.Amount));

            StateTransitionResult result = this.ApplyCall(state, message, contractCode, gasMeter);

            return(result);
        }
Esempio n. 6
0
        private StateTransitionResult ApplyCall(IState state, CallMessage message, byte[] contractCode)
        {
            var gasMeter = new GasMeter(message.GasLimit);

            gasMeter.Spend((Gas)GasPriceList.BaseCost);

            // This needs to happen after the base fee is charged, which is why it's in here.
            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.ErrorMessage != null;

            if (revert)
            {
                return(StateTransitionResult.Fail(
                           gasMeter.GasConsumed,
                           result.ErrorMessage));
            }

            return(StateTransitionResult.Ok(
                       gasMeter.GasConsumed,
                       message.To,
                       result.Result
                       ));
        }
Esempio n. 7
0
        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
                       ));
        }
Esempio n. 8
0
        /// <summary>
        /// Applies an internally generated contract creation message to the current state.
        /// </summary>
        public StateTransitionResult Apply(IState state, InternalCreateMessage message)
        {
            bool enoughBalance = this.EnsureContractHasEnoughBalance(state, message.From, message.Amount);

            if (!enoughBalance)
            {
                return(StateTransitionResult.Fail((Gas)0, StateTransitionErrorKind.InsufficientBalance));
            }

            byte[] contractCode = state.ContractState.GetCode(message.From);

            StateTransitionResult result = this.ApplyCreate(state, message.Parameters, contractCode, message, message.Type);

            // For successful internal creates we need to add the transfer to the internal transfer list.
            // For external creates we do not need to do this.
            if (result.IsSuccess)
            {
                state.AddInternalTransfer(new TransferInfo
                {
                    From  = message.From,
                    To    = result.Success.ContractAddress,
                    Value = message.Amount
                });
            }

            return(result);
        }
Esempio n. 9
0
        private StateTransitionResult ApplyCreate(IState state, object[] parameters, byte[] code, BaseMessage message, string type = null)
        {
            var gasMeter = new GasMeter(message.GasLimit);

            gasMeter.Spend((Gas)GasPriceList.BaseCost);

            uint160 address = state.GenerateAddress(this.AddressGenerator);

            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.ErrorMessage != null;

            if (revert)
            {
                return(StateTransitionResult.Fail(
                           gasMeter.GasConsumed,
                           result.ErrorMessage));
            }

            return(StateTransitionResult.Ok(
                       gasMeter.GasConsumed,
                       address,
                       result.Result
                       ));
        }
Esempio n. 10
0
        /// <summary>
        /// Applies an internally generated contract funds transfer message to the current state.
        /// </summary>
        public StateTransitionResult Apply(ContractTransferMessage message)
        {
            bool enoughBalance = this.EnsureContractHasEnoughBalance(message.From, message.Amount);

            if (!enoughBalance)
            {
                return(StateTransitionResult.Fail((Gas)0, StateTransitionErrorKind.InsufficientBalance));
            }

            // If it's not a contract, create a regular P2PKH tx
            // If it is a contract, do a regular contract call
            byte[] contractCode = this.intermediateState.GetCode(message.To);

            if (contractCode == null || contractCode.Length == 0)
            {
                // No contract at this address, create a regular P2PKH xfer
                this.internalTransfers.Add(new TransferInfo
                {
                    From  = message.From,
                    To    = message.To,
                    Value = message.Amount
                });

                return(StateTransitionResult.Ok((Gas)0, message.To));
            }

            return(this.ApplyCall(message, contractCode));
        }
Esempio n. 11
0
        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
                       ));
        }
Esempio n. 12
0
        /// <summary>
        /// Applies an externally generated contract method call message to the current state.
        /// </summary>
        public StateTransitionResult Apply(ExternalCallMessage message)
        {
            byte[] contractCode = this.intermediateState.GetCode(message.To);

            if (contractCode == null || contractCode.Length == 0)
            {
                return(StateTransitionResult.Fail((Gas)0, StateTransitionErrorKind.NoCode));
            }

            return(this.ApplyCall(message, contractCode));
        }
Esempio n. 13
0
        private StateTransitionResult ApplyCall(CallMessage message, byte[] contractCode)
        {
            if (this.GasRemaining < message.GasLimit || this.GasRemaining < GasPriceList.BaseCost)
            {
                return(StateTransitionResult.Fail((Gas)0, StateTransitionErrorKind.InsufficientGas));
            }

            var gasMeter = new GasMeter(message.GasLimit);

            gasMeter.Spend((Gas)GasPriceList.BaseCost);

            if (message.Method.Name == null)
            {
                return(StateTransitionResult.Fail(gasMeter.GasConsumed, StateTransitionErrorKind.NoMethodName));
            }

            StateSnapshot stateSnapshot = this.TakeSnapshot();

            IContractState state = this.CreateIntermediateState();

            string type = state.GetContractType(message.To);

            ISmartContractState smartContractState = this.CreateSmartContractState(gasMeter, message.To, message, state);

            VmExecutionResult result = this.Vm.ExecuteMethod(smartContractState, message.Method, contractCode, type);

            this.GasRemaining -= gasMeter.GasConsumed;

            bool revert = result.ExecutionException != null;

            if (revert)
            {
                this.Rollback(stateSnapshot);

                return(StateTransitionResult.Fail(
                           gasMeter.GasConsumed,
                           result.ExecutionException));
            }

            state.Commit();
            this.GasRemaining -= gasMeter.GasConsumed;

            return(StateTransitionResult.Ok(
                       gasMeter.GasConsumed,
                       message.To,
                       result.Result
                       ));
        }
Esempio n. 14
0
        private StateTransitionResult ApplyCreate(object[] parameters, byte[] code, BaseMessage message, string type = null)
        {
            if (this.GasRemaining < message.GasLimit || this.GasRemaining < GasPriceList.BaseCost)
            {
                return(StateTransitionResult.Fail((Gas)0, StateTransitionErrorKind.InsufficientGas));
            }

            StateSnapshot stateSnapshot = this.TakeSnapshot();

            var gasMeter = new GasMeter(message.GasLimit);

            gasMeter.Spend((Gas)GasPriceList.BaseCost);

            uint160 address = this.GetNewAddress();

            // Begin tracking the new intermediate state. We need to keep this reference around
            // for the scope of the transaction, so we can commit it later.
            IContractState state = this.CreateIntermediateState();

            state.CreateAccount(address);

            ISmartContractState smartContractState = this.CreateSmartContractState(gasMeter, address, message, state);

            VmExecutionResult result = this.Vm.Create(state, smartContractState, code, parameters, type);

            this.GasRemaining -= gasMeter.GasConsumed;

            bool revert = result.ExecutionException != null;

            if (revert)
            {
                this.Rollback(stateSnapshot);

                return(StateTransitionResult.Fail(
                           gasMeter.GasConsumed,
                           result.ExecutionException));
            }

            state.Commit();

            return(StateTransitionResult.Ok(
                       gasMeter.GasConsumed,
                       address,
                       result.Result
                       ));
        }
        /// <summary>
        /// Applies an externally generated contract method call message to the current state.
        /// </summary>
        public StateTransitionResult Apply(IState state, ExternalCallMessage message)
        {
            byte[] contractCode = state.ContractState.GetCode(message.To);

            if (contractCode == null || contractCode.Length == 0)
            {
                return(StateTransitionResult.Fail((Gas)0, StateTransitionErrorKind.NoCode));
            }

            // For external calls we need to increment the balance state to take into
            // account any funds sent as part of the original contract invocation transaction.
            state.AddInitialTransfer(new TransferInfo {
                Value = message.Amount, To = message.To, From = message.From
            });

            return(this.ApplyCall(state, message, contractCode));
        }
        ///<inheritdoc />
        public ITransferResult Call(
            ISmartContractState smartContractState,
            Address addressTo,
            ulong amountToTransfer,
            string methodName,
            object[] parameters,
            ulong gasLimit = 0)
        {
            Gas gasRemaining = smartContractState.GasMeter.GasAvailable;

            // For a method call, send all the gas unless an amount was selected.Should only call trusted methods so re - entrance is less problematic.
            ulong gasBudget = (gasLimit != 0) ? gasLimit : gasRemaining;

            if (gasRemaining < gasBudget || gasRemaining < GasPriceList.BaseCost)
            {
                return(TransferResult.Failed());
            }

            var message = new InternalCallMessage(
                addressTo.ToUint160(this.network),
                smartContractState.Message.ContractAddress.ToUint160(this.network),
                amountToTransfer,
                (Gas)gasBudget,
                new MethodCall(methodName, parameters)
                );

            // Create a snapshot of the current state
            IState newState = this.state.Snapshot();

            // Apply the message to the snapshot
            StateTransitionResult result = this.stateProcessor.Apply(newState, message);

            // Transition the current state to the new state
            if (result.IsSuccess)
            {
                this.state.TransitionTo(newState);
            }

            smartContractState.GasMeter.Spend(result.GasConsumed);

            return(result.IsSuccess
                ? TransferResult.Transferred(result.Success.ExecutionResult)
                : TransferResult.Failed());
        }
Esempio n. 17
0
        /// <summary>
        /// Applies an externally generated contract method call message to the current state.
        /// </summary>
        public StateTransitionResult Apply(IState state, ExternalCallMessage message)
        {
            var gasMeter = new GasMeter(message.GasLimit);

            gasMeter.Spend((Gas)GasPriceList.BaseCost);

            byte[] contractCode = state.ContractState.GetCode(message.To);

            if (contractCode == null || contractCode.Length == 0)
            {
                return(StateTransitionResult.Fail(gasMeter.GasConsumed, StateTransitionErrorKind.NoCode));
            }

            // For external calls we need to increment the balance state to take into
            // account any funds sent as part of the original contract invocation transaction.
            state.AddInitialTransfer(new TransferInfo(message.From, message.To, message.Amount));

            return(this.ApplyCall(state, message, contractCode, gasMeter));
        }
Esempio n. 18
0
        ///<inheritdoc />
        public ICreateResult Create <T>(ISmartContractState smartContractState,
                                        ulong amountToTransfer,
                                        object[] parameters,
                                        ulong gasLimit = 0)
        {
            Gas gasRemaining = smartContractState.GasMeter.GasAvailable;

            // For a method call, send all the gas unless an amount was selected.Should only call trusted methods so re - entrance is less problematic.
            ulong gasBudget = (gasLimit != 0) ? gasLimit : gasRemaining;

            Debug.WriteLine("Gas budget:" + gasBudget);

            if (gasRemaining < gasBudget || gasRemaining < GasPriceList.CreateCost)
            {
                return(CreateResult.Failed());
            }

            var message = new InternalCreateMessage(
                smartContractState.Message.ContractAddress.ToUint160(),
                amountToTransfer,
                (Gas)gasBudget,
                parameters,
                typeof(T).Name
                );

            // Create a snapshot of the current state
            IState newState = this.state.Snapshot();

            // Apply the message to the snapshot
            StateTransitionResult result = this.stateProcessor.Apply(newState, message);

            // Transition the current state to the new state
            if (result.IsSuccess)
            {
                this.state.TransitionTo(newState);
            }

            smartContractState.GasMeter.Spend(result.GasConsumed);

            return(result.IsSuccess
                ? CreateResult.Succeeded(result.Success.ContractAddress.ToAddress())
                : CreateResult.Failed());
        }
Esempio n. 19
0
        /// <summary>
        /// Applies an internally generated contract funds transfer message to the current state.
        /// </summary>
        public StateTransitionResult Apply(IState state, ContractTransferMessage message)
        {
            bool enoughBalance = this.EnsureContractHasEnoughBalance(state, message.From, message.Amount);

            if (!enoughBalance)
            {
                return(StateTransitionResult.Fail((Gas)0, StateTransitionErrorKind.InsufficientBalance));
            }

            // If it's not a contract, create a regular P2PKH tx
            // If it is a contract, do a regular contract call
            byte[] contractCode = state.ContractState.GetCode(message.To);

            if (contractCode == null || contractCode.Length == 0)
            {
                // No contract at this address, create a regular P2PKH xfer
                state.AddInternalTransfer(new TransferInfo
                {
                    From  = message.From,
                    To    = message.To,
                    Value = message.Amount
                });

                return(StateTransitionResult.Ok((Gas)0, message.To));
            }

            StateTransitionResult result = this.ApplyCall(state, message, contractCode);

            // For successful internal contract-contract transfers we need to add the transfer to the internal transfer list.
            if (result.IsSuccess)
            {
                state.AddInternalTransfer(new TransferInfo
                {
                    From  = message.From,
                    To    = message.To,
                    Value = message.Amount
                });
            }

            return(result);
        }
Esempio n. 20
0
        ///<inheritdoc />
        public ITransferResult Transfer(ISmartContractState smartContractState, Address addressTo, ulong amountToTransfer)
        {
            Gas gasRemaining = smartContractState.GasMeter.GasAvailable;

            if (gasRemaining < GasPriceList.TransferCost)
            {
                return(TransferResult.Failed());
            }

            ulong gasBudget = (gasRemaining < DefaultGasLimit)
                ? gasRemaining     // have enough for at least a transfer but not for the DefaultGasLimit
                : DefaultGasLimit; // have enough for anything

            var message = new ContractTransferMessage(
                addressTo.ToUint160(),
                smartContractState.Message.ContractAddress.ToUint160(),
                amountToTransfer,
                (Gas)gasBudget
                );

            // Create a snapshot of the current state
            IState newState = this.state.Snapshot();

            // Apply the message to the snapshot
            StateTransitionResult result = this.stateProcessor.Apply(newState, message);

            // Transition the current state to the new state
            if (result.IsSuccess)
            {
                this.state.TransitionTo(newState);
            }

            smartContractState.GasMeter.Spend(result.GasConsumed);

            return(result.IsSuccess
                ? TransferResult.Empty()
                : TransferResult.Failed());
        }
        ///<inheritdoc />
        public ITransferResult Transfer(ISmartContractState smartContractState, Address addressTo, ulong amountToTransfer)
        {
            this.logger.LogTrace("({0}:{1},{2}:{3})", nameof(addressTo), addressTo, nameof(amountToTransfer), amountToTransfer);

            ulong gasBudget = DefaultGasLimit; // for Transfer always send limited gas to prevent re-entrance.

            Gas gasRemaining = smartContractState.GasMeter.GasAvailable;

            if (gasRemaining < gasBudget || gasRemaining < GasPriceList.BaseCost)
            {
                return(TransferResult.Failed());
            }

            var message = new ContractTransferMessage(
                addressTo.ToUint160(this.network),
                smartContractState.Message.ContractAddress.ToUint160(this.network),
                amountToTransfer,
                (Gas)gasBudget
                );

            // Create a snapshot of the current state
            IState newState = this.state.Snapshot();

            // Apply the message to the snapshot
            StateTransitionResult result = this.stateProcessor.Apply(newState, message);

            // Transition the current state to the new state
            if (result.IsSuccess)
            {
                this.state.TransitionTo(newState);
            }

            smartContractState.GasMeter.Spend(result.GasConsumed);

            return(result.IsSuccess
                ? TransferResult.Empty()
                : TransferResult.Failed());
        }