/// <summary> /// Applies an internally generated contract method call message to the current state. /// </summary> public StateTransitionResult Apply(IState state, InternalCallMessage message) { bool enoughBalance = 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 = ApplyCall(state, message, contractCode, gasMeter); return(result); }
///<inheritdoc /> public ITransferResult Call( ISmartContractState smartContractState, Address addressTo, ulong amountToTransfer, string methodName, object[] parameters, ulong gasLimit = 0) { Gas gasRemaining = this.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(), smartContractState.Message.ContractAddress.ToUint160(), 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); } this.gasMeter.Spend(result.GasConsumed); return(result.IsSuccess ? TransferResult.Transferred(result.Success.ExecutionResult) : TransferResult.Failed()); }