public void Repository_CommitAndRollbackTest()
        {
            ISource <byte[], byte[]> stateDB    = new NoDeleteSource <byte[], byte[]>(new MemoryDictionarySource());
            ContractStateRoot        repository = new ContractStateRoot(stateDB);
            IContractState           txTrack    = repository.StartTracking();

            txTrack.CreateAccount(testAddress);
            txTrack.SetStorageValue(testAddress, dog, cat);
            txTrack.Commit();
            repository.Commit();
            byte[] root1 = repository.Root;

            IContractState txTrack2 = repository.StartTracking();

            txTrack2.SetStorageValue(testAddress, dog, fish);
            txTrack2.Rollback();

            IContractState txTrack3 = repository.StartTracking();

            txTrack3.SetStorageValue(testAddress, dodecahedron, bird);
            txTrack3.Commit();
            repository.Commit();

            byte[] upToDateRoot = repository.Root;

            Assert.Equal(cat, repository.GetStorageValue(testAddress, dog));
            Assert.Equal(bird, repository.GetStorageValue(testAddress, dodecahedron));

            IContractState snapshot = repository.GetSnapshotTo(root1);

            repository.SyncToRoot(root1);
            Assert.Equal(cat, snapshot.GetStorageValue(testAddress, dog));
            Assert.Null(snapshot.GetStorageValue(testAddress, dodecahedron));
        }
        public void Repository_CommitPushesToUnderlyingSource()
        {
            ISource <byte[], byte[]> stateDB    = new NoDeleteSource <byte[], byte[]>(new MemoryDictionarySource());
            ContractStateRoot        repository = new ContractStateRoot(stateDB);
            IContractState           txTrack    = repository.StartTracking();

            txTrack.CreateAccount(testAddress);
            txTrack.SetStorageValue(testAddress, dog, cat);
            Assert.Null(repository.GetStorageValue(testAddress, dog));
            txTrack.Commit();
            Assert.Equal(cat, repository.GetStorageValue(testAddress, dog));
        }
Ejemplo n.º 3
0
        private StateTransitionResult ApplyCreate(object[] parameters, byte[] code, BaseMessage message, string type = null)
        {
            if (this.GasRemaining < message.GasLimit || this.GasRemaining < GasPriceList.BaseCost)
            {
                return(StateTransitionResult.Fail((Gas)0, StateTransitionErrorKind.InsufficientGas));
            }

            StateSnapshot stateSnapshot = this.TakeSnapshot();

            var gasMeter = new GasMeter(message.GasLimit);

            gasMeter.Spend((Gas)GasPriceList.BaseCost);

            uint160 address = this.GetNewAddress();

            // Begin tracking the new intermediate state. We need to keep this reference around
            // for the scope of the transaction, so we can commit it later.
            IContractState state = this.CreateIntermediateState();

            state.CreateAccount(address);

            ISmartContractState smartContractState = this.CreateSmartContractState(gasMeter, address, message, state);

            VmExecutionResult result = this.Vm.Create(state, smartContractState, code, parameters, type);

            this.GasRemaining -= gasMeter.GasConsumed;

            bool revert = result.ExecutionException != null;

            if (revert)
            {
                this.Rollback(stateSnapshot);

                return(StateTransitionResult.Fail(
                           gasMeter.GasConsumed,
                           result.ExecutionException));
            }

            state.Commit();

            return(StateTransitionResult.Ok(
                       gasMeter.GasConsumed,
                       address,
                       result.Result
                       ));
        }
        /// <summary>
        /// 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()));
        }