private void EvaluateActions(Block <T> block) { HashDigest <SHA256>?prevHash = block.PreviousHash; var states = new AddressStateMap(); foreach (Transaction <T> tx in block.Transactions) { foreach (T action in tx.Actions) { IEnumerable <Address> requestedAddresses = action.RequestStates(tx.Sender, tx.Recipient); AddressStateMap requested = GetStates( requestedAddresses.Except(states.Keys), prevHash); states = (AddressStateMap)requested.SetItems(states); var prevState = new AddressStateMap( requestedAddresses .Where(states.ContainsKey) .ToImmutableDictionary(a => a, a => states[a])); AddressStateMap changes = action.Execute(tx.Sender, tx.Recipient, prevState); states = (AddressStateMap)states.SetItems(changes); } } Store.SetBlockStates(block.Hash, states); }
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); }
private void EvaluateActions(Block <T> block) { HashDigest <SHA256>?prevHash = block.PreviousHash; var states = new AddressStateMap(); int seed = BitConverter.ToInt32(block.Hash.ToByteArray(), 0); foreach (Transaction <T> tx in block.Transactions) { int txSeed = seed ^ BitConverter.ToInt32(tx.Signature, 0); foreach (T action in tx.Actions) { IEnumerable <Address> requestedAddresses = action.RequestStates(tx.Sender, tx.Recipient); AddressStateMap requested = GetStates( requestedAddresses.Except(states.Keys), prevHash); states = (AddressStateMap)requested.SetItems(states); var prevState = new AddressStateMap( requestedAddresses .Where(states.ContainsKey) .ToImmutableDictionary(a => a, a => states[a])); var context = new ActionContext( @from: tx.Sender, to: tx.Recipient, previousStates: prevState, randomSeed: unchecked (txSeed++) ); AddressStateMap changes = action.Execute(context); states = (AddressStateMap)states.SetItems(changes); } } Store.SetBlockStates(_id.ToString(), block.Hash, states); }
/// <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); }
/// <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); }
/// <summary> /// Gets the state of the given <paramref name="address"/> in the /// <see cref="BlockChain{T}"/> from <paramref name="offset"/>. /// </summary> /// <param name="address">An <see cref="Address"/> to get /// the states of.</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="address"/>.</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 GetState( Address address, 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]; Tuple <HashDigest <SHA256>, long> stateReference; _rwlock.EnterReadLock(); try { stateReference = Store.LookupStateReference(Id, address, block); } finally { _rwlock.ExitReadLock(); } if (stateReference is null) { return(states); } HashDigest <SHA256> hashValue = stateReference.Item1; 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 => GetState( 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 => address.Equals(kv.Key)) ); return(states); }
/// <summary> /// Gets the state of the given <paramref name="address"/> in the /// <see cref="BlockChain{T}"/> from <paramref name="offset"/>. /// </summary> /// <param name="address">An <see cref="Address"/> to get /// the states of.</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="address"/>.</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 GetState( Address address, 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 = this[offset.Value]; Tuple <HashDigest <SHA256>, long> stateReference; _rwlock.EnterReadLock(); try { stateReference = Store.LookupStateReference(Id, address, block); } finally { _rwlock.ExitReadLock(); } if (stateReference is null) { return(states); } HashDigest <SHA256> hashValue = stateReference.Item1; AddressStateMap blockStates = Store.GetBlockStates(hashValue); if (blockStates is null) { if (completeStates) { // Calculates and fills the incomplete states // on the fly. foreach (HashDigest <SHA256> hash in BlockHashes) { Block <T> b = this[hash]; if (!(Store.GetBlockStates(b.Hash) is null)) { continue; } IReadOnlyList <ActionEvaluation> evaluations = EvaluateActions(b); _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 => address.Equals(kv.Key)) ); return(states); }