private async Task <BlockStateSet> FindBlockStateSetWithKeyAsync(string key, long bestChainHeight, Hash blockHash) { var blockStateKey = blockHash.ToStorageKey(); var blockStateSet = await _blockStateSets.GetAsync(blockStateKey); while (blockStateSet != null && blockStateSet.BlockHeight > bestChainHeight) { if (blockStateSet.TryGetValue(key, out _)) { break; } blockStateKey = blockStateSet.PreviousHash?.ToStorageKey(); if (blockStateKey != null) { blockStateSet = await _blockStateSets.GetAsync(blockStateKey); } else { blockStateSet = null; } } return(blockStateSet); }
public async Task MergeBlockStateAsync(ChainStateInfo chainStateInfo, Hash blockStateHash) { var blockState = await _blockStateSets.GetAsync(blockStateHash.ToStorageKey()); if (blockState == null) { if (chainStateInfo.Status == ChainStateMergingStatus.Merged && chainStateInfo.MergingBlockHash == blockStateHash) { chainStateInfo.Status = ChainStateMergingStatus.Common; chainStateInfo.MergingBlockHash = null; await _chainStateInfoCollection.SetAsync(chainStateInfo.ChainId.ToStorageKey(), chainStateInfo); return; } throw new InvalidOperationException($"cannot get block state of {blockStateHash}"); } if (chainStateInfo.BlockHash == null || chainStateInfo.BlockHash == blockState.PreviousHash || (chainStateInfo.Status == ChainStateMergingStatus.Merged && chainStateInfo.MergingBlockHash == blockState.BlockHash)) { chainStateInfo.Status = ChainStateMergingStatus.Merging; chainStateInfo.MergingBlockHash = blockStateHash; await _chainStateInfoCollection.SetAsync(chainStateInfo.ChainId.ToStorageKey(), chainStateInfo); var dic = blockState.Changes.Concat(blockState.BlockExecutedData).Select(change => new VersionedState { Key = change.Key, Value = change.Value, BlockHash = blockState.BlockHash, BlockHeight = blockState.BlockHeight, //OriginBlockHash = origin.BlockHash }).ToDictionary(p => p.Key, p => p); await _versionedStates.SetAllAsync(dic); await _versionedStates.RemoveAllAsync(blockState.Deletes.ToList()); chainStateInfo.Status = ChainStateMergingStatus.Merged; chainStateInfo.BlockHash = blockState.BlockHash; chainStateInfo.BlockHeight = blockState.BlockHeight; await _chainStateInfoCollection.SetAsync(chainStateInfo.ChainId.ToStorageKey(), chainStateInfo); await _blockStateSets.RemoveAsync(blockStateHash.ToStorageKey()); chainStateInfo.Status = ChainStateMergingStatus.Common; chainStateInfo.MergingBlockHash = null; await _chainStateInfoCollection.SetAsync(chainStateInfo.ChainId.ToStorageKey(), chainStateInfo); } else { throw new InvalidOperationException( "cannot merge block not linked, check new block's previous block hash "); } }
/// <summary> /// /// </summary> /// <param name="key"></param> /// <param name="blockHeight"></param> /// <param name="blockHash">should already in store</param> /// <returns></returns> /// <exception cref="ArgumentException"></exception> public async Task <ByteString> GetStateAsync(string key, long blockHeight, Hash blockHash) { ByteString value = null; //first DB read var bestChainState = await _versionedStates.GetAsync(key); if (bestChainState != null) { if (bestChainState.BlockHash == blockHash) { value = bestChainState.Value; } else { if (bestChainState.BlockHeight >= blockHeight) { //because we may clear history state throw new InvalidOperationException($"cannot read history state, best chain state hash: {bestChainState.BlockHash.ToHex()}, key: {key}, block height: {blockHeight}, block hash{blockHash.ToHex()}"); } else { //find value in block state set var blockStateKey = blockHash.ToStorageKey(); var blockStateSet = await _blockStateSets.GetAsync(blockStateKey); while (blockStateSet != null && blockStateSet.BlockHeight > bestChainState.BlockHeight) { if (blockStateSet.Deletes.Contains(key)) { break; } if (blockStateSet.Changes.ContainsKey(key)) { value = blockStateSet.Changes[key]; break; } blockStateKey = blockStateSet.PreviousHash?.ToStorageKey(); if (blockStateKey != null) { blockStateSet = await _blockStateSets.GetAsync(blockStateKey); } else { blockStateSet = null; } } if (value == null && (blockStateSet == null || !blockStateSet.Deletes.Contains(key) || blockStateSet.BlockHeight <= bestChainState.BlockHeight)) { //not found value in block state sets. for example, best chain is 100, blockHeight is 105, //it will find 105 ~ 101 block state set. so the value could only be the best chain state value. // retry versioned state in case conflict of get state during merging bestChainState = await _versionedStates.GetAsync(key); value = bestChainState.Value; } } } } else { //best chain state is null, it will find value in block state set var blockStateKey = blockHash.ToStorageKey(); var blockStateSet = await _blockStateSets.GetAsync(blockStateKey); while (blockStateSet != null) { if (blockStateSet.Deletes.Contains(key)) { break; } if (blockStateSet.Changes.ContainsKey(key)) { value = blockStateSet.Changes[key]; break; } blockStateKey = blockStateSet.PreviousHash?.ToStorageKey(); if (blockStateKey != null) { blockStateSet = await _blockStateSets.GetAsync(blockStateKey); } else { blockStateSet = null; } } if (value == null && blockStateSet == null) { // retry versioned state in case conflict of get state during merging bestChainState = await _versionedStates.GetAsync(key); value = bestChainState?.Value; } } return(value); }