public void Contract_Transfer_To_Other_Contract_Success() { // There is code at the destination address, which causes an internal call to the receive method var vmExecutionResult = VmExecutionResult.Success(true, "Test"); var code = new byte[1]; var typeName = "Test"; var contractTransferMessage = new ContractTransferMessage( uint160.One, uint160.Zero, 10, (Gas)(GasPriceList.BaseCost + 100000) ); // Code must be returned for this test to ensure we apply the call. this.contractStateRoot .Setup(sr => sr.GetCode(contractTransferMessage.To)) .Returns(code); this.contractStateRoot .Setup(sr => sr.GetContractType(contractTransferMessage.To)) .Returns(typeName); this.vm.Setup(v => v.ExecuteMethod(It.IsAny <ISmartContractState>(), contractTransferMessage.Method, code, typeName)) .Returns(vmExecutionResult); var state = new Mock <IState>(); // Return the sent amount + 1 state.Setup(s => s.GetBalance(contractTransferMessage.From)).Returns(contractTransferMessage.Amount + 1); state.SetupGet(s => s.ContractState).Returns(this.contractStateRoot.Object); var stateProcessor = new StateProcessor(this.vm.Object, this.addressGenerator.Object); StateTransitionResult result = stateProcessor.Apply(state.Object, contractTransferMessage); // Verify we check the balance of the sender first state.Verify(s => s.GetBalance(contractTransferMessage.From)); // Verify we get the code from the destination address' code cache this.contractStateRoot.Verify(s => s.GetCode(contractTransferMessage.To), Times.Once); // Verify we set up the smart contract state state.Verify(s => s.CreateSmartContractState(state.Object, It.IsAny <GasMeter>(), contractTransferMessage.To, contractTransferMessage, this.contractStateRoot.Object)); // Verify the VM was invoked this.vm.Verify(v => v.ExecuteMethod(It.IsAny <ISmartContractState>(), contractTransferMessage.Method, code, typeName), Times.Once); // Verify the value was added to the internal transfer list state.Verify(s => s.AddInternalTransfer(It.Is <TransferInfo>(t => t.From == contractTransferMessage.From && t.To == contractTransferMessage.To && t.Value == contractTransferMessage.Amount))); Assert.True(result.IsSuccess); Assert.NotNull(result.Success); Assert.Equal(contractTransferMessage.To, result.Success.ContractAddress); Assert.Equal(vmExecutionResult.Result, result.Success.ExecutionResult); Assert.Equal(GasPriceList.BaseCost, result.GasConsumed); }
public void InternalCreate_Vm_Error() { var newContractAddress = uint160.One; var vmExecutionResult = VmExecutionResult.Fail(VmExecutionErrorKind.InvocationFailed, "Error"); // Code must have a length to pass precondition checks. var code = new byte[1]; var typeName = "Test"; var internalCreateMessage = new InternalCreateMessage( uint160.Zero, 10, (RuntimeObserver.Gas)(GasPriceList.BaseCost + 100000), new object[] { }, typeName ); this.vm.Setup(v => v.Create(It.IsAny <IStateRepository>(), It.IsAny <ISmartContractState>(), It.IsAny <ExecutionContext>(), It.IsAny <byte[]>(), It.IsAny <object[]>(), It.IsAny <string>())) .Returns(vmExecutionResult); // Need to return code for the sender this.contractStateRoot .Setup(sr => sr.GetCode(internalCreateMessage.From)) .Returns(code); var state = new Mock <IState>(); state.Setup(s => s.GetBalance(internalCreateMessage.From)).Returns(internalCreateMessage.Amount + 1); state.SetupGet(s => s.ContractState).Returns(this.contractStateRoot.Object); state.Setup(s => s.GenerateAddress(It.IsAny <IAddressGenerator>())).Returns(newContractAddress); var stateProcessor = new StateProcessor(this.vm.Object, this.addressGenerator.Object); StateTransitionResult result = stateProcessor.Apply(state.Object, internalCreateMessage); state.Verify(s => s.CreateSmartContractState(state.Object, It.IsAny <RuntimeObserver.IGasMeter>(), newContractAddress, internalCreateMessage, this.contractStateRoot.Object)); this.vm.Verify( v => v.Create( this.contractStateRoot.Object, It.IsAny <ISmartContractState>(), It.IsAny <ExecutionContext>(), code, internalCreateMessage.Parameters, internalCreateMessage.Type), Times.Once); Assert.True(result.IsFailure); Assert.NotNull(result.Error); Assert.Equal(result.Error.VmError, vmExecutionResult.Error.Message); Assert.Equal(StateTransitionErrorKind.VmError, result.Error.Kind); Assert.Equal(GasPriceList.CreateCost, result.GasConsumed); }
public void SmartContracts_GasInjector_MultipleParamConstructorGasInjectedSuccess() { SmartContractCompilationResult compilationResult = SmartContractCompiler.Compile(TestMultipleConstructorSource); Assert.True(compilationResult.Success); byte[] originalAssemblyBytes = compilationResult.Compilation; var gasLimit = (Gas)500000; var gasMeter = new GasMeter(gasLimit); var callData = new CreateData(gasLimit, originalAssemblyBytes, new[] { "Test Owner" }); var transactionContext = new TransactionContext( txHash: uint256.One, blockHeight: 0, coinbase: TestAddress.ToUint160(this.network), sender: TestAddress.ToUint160(this.network), amount: 0 ); VmExecutionResult result = this.vm.Create(gasMeter, this.repository, callData, transactionContext); // Constructor: 15 // Property setter: 12 // Storage: 150 Assert.Equal((Gas)177, result.GasConsumed); }
public void VM_ExecuteContract_WithParameters() { ContractCompilationResult compilationResult = ContractCompiler.CompileFile("SmartContracts/StorageTest.cs"); Assert.True(compilationResult.Success); byte[] contractExecutionCode = compilationResult.Compilation; byte[] codeHash = HashHelper.Keccak256(contractExecutionCode); var methodParameters = new object[] { (int)5 }; var callData = new MethodCall("OneParamTest", methodParameters); var executionContext = new ExecutionContext(new Observer(this.gasMeter, new MemoryMeter(100_000))); VmExecutionResult result = this.vm.ExecuteMethod(this.contractState, executionContext, callData, contractExecutionCode, "StorageTest"); CachedAssemblyPackage cachedAssembly = this.context.ContractCache.Retrieve(new uint256(codeHash)); // Check that it's been cached. Assert.NotNull(cachedAssembly); // Check that the observer has been reset. Assert.Null(cachedAssembly.Assembly.GetObserver()); Assert.True(result.IsSuccess); Assert.Null(result.Error); Assert.Equal(methodParameters[0], result.Success.Result); }
public void TestGasInjector_OutOfGasFails() { SmartContractCompilationResult compilationResult = SmartContractCompiler.CompileFile("SmartContracts/OutOfGasTest.cs"); Assert.True(compilationResult.Success); byte[] originalAssemblyBytes = compilationResult.Compilation; var gasLimit = (Gas)500000; var gasMeter = new GasMeter(gasLimit); uint160 address = TestAddress.ToUint160(this.network); var callData = new CallData(gasLimit, address, "UseAllGas"); var transactionContext = new TransactionContext(uint256.One, 0, address, address, 0); this.repository.SetCode(callData.ContractAddress, originalAssemblyBytes); this.repository.SetContractType(callData.ContractAddress, "OutOfGasTest"); VmExecutionResult result = this.vm.ExecuteMethod(gasMeter, this.repository, callData, transactionContext); Assert.NotNull(result.ExecutionException); Assert.Equal((Gas)0, gasMeter.GasAvailable); Assert.Equal(gasLimit, result.GasConsumed); Assert.Equal(gasLimit, gasMeter.GasConsumed); }
public void VM_Throws_Exception_CanCatch() { SmartContractCompilationResult compilationResult = SmartContractCompiler.CompileFile("SmartContracts/ThrowExceptionContract.cs"); Assert.True(compilationResult.Success); byte[] contractCode = compilationResult.Compilation; var gasLimit = (Gas)10000; var gasMeter = new GasMeter(gasLimit); uint160 address = TestAddress.ToUint160(this.network); var callData = new CallData(gasLimit, address, "ThrowException"); this.repository.SetCode(address, contractCode); this.repository.SetContractType(address, "ThrowExceptionContract"); var transactionContext = new TransactionContext(uint256.One, 0, address, address, 0); VmExecutionResult result = this.vm.ExecuteMethod(gasMeter, this.repository, callData, transactionContext); Assert.Equal(typeof(Exception), result.ExecutionException.GetType()); }
public void VM_Throws_Exception_CanCatch() { SmartContractCompilationResult compilationResult = SmartContractCompiler.CompileFile("SmartContracts/ThrowExceptionContract.cs"); Assert.True(compilationResult.Success); byte[] contractCode = compilationResult.Compilation; var gasLimit = (Gas)10000; var gasMeter = new GasMeter(gasLimit); var internalTxExecutorFactory = new InternalTransactionExecutorFactory(this.keyEncodingStrategy, this.loggerFactory, this.network); var vm = new ReflectionVirtualMachine(this.validator, internalTxExecutorFactory, this.loggerFactory, this.network, this.addressGenerator); uint160 address = TestAddress.ToUint160(this.network); var callData = new CallData(gasLimit, address, "ThrowException"); this.repository.SetCode(address, contractCode); this.repository.SetContractType(address, "ThrowExceptionContract"); var transactionContext = new TransactionContext(uint256.One, 0, address, address, 0); VmExecutionResult result = vm.ExecuteMethod(gasMeter, this.repository, callData, transactionContext); Assert.Equal(typeof(Exception), result.ExecutionException.GetType()); }
public void VM_ExecuteContract_ClearStorage() { ContractCompilationResult compilationResult = ContractCompiler.CompileFile("SmartContracts/ClearStorage.cs"); Assert.True(compilationResult.Success); byte[] contractExecutionCode = compilationResult.Compilation; var callData = new MethodCall(nameof(ClearStorage.ClearKey), new object[] { }); uint160 contractAddress = this.contractState.Message.ContractAddress.ToUint160(); byte[] keyToClear = Encoding.UTF8.GetBytes(ClearStorage.KeyToClear); // Set a value to be cleared this.state.SetStorageValue(contractAddress, keyToClear, new byte[] { 1, 2, 3 }); VmExecutionResult result = this.vm.ExecuteMethod(this.contractState, this.gasMeter, callData, contractExecutionCode, nameof(ClearStorage)); Assert.Null(result.Error); Assert.Null(this.state.GetStorageValue(contractAddress, keyToClear)); }
public void TestGasInjector_ContractMethodWithRecursion_GasInjectionSucceeds() { SmartContractCompilationResult compilationResult = SmartContractCompiler.CompileFile("SmartContracts/Recursion.cs"); Assert.True(compilationResult.Success); byte[] originalAssemblyBytes = compilationResult.Compilation; var gasLimit = (Gas)500000; var gasMeter = new GasMeter(gasLimit); uint160 address = TestAddress.ToUint160(this.network); var callData = new CallData(gasLimit, address, nameof(Recursion.DoRecursion)); var transactionContext = new TransactionContext(uint256.One, 0, address, address, 0); this.repository.SetCode(callData.ContractAddress, originalAssemblyBytes); this.repository.SetContractType(callData.ContractAddress, nameof(Recursion)); VmExecutionResult result = this.vm.ExecuteMethod(gasMeter, this.repository, callData, transactionContext); Assert.Null(result.ExecutionException); Assert.True(result.GasConsumed > 0); }
public void InternalCall_Vm_Error() { var vmExecutionResult = VmExecutionResult.Fail(VmExecutionErrorKind.InvocationFailed, "Error"); // Code must have a length to pass precondition checks. var code = new byte[1]; var typeName = "Test"; var internalCallMessage = new InternalCallMessage( uint160.One, uint160.Zero, 10, (Gas)(GasPriceList.BaseCost + 100000), new MethodCall("Test", new object[] { }) ); this.vm.Setup(v => v.ExecuteMethod( It.IsAny <ISmartContractState>(), It.IsAny <IGasMeter>(), internalCallMessage.Method, code, typeName)) .Returns(vmExecutionResult); this.contractStateRoot .Setup(sr => sr.GetCode(internalCallMessage.To)) .Returns(code); this.contractStateRoot .Setup(sr => sr.GetContractType(internalCallMessage.To)) .Returns(typeName); var state = new Mock <IState>(); state.Setup(s => s.GetBalance(internalCallMessage.From)) .Returns(internalCallMessage.Amount + 1); state.SetupGet(s => s.ContractState).Returns(this.contractStateRoot.Object); var stateProcessor = new StateProcessor(this.vm.Object, this.addressGenerator.Object); StateTransitionResult result = stateProcessor.Apply(state.Object, internalCallMessage); state.Verify(s => s.CreateSmartContractState(state.Object, It.IsAny <IGasMeter>(), internalCallMessage.To, internalCallMessage, this.contractStateRoot.Object)); this.vm.Verify( v => v.ExecuteMethod( It.IsAny <ISmartContractState>(), It.IsAny <IGasMeter>(), internalCallMessage.Method, code, typeName), Times.Once); Assert.True(result.IsFailure); Assert.NotNull(result.Error); Assert.Equal(result.Error.VmError, vmExecutionResult.Error.Message); Assert.Equal(StateTransitionErrorKind.VmError, result.Error.Kind); Assert.Equal(GasPriceList.BaseCost, result.GasConsumed); }
public void ExternalCall_Success() { var gasLimit = (Gas)(GasPriceList.BaseCost + 100000); var vmExecutionResult = VmExecutionResult.Ok(true, "Test"); // Code must have a length to pass precondition checks. var code = new byte[1]; var typeName = "Test"; var externalCallMessage = new ExternalCallMessage( uint160.Zero, uint160.Zero, 0, gasLimit, new MethodCall("Test", null) ); this.contractStateRoot .Setup(sr => sr.GetCode(externalCallMessage.To)) .Returns(code); this.contractStateRoot .Setup(sr => sr.GetContractType(externalCallMessage.To)) .Returns(typeName); this.vm.Setup(v => v.ExecuteMethod(It.IsAny <ISmartContractState>(), externalCallMessage.Method, code, typeName)) .Returns(vmExecutionResult); var state = new Mock <IState>(); state.SetupGet(s => s.ContractState).Returns(this.contractStateRoot.Object); var stateProcessor = new StateProcessor(this.vm.Object, this.addressGenerator.Object); StateTransitionResult result = stateProcessor.Apply(state.Object, externalCallMessage); state.Verify(s => s.AddInitialTransfer(It.Is <TransferInfo>(t => t.Value == externalCallMessage.Amount && t.To == externalCallMessage.To))); this.contractStateRoot.Verify(sr => sr.GetCode(externalCallMessage.To), Times.Once); this.contractStateRoot.Verify(sr => sr.GetContractType(externalCallMessage.To), Times.Once); state.Verify(s => s.CreateSmartContractState(state.Object, It.IsAny <GasMeter>(), externalCallMessage.To, externalCallMessage, this.contractStateRoot.Object)); this.vm.Verify( v => v.ExecuteMethod(It.IsAny <ISmartContractState>(), externalCallMessage.Method, code, typeName), Times.Once); Assert.True(result.IsSuccess); Assert.NotNull(result.Success); Assert.Equal(externalCallMessage.To, result.Success.ContractAddress); Assert.Equal(vmExecutionResult.Success.Result, result.Success.ExecutionResult); Assert.Equal(GasPriceList.BaseCost, result.GasConsumed); }
public void Create_Contract_Success() { var contractTxData = new ContractTxData(1, 1, (Gas)1000, new byte[] { 0xAA, 0xBB, 0xCC }); VmExecutionResult vmExecutionResult = VmExecutionResult.Ok(new object(), null); StateTransitionResult stateTransitionResult = StateTransitionResult.Ok((Gas)100, uint160.One, vmExecutionResult.Success.Result); var fixture = new ExecutorFixture(contractTxData); IState snapshot = fixture.State.Object.Snapshot(); fixture.StateProcessor .Setup(s => s.Apply(snapshot, It.IsAny <ExternalCreateMessage>())) .Returns(stateTransitionResult); IStateRepository trackedMock = Mock.Of <IStateRepository>(); fixture.ContractStateRoot.Setup(s => s.StartTracking()).Returns(trackedMock); var sut = new LocalExecutor( fixture.LoggerFactory, fixture.CallDataSerializer.Object, fixture.ContractStateRoot.Object, fixture.StateFactory.Object, fixture.StateProcessor.Object, fixture.ContractPrimitiveSerializer.Object); ILocalExecutionResult result = sut.Execute(fixture.ContractTransactionContext); fixture.CallDataSerializer.Verify(s => s.Deserialize(fixture.ContractTransactionContext.Data), Times.Once); fixture.StateFactory.Verify(sf => sf .Create( trackedMock, It.IsAny <IBlock>(), fixture.ContractTransactionContext.TxOutValue, fixture.ContractTransactionContext.TransactionHash), Times.Once); // We only apply the message to the snapshot. fixture.StateProcessor.Verify(sm => sm.Apply(snapshot, It.IsAny <ExternalCreateMessage>()), Times.Once); // Should never transition to the snapshot. fixture.State.Verify(sm => sm.TransitionTo(snapshot), Times.Never); // Should never save on the state fixture.ContractStateRoot.Verify(sr => sr.Commit(), Times.Never); Assert.Null(result.ErrorMessage); Assert.False(result.Revert); Assert.Equal(fixture.State.Object.InternalTransfers, result.InternalTransfers); Assert.Equal(stateTransitionResult.GasConsumed, result.GasConsumed); Assert.Equal(stateTransitionResult.Success.ExecutionResult, result.Return); Assert.Equal(fixture.State.Object.GetLogs(fixture.ContractPrimitiveSerializer.Object), result.Logs); }
public void ExternalCall_Vm_Error() { var gasLimit = (Gas)(GasPriceList.BaseCost + 100000); var vmExecutionResult = VmExecutionResult.Error(new ContractErrorMessage("Error")); // Code must have a length to pass precondition checks. var code = new byte[1]; var typeName = "Test"; var externalCallMessage = new ExternalCallMessage( uint160.Zero, uint160.Zero, 0, gasLimit, new MethodCall("Test") ); this.contractStateRoot .Setup(sr => sr.GetCode(externalCallMessage.To)) .Returns(code); this.contractStateRoot .Setup(sr => sr.GetContractType(externalCallMessage.To)) .Returns(typeName); this.vm.Setup(v => v.ExecuteMethod(It.IsAny <ISmartContractState>(), externalCallMessage.Method, code, typeName)) .Returns(vmExecutionResult); var state = new Mock <IState>(); state.SetupGet(s => s.ContractState).Returns(this.contractStateRoot.Object); var stateProcessor = new StateProcessor(this.vm.Object, this.addressGenerator.Object); StateTransitionResult result = stateProcessor.Apply(state.Object, externalCallMessage); this.contractStateRoot.Verify(sr => sr.GetCode(externalCallMessage.To), Times.Once); this.contractStateRoot.Verify(sr => sr.GetContractType(externalCallMessage.To), Times.Once); state.Verify(s => s.CreateSmartContractState(state.Object, It.IsAny <GasMeter>(), externalCallMessage.To, externalCallMessage, this.contractStateRoot.Object)); this.vm.Verify( v => v.ExecuteMethod(It.IsAny <ISmartContractState>(), externalCallMessage.Method, code, typeName), Times.Once); Assert.True(result.IsFailure); Assert.NotNull(result.Error); Assert.Equal(result.Error.VmError, vmExecutionResult.ErrorMessage); Assert.Equal(StateTransitionErrorKind.VmError, result.Error.Kind); Assert.Equal(GasPriceList.BaseCost, result.GasConsumed); }
public void VM_ExecuteContract_OutOfGas() { ContractCompilationResult compilationResult = ContractCompiler.Compile( @" using System; using Stratis.SmartContracts; public class Contract : SmartContract { public Contract(ISmartContractState state) : base(state) {} } "); Assert.True(compilationResult.Success); byte[] contractExecutionCode = compilationResult.Compilation; byte[] codeHash = HashHelper.Keccak256(contractExecutionCode); // Set up the state with an empty gasmeter so that out of gas occurs var contractState = Mock.Of <ISmartContractState>(s => s.Block == Mock.Of <IBlock>(b => b.Number == 1 && b.Coinbase == this.TestAddress) && s.Message == new Message(this.TestAddress, this.TestAddress, 0) && s.PersistentState == new PersistentState( new TestPersistenceStrategy(this.state), this.context.Serializer, this.TestAddress.ToUint160()) && s.Serializer == this.context.Serializer && s.ContractLogger == new ContractLogHolder() && s.InternalTransactionExecutor == Mock.Of <IInternalTransactionExecutor>() && s.InternalHashHelper == new InternalHashHelper() && s.GetBalance == new Func <ulong>(() => 0)); var emptyGasMeter = new GasMeter((RuntimeObserver.Gas) 0); var executionContext = new ExecutionContext(new Observer(emptyGasMeter, new MemoryMeter(100_000))); VmExecutionResult result = this.vm.Create( this.state, contractState, executionContext, contractExecutionCode, null); CachedAssemblyPackage cachedAssembly = this.context.ContractCache.Retrieve(new uint256(codeHash)); // Check that it's been cached, even though we ran out of gas. Assert.NotNull(cachedAssembly); // Check that the observer has been reset. Assert.Null(cachedAssembly.Assembly.GetObserver()); Assert.False(result.IsSuccess); Assert.Equal(VmExecutionErrorKind.OutOfGas, result.Error.ErrorKind); }
public void VM_CreateContract_WithParameters() { SmartContractCompilationResult compilationResult = SmartContractCompiler.CompileFile("SmartContracts/Auction.cs"); Assert.True(compilationResult.Success); byte[] contractExecutionCode = compilationResult.Compilation; var methodParameters = new object[] { (ulong)5 }; VmExecutionResult result = this.vm.Create(this.state, this.contractState, contractExecutionCode, methodParameters); Assert.Null(result.ExecutionException); }
public void TestGasInjector() { SmartContractCompilationResult compilationResult = SmartContractCompiler.Compile(TestSource); Assert.True(compilationResult.Success); byte[] originalAssemblyBytes = compilationResult.Compilation; var resolver = new DefaultAssemblyResolver(); resolver.AddSearchDirectory(AppContext.BaseDirectory); int aimGasAmount; using (ModuleDefinition moduleDefinition = ModuleDefinition.ReadModule( new MemoryStream(originalAssemblyBytes), new ReaderParameters { AssemblyResolver = resolver })) { TypeDefinition contractType = moduleDefinition.GetType(ContractName); MethodDefinition testMethod = contractType.Methods.FirstOrDefault(x => x.Name == MethodName); aimGasAmount = testMethod?.Body?.Instructions? .Count ?? 10000000; } var gasLimit = (Gas)500000; var gasMeter = new GasMeter(gasLimit); var internalTxExecutorFactory = new InternalTransactionExecutorFactory(this.keyEncodingStrategy, this.loggerFactory, this.network); var vm = new ReflectionVirtualMachine(this.validator, internalTxExecutorFactory, this.loggerFactory, this.network, this.addressGenerator); uint160 address = TestAddress.ToUint160(this.network); var callData = new CallData(gasLimit, address, "TestMethod", new object[] { 1 }); var transactionContext = new TransactionContext(uint256.One, 0, address, address, 0); this.repository.SetCode(callData.ContractAddress, originalAssemblyBytes); this.repository.SetContractType(callData.ContractAddress, "Test"); VmExecutionResult result = vm.ExecuteMethod(gasMeter, this.repository, callData, transactionContext); Assert.Equal((ulong)aimGasAmount, result.GasConsumed); }
public void VM_CreateContract_WithParameters() { //Get the contract execution code------------------------ SmartContractCompilationResult compilationResult = SmartContractCompiler.CompileFile("SmartContracts/Auction.cs"); Assert.True(compilationResult.Success); byte[] contractExecutionCode = compilationResult.Compilation; //------------------------------------------------------- //Set the calldata for the transaction---------- var methodParameters = new object[] { (ulong)5 }; var callData = new CreateData((Gas)5000000, contractExecutionCode, methodParameters); Money value = Money.Zero; //------------------------------------------------------- var repository = new ContractStateRepositoryRoot(new NoDeleteSource <byte[], byte[]>(new MemoryDictionarySource())); IContractStateRepository track = repository.StartTracking(); var gasMeter = new GasMeter(callData.GasLimit); var internalTxExecutorFactory = new InternalTransactionExecutorFactory(this.keyEncodingStrategy, this.loggerFactory, this.network); var vm = new ReflectionVirtualMachine(this.validator, internalTxExecutorFactory, this.loggerFactory, this.network, this.addressGenerator); var transactionContext = new TransactionContext( txHash: uint256.One, blockHeight: 1, coinbase: TestAddress.ToUint160(this.network), sender: TestAddress.ToUint160(this.network), amount: value ); VmExecutionResult result = vm.Create(gasMeter, repository, callData, transactionContext); track.Commit(); var endBlockValue = track.GetStorageValue(result.NewContractAddress, Encoding.UTF8.GetBytes("EndBlock")); Assert.Equal(6, BitConverter.ToInt16(endBlockValue, 0)); Assert.Equal(TestAddress.ToUint160(this.network).ToBytes(), track.GetStorageValue(result.NewContractAddress, Encoding.UTF8.GetBytes("Owner"))); }
public void VM_ExecuteContract_WithParameters() { SmartContractCompilationResult compilationResult = SmartContractCompiler.CompileFile("SmartContracts/StorageTest.cs"); Assert.True(compilationResult.Success); byte[] contractExecutionCode = compilationResult.Compilation; var methodParameters = new object[] { (int)5 }; var callData = new MethodCall("OneParamTest", methodParameters); VmExecutionResult result = this.vm.ExecuteMethod(this.contractState, callData, contractExecutionCode, "StorageTest"); Assert.Null(result.ExecutionException); Assert.Equal(methodParameters[0], result.Result); }
public void VM_ExecuteContract_WithoutParameters() { SmartContractCompilationResult compilationResult = SmartContractCompiler.CompileFile("SmartContracts/StorageTest.cs"); Assert.True(compilationResult.Success); byte[] contractExecutionCode = compilationResult.Compilation; var callData = new MethodCall("NoParamsTest"); VmExecutionResult result = this.vm.ExecuteMethod(this.contractState, callData, contractExecutionCode, "StorageTest"); Assert.Null(result.ExecutionException); Assert.True((bool)result.Result); }
public void VM_ExecuteContract_WithParameters() { //Get the contract execution code------------------------ SmartContractCompilationResult compilationResult = SmartContractCompiler.CompileFile("SmartContracts/StorageTestWithParameters.cs"); Assert.True(compilationResult.Success); byte[] contractExecutionCode = compilationResult.Compilation; //------------------------------------------------------- //Set the calldata for the transaction---------- var methodParameters = new object[] { (short)5 }; var callData = new CallData((Gas)5000000, new uint160(1), "StoreData", methodParameters); Money value = Money.Zero; //------------------------------------------------------- var repository = new ContractStateRepositoryRoot(new NoDeleteSource <byte[], byte[]>(new MemoryDictionarySource())); IContractStateRepository track = repository.StartTracking(); var gasMeter = new GasMeter(callData.GasLimit); var internalTxExecutorFactory = new InternalTransactionExecutorFactory(this.keyEncodingStrategy, this.loggerFactory, this.network); var vm = new ReflectionVirtualMachine(this.validator, internalTxExecutorFactory, this.loggerFactory, this.network, this.addressGenerator); uint160 address = TestAddress.ToUint160(this.network); var transactionContext = new TransactionContext(uint256.One, 1, address, address, value); repository.SetCode(callData.ContractAddress, contractExecutionCode); repository.SetContractType(callData.ContractAddress, "StorageTestWithParameters"); VmExecutionResult result = vm.ExecuteMethod(gasMeter, repository, callData, transactionContext); track.Commit(); Assert.Equal(5, BitConverter.ToInt16(track.GetStorageValue(callData.ContractAddress, Encoding.UTF8.GetBytes("orders")), 0)); Assert.Equal(5, BitConverter.ToInt16(repository.GetStorageValue(callData.ContractAddress, Encoding.UTF8.GetBytes("orders")), 0)); }
public void VM_ExecuteContract_OutOfGas() { ContractCompilationResult compilationResult = ContractCompiler.Compile( @" using System; using Stratis.SmartContracts; public class Contract : SmartContract { public Contract(ISmartContractState state) : base(state) {} } "); Assert.True(compilationResult.Success); byte[] contractExecutionCode = compilationResult.Compilation; // Set up the state with an empty gasmeter so that out of gas occurs var contractState = Mock.Of <ISmartContractState>(s => s.Block == Mock.Of <IBlock>(b => b.Number == 1 && b.Coinbase == this.TestAddress) && s.Message == new Message(this.TestAddress, this.TestAddress, 0) && s.PersistentState == new PersistentState( new TestPersistenceStrategy(this.state), this.context.Serializer, this.TestAddress.ToUint160()) && s.Serializer == this.context.Serializer && s.ContractLogger == new ContractLogHolder() && s.InternalTransactionExecutor == Mock.Of <IInternalTransactionExecutor>() && s.InternalHashHelper == new InternalHashHelper() && s.GetBalance == new Func <ulong>(() => 0)); var emptyGasMeter = new GasMeter((Gas)0); VmExecutionResult result = this.vm.Create( this.state, contractState, emptyGasMeter, contractExecutionCode, null); Assert.False(result.IsSuccess); Assert.Equal(VmExecutionErrorKind.OutOfGas, result.Error.ErrorKind); }
public void ExternalCreate_Success() { var newContractAddress = uint160.One; var vmExecutionResult = VmExecutionResult.Ok(true, "Test"); var externalCreateMessage = new ExternalCreateMessage( uint160.Zero, 10, (Gas)(GasPriceList.BaseCost + 100000), new byte[0], null ); this.vm.Setup(v => v.Create(this.contractStateRoot.Object, It.IsAny <ISmartContractState>(), externalCreateMessage.Code, externalCreateMessage.Parameters, null)) .Returns(vmExecutionResult); var state = new Mock <IState>(); state.SetupGet(s => s.ContractState).Returns(this.contractStateRoot.Object); state.Setup(s => s.GenerateAddress(It.IsAny <IAddressGenerator>())).Returns(newContractAddress); var stateProcessor = new StateProcessor(this.vm.Object, this.addressGenerator.Object); StateTransitionResult result = stateProcessor.Apply(state.Object, externalCreateMessage); state.Verify(s => s.AddInitialTransfer(It.Is <TransferInfo>(t => t.Value == externalCreateMessage.Amount && t.To == newContractAddress))); state.Verify(s => s.GenerateAddress(this.addressGenerator.Object), Times.Once); this.contractStateRoot.Verify(s => s.CreateAccount(newContractAddress), Times.Once); state.Verify(s => s.CreateSmartContractState(state.Object, It.IsAny <GasMeter>(), newContractAddress, externalCreateMessage, this.contractStateRoot.Object)); this.vm.Verify(v => v.Create(this.contractStateRoot.Object, It.IsAny <ISmartContractState>(), externalCreateMessage.Code, externalCreateMessage.Parameters, null), Times.Once); Assert.True(result.IsSuccess); Assert.NotNull(result.Success); Assert.Equal(newContractAddress, result.Success.ContractAddress); Assert.Equal(vmExecutionResult.Success.Result, result.Success.ExecutionResult); Assert.Equal(GasPriceList.CreateCost, result.GasConsumed); }
public void VM_ExecuteContract_WithoutParameters() { //Get the contract execution code------------------------ SmartContractCompilationResult compilationResult = SmartContractCompiler.CompileFile("SmartContracts/StorageTest.cs"); Assert.True(compilationResult.Success); byte[] contractExecutionCode = compilationResult.Compilation; //------------------------------------------------------- //Set the calldata for the transaction---------- var callData = new CallData((Gas)5000000, new uint160(1), "StoreData"); Money value = Money.Zero; //------------------------------------------------------- var repository = new ContractStateRepositoryRoot(new NoDeleteSource <byte[], byte[]>(new MemoryDictionarySource())); IContractStateRepository stateRepository = repository.StartTracking(); var gasMeter = new GasMeter(callData.GasLimit); uint160 address = TestAddress.ToUint160(this.network); var transactionContext = new TransactionContext(uint256.One, 1, address, address, 0); repository.SetCode(callData.ContractAddress, contractExecutionCode); repository.SetContractType(callData.ContractAddress, "StorageTest"); VmExecutionResult result = this.vm.ExecuteMethod(gasMeter, repository, callData, transactionContext); stateRepository.Commit(); Assert.Equal(Encoding.UTF8.GetBytes("TestValue"), stateRepository.GetStorageValue(callData.ContractAddress, Encoding.UTF8.GetBytes("TestKey"))); Assert.Equal(Encoding.UTF8.GetBytes("TestValue"), repository.GetStorageValue(callData.ContractAddress, Encoding.UTF8.GetBytes("TestKey"))); }
public void ExternalCreate_Vm_Error() { var newContractAddress = uint160.One; var vmExecutionResult = VmExecutionResult.Error(new ContractErrorMessage("Error")); var externalCreateMessage = new ExternalCreateMessage( uint160.Zero, 10, (Gas)(GasPriceList.BaseCost + 100000), new byte[0], null ); this.vm.Setup(v => v.Create(this.contractStateRoot.Object, It.IsAny <ISmartContractState>(), externalCreateMessage.Code, externalCreateMessage.Parameters, null)) .Returns(vmExecutionResult); var state = new Mock <IState>(); state.SetupGet(s => s.ContractState).Returns(this.contractStateRoot.Object); state.Setup(s => s.GenerateAddress(It.IsAny <IAddressGenerator>())).Returns(newContractAddress); var stateProcessor = new StateProcessor(this.vm.Object, this.addressGenerator.Object); StateTransitionResult result = stateProcessor.Apply(state.Object, externalCreateMessage); state.Verify(s => s.GenerateAddress(this.addressGenerator.Object), Times.Once); this.contractStateRoot.Verify(ts => ts.CreateAccount(newContractAddress), Times.Once); this.vm.Verify(v => v.Create(this.contractStateRoot.Object, It.IsAny <ISmartContractState>(), externalCreateMessage.Code, externalCreateMessage.Parameters, null), Times.Once); Assert.False(result.IsSuccess); Assert.True(result.IsFailure); Assert.NotNull(result.Error); Assert.Equal(vmExecutionResult.ErrorMessage, result.Error.VmError); Assert.Equal(StateTransitionErrorKind.VmError, result.Error.Kind); Assert.Equal(GasPriceList.BaseCost, result.GasConsumed); }
public void VM_ExecuteContract_ClearStorage() { ContractCompilationResult compilationResult = ContractCompiler.CompileFile("SmartContracts/ClearStorage.cs"); Assert.True(compilationResult.Success); byte[] contractExecutionCode = compilationResult.Compilation; byte[] codeHash = HashHelper.Keccak256(contractExecutionCode); var callData = new MethodCall(nameof(ClearStorage.ClearKey), new object[] { }); uint160 contractAddress = this.contractState.Message.ContractAddress.ToUint160(); byte[] keyToClear = Encoding.UTF8.GetBytes(ClearStorage.KeyToClear); // Set a value to be cleared this.state.SetStorageValue(contractAddress, keyToClear, new byte[] { 1, 2, 3 }); var executionContext = new ExecutionContext(new Observer(this.gasMeter, new MemoryMeter(100_000))); VmExecutionResult result = this.vm.ExecuteMethod(this.contractState, executionContext, callData, contractExecutionCode, nameof(ClearStorage)); CachedAssemblyPackage cachedAssembly = this.context.ContractCache.Retrieve(new uint256(codeHash)); // Check that it's been cached for a successful call. Assert.NotNull(cachedAssembly); // Check that the observer has been reset. Assert.Null(cachedAssembly.Assembly.GetObserver()); Assert.Null(result.Error); Assert.Null(this.state.GetStorageValue(contractAddress, keyToClear)); }
public void VM_CreateContract_WithParameters() { ContractCompilationResult compilationResult = ContractCompiler.CompileFile("SmartContracts/Auction.cs"); Assert.True(compilationResult.Success); byte[] contractExecutionCode = compilationResult.Compilation; byte[] codeHash = HashHelper.Keccak256(contractExecutionCode); var methodParameters = new object[] { (ulong)5 }; var executionContext = new ExecutionContext(new Observer(this.gasMeter, new MemoryMeter(100_000))); VmExecutionResult result = this.vm.Create(this.state, this.contractState, executionContext, contractExecutionCode, methodParameters); CachedAssemblyPackage cachedAssembly = this.context.ContractCache.Retrieve(new uint256(codeHash)); // Check that it's been cached. Assert.NotNull(cachedAssembly); // Check that the observer has been reset. Assert.Null(cachedAssembly.Assembly.GetObserver()); Assert.True(result.IsSuccess); Assert.Null(result.Error); }
public void Call_Contract_Success() { var parameters = new object[] { }; var contractTxData = new ContractTxData(1, 1, (RuntimeObserver.Gas) 1000, uint160.One, "TestMethod", parameters); VmExecutionResult vmExecutionResult = VmExecutionResult.Ok(new object(), null); StateTransitionResult stateTransitionResult = StateTransitionResult.Ok((RuntimeObserver.Gas) 100, uint160.One, vmExecutionResult.Success.Result); var fixture = new ExecutorFixture(contractTxData); IState snapshot = fixture.State.Object.Snapshot(); fixture.StateProcessor .Setup(s => s.Apply(snapshot, It.IsAny <ExternalCallMessage>())) .Returns(stateTransitionResult); IStateRepository trackedMock = Mock.Of <IStateRepository>(); fixture.ContractStateRoot.Setup(s => s.StartTracking()).Returns(trackedMock); var sut = new LocalExecutor( fixture.LoggerFactory, fixture.CallDataSerializer.Object, fixture.ContractStateRoot.Object, fixture.StateFactory.Object, fixture.StateProcessor.Object, fixture.ContractPrimitiveSerializer.Object); ILocalExecutionResult result = sut.Execute(fixture.ContractTransactionContext.BlockHeight, fixture.ContractTransactionContext.Sender, fixture.ContractTransactionContext.TxOutValue, contractTxData); // Local executor used a tracked staterepository fixture.StateFactory.Verify(sf => sf .Create( trackedMock, It.IsAny <IBlock>(), fixture.ContractTransactionContext.TxOutValue, It.IsAny <uint256>()), Times.Once); // We only apply the message to the snapshot. fixture.StateProcessor.Verify(sm => sm.Apply(snapshot, It.Is <ExternalCallMessage>(m => m.Method.Name == contractTxData.MethodName && m.Method.Parameters == contractTxData.MethodParameters && m.Amount == fixture.ContractTransactionContext.TxOutValue && m.From == fixture.ContractTransactionContext.Sender && m.To == contractTxData.ContractAddress)), Times.Once); // Should never transition to the snapshot. fixture.State.Verify(sm => sm.TransitionTo(snapshot), Times.Never); // Should never save on the state fixture.ContractStateRoot.Verify(sr => sr.Commit(), Times.Never); Assert.Null(result.ErrorMessage); Assert.False(result.Revert); Assert.Equal <IReadOnlyList <TransferInfo> >(fixture.State.Object.InternalTransfers, result.InternalTransfers); Assert.Equal(stateTransitionResult.GasConsumed, result.GasConsumed); Assert.Equal(stateTransitionResult.Success.ExecutionResult, result.Return); Assert.Equal <IList <Log> >(fixture.State.Object.GetLogs(fixture.ContractPrimitiveSerializer.Object), result.Logs); }
public void Create_Contract_Success() { uint160 newContractAddress = uint160.One; var gasConsumed = (Gas)100; var code = new byte[] { 0xAA, 0xBB, 0xCC }; var contractTxData = new ContractTxData(1, 1, (Gas)1000, code); var refund = new Money(0); const ulong mempoolFee = 2UL; // MOQ doesn't like it when you use a type with implicit conversions (Money) ISmartContractTransactionContext context = Mock.Of <ISmartContractTransactionContext>(c => c.Data == code && c.MempoolFee == mempoolFee && c.Sender == uint160.One); var logger = new Mock <ILogger>(); ILoggerFactory loggerFactory = Mock.Of <ILoggerFactory> (l => l.CreateLogger(It.IsAny <string>()) == logger.Object); var serializer = new Mock <ICallDataSerializer>(); serializer .Setup(s => s.Deserialize(It.IsAny <byte[]>())) .Returns(Result.Ok(contractTxData)); var contractPrimitiveSerializer = new Mock <IContractPrimitiveSerializer>(); var vmExecutionResult = VmExecutionResult.CreationSuccess( newContractAddress, new List <TransferInfo>(), gasConsumed, null, null); var state = new Mock <IContractState>(); var transferProcessor = new Mock <ISmartContractResultTransferProcessor>(); (Money refund, List <TxOut>)refundResult = (refund, new List <TxOut>()); var refundProcessor = new Mock <ISmartContractResultRefundProcessor>(); refundProcessor .Setup(r => r.Process( contractTxData, mempoolFee, context.Sender, vmExecutionResult.GasConsumed, vmExecutionResult.ExecutionException)) .Returns(refundResult); var vm = new Mock <ISmartContractVirtualMachine>(); vm.Setup(v => v.Create(It.Is <IGasMeter>(x => x.GasConsumed == GasPriceList.BaseCost), It.IsAny <IContractState>(), It.IsAny <ICreateData>(), It.IsAny <ITransactionContext>(), It.IsAny <string>())) .Returns(vmExecutionResult); var sut = new Executor( loggerFactory, contractPrimitiveSerializer.Object, serializer.Object, state.Object, refundProcessor.Object, transferProcessor.Object, vm.Object ); sut.Execute(context); serializer.Verify(s => s.Deserialize(code), Times.Once); vm.Verify(v => v.Create( It.IsAny <IGasMeter>(), state.Object, contractTxData, It.IsAny <TransactionContext>(), It.IsAny <string>()), Times.Once); transferProcessor.Verify(t => t .Process( state.Object, vmExecutionResult.NewContractAddress, It.IsAny <ISmartContractTransactionContext>(), vmExecutionResult.InternalTransfers, false), Times.Once); refundProcessor.Verify(t => t .Process( contractTxData, mempoolFee, context.Sender, vmExecutionResult.GasConsumed, vmExecutionResult.ExecutionException), Times.Once); state.Verify(s => s.Commit(), Times.Once); state.Verify(s => s.Rollback(), Times.Never); }
public void VM_ExecuteContract_CachedAssembly_WithExistingObserver() { ContractCompilationResult compilationResult = ContractCompiler.Compile( @" using System; using Stratis.SmartContracts; public class Contract : SmartContract { public Contract(ISmartContractState state) : base(state) {} public void Test() {} } "); Assert.True(compilationResult.Success); byte[] contractExecutionCode = compilationResult.Compilation; byte[] codeHash = HashHelper.Keccak256(contractExecutionCode); byte[] rewrittenCode; // Rewrite the assembly to have an observer. using (IContractModuleDefinition moduleDefinition = this.context.ModuleDefinitionReader.Read(contractExecutionCode).Value) { var rewriter = new ObserverInstanceRewriter(); moduleDefinition.Rewrite(rewriter); rewrittenCode = moduleDefinition.ToByteCode().Value; } var contractAssembly = new ContractAssembly(Assembly.Load(rewrittenCode)); // Cache the assembly. this.context.ContractCache.Store(new uint256(codeHash), new CachedAssemblyPackage(contractAssembly)); // Set an observer on the cached rewritten assembly. var initialObserver = new Observer(new GasMeter((Gas)(this.gasMeter.GasAvailable + 1000)), new MemoryMeter(100_000)); Assert.True(contractAssembly.SetObserver(initialObserver)); var callData = new MethodCall("Test"); // Run the execution with an empty gas meter, which means it should fail if the correct observer is used. var emptyGasMeter = new GasMeter((Gas)0); var executionContext = new ExecutionContext(new Observer(emptyGasMeter, new MemoryMeter(100_000))); VmExecutionResult result = this.vm.ExecuteMethod(this.contractState, executionContext, callData, contractExecutionCode, "Contract"); CachedAssemblyPackage cachedAssembly = this.context.ContractCache.Retrieve(new uint256(codeHash)); // Check that it's still cached. Assert.NotNull(cachedAssembly); // Check that the observer has been reset to the original. Assert.Same(initialObserver, cachedAssembly.Assembly.GetObserver()); Assert.False(result.IsSuccess); Assert.Equal(VmExecutionErrorKind.OutOfGas, result.Error.ErrorKind); }
public void InternalCall_Success() { // The difference between an internal and an external call: // - Internal call performs a balance check before execution // - Internal call appends a new internal transfer if successful var vmExecutionResult = VmExecutionResult.Success(true, "Test"); var code = new byte[1]; var typeName = "Test"; var internalCallMessage = new InternalCallMessage( uint160.One, uint160.Zero, 10, (Gas)(GasPriceList.BaseCost + 100000), new MethodCall("Test", new object[] {}) ); this.contractStateRoot .Setup(sr => sr.GetCode(internalCallMessage.To)) .Returns(code); this.contractStateRoot .Setup(sr => sr.GetContractType(internalCallMessage.To)) .Returns(typeName); this.vm.Setup(v => v.ExecuteMethod(It.IsAny <ISmartContractState>(), internalCallMessage.Method, code, typeName)) .Returns(vmExecutionResult); var state = new Mock <IState>(); // Return the sent amount + 1 state.Setup(s => s.GetBalance(internalCallMessage.From)).Returns(internalCallMessage.Amount + 1); state.SetupGet(s => s.ContractState).Returns(this.contractStateRoot.Object); var stateProcessor = new StateProcessor(this.vm.Object, this.addressGenerator.Object); StateTransitionResult result = stateProcessor.Apply(state.Object, internalCallMessage); // Verify we check the balance of the sender first state.Verify(s => s.GetBalance(internalCallMessage.From)); // Verify we get the code from the destination address' code cache this.contractStateRoot.Verify(s => s.GetCode(internalCallMessage.To), Times.Once); // Verify we set up the smart contract state state.Verify(s => s.CreateSmartContractState(state.Object, It.IsAny <GasMeter>(), internalCallMessage.To, internalCallMessage, this.contractStateRoot.Object)); // Verify the VM was invoked this.vm.Verify(v => v.ExecuteMethod(It.IsAny <ISmartContractState>(), internalCallMessage.Method, code, typeName), Times.Once); // Verify the value was added to the internal transfer list state.Verify(s => s.AddInternalTransfer(It.Is <TransferInfo>(t => t.From == internalCallMessage.From && t.To == internalCallMessage.To && t.Value == internalCallMessage.Amount))); Assert.True(result.IsSuccess); Assert.NotNull(result.Success); Assert.Equal(internalCallMessage.To, result.Success.ContractAddress); Assert.Equal(vmExecutionResult.Result, result.Success.ExecutionResult); Assert.Equal(GasPriceList.BaseCost, result.GasConsumed); }