Beispiel #1
0
        private Assembly GetAssembly(uint160 address)
        {
            var codeHashBytes = this.stateRepositoryRoot.GetCodeHash(address);

            if (codeHashBytes == null)
            {
                return(null);
            }

            var codeHash = new uint256(codeHashBytes);

            // Attempt to load from cache.
            CachedAssemblyPackage cachedAssembly = this.contractAssemblyCache.Retrieve(codeHash);

            if (cachedAssembly == null)
            {
                // Cache is not thread-safe so don't load into the cache if not found - leave that for consensus for now.
                var byteCode = this.stateRepositoryRoot.GetCode(address);

                if (byteCode == null)
                {
                    return(null);
                }

                return(Assembly.Load(byteCode));
            }

            return(cachedAssembly.Assembly.Assembly);
        }
Beispiel #2
0
        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);
        }
Beispiel #3
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;
            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);
        }
Beispiel #4
0
        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));
        }
Beispiel #5
0
        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);
        }
Beispiel #6
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);
        }
Beispiel #7
0
        /// <summary>
        /// Creates a new instance of a smart contract by invoking the contract's constructor
        /// </summary>
        public VmExecutionResult Create(IStateRepository repository,
                                        ISmartContractState contractState,
                                        ExecutionContext executionContext,
                                        byte[] contractCode,
                                        object[] parameters,
                                        string typeName = null)
        {
            // The type and code that will ultimately be executed. Assigned based on which method we use to rewrite contract code.
            string    typeToInstantiate;
            IContract contract;
            Observer  previousObserver = null;

            // Hash the code
            byte[]  codeHash        = HashHelper.Keccak256(contractCode);
            uint256 codeHashUint256 = new uint256(codeHash);

            // Lets see if we already have an assembly
            CachedAssemblyPackage assemblyPackage = this.assemblyCache.Retrieve(codeHashUint256);

            if (assemblyPackage != null)
            {
                // If the assembly is in the cache, keep a reference to its observer around.
                // We might be in a nested execution for the same assembly,
                // in which case we need to restore the previous observer later.
                previousObserver = assemblyPackage.Assembly.GetObserver();

                typeToInstantiate = typeName ?? assemblyPackage.Assembly.DeployedType.Name;

                Type type = assemblyPackage.Assembly.GetType(typeToInstantiate);

                uint160 address = contractState.Message.ContractAddress.ToUint160();
                contract = Contract.CreateUninitialized(type, contractState, address);


                // TODO: Type not found?

                // TODO: Setting observer error?

                // TODO: Error instantiating contract?
            }
            else
            {
                // Create from scratch
                // Validate then rewrite the entirety of the incoming code.
                Result <IContractModuleDefinition> moduleResult = this.moduleDefinitionReader.Read(contractCode);

                if (moduleResult.IsFailure)
                {
                    this.logger.LogDebug(moduleResult.Error);
                    this.logger.LogTrace("(-)[CONTRACT_BYTECODE_INVALID]");
                    return(VmExecutionResult.Fail(VmExecutionErrorKind.LoadFailed,
                                                  "Contract bytecode is not valid IL."));
                }

                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]");
                        return(VmExecutionResult.Fail(VmExecutionErrorKind.ValidationFailed,
                                                      new SmartContractValidationException(validation.Errors).ToString()));
                    }

                    var rewriter = new ObserverInstanceRewriter();

                    if (!this.Rewrite(moduleDefinition, rewriter))
                    {
                        return(VmExecutionResult.Fail(VmExecutionErrorKind.RewriteFailed, "Rewrite module failed"));
                    }

                    Result <ContractByteCode> getCodeResult = this.GetByteCode(moduleDefinition);

                    if (!getCodeResult.IsSuccess)
                    {
                        return(VmExecutionResult.Fail(VmExecutionErrorKind.RewriteFailed, "Serialize module failed"));
                    }

                    // Everything worked. Assign what will get executed.
                    typeToInstantiate = typeName ?? moduleDefinition.ContractType.Name;
                    ContractByteCode code = getCodeResult.Value;

                    Result <IContract> contractLoadResult = this.Load(
                        code,
                        typeToInstantiate,
                        contractState.Message.ContractAddress.ToUint160(),
                        contractState);

                    if (!contractLoadResult.IsSuccess)
                    {
                        return(VmExecutionResult.Fail(VmExecutionErrorKind.LoadFailed, contractLoadResult.Error));
                    }

                    contract = contractLoadResult.Value;

                    assemblyPackage = new CachedAssemblyPackage(new ContractAssembly(contract.Type.Assembly));

                    // Cache this completely validated and rewritten contract to reuse later.
                    this.assemblyCache.Store(codeHashUint256, assemblyPackage);
                }
            }

            this.LogExecutionContext(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);

            // Set Observer and load and execute.
            assemblyPackage.Assembly.SetObserver(executionContext.Observer);

            // Invoke the constructor of the provided contract code
            IContractInvocationResult invocationResult = contract.InvokeConstructor(parameters);

            // Always reset the observer, even if the previous was null.
            assemblyPackage.Assembly.SetObserver(previousObserver);

            if (!invocationResult.IsSuccess)
            {
                this.logger.LogDebug("CREATE_CONTRACT_INSTANTIATION_FAILED");
                return(GetInvocationVmErrorResult(invocationResult));
            }

            this.logger.LogDebug("CREATE_CONTRACT_INSTANTIATION_SUCCEEDED");

            return(VmExecutionResult.Ok(invocationResult.Return, typeToInstantiate));
        }
Beispiel #8
0
        /// <summary>
        /// Invokes a method on an existing smart contract
        /// </summary>
        public VmExecutionResult ExecuteMethod(ISmartContractState contractState, ExecutionContext executionContext,
                                               MethodCall methodCall, byte[] contractCode, string typeName)
        {
            IContract contract;
            Observer  previousObserver = null;

            // Hash the code
            byte[]  codeHash        = HashHelper.Keccak256(contractCode);
            uint256 codeHashUint256 = new uint256(codeHash);

            // Lets see if we already have an assembly
            CachedAssemblyPackage assemblyPackage = this.assemblyCache.Retrieve(codeHashUint256);

            if (assemblyPackage != null)
            {
                // If the assembly is in the cache, keep a reference to its observer around.
                // We might be in a nested execution for the same assembly,
                // in which case we need to restore the previous observer later.
                previousObserver = assemblyPackage.Assembly.GetObserver();

                Type type = assemblyPackage.Assembly.GetType(typeName);

                uint160 address = contractState.Message.ContractAddress.ToUint160();
                contract = Contract.CreateUninitialized(type, contractState, address);
            }
            else
            {
                // Rewrite from scratch.
                using (IContractModuleDefinition moduleDefinition = this.moduleDefinitionReader.Read(contractCode).Value)
                {
                    var rewriter = new ObserverInstanceRewriter();

                    if (!this.Rewrite(moduleDefinition, rewriter))
                    {
                        return(VmExecutionResult.Fail(VmExecutionErrorKind.RewriteFailed, "Rewrite module failed"));
                    }

                    Result <ContractByteCode> getCodeResult = this.GetByteCode(moduleDefinition);

                    if (!getCodeResult.IsSuccess)
                    {
                        return(VmExecutionResult.Fail(VmExecutionErrorKind.RewriteFailed, "Serialize module failed"));
                    }

                    // Everything worked. Assign the code that will be executed.
                    ContractByteCode code = getCodeResult.Value;

                    // Creating a new observer instance here is necessary due to nesting.
                    // If a nested call takes place it will use a new gas meter instance,
                    // due to the fact that the nested call's gas limit may be specified by the user.
                    // Because of that we can't reuse the same observer for a single execution.

                    Result <IContract> contractLoadResult = this.Load(
                        code,
                        typeName,
                        contractState.Message.ContractAddress.ToUint160(),
                        contractState);

                    if (!contractLoadResult.IsSuccess)
                    {
                        return(VmExecutionResult.Fail(VmExecutionErrorKind.LoadFailed, contractLoadResult.Error));
                    }

                    contract = contractLoadResult.Value;

                    assemblyPackage = new CachedAssemblyPackage(new ContractAssembly(contract.Type.Assembly));

                    // Cache this completely validated and rewritten contract to reuse later.
                    this.assemblyCache.Store(codeHashUint256, assemblyPackage);
                }
            }

            this.LogExecutionContext(contract.State.Block, contract.State.Message, contract.Address);

            // Set new Observer and load and execute.
            assemblyPackage.Assembly.SetObserver(executionContext.Observer);

            IContractInvocationResult invocationResult = contract.Invoke(methodCall);

            // Always reset the observer, even if the previous was null.
            assemblyPackage.Assembly.SetObserver(previousObserver);

            if (!invocationResult.IsSuccess)
            {
                this.logger.LogTrace("(-)[CALLCONTRACT_INSTANTIATION_FAILED]");

                return(GetInvocationVmErrorResult(invocationResult));
            }

            this.logger.LogDebug("CALL_CONTRACT_INSTANTIATION_SUCCEEDED");

            return(VmExecutionResult.Ok(invocationResult.Return, typeName));
        }