/// <summary> /// Sets up a new <see cref="ISmartContractState"/> based on the current state. /// </summary> public ISmartContractState Create(IState state, IGasMeter gasMeter, uint160 address, BaseMessage message, IStateRepository repository) { IPersistenceStrategy persistenceStrategy = new MeteredPersistenceStrategy(repository, gasMeter, new BasicKeyEncodingStrategy()); var persistentState = new PersistentState(persistenceStrategy, this.Serializer, address); var contractLogger = new MeteredContractLogger(gasMeter, state.LogHolder, this.Network, this.Serializer); var contractState = new SmartContractState( state.Block, new Message( address.ToAddress(this.Network), message.From.ToAddress(this.Network), message.Amount ), persistentState, this.Serializer, gasMeter, contractLogger, this.InternalTransactionExecutorFactory.Create(state), new InternalHashHelper(), () => state.GetBalance(address)); return(contractState); }
/// <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); IContractStateRepository track = this.contractStateRepository.StartTracking(); IPersistenceStrategy persistenceStrategy = new MeteredPersistenceStrategy(track, smartContractState.GasMeter, this.keyEncodingStrategy); IPersistentState newPersistentState = new PersistentState(persistenceStrategy, addressTo.ToUint160(this.network), this.network); var newMessage = new Message(addressTo, smartContractState.Message.ContractAddress, amountToTransfer, (Gas)(smartContractState.Message.GasLimit - smartContractState.GasMeter.GasConsumed)); ISmartContractExecutionContext newContext = new SmartContractExecutionContext(smartContractState.Block, newMessage, addressTo.ToUint160(this.network), 0, contractDetails.MethodParameters); ISmartContractVirtualMachine vm = new ReflectionVirtualMachine(new InternalTransactionExecutorFactory(this.keyEncodingStrategy, this.loggerFactory, this.network), this.loggerFactory); var result = vm.ExecuteMethod( contractCode, contractDetails.ContractMethodName, newContext, smartContractState.GasMeter, newPersistentState, track); 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)); }
/// <summary> /// Sets up a new <see cref="ISmartContractState"/> based on the current state. /// </summary> private ISmartContractState CreateSmartContractState(IGasMeter gasMeter, uint160 address, BaseMessage message, IContractState repository) { IPersistenceStrategy persistenceStrategy = new MeteredPersistenceStrategy(repository, gasMeter, new BasicKeyEncodingStrategy()); var persistentState = new PersistentState(persistenceStrategy, new ContractPrimitiveSerializer(this.Network), address); var contractState = new SmartContractState( this.Block, new Message( address.ToAddress(this.Network), message.From.ToAddress(this.Network), message.Amount ), persistentState, this.Serializer, gasMeter, this.LogHolder, this.InternalTransactionExecutorFactory.Create(this), new InternalHashHelper(), () => this.BalanceState.GetBalance(address)); return(contractState); }
/// <summary> /// Sets up the state object for the contract execution /// </summary> private ISmartContractState SetupState( IContractLogHolder contractLogger, List <TransferInfo> internalTransferList, IGasMeter gasMeter, IContractState repository, ITransactionContext transactionContext, uint160 contractAddress) { IPersistenceStrategy persistenceStrategy = new MeteredPersistenceStrategy(repository, gasMeter, new BasicKeyEncodingStrategy()); var persistentState = new PersistentState(persistenceStrategy, this.contractPrimitiveSerializer, contractAddress); IInternalTransactionExecutor internalTransactionExecutor = this.internalTransactionExecutorFactory.Create(this, contractLogger, 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, this.contractPrimitiveSerializer, gasMeter, contractLogger, internalTransactionExecutor, new InternalHashHelper(), () => balanceState.GetBalance(contractAddress)); return(contractState); }
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); }
/// <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> /// 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("()"); 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); }