/// <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((RuntimeObserver.Gas) 0, StateTransitionErrorKind.InsufficientBalance)); // Trivial - just return and let the MethodCall gas account for it. } var gasMeter = new GasMeter(message.GasLimit); gasMeter.Spend((RuntimeObserver.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); }
private StateTransitionResult ApplyCreate(IState state, object[] parameters, byte[] code, BaseMessage message, uint160 address, RuntimeObserver.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, gasMeter, 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 )); }
/// <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((RuntimeObserver.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((RuntimeObserver.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((RuntimeObserver.Gas)GasPriceList.BaseCost); StateTransitionResult result = this.ApplyCall(state, message, contractCode, gasMeter); return(result); }
/// <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((RuntimeObserver.Gas) 0, StateTransitionErrorKind.InsufficientBalance)); // Trivial - just return and let the MethodCall gas account for it. } var gasMeter = new GasMeter(message.GasLimit); gasMeter.Spend((RuntimeObserver.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); }
private StateTransitionResult ApplyCall(IState state, CallMessage message, byte[] contractCode, RuntimeObserver.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, gasMeter, 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 )); }
private StateTransitionResult ApplyCall(IState state, CallMessage message, byte[] contractCode, ExecutionContext executionContext) { // 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(executionContext.GasMeter.GasConsumed, StateTransitionErrorKind.NoMethodName)); } string type = state.ContractState.GetContractType(message.To); ISmartContractState smartContractState = state.CreateSmartContractState(state, executionContext.GasMeter, message.To, message, state.ContractState); VmExecutionResult result = this.Vm.ExecuteMethod(smartContractState, executionContext, message.Method, contractCode, type); bool revert = !result.IsSuccess; if (revert) { return(StateTransitionResult.Fail( executionContext.GasMeter.GasConsumed, result.Error)); } return(StateTransitionResult.Ok( executionContext.GasMeter.GasConsumed, message.To, result.Success.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()); }
/// <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(ApplyCall(state, message, contractCode, gasMeter)); }
///<inheritdoc /> public ICreateResult Create <T>(ISmartContractState smartContractState, ulong amountToTransfer, 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; 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); } this.gasMeter.Spend(result.GasConsumed); return(result.IsSuccess ? CreateResult.Succeeded(result.Success.ContractAddress.ToAddress()) : CreateResult.Failed()); }
///<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()); }