Example #1
0
        public void Spend(Coin c)
        {
            UnspentOutputs coin = this.pendingCoins.FirstOrDefault(u => u.TransactionId == c.Outpoint.Hash);

            if (coin == null)
            {
                FetchCoinsResponse result = this.coinView.FetchCoins(new[] { c.Outpoint.Hash });
                if (result.BlockHash != this.hash)
                {
                    throw new InvalidOperationException("Unexepected hash");
                }
                if (result.UnspentOutputs[0] == null)
                {
                    throw new InvalidOperationException("Coin unavailable");
                }

                if (!result.UnspentOutputs[0].Spend(c.Outpoint.N))
                {
                    throw new InvalidOperationException("Coin unspendable");
                }
                this.pendingCoins.Add(result.UnspentOutputs[0]);
            }
            else
            {
                if (!coin.Spend(c.Outpoint.N))
                {
                    throw new InvalidOperationException("Coin unspendable");
                }
            }
        }
        public async Task TestRewindAsync()
        {
            uint256 tip = await this.cachedCoinView.GetTipHashAsync();

            Assert.Equal(this.concurrentChain.Genesis.HashBlock, tip);

            int currentHeight = 0;

            // Create a lot of new coins.
            List <UnspentOutputs> outputsList = this.CreateOutputsList(currentHeight + 1, 100);

            await this.SaveChangesAsync(outputsList, new List <TxOut[]>(), currentHeight + 1);

            currentHeight++;

            await this.cachedCoinView.FlushAsync(true);

            uint256 tipAfterOriginalCoinsCreation = await this.cachedCoinView.GetTipHashAsync();

            // Collection that will be used as a coinview that we will update in parallel. Needed to verify that actual coinview is ok.
            List <OutPoint> outPoints = this.ConvertToListOfOutputPoints(outputsList);

            // Copy of current state to later rewind and verify against it.
            List <OutPoint> copyOfOriginalOutPoints = new List <OutPoint>(outPoints);

            List <OutPoint> copyAfterHalfOfAdditions = new List <OutPoint>();
            uint256         coinviewTipAfterHalf     = null;

            int addChangesTimes = 500;

            // Spend some coins in the next N saves.
            for (int i = 0; i < addChangesTimes; ++i)
            {
                uint256         txId     = outPoints[this.random.Next(0, outPoints.Count)].Hash;
                List <OutPoint> txPoints = outPoints.Where(x => x.Hash == txId).ToList();
                this.Shuffle(txPoints);
                List <OutPoint> txPointsToSpend = txPoints.Take(txPoints.Count / 2).ToList();

                // First spend in cached coinview
                FetchCoinsResponse response = await this.cachedCoinView.FetchCoinsAsync(new[] { txId });

                Assert.Single(response.UnspentOutputs);

                UnspentOutputs coins          = response.UnspentOutputs[0];
                UnspentOutputs unchangedClone = coins.Clone();

                foreach (OutPoint outPointToSpend in txPointsToSpend)
                {
                    coins.Spend(outPointToSpend.N);
                }

                // Spend from outPoints.
                outPoints.RemoveAll(x => txPointsToSpend.Contains(x));

                // Save coinview
                await this.SaveChangesAsync(new List <UnspentOutputs>() { coins }, new List <TxOut[]>() { unchangedClone.Outputs }, currentHeight + 1);

                currentHeight++;

                if (i == addChangesTimes / 2)
                {
                    copyAfterHalfOfAdditions = new List <OutPoint>(outPoints);
                    coinviewTipAfterHalf     = await this.cachedCoinView.GetTipHashAsync();
                }
            }

            await this.ValidateCoinviewIntegrityAsync(outPoints);

            for (int i = 0; i < addChangesTimes; i++)
            {
                await this.cachedCoinView.RewindAsync();

                uint256 currentTip = await this.cachedCoinView.GetTipHashAsync();

                if (currentTip == coinviewTipAfterHalf)
                {
                    await this.ValidateCoinviewIntegrityAsync(copyAfterHalfOfAdditions);
                }
            }

            Assert.Equal(tipAfterOriginalCoinsCreation, await this.cachedCoinView.GetTipHashAsync());

            await this.ValidateCoinviewIntegrityAsync(copyOfOriginalOutPoints);
        }