private StateTransitionResult ApplyCreate(IState state, object[] parameters, byte[] code, BaseMessage message, uint160 address, string type = null) { var gasMeter = new GasMeter(message.GasLimit); gasMeter.Spend((Gas)GasPriceList.BaseCost); state.ContractState.CreateAccount(address); ISmartContractState smartContractState = state.CreateSmartContractState(state, gasMeter, address, message, state.ContractState); VmExecutionResult result = this.Vm.Create(state.ContractState, smartContractState, 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 )); }
private StateTransitionResult ApplyCall(IState state, CallMessage message, byte[] contractCode) { var gasMeter = new GasMeter(message.GasLimit); gasMeter.Spend((Gas)GasPriceList.BaseCost); // 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, 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 )); }
///<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()); IContractStateRepository 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(); // TODO: Add internaltransfer update here 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, 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)); }
private static VmExecutionResult GetInvocationVmErrorResult(IContractInvocationResult invocationResult) { if (invocationResult.InvocationErrorType == ContractInvocationErrorType.OutOfGas) { return(VmExecutionResult.Fail(VmExecutionErrorKind.OutOfGas, invocationResult.ErrorMessage)); } if (invocationResult.InvocationErrorType == ContractInvocationErrorType.OverMemoryLimit) { return(VmExecutionResult.Fail(VmExecutionErrorKind.OutOfResources, invocationResult.ErrorMessage)); } return(VmExecutionResult.Fail(VmExecutionErrorKind.InvocationFailed, invocationResult.ErrorMessage)); }
/// <summary> /// Invokes a method on an existing smart contract /// </summary> public VmExecutionResult ExecuteMethod(ISmartContractState contractState, MethodCall methodCall, byte[] contractCode, string typeName) { this.logger.LogTrace("(){0}:{1}", nameof(methodCall.Name), methodCall.Name); ContractByteCode code; // Code we're loading from database - can assume it's valid. using (IContractModuleDefinition moduleDefinition = this.moduleDefinitionReader.Read(contractCode).Value) { var observer = new Observer(contractState.GasMeter, MemoryUnitLimit); var rewriter = new ObserverRewriter(observer); moduleDefinition.Rewrite(rewriter); code = moduleDefinition.ToByteCode(); } Result <IContract> contractLoadResult = this.Load( code, typeName, contractState.Message.ContractAddress.ToUint160(this.network), contractState); if (!contractLoadResult.IsSuccess) { LogErrorMessage(contractLoadResult.Error); this.logger.LogTrace("(-)[LOAD_CONTRACT_FAILED]"); return(VmExecutionResult.Fail(VmExecutionErrorKind.LoadFailed, contractLoadResult.Error)); } IContract contract = contractLoadResult.Value; LogExecutionContext(this.logger, contract.State.Block, contract.State.Message, contract.Address); IContractInvocationResult invocationResult = contract.Invoke(methodCall); if (!invocationResult.IsSuccess) { this.logger.LogTrace("(-)[CALLCONTRACT_INSTANTIATION_FAILED]"); return(GetInvocationVmErrorResult(invocationResult)); } this.logger.LogTrace("[CALL_CONTRACT_INSTANTIATION_SUCCEEDED]"); this.logger.LogTrace("(-)"); return(VmExecutionResult.Ok(invocationResult.Return, typeName)); }
/// <summary> /// Invokes a method on an existing smart contract /// </summary> public VmExecutionResult ExecuteMethod(ISmartContractState contractState, MethodCall methodCall, byte[] contractCode, string typeName) { this.logger.LogTrace("(){0}:{1}", nameof(methodCall.Name), methodCall.Name); ContractByteCode code; using (IContractModuleDefinition moduleDefinition = this.moduleDefinitionReader.Read(contractCode)) { moduleDefinition.InjectMethodGas(typeName, methodCall); code = moduleDefinition.ToByteCode(); } Result <IContract> contractLoadResult = this.Load( code, typeName, contractState.Message.ContractAddress.ToUint160(this.network), contractState); if (!contractLoadResult.IsSuccess) { // TODO this is temporary until we improve error handling overloads var exception = new Exception(contractLoadResult.Error); LogException(exception); this.logger.LogTrace("(-)[LOAD_CONTRACT_FAILED]"); return(VmExecutionResult.Error(exception)); } IContract contract = contractLoadResult.Value; LogExecutionContext(this.logger, contract.State.Block, contract.State.Message, contract.Address); IContractInvocationResult invocationResult = contract.Invoke(methodCall); if (!invocationResult.IsSuccess) { this.logger.LogTrace("(-)[CALLCONTRACT_INSTANTIATION_FAILED]"); return(VmExecutionResult.Error(new Exception("Method invocation failed!"))); } this.logger.LogTrace("[CALL_CONTRACT_INSTANTIATION_SUCCEEDED]"); this.logger.LogTrace("(-)"); return(VmExecutionResult.Success(invocationResult.Return, typeName)); }
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 )); }
/// <summary> /// Creates a new instance of a smart contract by invoking the contract's constructor /// </summary> public VmExecutionResult Create(byte[] contractCode, ISmartContractExecutionContext context, IGasMeter gasMeter, IPersistentState persistentState, IContractStateRepository repository) { this.logger.LogTrace("()"); byte[] gasInjectedCode = SmartContractGasInjector.AddGasCalculationToConstructor(contractCode); Type contractType = Load(gasInjectedCode); var internalTransferList = new List <TransferInfo>(); IInternalTransactionExecutor internalTransactionExecutor = this.internalTransactionExecutorFactory.Create(repository, internalTransferList); var balanceState = new BalanceState(repository, context.Message.Value, internalTransferList); var contractState = new SmartContractState( context.Block, context.Message, persistentState, gasMeter, internalTransactionExecutor, new InternalHashHelper(), () => balanceState.GetBalance(context.ContractAddress)); // Invoke the constructor of the provided contract code LifecycleResult result = SmartContractConstructor.Construct(contractType, contractState, context.Parameters); if (!result.Success) { LogException(result.Exception); this.logger.LogTrace("(-)[CREATE_CONTRACT_INSTANTIATION_FAILED]:{0}={1}", nameof(gasMeter.GasConsumed), gasMeter.GasConsumed); return(VmExecutionResult.Error(gasMeter.GasConsumed, result.Exception.InnerException ?? result.Exception)); } else { this.logger.LogTrace("[CREATE_CONTRACT_INSTANTIATION_SUCCEEDED]"); } this.logger.LogTrace("(-):{0}={1}", nameof(gasMeter.GasConsumed), gasMeter.GasConsumed); return(VmExecutionResult.Success(internalTransferList, gasMeter.GasConsumed, result.Object)); }
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> /// Invokes a method on an existing smart contract /// </summary> public VmExecutionResult ExecuteMethod( IGasMeter gasMeter, IContractStateRepository repository, ICallData callData, ITransactionContext transactionContext) { this.logger.LogTrace("(){0}:{1}", nameof(callData.MethodName), callData.MethodName); if (callData.MethodName == null) { this.logger.LogTrace("(-)[CALLCONTRACT_METHODNAME_NOT_GIVEN]"); return(VmExecutionResult.Error(gasMeter.GasConsumed, null)); } byte[] contractExecutionCode = repository.GetCode(callData.ContractAddress); string typeName = repository.GetContractType(callData.ContractAddress); if (contractExecutionCode == null) { return(VmExecutionResult.Error(gasMeter.GasConsumed, new SmartContractDoesNotExistException(callData.MethodName))); } byte[] gasInjectedCode = SmartContractGasInjector.AddGasCalculationToContractMethod(contractExecutionCode, typeName, callData.MethodName); Type contractType = Load(gasInjectedCode, typeName); if (contractType == null) { this.logger.LogTrace("(-)[CALLCONTRACT_CONTRACTTYPE_NULL]"); return(VmExecutionResult.Error(gasMeter.GasConsumed, null)); } uint160 contractAddress = callData.ContractAddress; IPersistenceStrategy persistenceStrategy = new MeteredPersistenceStrategy(repository, gasMeter, new BasicKeyEncodingStrategy()); IPersistentState persistentState = new PersistentState(persistenceStrategy, contractAddress, this.network); var internalTransferList = new List <TransferInfo>(); IInternalTransactionExecutor internalTransactionExecutor = this.internalTransactionExecutorFactory.Create(this, repository, internalTransferList, transactionContext); var balanceState = new BalanceState(repository, transactionContext.Amount, internalTransferList); var contractState = new SmartContractState( new Block( transactionContext.BlockHeight, transactionContext.Coinbase.ToAddress(this.network) ), new Message( callData.ContractAddress.ToAddress(this.network), transactionContext.From.ToAddress(this.network), transactionContext.Amount ), persistentState, gasMeter, internalTransactionExecutor, new InternalHashHelper(), () => balanceState.GetBalance(callData.ContractAddress)); LogExecutionContext(this.logger, contractState.Block, contractState.Message, contractAddress, callData); LifecycleResult result = SmartContractRestorer.Restore(contractType, contractState); if (!result.Success) { LogException(result.Exception); this.logger.LogTrace("(-)[CALLCONTRACT_INSTANTIATION_FAILED]:{0}={1}", nameof(gasMeter.GasConsumed), gasMeter.GasConsumed); return(VmExecutionResult.Error(gasMeter.GasConsumed, result.Exception.InnerException ?? result.Exception)); } else { this.logger.LogTrace("[CALL_CONTRACT_INSTANTIATION_SUCCEEDED]"); } object methodResult = null; try { MethodInfo methodToInvoke = contractType.GetMethod(callData.MethodName); if (methodToInvoke == null) { throw new ArgumentException(string.Format("[CALLCONTRACT_METHODTOINVOKE_NULL_DOESNOT_EXIST]:{0}={1}", nameof(callData.MethodName), callData.MethodName)); } if (methodToInvoke.IsConstructor) { throw new ConstructorInvocationException("[CALLCONTRACT_CANNOT_INVOKE_CTOR]"); } SmartContract smartContract = result.Object; methodResult = methodToInvoke.Invoke(smartContract, callData.MethodParameters); } catch (ArgumentException argumentException) { LogException(argumentException); return(VmExecutionResult.Error(gasMeter.GasConsumed, argumentException)); } catch (TargetInvocationException targetException) { LogException(targetException); return(VmExecutionResult.Error(gasMeter.GasConsumed, targetException.InnerException ?? targetException)); } catch (TargetParameterCountException parameterException) { LogException(parameterException); } catch (ConstructorInvocationException constructorInvocationException) { LogException(constructorInvocationException); return(VmExecutionResult.Error(gasMeter.GasConsumed, constructorInvocationException)); } this.logger.LogTrace("(-):{0}={1}", nameof(gasMeter.GasConsumed), gasMeter.GasConsumed); return(VmExecutionResult.Success(internalTransferList, gasMeter.GasConsumed, methodResult)); }
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); }
/// <summary> /// Invokes a method on an existing smart contract /// </summary> public VmExecutionResult ExecuteMethod(byte[] contractCode, string contractMethodName, ISmartContractExecutionContext context, IGasMeter gasMeter, IPersistentState persistentState, IContractStateRepository repository) { this.logger.LogTrace("(){0}:{1}", nameof(contractMethodName), contractMethodName); ISmartContractExecutionResult executionResult = new SmartContractExecutionResult(); if (contractMethodName == null) { this.logger.LogTrace("(-)[CALLCONTRACT_METHODNAME_NOT_GIVEN]"); return(VmExecutionResult.Error(gasMeter.GasConsumed, null)); } byte[] gasInjectedCode = SmartContractGasInjector.AddGasCalculationToContractMethod(contractCode, contractMethodName); Type contractType = Load(gasInjectedCode); if (contractType == null) { this.logger.LogTrace("(-)[CALLCONTRACT_CONTRACTTYPE_NULL]"); return(VmExecutionResult.Error(gasMeter.GasConsumed, null)); } var internalTransferList = new List <TransferInfo>(); IInternalTransactionExecutor internalTransactionExecutor = this.internalTransactionExecutorFactory.Create(repository, internalTransferList); var balanceState = new BalanceState(repository, context.Message.Value, internalTransferList); var contractState = new SmartContractState( context.Block, context.Message, persistentState, gasMeter, internalTransactionExecutor, new InternalHashHelper(), () => balanceState.GetBalance(context.ContractAddress)); LifecycleResult result = SmartContractRestorer.Restore(contractType, contractState); if (!result.Success) { LogException(result.Exception); this.logger.LogTrace("(-)[CALLCONTRACT_INSTANTIATION_FAILED]:{0}={1}", nameof(gasMeter.GasConsumed), gasMeter.GasConsumed); executionResult.Exception = result.Exception.InnerException ?? result.Exception; executionResult.GasConsumed = gasMeter.GasConsumed; return(VmExecutionResult.Error(gasMeter.GasConsumed, result.Exception.InnerException ?? result.Exception)); } else { this.logger.LogTrace("[CALL_CONTRACT_INSTANTIATION_SUCCEEDED]"); } object methodResult = null; try { MethodInfo methodToInvoke = contractType.GetMethod(contractMethodName); if (methodToInvoke == null) { throw new ArgumentException(string.Format("[CALLCONTRACT_METHODTOINVOKE_NULL_DOESNOT_EXIST]:{0}={1}", nameof(contractMethodName), contractMethodName)); } if (methodToInvoke.IsConstructor) { throw new ConstructorInvocationException("[CALLCONTRACT_CANNOT_INVOKE_CTOR]"); } SmartContract smartContract = result.Object; methodResult = methodToInvoke.Invoke(smartContract, context.Parameters); } catch (ArgumentException argumentException) { LogException(argumentException); return(VmExecutionResult.Error(gasMeter.GasConsumed, argumentException)); } catch (TargetInvocationException targetException) { LogException(targetException); return(VmExecutionResult.Error(gasMeter.GasConsumed, targetException.InnerException ?? targetException)); } catch (TargetParameterCountException parameterException) { LogException(parameterException); } catch (ConstructorInvocationException constructorInvocationException) { LogException(constructorInvocationException); return(VmExecutionResult.Error(gasMeter.GasConsumed, constructorInvocationException)); } this.logger.LogTrace("(-):{0}={1}", nameof(gasMeter.GasConsumed), gasMeter.GasConsumed); return(VmExecutionResult.Success(internalTransferList, gasMeter.GasConsumed, methodResult)); }
/// <summary> /// Invokes a method on an existing smart contract /// </summary> public VmExecutionResult ExecuteMethod( IGasMeter gasMeter, IContractState repository, ICallData callData, ITransactionContext transactionContext) { this.logger.LogTrace("(){0}:{1}", nameof(callData.MethodName), callData.MethodName); if (callData.MethodName == null) { this.logger.LogTrace("(-)[CALLCONTRACT_METHODNAME_NOT_GIVEN]"); return(VmExecutionResult.Error(gasMeter.GasConsumed, null)); } byte[] contractExecutionCode = repository.GetCode(callData.ContractAddress); string typeName = repository.GetContractType(callData.ContractAddress); if (contractExecutionCode == null) { return(VmExecutionResult.Error(gasMeter.GasConsumed, new SmartContractDoesNotExistException(callData.MethodName))); } // TODO consolidate this with CallData. MethodCall methodCall = new MethodCall(callData.MethodName, callData.MethodParameters); ContractByteCode code; using (IContractModuleDefinition moduleDefinition = this.moduleDefinitionReader.Read(contractExecutionCode)) { moduleDefinition.InjectMethodGas(typeName, methodCall); code = moduleDefinition.ToByteCode(); } var internalTransferList = new List <TransferInfo>(); var contractLogger = new ContractLogHolder(this.network); ISmartContractState contractState = this.SetupState(contractLogger, internalTransferList, gasMeter, repository, transactionContext, callData.ContractAddress); Result <IContract> contractLoadResult = this.Load( code, typeName, callData.ContractAddress, contractState); if (!contractLoadResult.IsSuccess) { // TODO this is temporary until we improve error handling overloads var exception = new Exception(contractLoadResult.Error); LogException(exception); this.logger.LogTrace("(-)[LOAD_CONTRACT_FAILED]:{0}={1}", nameof(gasMeter.GasConsumed), gasMeter.GasConsumed); return(VmExecutionResult.Error(gasMeter.GasConsumed, exception)); } IContract contract = contractLoadResult.Value; LogExecutionContext(this.logger, contract.State.Block, contract.State.Message, contract.Address, callData); IContractInvocationResult invocationResult = contract.Invoke(methodCall); if (!invocationResult.IsSuccess) { this.logger.LogTrace("(-)[CALLCONTRACT_INSTANTIATION_FAILED]:{0}={1}", nameof(gasMeter.GasConsumed), gasMeter.GasConsumed); return(VmExecutionResult.Error(gasMeter.GasConsumed, new Exception("Method invocation failed!"))); } this.logger.LogTrace("[CALL_CONTRACT_INSTANTIATION_SUCCEEDED]"); this.logger.LogTrace("(-):{0}={1}", nameof(gasMeter.GasConsumed), gasMeter.GasConsumed); return(VmExecutionResult.Success(internalTransferList, gasMeter.GasConsumed, invocationResult.Return, contractLogger.GetRawLogs())); }
/// <summary> /// Creates a new instance of a smart contract by invoking the contract's constructor /// </summary> public VmExecutionResult Create(IContractState repository, ISmartContractState contractState, byte[] contractCode, object[] parameters, string typeName = null) { this.logger.LogTrace("()"); string typeToInstantiate; ContractByteCode code; // Decompile the contract execution code and validate it. using (IContractModuleDefinition moduleDefinition = this.moduleDefinitionReader.Read(contractCode)) { SmartContractValidationResult validation = moduleDefinition.Validate(this.validator); // If validation failed, refund the sender any remaining gas. if (!validation.IsValid) { this.logger.LogTrace("(-)[CONTRACT_VALIDATION_FAILED]"); return(VmExecutionResult.Error(new SmartContractValidationException(validation.Errors))); } typeToInstantiate = typeName ?? moduleDefinition.ContractType.Name; moduleDefinition.InjectConstructorGas(); code = moduleDefinition.ToByteCode(); } Result <IContract> contractLoadResult = this.Load( code, typeToInstantiate, contractState.Message.ContractAddress.ToUint160(this.network), contractState); if (!contractLoadResult.IsSuccess) { // TODO this is temporary until we improve error handling overloads var exception = new Exception(contractLoadResult.Error); LogException(exception); this.logger.LogTrace("(-)[LOAD_CONTRACT_FAILED]"); return(VmExecutionResult.Error(exception)); } IContract contract = contractLoadResult.Value; LogExecutionContext(this.logger, contract.State.Block, contract.State.Message, contract.Address); // Set the code and the Type before the method is invoked repository.SetCode(contract.Address, contractCode); repository.SetContractType(contract.Address, typeToInstantiate); // Invoke the constructor of the provided contract code IContractInvocationResult invocationResult = contract.InvokeConstructor(parameters); if (!invocationResult.IsSuccess) { this.logger.LogTrace("[CREATE_CONTRACT_INSTANTIATION_FAILED]"); return(VmExecutionResult.Error(new Exception("Constructor invocation failed!"))); } this.logger.LogTrace("[CREATE_CONTRACT_INSTANTIATION_SUCCEEDED]"); this.logger.LogTrace("(-):{0}={1}", nameof(contract.Address), contract.Address); return(VmExecutionResult.Success(invocationResult.Return, typeToInstantiate)); }
/// <summary> /// Creates a new instance of a smart contract by invoking the contract's constructor /// </summary> public VmExecutionResult Create(IGasMeter gasMeter, IContractStateRepository repository, ICreateData createData, ITransactionContext transactionContext) { this.logger.LogTrace("()"); // TODO: Spend Validation + Creation Fee here. // Decompile the contract execution code and validate it. SmartContractDecompilation decompilation = SmartContractDecompiler.GetModuleDefinition(createData.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(VmExecutionResult.Error(gasMeter.GasConsumed, new SmartContractValidationException(validation.Errors))); } byte[] gasInjectedCode = SmartContractGasInjector.AddGasCalculationToConstructor(createData.ContractExecutionCode, decompilation.ContractType.Name); Type contractType = Load(gasInjectedCode, decompilation.ContractType.Name); uint160 contractAddress = this.addressGenerator.GenerateAddress(transactionContext.TransactionHash, transactionContext.GetNonceAndIncrement()); // Create an account for the contract in the state repository. repository.CreateAccount(contractAddress); IPersistenceStrategy persistenceStrategy = new MeteredPersistenceStrategy(repository, gasMeter, new BasicKeyEncodingStrategy()); var persistentState = new PersistentState(persistenceStrategy, contractAddress, this.network); var internalTransferList = new List <TransferInfo>(); IInternalTransactionExecutor internalTransactionExecutor = this.internalTransactionExecutorFactory.Create(this, repository, internalTransferList, transactionContext); var balanceState = new BalanceState(repository, transactionContext.Amount, internalTransferList); var contractState = new SmartContractState( new Block( transactionContext.BlockHeight, transactionContext.Coinbase.ToAddress(this.network) ), new Message( contractAddress.ToAddress(this.network), transactionContext.From.ToAddress(this.network), transactionContext.Amount ), persistentState, gasMeter, internalTransactionExecutor, new InternalHashHelper(), () => balanceState.GetBalance(contractAddress)); LogExecutionContext(this.logger, contractState.Block, contractState.Message, contractAddress, createData); // Invoke the constructor of the provided contract code LifecycleResult result = SmartContractConstructor.Construct(contractType, contractState, createData.MethodParameters); if (!result.Success) { LogException(result.Exception); this.logger.LogTrace("(-)[CREATE_CONTRACT_INSTANTIATION_FAILED]:{0}={1}", nameof(gasMeter.GasConsumed), gasMeter.GasConsumed); return(VmExecutionResult.Error(gasMeter.GasConsumed, result.Exception.InnerException ?? result.Exception)); } this.logger.LogTrace("[CREATE_CONTRACT_INSTANTIATION_SUCCEEDED]"); this.logger.LogTrace("(-):{0}={1}, {2}={3}", nameof(contractAddress), contractAddress, nameof(gasMeter.GasConsumed), gasMeter.GasConsumed); repository.SetCode(contractAddress, createData.ContractExecutionCode); repository.SetContractType(contractAddress, contractType.Name); return(VmExecutionResult.CreationSuccess(contractAddress, internalTransferList, gasMeter.GasConsumed, result.Object)); }
/// <summary> /// Creates a new instance of a smart contract by invoking the contract's constructor /// </summary> public VmExecutionResult Create(IStateRepository repository, ISmartContractState contractState, byte[] contractCode, object[] parameters, string typeName = null) { this.logger.LogTrace("()"); string typeToInstantiate; ContractByteCode code; // Decompile the contract execution code Result <IContractModuleDefinition> moduleResult = this.moduleDefinitionReader.Read(contractCode); if (moduleResult.IsFailure) { this.logger.LogTrace("(-)[CONTRACT_BYTECODE_INVALID]"); return(VmExecutionResult.Error(new ContractErrorMessage("Contract bytecode is not valid IL."))); } // Validate contract execution code using (IContractModuleDefinition moduleDefinition = moduleResult.Value) { SmartContractValidationResult validation = moduleDefinition.Validate(this.validator); // If validation failed, refund the sender any remaining gas. if (!validation.IsValid) { this.logger.LogTrace("(-)[CONTRACT_VALIDATION_FAILED]"); // TODO: List errors by string. return(VmExecutionResult.Error(new ContractErrorMessage(new SmartContractValidationException(validation.Errors).ToString()))); } typeToInstantiate = typeName ?? moduleDefinition.ContractType.Name; var observer = new Observer(contractState.GasMeter, MemoryUnitLimit); var rewriter = new ObserverRewriter(observer); moduleDefinition.Rewrite(rewriter); code = moduleDefinition.ToByteCode(); } Result <IContract> contractLoadResult = this.Load( code, typeToInstantiate, contractState.Message.ContractAddress.ToUint160(this.network), contractState); if (!contractLoadResult.IsSuccess) { LogErrorMessage(contractLoadResult.Error); this.logger.LogTrace("(-)[LOAD_CONTRACT_FAILED]"); return(VmExecutionResult.Error(new ContractErrorMessage(contractLoadResult.Error))); } IContract contract = contractLoadResult.Value; LogExecutionContext(this.logger, contract.State.Block, contract.State.Message, contract.Address); // Set the code and the Type before the method is invoked repository.SetCode(contract.Address, contractCode); repository.SetContractType(contract.Address, typeToInstantiate); // Invoke the constructor of the provided contract code IContractInvocationResult invocationResult = contract.InvokeConstructor(parameters); if (!invocationResult.IsSuccess) { this.logger.LogTrace("[CREATE_CONTRACT_INSTANTIATION_FAILED]"); return(VmExecutionResult.Error(invocationResult.ErrorMessage)); } this.logger.LogTrace("[CREATE_CONTRACT_INSTANTIATION_SUCCEEDED]"); this.logger.LogTrace("(-):{0}={1}", nameof(contract.Address), contract.Address); return(VmExecutionResult.Success(invocationResult.Return, typeToInstantiate)); }
/// <summary> /// Creates a new instance of a smart contract by invoking the contract's constructor /// </summary> public VmExecutionResult Create(IGasMeter gasMeter, IContractState repository, ICreateData createData, ITransactionContext transactionContext, string typeName = null) { this.logger.LogTrace("()"); // TODO: Spend Validation + Creation Fee here. string typeToInstantiate; ContractByteCode code; // Decompile the contract execution code and validate it. using (IContractModuleDefinition moduleDefinition = this.moduleDefinitionReader.Read(createData.ContractExecutionCode)) { SmartContractValidationResult validation = moduleDefinition.Validate(this.validator); // If validation failed, refund the sender any remaining gas. if (!validation.IsValid) { this.logger.LogTrace("(-)[CONTRACT_VALIDATION_FAILED]"); return(VmExecutionResult.Error(gasMeter.GasConsumed, new SmartContractValidationException(validation.Errors))); } typeToInstantiate = typeName ?? moduleDefinition.ContractType.Name; moduleDefinition.InjectConstructorGas(); code = moduleDefinition.ToByteCode(); } var internalTransferList = new List <TransferInfo>(); uint160 address = this.addressGenerator.GenerateAddress(transactionContext.TransactionHash, transactionContext.GetNonceAndIncrement()); var contractLogger = new ContractLogHolder(this.network); ISmartContractState contractState = this.SetupState(contractLogger, internalTransferList, gasMeter, repository, transactionContext, address); Result <IContract> contractLoadResult = this.Load( code, typeToInstantiate, address, contractState); if (!contractLoadResult.IsSuccess) { // TODO this is temporary until we improve error handling overloads var exception = new Exception(contractLoadResult.Error); LogException(exception); this.logger.LogTrace("(-)[LOAD_CONTRACT_FAILED]:{0}={1}", nameof(gasMeter.GasConsumed), gasMeter.GasConsumed); return(VmExecutionResult.Error(gasMeter.GasConsumed, exception)); } IContract contract = contractLoadResult.Value; LogExecutionContext(this.logger, contract.State.Block, contract.State.Message, contract.Address, createData); // Create an account for the contract in the state repository. repository.CreateAccount(contract.Address); // Invoke the constructor of the provided contract code IContractInvocationResult invocationResult = contract.InvokeConstructor(createData.MethodParameters); if (!invocationResult.IsSuccess) { this.logger.LogTrace("[CREATE_CONTRACT_INSTANTIATION_FAILED]"); return(VmExecutionResult.Error(gasMeter.GasConsumed, new Exception("Constructor invocation failed!"))); } this.logger.LogTrace("[CREATE_CONTRACT_INSTANTIATION_SUCCEEDED]"); this.logger.LogTrace("(-):{0}={1}, {2}={3}", nameof(contract.Address), contract.Address, nameof(gasMeter.GasConsumed), gasMeter.GasConsumed); repository.SetCode(contract.Address, createData.ContractExecutionCode); repository.SetContractType(contract.Address, contract.Type.Name); return(VmExecutionResult.CreationSuccess(contract.Address, internalTransferList, gasMeter.GasConsumed, invocationResult.Return, contractLogger.GetRawLogs())); }