Пример #1
0
        ///<inheritdoc />
        public ITransferResult TransferFunds(ISmartContractState smartContractState, Address addressTo, ulong amountToTransfer, TransferFundsToContract contractDetails)
        {
            this.logger.LogTrace("({0}:{1},{2}:{3})", nameof(addressTo), addressTo, nameof(amountToTransfer), amountToTransfer);

            // TODO: Spend BaseFee here

            var balance = smartContractState.GetBalance();

            if (balance < amountToTransfer)
            {
                this.logger.LogTrace("(-)[INSUFFICIENT_BALANCE]:{0}={1}", nameof(balance), balance);
                throw new InsufficientBalanceException();
            }

            // Discern whether this is a contract or an ordinary address.
            byte[] contractCode = this.contractStateRepository.GetCode(addressTo.ToUint160(this.network));
            if (contractCode == null || contractCode.Length == 0)
            {
                this.internalTransferList.Add(new TransferInfo
                {
                    From  = smartContractState.Message.ContractAddress.ToUint160(this.network),
                    To    = addressTo.ToUint160(this.network),
                    Value = amountToTransfer
                });

                this.logger.LogTrace("(-)[TRANSFER_TO_SENDER]:Transfer {0} from {1} to {2}.", smartContractState.Message.ContractAddress, addressTo, amountToTransfer);
                return(TransferResult.Empty());
            }

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

            return(ExecuteTransferFundsToContract(contractCode, smartContractState, addressTo, amountToTransfer, contractDetails));
        }
Пример #2
0
        ///<inheritdoc />
        public ITransferResult Transfer(ISmartContractState smartContractState, Address addressTo, ulong amountToTransfer)
        {
            this.logger.LogTrace("({0}:{1},{2}:{3})", nameof(addressTo), addressTo, nameof(amountToTransfer), amountToTransfer);

            // TODO: Spend BaseFee here

            EnsureContractHasEnoughBalance(smartContractState, amountToTransfer);

            // Discern whether this is a contract or an ordinary address.
            byte[] contractCode = this.contractStateRepository.GetCode(addressTo.ToUint160(this.network));
            if (contractCode == null || contractCode.Length == 0)
            {
                this.internalTransferList.Add(new TransferInfo
                {
                    From  = smartContractState.Message.ContractAddress.ToUint160(this.network),
                    To    = addressTo.ToUint160(this.network),
                    Value = amountToTransfer
                });

                this.logger.LogTrace("(-)[TRANSFER_TO_SENDER]:Transfer {0} from {1} to {2}.", smartContractState.Message.ContractAddress, addressTo, amountToTransfer);
                return(TransferResult.Empty());
            }

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

            // Calling a receive handler:
            string methodName = MethodCall.ExternalReceiveHandlerName;

            object[] parameters = new object[] { };
            ulong    gasBudget  = DefaultGasLimit; // for Transfer always send limited gas to prevent re-entrance.

            return(ExecuteTransferFundsToContract(contractCode, smartContractState, addressTo, amountToTransfer, methodName, parameters, gasBudget));
        }
        ///<inheritdoc />
        public ITransferResult Call(
            ISmartContractState smartContractState,
            Address addressTo,
            ulong amountToTransfer,
            string methodName,
            object[] parameters,
            ulong gasLimit = 0)
        {
            // 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 : smartContractState.GasMeter.GasAvailable;

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

            var result = this.state.Apply(message);

            return(result.IsSuccess
                ? TransferResult.Transferred(result.Success.ExecutionResult)
                : TransferResult.Failed());
        }
Пример #4
0
        /// <summary>
        /// Constructs a result when a transfer from a contract succeeded.
        /// </summary>
        /// <param name="returnValue">The object that was returned from the executor.</param>
        public static TransferResult Transferred(object returnValue)
        {
            var result = new TransferResult
            {
                ReturnValue = returnValue
            };

            return(result);
        }
Пример #5
0
        /// <summary>
        /// Constructs a result when a transfer from a contract failed.
        /// </summary>
        /// <param name="thrownException">The exception that was thrown by the executor.</param>
        public static TransferResult Failed(Exception thrownException)
        {
            var result = new TransferResult
            {
                ThrownException = thrownException
            };

            return(result);
        }
Пример #6
0
        /// <summary>
        /// If the address to where the funds will be tranferred to is a contract, instantiate and execute it.
        /// </summary>
        private ITransferResult ExecuteTransferFundsToContract(byte[] contractCode, ISmartContractState smartContractState, Address addressTo, ulong amountToTransfer, TransferFundsToContract contractDetails)
        {
            this.logger.LogTrace("({0}:{1},{2}:{3})", nameof(addressTo), addressTo, nameof(amountToTransfer), amountToTransfer);

            ulong gasBudget = contractDetails.GasBudget == 0 ? DefaultGasLimit : contractDetails.GasBudget;

            // Ensure we have enough gas left to be able to fund the new GasMeter.
            if (smartContractState.GasMeter.GasAvailable < gasBudget)
            {
                throw new InsufficientGasException();
            }

            var nestedGasMeter = new GasMeter((Gas)gasBudget);

            IContractStateRepository track = this.contractStateRepository.StartTracking();

            var callData = new CallData(smartContractState.GasMeter.GasLimit, addressTo.ToUint160(this.network), contractDetails.ContractMethodName, contractDetails.MethodParameters);

            var context = new TransactionContext(
                this.transactionContext.TransactionHash,
                this.transactionContext.BlockHeight,
                this.transactionContext.Coinbase,
                smartContractState.Message.ContractAddress.ToUint160(this.network),
                amountToTransfer,
                this.transactionContext.GetNonceAndIncrement());

            VmExecutionResult result = this.vm.ExecuteMethod(nestedGasMeter,
                                                             track,
                                                             callData,
                                                             context);

            // Update parent gas meter.
            smartContractState.GasMeter.Spend(nestedGasMeter.GasConsumed);

            var revert = result.ExecutionException != null;

            if (revert)
            {
                track.Rollback();
                return(TransferResult.Failed(result.ExecutionException));
            }

            track.Commit();

            this.internalTransferList.Add(new TransferInfo
            {
                From  = smartContractState.Message.ContractAddress.ToUint160(this.network),
                To    = addressTo.ToUint160(this.network),
                Value = amountToTransfer
            });

            this.logger.LogTrace("(-)");

            return(TransferResult.Transferred(result.Result));
        }
        ///<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());
        }
        /// <summary>
        /// If the address to where the funds will be tranferred to is a contract, instantiate and execute it.
        /// </summary>
        private ITransferResult ExecuteTransferFundsToContract(byte[] contractCode, ISmartContractState smartContractState, Address addressTo, ulong amountToTransfer, TransferFundsToContract contractDetails)
        {
            this.logger.LogTrace("({0}:{1},{2}:{3})", nameof(addressTo), addressTo, nameof(amountToTransfer), amountToTransfer);

            IContractStateRepository track = this.contractStateRepository.StartTracking();
            IPersistenceStrategy     persistenceStrategy = new MeteredPersistenceStrategy(track, smartContractState.GasMeter, this.keyEncodingStrategy);
            IPersistentState         newPersistentState  = new PersistentState(persistenceStrategy, addressTo.ToUint160(this.network), this.network);

            var newMessage = new Message(addressTo, smartContractState.Message.ContractAddress, amountToTransfer, (Gas)(smartContractState.Message.GasLimit - smartContractState.GasMeter.GasConsumed));

            ISmartContractExecutionContext newContext = new SmartContractExecutionContext(smartContractState.Block, newMessage, addressTo.ToUint160(this.network), 0, contractDetails.MethodParameters);

            ISmartContractVirtualMachine vm = new ReflectionVirtualMachine(new InternalTransactionExecutorFactory(this.keyEncodingStrategy, this.loggerFactory, this.network), this.loggerFactory);

            var result = vm.ExecuteMethod(
                contractCode,
                contractDetails.ContractMethodName,
                newContext,
                smartContractState.GasMeter,
                newPersistentState,
                track);

            var revert = result.ExecutionException != null;

            if (revert)
            {
                track.Rollback();
                return(TransferResult.Failed(result.ExecutionException));
            }

            track.Commit();

            this.internalTransferList.Add(new TransferInfo
            {
                From  = smartContractState.Message.ContractAddress.ToUint160(this.network),
                To    = addressTo.ToUint160(this.network),
                Value = amountToTransfer
            });

            this.logger.LogTrace("(-)");

            return(TransferResult.Transferred(result.Result));
        }
        ///<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.

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

            var result = this.state.Apply(message);

            return(result.IsSuccess
                ? TransferResult.Empty()
                : TransferResult.Failed());
        }
        /// <summary>
        /// If the address to where the funds will be tranferred to is a contract, instantiate and execute it.
        /// </summary>
        private ITransferResult ExecuteTransferFundsToContract(byte[] contractCode, ISmartContractState smartContractState, Address addressTo, ulong amountToTransfer, TransferFundsToContract contractDetails)
        {
            this.logger.LogTrace("({0}:{1},{2}:{3})", nameof(addressTo), addressTo, nameof(amountToTransfer), amountToTransfer);

            IContractStateRepository track = this.contractStateRepository.StartTracking();

            var callData = new CallData(smartContractState.GasMeter.GasLimit, addressTo.ToUint160(this.network), contractDetails.ContractMethodName, contractDetails.MethodParameters);

            var context = new TransactionContext(
                this.transactionContext.TransactionHash,
                this.transactionContext.BlockHeight,
                this.transactionContext.Coinbase,
                smartContractState.Message.ContractAddress.ToUint160(this.network),
                amountToTransfer);

            var result = this.vm.ExecuteMethod(smartContractState.GasMeter,
                                               track,
                                               callData,
                                               context);

            var revert = result.ExecutionException != null;

            if (revert)
            {
                track.Rollback();
                return(TransferResult.Failed(result.ExecutionException));
            }

            track.Commit();

            this.internalTransferList.Add(new TransferInfo
            {
                From  = smartContractState.Message.ContractAddress.ToUint160(this.network),
                To    = addressTo.ToUint160(this.network),
                Value = amountToTransfer
            });

            this.logger.LogTrace("(-)");

            return(TransferResult.Transferred(result.Result));
        }
        ///<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());
        }
Пример #12
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());
        }
Пример #13
0
        ///<inheritdoc />
        public ITransferResult Call(
            ISmartContractState smartContractState,
            Address addressTo,
            ulong amountToTransfer,
            string methodName,
            object[] parameters,
            ulong gasLimit = 0)
        {
            // TODO: Spend BaseFee here

            EnsureContractHasEnoughBalance(smartContractState, amountToTransfer);

            byte[] contractCode = this.contractStateRepository.GetCode(addressTo.ToUint160(this.network));
            if (contractCode == null || contractCode.Length == 0)
            {
                return(TransferResult.Empty());
            }

            // Here, we know contract has code, so we execute it
            // 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 : smartContractState.GasMeter.GasAvailable;

            return(ExecuteTransferFundsToContract(contractCode, smartContractState, addressTo, amountToTransfer, methodName, parameters, gasBudget));
        }