/// <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 Result <ContractByteCode> GetByteCode(IContractModuleDefinition moduleDefinition)
        {
            try
            {
                ContractByteCode code = moduleDefinition.ToByteCode();

                return(Result.Ok(code));
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                this.logger.LogTrace("(-)[CONTRACT_TOBYTECODE_FAILED]");

                return(Result.Fail <ContractByteCode>("Exception occurred while serializing module"));
            }
        }
Exemplo n.º 4
0
        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);
        }
Exemplo n.º 5
0
        private IContract GetContractAfterRewrite(string filename)
        {
            ContractCompilationResult compilationResult = ContractCompiler.CompileFile(filename);

            Assert.True(compilationResult.Success);

            byte[] originalAssemblyBytes = compilationResult.Compilation;

            IContractModuleDefinition module = this.moduleReader.Read(originalAssemblyBytes).Value;

            module.Rewrite(this.rewriter);

            CSharpFunctionalExtensions.Result <IContractAssembly> assembly = this.assemblyLoader.Load(module.ToByteCode());

            return(Contract.CreateUninitialized(assembly.Value.GetType(module.ContractType.Name), this.state, null));
        }
Exemplo n.º 6
0
        public void TestGasInjector_NestedType_GasInjectionSucceeds()
        {
            ContractCompilationResult compilationResult = ContractCompiler.Compile(@"
using System;
using Stratis.SmartContracts;

public class Test : SmartContract
{
    public Test(ISmartContractState state): base(state) {
        var other = new Other.NestedOther();
        other.Loop();
    }
}

public static class Other
{
    public struct NestedOther {
        public void Loop() { while(true) {}}
    }
}
");

            Assert.True(compilationResult.Success);

            byte[] originalAssemblyBytes = compilationResult.Compilation;

            IContractModuleDefinition module = this.moduleReader.Read(originalAssemblyBytes).Value;

            module.Rewrite(this.rewriter);

            CSharpFunctionalExtensions.Result <IContractAssembly> assembly = this.assemblyLoader.Load(module.ToByteCode());

            IContract contract = Contract.CreateUninitialized(assembly.Value.GetType(module.ContractType.Name), this.state, null);

            IContractInvocationResult result = contract.InvokeConstructor(null);

            Assert.False(result.IsSuccess);
            Assert.Equal(this.gasMeter.GasLimit, this.gasMeter.GasConsumed);
        }
Exemplo n.º 7
0
        public void TestGasInjector_ContractMethodWithRecursion_GasInjectionSucceeds()
        {
            ContractCompilationResult compilationResult = ContractCompiler.CompileFile("SmartContracts/Recursion.cs");

            Assert.True(compilationResult.Success);

            byte[] originalAssemblyBytes = compilationResult.Compilation;

            var callData = new MethodCall(nameof(Recursion.DoRecursion));

            IContractModuleDefinition module = this.moduleReader.Read(originalAssemblyBytes).Value;

            module.Rewrite(this.rewriter);

            CSharpFunctionalExtensions.Result <IContractAssembly> assembly = this.assemblyLoader.Load(module.ToByteCode());

            IContract contract = Contract.CreateUninitialized(assembly.Value.GetType(module.ContractType.Name), this.state, null);

            IContractInvocationResult result = contract.Invoke(callData);

            Assert.True(result.IsSuccess);
            Assert.True(this.gasMeter.GasConsumed > 0);
        }
Exemplo n.º 8
0
        public void SmartContracts_GasInjector_MultipleParamConstructorGasInjectedSuccess()
        {
            ContractCompilationResult compilationResult =
                ContractCompiler.Compile(TestMultipleConstructorSource);

            Assert.True(compilationResult.Success);
            byte[] originalAssemblyBytes = compilationResult.Compilation;

            IContractModuleDefinition module = this.moduleReader.Read(originalAssemblyBytes).Value;

            module.Rewrite(this.rewriter);

            CSharpFunctionalExtensions.Result <IContractAssembly> assembly = this.assemblyLoader.Load(module.ToByteCode());

            IContract contract = Contract.CreateUninitialized(assembly.Value.GetType(module.ContractType.Name), this.state, null);

            IContractInvocationResult result = contract.InvokeConstructor(new[] { "Test Owner" });

            // Number here shouldn't be hardcoded - note this is really only to let us know of consensus failure
            Assert.Equal((RuntimeObserver.Gas) 328, this.gasMeter.GasConsumed);
        }
Exemplo n.º 9
0
        public void TestGasInjector_OutOfGasFails()
        {
            ContractCompilationResult compilationResult = ContractCompiler.CompileFile("SmartContracts/OutOfGasTest.cs");

            Assert.True(compilationResult.Success);

            byte[] originalAssemblyBytes = compilationResult.Compilation;

            var callData = new MethodCall("UseAllGas");

            IContractModuleDefinition module = this.moduleReader.Read(originalAssemblyBytes).Value;

            module.Rewrite(this.rewriter);

            CSharpFunctionalExtensions.Result <IContractAssembly> assembly = this.assemblyLoader.Load(module.ToByteCode());

            IContract contract = Contract.CreateUninitialized(assembly.Value.GetType(module.ContractType.Name), this.state, null);

            // Because our contract contains an infinite loop, we want to kill our test after
            // some amount of time without achieving a result. 3 seconds is an arbitrarily high enough timeout
            // for the method body to have finished execution while minimising the amount of time we spend
            // running tests
            // If you're running with the debugger on this will obviously be a source of failures
            IContractInvocationResult result = TimeoutHelper.RunCodeWithTimeout(5, () => contract.Invoke(callData));

            Assert.False(result.IsSuccess);
            Assert.Equal((RuntimeObserver.Gas) 0, this.gasMeter.GasAvailable);
            Assert.Equal(this.gasMeter.GasLimit, this.gasMeter.GasConsumed);
            Assert.Equal(this.gasMeter.GasLimit, this.gasMeter.GasConsumed);
        }
        /// <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()));
        }
        /// <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()));
        }
Exemplo n.º 12
0
        /// <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));
        }
Exemplo n.º 13
0
        public void SmartContracts_GasInjector_MultipleParamConstructorGasInjectedSuccess()
        {
            SmartContractCompilationResult compilationResult =
                SmartContractCompiler.Compile(TestMultipleConstructorSource);

            Assert.True(compilationResult.Success);
            byte[] originalAssemblyBytes = compilationResult.Compilation;

            IContractModuleDefinition module = this.moduleReader.Read(originalAssemblyBytes);

            module.Rewrite(this.rewriter);

            CSharpFunctionalExtensions.Result <IContractAssembly> assembly = this.assemblyLoader.Load(module.ToByteCode());

            IContract contract = Contract.CreateUninitialized(assembly.Value.GetType(module.ContractType.Name), this.state, null);

            IContractInvocationResult result = contract.InvokeConstructor(new[] { "Test Owner" });

            // Constructor: 15
            // Property setter: 12
            // Storage: 150
            Assert.Equal((Gas)177, this.gasMeter.GasConsumed);
        }
Exemplo n.º 14
0
        public void TestGasInjector_OutOfGasFails()
        {
            SmartContractCompilationResult compilationResult = SmartContractCompiler.CompileFile("SmartContracts/OutOfGasTest.cs");

            Assert.True(compilationResult.Success);

            byte[] originalAssemblyBytes = compilationResult.Compilation;

            var callData = new MethodCall("UseAllGas");

            IContractModuleDefinition module = this.moduleReader.Read(originalAssemblyBytes);

            module.Rewrite(this.rewriter);

            CSharpFunctionalExtensions.Result <IContractAssembly> assembly = this.assemblyLoader.Load(module.ToByteCode());

            IContract contract = Contract.CreateUninitialized(assembly.Value.GetType(module.ContractType.Name), this.state, null);

            IContractInvocationResult result = contract.Invoke(callData);

            Assert.False(result.IsSuccess);
            Assert.Equal((Gas)0, this.gasMeter.GasAvailable);
            Assert.Equal(this.gasMeter.GasLimit, this.gasMeter.GasConsumed);
            Assert.Equal(this.gasMeter.GasLimit, this.gasMeter.GasConsumed);
        }
Exemplo n.º 15
0
        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 callData = new MethodCall("TestMethod", new object[] { 1 });

            IContractModuleDefinition module = this.moduleReader.Read(originalAssemblyBytes);

            module.Rewrite(this.rewriter);

            CSharpFunctionalExtensions.Result <IContractAssembly> assembly = this.assemblyLoader.Load(module.ToByteCode());

            IContract contract = Contract.CreateUninitialized(assembly.Value.GetType(module.ContractType.Name), this.state, null);

            IContractInvocationResult result = contract.Invoke(callData);

            Assert.Equal((ulong)aimGasAmount, this.state.GasMeter.GasConsumed);
        }
Exemplo n.º 16
0
        public void TestGasInjector()
        {
            ContractCompilationResult compilationResult = ContractCompiler.Compile(TestSource);

            Assert.True(compilationResult.Success);

            byte[] originalAssemblyBytes = compilationResult.Compilation;

            var resolver = new DefaultAssemblyResolver();

            resolver.AddSearchDirectory(AppContext.BaseDirectory);

            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);
            }

            var callData = new MethodCall("TestMethod", new object[] { 1 });

            IContractModuleDefinition module = this.moduleReader.Read(originalAssemblyBytes).Value;

            module.Rewrite(this.rewriter);

            CSharpFunctionalExtensions.Result <IContractAssembly> assembly = this.assemblyLoader.Load(module.ToByteCode());

            IContract contract = Contract.CreateUninitialized(assembly.Value.GetType(module.ContractType.Name), this.state, null);

            IContractInvocationResult result = contract.Invoke(callData);

            // Number here shouldn't be hardcoded - note this is really only to let us know of consensus failure
            Assert.Equal(22uL, this.gasMeter.GasConsumed);
        }
Exemplo n.º 17
0
        /// <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));
        }
        public void Separate_Threads_Have_Different_Observer_Instances()
        {
            // Create a contract assembly.
            ContractCompilationResult compilationResult = ContractCompiler.Compile(TestSource);

            Assert.True(compilationResult.Success);

            byte[] originalAssemblyBytes = compilationResult.Compilation;

            IContractModuleDefinition module = this.moduleReader.Read(originalAssemblyBytes).Value;

            module.Rewrite(this.rewriter);

            CSharpFunctionalExtensions.Result <IContractAssembly> assemblyLoadResult = this.assemblyLoader.Load(module.ToByteCode());

            IContractAssembly assembly = assemblyLoadResult.Value;

            var threadCount = 10;

            var observers = new Observer[threadCount];

            var countdown = new CountdownEvent(threadCount);

            var threads = new Thread[threadCount];

            for (var i = 0; i < threadCount; i++)
            {
                // Create a fake observer with a counter.
                var observer = new Observer(new GasMeter((Gas)1000000), new MemoryMeter(1000000));

                observers[i] = observer;

                // Set a different observer on each thread.
                // Invoke the same method on each thread.
                Thread newThread = new Thread(() =>
                {
                    assembly.SetObserver(observer);

                    Assert.Same(observer, assembly.GetObserver());
                    var callData       = new MethodCall("TestMethod", new object[] { 1 });
                    IContract contract = Contract.CreateUninitialized(assembly.GetType(module.ContractType.Name), this.state, null);

                    IContractInvocationResult result = contract.Invoke(callData);

                    countdown.Signal();
                });

                newThread.Name = i.ToString();
                threads[i]     = newThread;
            }

            // Start the threads on a separate loop to ensure all time is spent on processing.
            foreach (Thread thread in threads)
            {
                thread.Start();
            }

            countdown.Wait();

            foreach (Observer observer in observers)
            {
                Assert.Equal(observers[0].GasMeter.GasConsumed, observer.GasMeter.GasConsumed);
                Assert.Equal(observers[0].MemoryMeter.MemoryConsumed, observer.MemoryMeter.MemoryConsumed);
            }
        }