/// <summary>
        /// Applies an internally generated contract funds transfer message to the current state.
        /// </summary>
        public StateTransitionResult Apply(IState state, ContractTransferMessage message)
        {
            bool enoughBalance = 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 = ApplyCall(state, message, contractCode, gasMeter);

            return(result);
        }
Esempio n. 2
0
        ///<inheritdoc />
        public ITransferResult Transfer(ISmartContractState smartContractState, Address addressTo, ulong amountToTransfer)
        {
            Gas gasRemaining = this.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);
            }

            this.gasMeter.Spend(result.GasConsumed);

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