public Task <ByteString> GetStateAsync(Address contractAddress, string key, long blockHeight, Hash blockHash)
        {
            var address = contractAddress.GetFormatted();

            if (!key.StartsWith(address))
            {
                throw new InvalidOperationException("a contract cannot access other contracts data");
            }

            return(_blockchainStateManager.GetStateAsync(key, blockHeight,
                                                         blockHash));
        }
        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);
        }