public async Task MergeBlockStateAsync(long lastIrreversibleBlockHeight, Hash lastIrreversibleBlockHash) { var chainStateInfo = await _blockStateSetManger.GetChainStateInfoAsync(); var firstHeightToMerge = chainStateInfo.BlockHeight == 0L ? AElfConstants.GenesisBlockHeight : chainStateInfo.BlockHeight + 1; var mergeCount = lastIrreversibleBlockHeight - firstHeightToMerge; if (mergeCount < 0) { Logger.LogWarning( $"Last merge height: {chainStateInfo.BlockHeight}, lib height: {lastIrreversibleBlockHeight}, needn't merge"); return; } var blockIndexes = new List <IBlockIndex>(); if (chainStateInfo.Status == ChainStateMergingStatus.Merged) { blockIndexes.Add(new BlockIndex(chainStateInfo.MergingBlockHash, -1)); } var reversedBlockIndexes = await _blockchainService.GetReversedBlockIndexes(lastIrreversibleBlockHash, (int)mergeCount); reversedBlockIndexes.Reverse(); blockIndexes.AddRange(reversedBlockIndexes); blockIndexes.Add(new BlockIndex(lastIrreversibleBlockHash, lastIrreversibleBlockHeight)); Logger.LogDebug( $"Start merge lib height: {lastIrreversibleBlockHeight}, lib block hash: {lastIrreversibleBlockHash}, merge count: {blockIndexes.Count}"); foreach (var blockIndex in blockIndexes) { try { Logger.LogTrace($"Merging state {chainStateInfo} for block {blockIndex}"); await _blockStateSetManger.MergeBlockStateAsync(chainStateInfo, blockIndex.BlockHash); } catch (Exception e) { Logger.LogError(e, $"Exception while merge state {chainStateInfo} for block {blockIndex}"); throw; } } }
public async Task AddBlockStateSet_Test() { // one level tests without best chain state await _blockStateSetManger.SetBlockStateSetAsync(new BlockStateSet() { BlockHash = _tv[1].BlockHash, BlockHeight = _tv[1].BlockHeight, PreviousHash = null, Changes = { { _tv[1].Key, _tv[1].Value }, { _tv[2].Key, _tv[2].Value }, } }); var check1 = new Func <Task>(async() => { (await _blockchainStateManager.GetStateAsync(_tv[1].Key, _tv[1].BlockHeight, _tv[1].BlockHash)) .ShouldBe(_tv[1].Value); (await _blockchainStateManager.GetStateAsync(_tv[2].Key, _tv[1].BlockHeight, _tv[1].BlockHash)) .ShouldBe(_tv[2].Value); }); await check1(); //two level tests without best chain state await _blockStateSetManger.SetBlockStateSetAsync(new BlockStateSet() { BlockHash = _tv[2].BlockHash, BlockHeight = _tv[2].BlockHeight, PreviousHash = _tv[1].BlockHash, Changes = { //override key 1 { _tv[1].Key, _tv[2].Value }, } }); var check2 = new Func <Task>(async() => { //key 1 was changed, value was changed to value 2 (await _blockchainStateManager.GetStateAsync(_tv[1].Key, _tv[2].BlockHeight, _tv[2].BlockHash)) .ShouldBe(_tv[2].Value); (await _blockchainStateManager.GetStateAsync(_tv[2].Key, _tv[2].BlockHeight, _tv[2].BlockHash)) .ShouldBe(_tv[2].Value); }); await check2(); //but when we we can get value at the height of block height 1, also block hash 1 await check1(); await _blockStateSetManger.SetBlockStateSetAsync(new BlockStateSet() { BlockHash = _tv[3].BlockHash, BlockHeight = _tv[3].BlockHeight, PreviousHash = _tv[2].BlockHash, Changes = { //override key 2 at height 3 { _tv[2].Key, _tv[4].Value }, } }); var check3_1 = new Func <Task>(async() => { (await _blockchainStateManager.GetStateAsync(_tv[2].Key, _tv[3].BlockHeight, _tv[3].BlockHash)) .ShouldBe(_tv[4].Value); }); await check1(); await check2(); await check3_1(); await _blockStateSetManger.SetBlockStateSetAsync(new BlockStateSet() { BlockHash = _tv[4].BlockHash, BlockHeight = _tv[3].BlockHeight, // it's a branch PreviousHash = _tv[2].BlockHash, Changes = { //override key 2 at height 3 { _tv[2].Key, _tv[5].Value }, } }); var check3_2 = new Func <Task>(async() => { (await _blockchainStateManager.GetStateAsync(_tv[2].Key, _tv[3].BlockHeight, _tv[4].BlockHash)) .ShouldBe(_tv[5].Value); }); await check1(); await check2(); await check3_2(); var chainStateInfo = await _blockStateSetManger.GetChainStateInfoAsync(); await _blockStateSetManger.MergeBlockStateAsync(chainStateInfo, _tv[1].BlockHash); await check1(); await check2(); await check3_1(); await check3_2(); await Assert.ThrowsAsync <InvalidOperationException>(async() => await _blockStateSetManger.MergeBlockStateAsync(chainStateInfo, _tv[3].BlockHash)); await _blockStateSetManger.MergeBlockStateAsync(chainStateInfo, _tv[2].BlockHash); await Assert.ThrowsAsync <InvalidOperationException>(async() => await check1()); await check2(); //test failed merge recover chainStateInfo.Status = ChainStateMergingStatus.Merging; chainStateInfo.MergingBlockHash = _tv[4].BlockHash; //merge best to second branch await _blockStateSetManger.MergeBlockStateAsync(chainStateInfo, _tv[4].BlockHash); await Assert.ThrowsAsync <InvalidOperationException>(async() => await check1()); await Assert.ThrowsAsync <InvalidOperationException>(async() => await check2()); await Assert.ThrowsAsync <InvalidOperationException>(async() => await check3_1()); await check3_2(); //test failed merge recover chainStateInfo.Status = ChainStateMergingStatus.Merged; chainStateInfo.MergingBlockHash = _tv[4].BlockHash; await _blockStateSetManger.MergeBlockStateAsync(chainStateInfo, _tv[4].BlockHash); }