/// <summary> /// Applies an externally generated contract creation message to the current state. /// </summary> public StateTransitionResult Apply(IState state, ExternalCreateMessage message) { var gasMeter = new GasMeter(message.GasLimit); gasMeter.Spend((Gas)GasPriceList.CreateCost); // We need to generate an address here so that we can set the initial balance. uint160 address = state.GenerateAddress(this.AddressGenerator); // For external creates 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, address, message.Amount)); return(this.ApplyCreate(state, message.Parameters, message.Code, message, address, gasMeter)); }
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 )); }
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) { 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)); }
/// <summary> /// Sets up a new <see cref="ISmartContractState"/> based on the current state. /// </summary> public ISmartContractState CreateSmartContractState(IState state, GasMeter gasMeter, uint160 address, BaseMessage message, IStateRepository repository) { return(this.smartContractStateFactory.Create(state, gasMeter, address, message, repository)); }
private ISmartContractExecutionResult CreateContextAndExecute(uint160 contractAddress, byte[] contractCode, string methodName, ISmartContractTransactionContext transactionContext, SmartContractCarrier carrier) { this.logger.LogTrace("()"); var block = new Block(transactionContext.BlockHeight, transactionContext.CoinbaseAddress.ToAddress(this.network)); var executionContext = new SmartContractExecutionContext ( block, new Message( contractAddress.ToAddress(this.network), carrier.Sender.ToAddress(this.network), carrier.Value, carrier.CallData.GasLimit ), contractAddress, carrier.CallData.GasPrice, carrier.MethodParameters ); LogExecutionContext(this.logger, block, executionContext.Message, contractAddress, carrier); var gasMeter = new GasMeter(carrier.CallData.GasLimit); IPersistenceStrategy persistenceStrategy = new MeteredPersistenceStrategy(this.stateSnapshot, gasMeter, this.keyEncodingStrategy); var persistentState = new PersistentState(persistenceStrategy, contractAddress, this.network); gasMeter.Spend((Gas)GasPriceList.BaseCost); var result = this.vm.ExecuteMethod( contractCode, methodName, executionContext, gasMeter, persistentState, this.stateSnapshot); var revert = result.ExecutionException != null; this.logger.LogTrace("(-)"); var internalTransaction = this.transferProcessor.Process( carrier, this.stateSnapshot, transactionContext, result.InternalTransfers, revert); (var fee, var refundTxOuts) = this.refundProcessor.Process( carrier, transactionContext.MempoolFee, result.GasConsumed, result.ExecutionException); var executionResult = new SmartContractExecutionResult { Exception = result.ExecutionException, GasConsumed = result.GasConsumed, Return = result.Result, InternalTransaction = internalTransaction, Fee = fee, Refunds = refundTxOuts }; if (revert) { this.stateSnapshot.Rollback(); } else { this.stateSnapshot.Commit(); } return(executionResult); }
public ISmartContractExecutionResult Execute(ISmartContractTransactionContext transactionContext) { this.logger.LogTrace("()"); // Deserialization can't fail because this has already been through SmartContractFormatRule. Result <ContractTxData> callDataDeserializationResult = this.serializer.Deserialize(transactionContext.ScriptPubKey.ToBytes()); ContractTxData callData = callDataDeserializationResult.Value; var gasMeter = new GasMeter(callData.GasLimit); gasMeter.Spend((Gas)GasPriceList.BaseCost); var context = new TransactionContext( transactionContext.TransactionHash, transactionContext.BlockHeight, transactionContext.CoinbaseAddress, transactionContext.Sender, transactionContext.TxOutValue); var creation = IsCreateContract(callData); VmExecutionResult result = creation ? this.vm.Create(gasMeter, this.stateSnapshot, callData, context) : this.vm.ExecuteMethod(gasMeter, this.stateSnapshot, callData, context); var revert = result.ExecutionException != null; Transaction internalTransaction = this.transferProcessor.Process( this.stateSnapshot, creation ? result.NewContractAddress : callData.ContractAddress, transactionContext, result.InternalTransfers, revert); (Money fee, List <TxOut> refundTxOuts) = this.refundProcessor.Process( callData, transactionContext.MempoolFee, transactionContext.Sender, result.GasConsumed, result.ExecutionException); var executionResult = new SmartContractExecutionResult { NewContractAddress = !revert && creation ? result.NewContractAddress : null, Exception = result.ExecutionException, GasConsumed = result.GasConsumed, Return = result.Result, InternalTransaction = internalTransaction, Fee = fee, Refunds = refundTxOuts, Logs = result.RawLogs.ToLogs(this.contractPrimitiveSerializer) }; if (revert) { this.logger.LogTrace("(-)[CONTRACT_EXECUTION_FAILED]"); this.stateSnapshot.Rollback(); } else { this.logger.LogTrace("(-)[CONTRACT_EXECUTION_SUCCEEDED]"); this.stateSnapshot.Commit(); } return(executionResult); }
public ISmartContractExecutionResult Execute(ISmartContractTransactionContext transactionContext) { this.logger.LogTrace("()"); var carrier = SmartContractCarrier.Deserialize(transactionContext); // Create a new address for the contract. uint160 newContractAddress = carrier.GetNewContractAddress(); // Create an account for the contract in the state repository. this.stateSnapshot.CreateAccount(newContractAddress); // Decompile the contract execution code and validate it. SmartContractDecompilation decompilation = SmartContractDecompiler.GetModuleDefinition(carrier.CallData.ContractExecutionCode); SmartContractValidationResult validation = this.validator.Validate(decompilation); // If validation failed, refund the sender any remaining gas. if (!validation.IsValid) { this.logger.LogTrace("(-)[CONTRACT_VALIDATION_FAILED]"); return(SmartContractExecutionResult.ValidationFailed(carrier, validation)); } var block = new Block(transactionContext.BlockHeight, transactionContext.CoinbaseAddress.ToAddress(this.network)); var executionContext = new SmartContractExecutionContext ( block, new Message( newContractAddress.ToAddress(this.network), carrier.Sender.ToAddress(this.network), carrier.Value, carrier.CallData.GasLimit ), newContractAddress, carrier.CallData.GasPrice, carrier.MethodParameters ); LogExecutionContext(this.logger, block, executionContext.Message, newContractAddress, carrier); var gasMeter = new GasMeter(carrier.CallData.GasLimit); IPersistenceStrategy persistenceStrategy = new MeteredPersistenceStrategy(this.stateSnapshot, gasMeter, new BasicKeyEncodingStrategy()); var persistentState = new PersistentState(persistenceStrategy, newContractAddress, this.network); gasMeter.Spend((Gas)GasPriceList.BaseCost); var result = this.vm.Create(carrier.CallData.ContractExecutionCode, executionContext, gasMeter, persistentState, this.stateSnapshot); var revert = result.ExecutionException != null; var internalTransaction = this.transferProcessor.Process( carrier, this.stateSnapshot, transactionContext, result.InternalTransfers, revert); (var fee, var refundTxOuts) = this.refundProcessor.Process( carrier, transactionContext.MempoolFee, result.GasConsumed, result.ExecutionException); var executionResult = new SmartContractExecutionResult { NewContractAddress = revert ? null : newContractAddress, Exception = result.ExecutionException, GasConsumed = result.GasConsumed, Return = result.Result, InternalTransaction = internalTransaction, Fee = fee, Refunds = refundTxOuts }; if (revert) { this.logger.LogTrace("(-)[CONTRACT_EXECUTION_FAILED]"); this.stateSnapshot.Rollback(); } else { this.logger.LogTrace("(-):{0}={1}", nameof(newContractAddress), newContractAddress); this.stateSnapshot.SetCode(newContractAddress, carrier.CallData.ContractExecutionCode); this.stateSnapshot.Commit(); } return(executionResult); }
///<inheritdoc /> public ICreateResult Create <T>(ISmartContractState smartContractState, ulong amountToTransfer, object[] parameters, ulong gasLimit = 0) { // TODO: Expend any neccessary costs. ulong gasBudget = (gasLimit != 0) ? gasLimit : DefaultGasLimit; // 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); // Check balance. EnsureContractHasEnoughBalance(smartContractState, amountToTransfer); // Build objects for VM byte[] contractCode = this.contractStateRepository.GetCode(smartContractState.Message.ContractAddress.ToUint160(this.network)); // TODO: Fix this when calling from constructor. var context = new TransactionContext( this.transactionContext.TransactionHash, this.transactionContext.BlockHeight, this.transactionContext.Coinbase, smartContractState.Message.ContractAddress.ToUint160(this.network), amountToTransfer, this.transactionContext.GetNonceAndIncrement()); IContractState track = this.contractStateRepository.StartTracking(); var createData = new CreateData(nestedGasMeter.GasLimit, contractCode, parameters); // Do create in vm VmExecutionResult result = this.vm.Create(nestedGasMeter, track, createData, context, typeof(T).Name); // Update parent gas meter. smartContractState.GasMeter.Spend(nestedGasMeter.GasConsumed); var revert = result.ExecutionException != null; if (revert) { this.logger.LogTrace("(-)[CONTRACT_EXECUTION_FAILED]"); track.Rollback(); return(CreateResult.Failed()); } this.logger.LogTrace("(-)[CONTRACT_EXECUTION_SUCCEEDED]"); track.Commit(); this.internalTransferList.Add(new TransferInfo { From = smartContractState.Message.ContractAddress.ToUint160(this.network), To = result.NewContractAddress, Value = amountToTransfer }); this.contractLogHolder.AddRawLogs(result.RawLogs); return(CreateResult.Succeeded(result.NewContractAddress.ToAddress(this.network))); }
/// <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, string methodName, object[] parameters, ulong gasBudget) { this.logger.LogTrace("({0}:{1},{2}:{3})", nameof(addressTo), addressTo, nameof(amountToTransfer), amountToTransfer); // 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); IContractState track = this.contractStateRepository.StartTracking(); var callData = new CallData((Gas)gasBudget, addressTo.ToUint160(this.network), methodName, parameters); var context = new TransactionContext( this.transactionContext.TransactionHash, this.transactionContext.BlockHeight, this.transactionContext.Coinbase, smartContractState.Message.ContractAddress.ToUint160(this.network), amountToTransfer, this.transactionContext.Nonce); 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.contractLogHolder.AddRawLogs(result.RawLogs); this.logger.LogTrace("(-)"); return(TransferResult.Transferred(result.Result)); }
public ISmartContractExecutionResult Execute(ISmartContractTransactionContext transactionContext) { this.logger.LogTrace("()"); var callDataDeserializationResult = this.serializer.Deserialize(transactionContext.ScriptPubKey.ToBytes()); // TODO Handle deserialization failure var callData = callDataDeserializationResult.Value; var gasMeter = new GasMeter(callData.GasLimit); var context = new TransactionContext( transactionContext.TransactionHash, transactionContext.BlockHeight, transactionContext.CoinbaseAddress, transactionContext.Sender, transactionContext.TxOutValue); var creation = IsCreateContract(callData); var result = creation ? this.vm.Create(gasMeter, this.stateSnapshot, callData, context) : this.vm.ExecuteMethod(gasMeter, this.stateSnapshot, callData, context); var revert = result.ExecutionException != null; var internalTransaction = this.transferProcessor.Process( this.stateSnapshot, creation ? result.NewContractAddress : callData.ContractAddress, transactionContext, result.InternalTransfers, revert); (var fee, var refundTxOuts) = this.refundProcessor.Process( callData, transactionContext.MempoolFee, transactionContext.Sender, result.GasConsumed, result.ExecutionException); var executionResult = new SmartContractExecutionResult { NewContractAddress = !revert && creation ? result.NewContractAddress : null, Exception = result.ExecutionException, GasConsumed = result.GasConsumed, Return = result.Result, InternalTransaction = internalTransaction, Fee = fee, Refunds = refundTxOuts }; if (revert) { this.logger.LogTrace("(-)[CONTRACT_EXECUTION_FAILED]"); this.stateSnapshot.Rollback(); } else { this.logger.LogTrace("(-)[CONTRACT_EXECUTION_SUCCEEDED]"); this.stateSnapshot.Commit(); } return(executionResult); }