public void SmartContractList_ListHasValueTest()
        {
            var listName = "testList";

            var source = new MemoryDictionarySource();
            var root   = new ContractStateRoot(source);

            IContractState       state = root.StartTracking();
            IPersistenceStrategy persistenceStrategy = new PersistenceStrategy(state);

            var persistentState = new PersistentState(
                persistenceStrategy,
                this.contractPrimitiveSerializer,
                uint160.One);

            var list = new SmartContractList <string>(persistentState, listName);

            Assert.Equal((uint)0, list.Count);

            var testItem = "Test";

            list.Add(testItem);
            var item1 = list.GetValue(0);

            Assert.Equal(testItem, item1);
        }
        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));
        }
 /// <summary>
 /// Initialize a smart contract executor for the block assembler or consensus validator.
 /// <para>
 /// After the contract has been executed, it will process any fees and/or refunds.
 /// </para>
 /// </summary>
 public ISmartContractExecutor CreateExecutor(
     IContractState stateRepository,
     ISmartContractTransactionContext transactionContext)
 {
     return(new Executor(this.loggerFactory, this.contractPrimitiveSerializer, this.serializer,
                         stateRepository, this.refundProcessor, this.transferProcessor, this.vm));
 }
        public void SmartContractList_CanAccessValueUsingIndexTest()
        {
            var listName = "testList";

            var source = new MemoryDictionarySource();
            var root   = new ContractStateRoot(source);

            IContractState       state = root.StartTracking();
            IPersistenceStrategy persistenceStrategy = new PersistenceStrategy(state);
            var persistentState = new PersistentState(
                persistenceStrategy,
                this.contractPrimitiveSerializer,
                uint160.One);

            var list = new SmartContractList <string>(persistentState, listName);

            Assert.Equal((uint)0, list.Count);

            // Set a value in the list
            list.Add("Test");
            list.Add("Test2");

            var testItem  = list.GetValue(0);
            var testItem2 = list.GetValue(1);

            var firstItemHash  = $"{listName}[0]";
            var secondItemHash = $"{listName}[1]";

            var item  = persistentState.GetString(firstItemHash);
            var item2 = persistentState.GetString(secondItemHash);

            Assert.Equal(testItem, item);
            Assert.Equal(testItem2, item2);
        }
示例#5
0
        public MeteredPersistenceStrategy(IContractState stateDb, IGasMeter gasMeter, IKeyEncodingStrategy keyEncodingStrategy)
        {
            Guard.NotNull(stateDb, nameof(stateDb));
            Guard.NotNull(gasMeter, nameof(gasMeter));
            Guard.NotNull(gasMeter, nameof(keyEncodingStrategy));

            this.stateDb             = stateDb;
            this.gasMeter            = gasMeter;
            this.keyEncodingStrategy = keyEncodingStrategy;
        }
        public void Test20DBreeze()
        {
            DBreezeEngine engine = new DBreezeEngine(DbreezeTestLocation);

            using (DBreeze.Transactions.Transaction t = engine.GetTransaction())
            {
                t.RemoveAllKeys(DbreezeTestDb, true);
                t.Commit();
            }
            ISource <byte[], byte[]> stateDB    = new NoDeleteSource <byte[], byte[]>(new DBreezeByteStore(engine, DbreezeTestDb));
            ContractStateRoot        repository = new ContractStateRoot(stateDB);

            byte[] root = repository.Root;

            uint160 cowAddress   = new uint160(cow);
            uint160 horseAddress = new uint160(horse);

            IContractState track2 = repository.StartTracking(); //repository

            track2.SetStorageValue(cowAddress, cowKey1, cowVal1);
            track2.SetStorageValue(horseAddress, horseKey1, horseVal1);
            track2.Commit();
            repository.Commit();

            byte[] root2 = repository.Root;

            track2 = repository.StartTracking(); //repository
            track2.SetStorageValue(cowAddress, cowKey2, cowVal0);
            track2.SetStorageValue(horseAddress, horseKey2, horseVal0);
            track2.Commit();
            repository.Commit();

            byte[] root3 = repository.Root;

            IContractState snapshot = new ContractStateRoot(stateDB, root);

            Assert.Null(snapshot.GetStorageValue(cowAddress, cowKey1));
            Assert.Null(snapshot.GetStorageValue(cowAddress, cowKey2));
            Assert.Null(snapshot.GetStorageValue(horseAddress, horseKey1));
            Assert.Null(snapshot.GetStorageValue(horseAddress, horseKey2));

            snapshot = new ContractStateRoot(stateDB, root2);
            Assert.Equal(cowVal1, snapshot.GetStorageValue(cowAddress, cowKey1));
            Assert.Null(snapshot.GetStorageValue(cowAddress, cowKey2));
            Assert.Equal(horseVal1, snapshot.GetStorageValue(horseAddress, horseKey1));
            Assert.Null(snapshot.GetStorageValue(horseAddress, horseKey2));

            snapshot = new ContractStateRoot(stateDB, root3);
            Assert.Equal(cowVal1, snapshot.GetStorageValue(cowAddress, cowKey1));
            Assert.Equal(cowVal0, snapshot.GetStorageValue(cowAddress, cowKey2));
            Assert.Equal(horseVal1, snapshot.GetStorageValue(horseAddress, horseKey1));
            Assert.Equal(horseVal0, snapshot.GetStorageValue(horseAddress, horseKey2));
        }
示例#7
0
 public TransactionCondenser(uint160 contractAddress, ILoggerFactory loggerFactory, IReadOnlyList <TransferInfo> transfers, IContractState stateRepository, Network network, ISmartContractTransactionContext transactionContext)
 {
     this.contractAddress    = contractAddress;
     this.logger             = loggerFactory.CreateLogger(this.GetType().Name);
     this.network            = network;
     this.transactionContext = transactionContext;
     this.stateRepository    = stateRepository;
     this.transfers          = transfers;
     this.nVouts             = new Dictionary <uint160, uint>();
     this.txBalances         = new Dictionary <uint160, ulong>();
     this.unspents           = new List <ContractUnspentOutput>();
 }
        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));
        }
        public void Test4()
        {
            MemoryDictionarySource source = new MemoryDictionarySource();
            ContractStateRoot      root   = new ContractStateRoot(source);

            IContractState repository = root.StartTracking();

            repository.SetStorageValue(new uint160(cow), cowKey, cowValue);
            repository.SetStorageValue(new uint160(horse), horseKey, horseValue);
            repository.Commit();

            Assert.Equal(cowValue, root.GetStorageValue(new uint160(cow), cowKey));
            Assert.Equal(horseValue, root.GetStorageValue(new uint160(horse), horseKey));
        }
示例#10
0
        /// <summary>
        /// Reverts the state to a previous snapshot.
        /// </summary>
        private void Rollback(StateSnapshot snapshot)
        {
            this.intermediateState = snapshot.ContractState;

            // Reset the nonce
            this.Nonce = snapshot.Nonce;

            // Rollback internal transfers
            this.internalTransfers.Clear();
            this.internalTransfers.AddRange(snapshot.InternalTransfers);

            // Rollback logs
            this.LogHolder.Clear();
            this.LogHolder.AddRawLogs(snapshot.Logs);
        }
示例#11
0
        private StateTransitionResult ApplyCall(CallMessage message, byte[] contractCode)
        {
            if (this.GasRemaining < message.GasLimit || this.GasRemaining < GasPriceList.BaseCost)
            {
                return(StateTransitionResult.Fail((Gas)0, StateTransitionErrorKind.InsufficientGas));
            }

            var gasMeter = new GasMeter(message.GasLimit);

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

            if (message.Method.Name == null)
            {
                return(StateTransitionResult.Fail(gasMeter.GasConsumed, StateTransitionErrorKind.NoMethodName));
            }

            StateSnapshot stateSnapshot = this.TakeSnapshot();

            IContractState state = this.CreateIntermediateState();

            string type = state.GetContractType(message.To);

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

            VmExecutionResult result = this.Vm.ExecuteMethod(smartContractState, message.Method, contractCode, type);

            this.GasRemaining -= gasMeter.GasConsumed;

            bool revert = result.ExecutionException != null;

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

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

            state.Commit();
            this.GasRemaining -= gasMeter.GasConsumed;

            return(StateTransitionResult.Ok(
                       gasMeter.GasConsumed,
                       message.To,
                       result.Result
                       ));
        }
示例#12
0
 public Executor(ILoggerFactory loggerFactory,
                 IContractPrimitiveSerializer contractPrimitiveSerializer,
                 ICallDataSerializer serializer,
                 IContractState stateSnapshot,
                 ISmartContractResultRefundProcessor refundProcessor,
                 ISmartContractResultTransferProcessor transferProcessor,
                 ISmartContractVirtualMachine vm)
 {
     this.logger = loggerFactory.CreateLogger(this.GetType());
     this.contractPrimitiveSerializer = contractPrimitiveSerializer;
     this.stateSnapshot     = stateSnapshot;
     this.refundProcessor   = refundProcessor;
     this.transferProcessor = transferProcessor;
     this.vm         = vm;
     this.serializer = serializer;
 }
        public void Test12()
        {
            ContractStateRoot repository = new ContractStateRoot(new MemoryDictionarySource());
            IContractState    track      = repository.StartTracking();

            track.SetCode(new uint160(cow), cowCode);
            track.SetCode(new uint160(horse), horseCode);

            Assert.Equal(cowCode, track.GetCode(new uint160(cow)));
            Assert.Equal(horseCode, track.GetCode(new uint160(horse)));

            track.Rollback();

            Assert.Null(repository.GetCode(new uint160(cow)));
            Assert.Null(repository.GetCode(new uint160(horse)));
        }
        public void Test20()
        {
            ISource <byte[], byte[]> stateDB    = new NoDeleteSource <byte[], byte[]>(new MemoryDictionarySource());
            ContractStateRoot        repository = new ContractStateRoot(stateDB);

            byte[] root = repository.Root;

            uint160 cowAddress   = new uint160(cow);
            uint160 horseAddress = new uint160(horse);

            IContractState track2 = repository.StartTracking(); //repository

            track2.SetStorageValue(cowAddress, cowKey1, cowVal1);
            track2.SetStorageValue(horseAddress, horseKey1, horseVal1);
            track2.Commit();
            repository.Commit();

            byte[] root2 = repository.Root;

            track2 = repository.StartTracking(); //repository
            track2.SetStorageValue(cowAddress, cowKey2, cowVal0);
            track2.SetStorageValue(horseAddress, horseKey2, horseVal0);
            track2.Commit();
            repository.Commit();

            byte[] root3 = repository.Root;

            IContractState snapshot = new ContractStateRoot(stateDB, root);

            Assert.Null(snapshot.GetStorageValue(cowAddress, cowKey1));
            Assert.Null(snapshot.GetStorageValue(cowAddress, cowKey2));
            Assert.Null(snapshot.GetStorageValue(horseAddress, horseKey1));
            Assert.Null(snapshot.GetStorageValue(horseAddress, horseKey2));

            snapshot = new ContractStateRoot(stateDB, root2);
            Assert.Equal(cowVal1, snapshot.GetStorageValue(cowAddress, cowKey1));
            Assert.Null(snapshot.GetStorageValue(cowAddress, cowKey2));
            Assert.Equal(horseVal1, snapshot.GetStorageValue(horseAddress, horseKey1));
            Assert.Null(snapshot.GetStorageValue(horseAddress, horseKey2));

            snapshot = new ContractStateRoot(stateDB, root3);
            Assert.Equal(cowVal1, snapshot.GetStorageValue(cowAddress, cowKey1));
            Assert.Equal(cowVal0, snapshot.GetStorageValue(cowAddress, cowKey2));
            Assert.Equal(horseVal1, snapshot.GetStorageValue(horseAddress, horseKey1));
            Assert.Equal(horseVal0, snapshot.GetStorageValue(horseAddress, horseKey2));
        }
 public IInternalTransactionExecutor Create(ISmartContractVirtualMachine vm,
                                            IContractLogHolder contractLogHolder,
                                            IContractState stateRepository,
                                            List <TransferInfo> internalTransferList,
                                            ITransactionContext transactionContext)
 {
     return(new InternalTransactionExecutor(
                transactionContext,
                vm,
                contractLogHolder,
                stateRepository,
                internalTransferList,
                this.keyEncodingStrategy,
                this.loggerFactory,
                this.network
                ));
 }
示例#16
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
                       ));
        }
        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 ContractStateRoot(new NoDeleteSource <byte[], byte[]>(new MemoryDictionarySource()));
            IContractState track      = repository.StartTracking();

            var gasMeter = new GasMeter(callData.GasLimit);

            var transactionContext = new TransactionContext(
                txHash: uint256.One,
                blockHeight: 1,
                coinbase: TestAddress.ToUint160(this.network),
                sender: TestAddress.ToUint160(this.network),
                amount: value
                );

            VmExecutionResult result = this.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")));
        }
示例#18
0
 public InternalTransactionExecutor(ITransactionContext transactionContext,
                                    ISmartContractVirtualMachine vm,
                                    IContractLogHolder contractLogHolder,
                                    IContractState contractStateRepository,
                                    List <TransferInfo> internalTransferList,
                                    IKeyEncodingStrategy keyEncodingStrategy,
                                    ILoggerFactory loggerFactory,
                                    Network network)
 {
     this.transactionContext      = transactionContext;
     this.contractLogHolder       = contractLogHolder;
     this.contractStateRepository = contractStateRepository;
     this.internalTransferList    = internalTransferList;
     this.keyEncodingStrategy     = keyEncodingStrategy;
     this.loggerFactory           = loggerFactory;
     this.logger  = loggerFactory.CreateLogger(this.GetType());
     this.network = network;
     this.vm      = vm;
 }
        public ReflectionVirtualMachineTests()
        {
            // Take what's needed for these tests
            var context = new ContractExecutorTestContext();

            this.network       = context.Network;
            this.vm            = context.Vm;
            this.state         = context.State;
            this.contractState = new SmartContractState(
                new Block(1, TestAddress),
                new Message(TestAddress, TestAddress, 0),
                new PersistentState(new PersistenceStrategy(this.state),
                                    context.ContractPrimitiveSerializer, TestAddress.ToUint160(this.network)),
                context.ContractPrimitiveSerializer,
                new GasMeter((Gas)5000000),
                new ContractLogHolder(this.network),
                Mock.Of <IInternalTransactionExecutor>(),
                new InternalHashHelper(),
                () => 1000);
        }
示例#20
0
        public ObserverTests()
        {
            var context = new ContractExecutorTestContext();

            this.repository     = context.State;
            this.network        = context.Network;
            this.moduleReader   = new ContractModuleDefinitionReader();
            this.assemblyLoader = new ContractAssemblyLoader();
            this.gasMeter       = new GasMeter((Gas)5000000);

            var block = new TestBlock
            {
                Coinbase = TestAddress,
                Number   = 1
            };
            var message = new TestMessage
            {
                ContractAddress = TestAddress,
                GasLimit        = (Gas)GasLimit,
                Sender          = TestAddress,
                Value           = Value
            };
            var getBalance      = new Func <ulong>(() => Balance);
            var persistentState = new TestPersistentState();
            var network         = new SmartContractsRegTest();
            var serializer      = new ContractPrimitiveSerializer(network);

            this.state = new SmartContractState(
                new Stratis.SmartContracts.Core.Block(1, TestAddress),
                new Message(TestAddress, TestAddress, 0),
                new PersistentState(new MeteredPersistenceStrategy(this.repository, this.gasMeter, new BasicKeyEncodingStrategy()),
                                    context.ContractPrimitiveSerializer, TestAddress.ToUint160(this.network)),
                context.ContractPrimitiveSerializer,
                this.gasMeter,
                new ContractLogHolder(this.network),
                Mock.Of <IInternalTransactionExecutor>(),
                new InternalHashHelper(),
                () => 1000);

            this.rewriter = new ObserverRewriter(new Observer(this.gasMeter));
        }
        /// <inheritdoc />
        public Transaction Process(IContractState stateSnapshot,
                                   uint160 contractAddress,
                                   ISmartContractTransactionContext transactionContext,
                                   IList <TransferInfo> internalTransfers,
                                   bool reversionRequired)
        {
            if (reversionRequired)
            {
                // Send back funds
                if (transactionContext.TxOutValue > 0)
                {
                    return(CreateRefundTransaction(transactionContext));
                }
            }

            // If contract received no funds and made no transfers, do nothing.
            if (transactionContext.TxOutValue == 0 && !internalTransfers.Any(x => x.Value > 0)) // TODO: In future discern whether we should even record internal transfers of 0.
            {
                return(null);
            }

            // If contract had no balance, received funds, but made no transfers, assign the current UTXO.
            if (stateSnapshot.GetUnspent(contractAddress) == null && transactionContext.TxOutValue > 0 && !internalTransfers.Any())
            {
                stateSnapshot.SetUnspent(contractAddress, new ContractUnspentOutput
                {
                    Value = transactionContext.TxOutValue,
                    Hash  = transactionContext.TransactionHash,
                    Nvout = transactionContext.Nvout
                });

                return(null);
            }

            // All other cases we need a condensing transaction
            var transactionCondenser = new TransactionCondenser(contractAddress, this.loggerFactory, internalTransfers, stateSnapshot, this.network, transactionContext);

            return(transactionCondenser.CreateCondensingTransaction());
        }
        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 ContractStateRoot(new NoDeleteSource <byte[], byte[]>(new MemoryDictionarySource()));
            IContractState track      = repository.StartTracking();

            var gasMeter = new GasMeter(callData.GasLimit);

            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 = this.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 SmartContractList_ListEnumerationTest()
        {
            var listName = "testList";

            var source = new MemoryDictionarySource();
            var root   = new ContractStateRoot(source);

            IContractState       state = root.StartTracking();
            IPersistenceStrategy persistenceStrategy = new PersistenceStrategy(state);
            var persistentState = new PersistentState(
                persistenceStrategy,
                this.contractPrimitiveSerializer,
                uint160.One);

            var list = new SmartContractList <string>(persistentState, listName);

            Assert.Equal((uint)0, list.Count);

            var items = new List <string>
            {
                "this is a test",
                "we should try to find a way",
                "to paramaterize our tests"
            };

            foreach (var item in items)
            {
                // No AddRange
                list.Add(item);
            }

            Assert.Equal((uint)items.Count, list.Count);

            for (var i = 0; i < list.Count; i++)
            {
                Assert.Equal(items[i], list.GetValue((uint)i));
            }
        }
        /// <summary>
        /// Sets up the state object for the contract execution
        /// </summary>
        private ISmartContractState SetupState(
            IContractLogHolder contractLogger,
            List <TransferInfo> internalTransferList,
            IGasMeter gasMeter,
            IContractState repository,
            ITransactionContext transactionContext,
            uint160 contractAddress)
        {
            IPersistenceStrategy persistenceStrategy =
                new MeteredPersistenceStrategy(repository, gasMeter, new BasicKeyEncodingStrategy());

            var persistentState = new PersistentState(persistenceStrategy, this.contractPrimitiveSerializer, contractAddress);

            IInternalTransactionExecutor internalTransactionExecutor = this.internalTransactionExecutorFactory.Create(this, contractLogger, repository, internalTransferList, transactionContext);

            var balanceState = new BalanceState(repository, transactionContext.Amount, internalTransferList);

            var contractState = new SmartContractState(
                new Block(
                    transactionContext.BlockHeight,
                    transactionContext.Coinbase.ToAddress(this.network)
                    ),
                new Message(
                    contractAddress.ToAddress(this.network),
                    transactionContext.From.ToAddress(this.network),
                    transactionContext.Amount
                    ),
                persistentState,
                this.contractPrimitiveSerializer,
                gasMeter,
                contractLogger,
                internalTransactionExecutor,
                new InternalHashHelper(),
                () => balanceState.GetBalance(contractAddress));

            return(contractState);
        }
        /// <inheritdoc />
        public async override Task RunAsync(RuleContext context)
        {
            this.Logger.LogTrace("()");

            this.blockTxsProcessed = new List <Transaction>();
            NBitcoin.Block   block = context.ValidationContext.Block;
            ChainedHeader    index = context.ValidationContext.ChainedHeader;
            DeploymentFlags  flags = context.Flags;
            UnspentOutputSet view  = ((UtxoRuleContext)context).UnspentOutputSet;

            this.Parent.PerformanceCounter.AddProcessedBlocks(1);

            // Start state from previous block's root
            this.ContractCoinviewRule.OriginalStateRoot.SyncToRoot(((SmartContractBlockHeader)context.ConsensusTip.Header).HashStateRoot.ToBytes());
            IContractState trackedState = this.ContractCoinviewRule.OriginalStateRoot.StartTracking();

            this.receipts = new List <Receipt>();

            this.refundCounter = 1;
            long  sigOpsCost  = 0;
            Money fees        = Money.Zero;
            var   checkInputs = new List <Task <bool> >();

            for (int txIndex = 0; txIndex < block.Transactions.Count; txIndex++)
            {
                this.Parent.PerformanceCounter.AddProcessedTransactions(1);
                Transaction tx = block.Transactions[txIndex];
                if (!context.SkipValidation)
                {
                    if (!this.IsProtocolTransaction(tx))
                    {
                        if (!view.HaveInputs(tx))
                        {
                            this.Logger.LogTrace("(-)[BAD_TX_NO_INPUT]");
                            ConsensusErrors.BadTransactionMissingInput.Throw();
                        }

                        var prevheights = new int[tx.Inputs.Count];
                        // Check that transaction is BIP68 final.
                        // BIP68 lock checks (as opposed to nLockTime checks) must
                        // be in ConnectBlock because they require the UTXO set.
                        for (int j = 0; j < tx.Inputs.Count; j++)
                        {
                            prevheights[j] = (int)view.AccessCoins(tx.Inputs[j].PrevOut.Hash).Height;
                        }

                        if (!tx.CheckSequenceLocks(prevheights, index, flags.LockTimeFlags))
                        {
                            this.Logger.LogTrace("(-)[BAD_TX_NON_FINAL]");
                            ConsensusErrors.BadTransactionNonFinal.Throw();
                        }
                    }

                    // GetTransactionSignatureOperationCost counts 3 types of sigops:
                    // * legacy (always),
                    // * p2sh (when P2SH enabled in flags and excludes coinbase),
                    // * witness (when witness enabled in flags and excludes coinbase).
                    sigOpsCost += this.GetTransactionSignatureOperationCost(tx, view, flags);
                    if (sigOpsCost > this.ConsensusOptions.MaxBlockSigopsCost)
                    {
                        ConsensusErrors.BadBlockSigOps.Throw();
                    }

                    if (!this.IsProtocolTransaction(tx))
                    {
                        this.CheckInputs(tx, view, index.Height);
                        fees += view.GetValueIn(tx) - tx.TotalOut;
                        var txData = new PrecomputedTransactionData(tx);
                        for (int inputIndex = 0; inputIndex < tx.Inputs.Count; inputIndex++)
                        {
                            this.Parent.PerformanceCounter.AddProcessedInputs(1);
                            TxIn  input          = tx.Inputs[inputIndex];
                            int   inputIndexCopy = inputIndex;
                            TxOut txout          = view.GetOutputFor(input);
                            var   checkInput     = new Task <bool>(() =>
                            {
                                if (txout.ScriptPubKey.IsSmartContractExec() || txout.ScriptPubKey.IsSmartContractInternalCall())
                                {
                                    return(input.ScriptSig.IsSmartContractSpend());
                                }

                                var checker      = new TransactionChecker(tx, inputIndexCopy, txout.Value, txData);
                                var ctx          = new ScriptEvaluationContext(this.Parent.Network);
                                ctx.ScriptVerify = flags.ScriptFlags;
                                return(ctx.VerifyScript(input.ScriptSig, txout.ScriptPubKey, checker));
                            });

                            checkInput.Start();
                            checkInputs.Add(checkInput);
                        }
                    }
                }

                this.UpdateCoinView(context, tx);

                this.blockTxsProcessed.Add(tx);
            }

            if (!context.SkipValidation)
            {
                this.CheckBlockReward(context, fees, index.Height, block);

                foreach (Task <bool> checkInput in checkInputs)
                {
                    if (await checkInput.ConfigureAwait(false))
                    {
                        continue;
                    }

                    this.Logger.LogTrace("(-)[BAD_TX_SCRIPT]");
                    ConsensusErrors.BadTransactionScriptError.Throw();
                }
            }
            else
            {
                this.Logger.LogTrace("BIP68, SigOp cost, and block reward validation skipped for block at height {0}.", index.Height);
            }

            if (new uint256(this.ContractCoinviewRule.OriginalStateRoot.Root) != ((SmartContractBlockHeader)block.Header).HashStateRoot)
            {
                SmartContractConsensusErrors.UnequalStateRoots.Throw();
            }

            ValidateAndStoreReceipts(((SmartContractBlockHeader)block.Header).ReceiptRoot);

            this.ContractCoinviewRule.OriginalStateRoot.Commit();

            this.Logger.LogTrace("(-)");
        }
示例#26
0
        ///<inheritdoc />
        public ICreateResult Create <T>(ISmartContractState smartContractState,
                                        ulong amountToTransfer,
                                        object[] parameters,
                                        ulong gasLimit = 0)
        {
            // TODO: Expend any neccessary costs.

            ulong gasBudget = (gasLimit != 0) ? gasLimit : DefaultGasLimit;

            // Ensure we have enough gas left to be able to fund the new GasMeter.
            if (smartContractState.GasMeter.GasAvailable < gasBudget)
            {
                throw new InsufficientGasException();
            }

            var nestedGasMeter = new GasMeter((Gas)gasBudget);

            // Check balance.
            EnsureContractHasEnoughBalance(smartContractState, amountToTransfer);

            // Build objects for VM
            byte[] contractCode = this.contractStateRepository.GetCode(smartContractState.Message.ContractAddress.ToUint160(this.network)); // TODO: Fix this when calling from constructor.

            var context = new TransactionContext(
                this.transactionContext.TransactionHash,
                this.transactionContext.BlockHeight,
                this.transactionContext.Coinbase,
                smartContractState.Message.ContractAddress.ToUint160(this.network),
                amountToTransfer,
                this.transactionContext.GetNonceAndIncrement());

            IContractState track = this.contractStateRepository.StartTracking();

            var createData = new CreateData(nestedGasMeter.GasLimit, contractCode, parameters);

            // Do create in vm
            VmExecutionResult result = this.vm.Create(nestedGasMeter, track, createData, context, typeof(T).Name);

            // Update parent gas meter.
            smartContractState.GasMeter.Spend(nestedGasMeter.GasConsumed);

            var revert = result.ExecutionException != null;

            if (revert)
            {
                this.logger.LogTrace("(-)[CONTRACT_EXECUTION_FAILED]");
                track.Rollback();
                return(CreateResult.Failed());
            }

            this.logger.LogTrace("(-)[CONTRACT_EXECUTION_SUCCEEDED]");
            track.Commit();

            this.internalTransferList.Add(new TransferInfo
            {
                From  = smartContractState.Message.ContractAddress.ToUint160(this.network),
                To    = result.NewContractAddress,
                Value = amountToTransfer
            });

            this.contractLogHolder.AddRawLogs(result.RawLogs);

            return(CreateResult.Succeeded(result.NewContractAddress.ToAddress(this.network)));
        }
        /// <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));
        }
 public PersistenceStrategy(IContractState stateDb)
 {
     this.stateDb = stateDb;
 }
        /// <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()));
        }
        /// <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()));
        }