Esempio n. 1
0
        public AddressStateMap GetStates(
            IEnumerable <Address> addresses, HashDigest <SHA256>?offset = null)
        {
            if (offset == null)
            {
                offset = Store.IndexBlockHash(-1);
            }

            var states = new AddressStateMap();

            while (offset != null)
            {
                states = (AddressStateMap)states.SetItems(
                    Store.GetBlockStates(offset.Value)
                    .Where(
                        kv => addresses.Contains(kv.Key) &&
                        !states.ContainsKey(kv.Key))
                    );
                if (states.Keys.SequenceEqual(addresses))
                {
                    break;
                }

                offset = Blocks[offset.Value].PreviousHash;
            }

            return(states);
        }
        public void GetStatesReturnsEarlyForNonexistentAccount()
        {
            var tracker = new StoreTracker(_fx.Store);
            var chain   = new BlockChain <DumbAction>(
                new NullPolicy <DumbAction>(),
                tracker
                );

            Block <DumbAction> b = TestUtils.MineGenesis <DumbAction>();

            chain.Append(b);
            for (int i = 0; i < 20; ++i)
            {
                b = TestUtils.MineNext(b);
                chain.Append(b);
            }

            tracker.ClearLogs();
            Address         nonexistent = new PrivateKey().PublicKey.ToAddress();
            AddressStateMap result      = chain.GetStates(new[] { nonexistent });

            Assert.False(result.ContainsKey(nonexistent));
            var callCount = tracker.Logs.Where(
                triple => triple.Item1.Equals("GetBlockStates")
                ).Select(triple => triple.Item2).Count();

            Assert.True(
                callCount <= 1,
                $"GetBlocksStates() was called {callCount} times"
                );
        }
        public void GetStatesOnlyDrillsDownUntilRequestedAddressesAreFound()
        {
            var tracker = new StoreTracker(_fx.Store);
            var chain   = new BlockChain <DumbAction>(
                new NullPolicy <DumbAction>(),
                tracker
                );

            Block <DumbAction> b = TestUtils.MineGenesis <DumbAction>();

            chain.Append(b);
            Address[] addresses = new Address[30];
            for (int i = 0; i < addresses.Length; ++i)
            {
                var     privateKey = new PrivateKey();
                Address address    = privateKey.PublicKey.ToAddress();
                addresses[i] = address;
                DumbAction[] actions =
                {
                    new DumbAction(address,                            "foo"),
                    new DumbAction(i < 1 ? address : addresses[i - 1], "bar"),
                };
                Transaction <DumbAction>[] txs =
                {
                    Transaction <DumbAction> .Create(privateKey, actions),
                };
                b = TestUtils.MineNext(b, txs);
                chain.Append(b);
            }

            tracker.ClearLogs();
            int testingDepth = addresses.Length / 2;

            Address[] targetAddresses = Enumerable.Range(
                testingDepth,
                Math.Min(10, addresses.Length - testingDepth - 1)
                ).Select(i => addresses[i]).ToArray();
            AddressStateMap result       = chain.GetStates(targetAddresses);
            string          resultString = string.Join(", ", result.Keys);
            string          targetString = string.Join(", ", targetAddresses);
            string          message      =
                $"The result dictionary ({resultString}) does not " +
                $"cover all requested addresses ({targetString}).";

            foreach (Address targetAddress in targetAddresses)
            {
                Assert.True(result.ContainsKey(targetAddress), message);
            }

            var callCount = tracker.Logs.Where(
                triple => triple.Item1.Equals("GetBlockStates")
                ).Select(triple => triple.Item2).Count();

            Assert.True(testingDepth >= callCount);
        }
Esempio n. 4
0
        /// <summary>
        /// Gets the state of the given <paramref name="addresses"/> in the
        /// <see cref="BlockChain{T}"/> from <paramref name="offset"/>.
        /// </summary>
        /// <param name="addresses">The list of <see cref="Address"/>es to get
        /// their states.</param>
        /// <param name="offset">The <see cref="HashDigest{T}"/> of the block to
        /// start finding the state. It will be The tip of the
        /// <see cref="BlockChain{T}"/> if it is <c>null</c>.</param>
        /// <returns>The <see cref="AddressStateMap"/> of given
        /// <paramref name="addresses"/>.</returns>
        public AddressStateMap GetStates(
            IEnumerable <Address> addresses, HashDigest <SHA256>?offset = null)
        {
            _rwlock.EnterReadLock();
            try
            {
                if (offset == null)
                {
                    offset = Store.IndexBlockHash(Id.ToString(), -1);
                }
            }
            finally
            {
                _rwlock.ExitReadLock();
            }

            var states = new AddressStateMap();

            if (offset == null)
            {
                return(states);
            }

            Block <T> block = Blocks[offset.Value];

            ImmutableHashSet <Address> requestedAddresses =
                addresses.ToImmutableHashSet();
            var hashValues = new HashSet <HashDigest <SHA256> >();

            foreach (var address in requestedAddresses)
            {
                var hashDigest = Store.LookupStateReference(
                    Id.ToString(), address, block);
                if (!(hashDigest is null))
                {
                    hashValues.Add(hashDigest.Value);
                }
            }

            foreach (var hashValue in hashValues)
            {
                states = (AddressStateMap)states.SetItems(
                    Store.GetBlockStates(hashValue)
                    .Where(
                        kv => requestedAddresses.Contains(kv.Key) &&
                        !states.ContainsKey(kv.Key)));
            }

            return(states);
        }
Esempio n. 5
0
        /// <summary>
        /// Gets the state of the given <paramref name="addresses"/> in the
        /// <see cref="BlockChain{T}"/> from <paramref name="offset"/>.
        /// </summary>
        /// <param name="addresses">The list of <see cref="Address"/>es to get
        /// their states.</param>
        /// <param name="offset">The <see cref="HashDigest{T}"/> of the block to
        /// start finding the state. It will be The tip of the
        /// <see cref="BlockChain{T}"/> if it is <c>null</c>.</param>
        /// <param name="completeStates">When the <see cref="BlockChain{T}"/>
        /// instance does not contain states dirty of the block which lastly
        /// updated states of a requested address, this option makes
        /// the incomplete states calculated and filled on the fly.
        /// If this option is turned off (which is default) this method throws
        /// <see cref="IncompleteBlockStatesException"/> instead
        /// for the same situation.
        /// Just-in-time calculation of states could take a long time so that
        /// the overall latency of an application may rise.</param>
        /// <returns>The <see cref="AddressStateMap"/> of given
        /// <paramref name="addresses"/>.</returns>
        /// <exception cref="IncompleteBlockStatesException">Thrown when
        /// the <see cref="BlockChain{T}"/> instance does not contain
        /// states dirty of the block which lastly updated states of a requested
        /// address, because actions in the block have never been executed.
        /// If <paramref name="completeStates"/> option is turned on
        /// this exception is not thrown and incomplete states are calculated
        /// and filled on the fly instead.
        /// </exception>
        public AddressStateMap GetStates(
            IEnumerable <Address> addresses,
            HashDigest <SHA256>?offset = null,
            bool completeStates        = false
            )
        {
            _rwlock.EnterReadLock();
            try
            {
                if (offset == null)
                {
                    offset = Store.IndexBlockHash(Id, -1);
                }
            }
            finally
            {
                _rwlock.ExitReadLock();
            }

            var states = new AddressStateMap();

            if (offset == null)
            {
                return(states);
            }

            Block <T> block = Blocks[offset.Value];

            ImmutableHashSet <Address> requestedAddresses =
                addresses.ToImmutableHashSet();
            var stateReferences = new HashSet <Tuple <HashDigest <SHA256>, long> >();

            foreach (var address in requestedAddresses)
            {
                Tuple <HashDigest <SHA256>, long> sr;
                _rwlock.EnterReadLock();
                try
                {
                    sr = Store.LookupStateReference(Id, address, block);
                }
                finally
                {
                    _rwlock.ExitReadLock();
                }

                if (!(sr is null))
                {
                    stateReferences.Add(sr);
                }
            }

            IEnumerable <HashDigest <SHA256> > hashValues = stateReferences
                                                            .OrderByDescending(sr => sr.Item2)
                                                            .Select(sr => sr.Item1);

            foreach (var hashValue in hashValues)
            {
                AddressStateMap blockStates = Store.GetBlockStates(hashValue);
                if (blockStates is null)
                {
                    if (completeStates)
                    {
                        // Calculates and fills the incomplete states
                        // on the fly.
                        foreach (Block <T> b in this)
                        {
                            if (!(Store.GetBlockStates(b.Hash) is null))
                            {
                                continue;
                            }

                            List <ActionEvaluation> evaluations =
                                b.Evaluate(
                                    DateTimeOffset.UtcNow,
                                    a => GetStates(
                                        new[] { a },
                                        b.PreviousHash
                                        ).GetValueOrDefault(a)
                                    ).ToList();

                            if (Policy.BlockAction is IAction)
                            {
                                evaluations.Add(EvaluateBlockAction(b, evaluations));
                            }

                            _rwlock.EnterWriteLock();

                            try
                            {
                                SetStates(b, evaluations, buildStateReferences: false);
                            }
                            finally
                            {
                                _rwlock.ExitWriteLock();
                            }
                        }

                        blockStates = Store.GetBlockStates(hashValue);
                        if (blockStates is null)
                        {
                            throw new NullReferenceException();
                        }
                    }
                    else
                    {
                        throw new IncompleteBlockStatesException(hashValue);
                    }
                }

                states = (AddressStateMap)states.SetItems(
                    blockStates.Where(kv =>
                                      requestedAddresses.Contains(kv.Key) &&
                                      !states.ContainsKey(kv.Key)
                                      )
                    );
            }

            return(states);
        }