private void RecordCrossChainData(IEnumerable <int> chainIdList) { var indexedSideChainBlockData = new IndexedSideChainBlockData(); foreach (var chainId in chainIdList) { var pendingProposalExists = TryGetIndexingProposalWithStatus(chainId, CrossChainIndexingProposalStatus.Pending, out var pendingCrossChainIndexingProposal); Assert(pendingProposalExists, "Chain indexing not proposed."); if (chainId == State.ParentChainId.Value) { IndexParentChainBlockData(pendingCrossChainIndexingProposal.ProposedCrossChainBlockData .ParentChainBlockDataList); } else { indexedSideChainBlockData.SideChainBlockDataList.Add(IndexSideChainBlockData( pendingCrossChainIndexingProposal.ProposedCrossChainBlockData.SideChainBlockDataList, pendingCrossChainIndexingProposal.Proposer, chainId)); } SetCrossChainIndexingProposalStatus(pendingCrossChainIndexingProposal, CrossChainIndexingProposalStatus.Accepted); } if (indexedSideChainBlockData.SideChainBlockDataList.Count > 0) { State.IndexedSideChainBlockData.Set(Context.CurrentHeight, indexedSideChainBlockData); Context.Fire(new SideChainBlockDataIndexed()); } }
/// <summary> /// Index side chain block data. /// </summary> /// <param name="sideChainBlockData">Side chain block data to be indexed.</param> /// <returns>Valid side chain block data which are indexed.</returns> private IndexedSideChainBlockData IndexSideChainBlockData(IList <SideChainBlockData> sideChainBlockData) { // only miner can do this. // Api.IsMiner("Not authorized to do this."); var indexedSideChainBlockData = new IndexedSideChainBlockData(); foreach (var blockInfo in sideChainBlockData) { var chainId = blockInfo.ChainId; var info = State.SideChainInfo[chainId]; if (info == null || info.SideChainStatus != SideChainStatus.Active) { continue; } var currentSideChainHeight = State.CurrentSideChainHeight[chainId]; var target = currentSideChainHeight != 0 ? currentSideChainHeight + 1 : Constants.GenesisBlockHeight; long sideChainHeight = blockInfo.Height; if (target != sideChainHeight) { continue; } // indexing fee var indexingPrice = info.SideChainCreationRequest.IndexingPrice; var lockedToken = State.IndexingBalance[chainId]; lockedToken -= indexingPrice; State.IndexingBalance[chainId] = lockedToken; if (lockedToken < indexingPrice) { info.SideChainStatus = SideChainStatus.Terminated; } State.SideChainInfo[chainId] = info; if (indexingPrice > 0) { Transfer(new TransferInput { To = Context.Sender, Symbol = Context.Variables.NativeSymbol, Amount = indexingPrice, Memo = "Index fee." }); } State.CurrentSideChainHeight[chainId] = sideChainHeight; indexedSideChainBlockData.SideChainBlockData.Add(blockInfo); } return(indexedSideChainBlockData); }
public static ByteString ExtractCrossChainExtraDataFromCrossChainBlockData( this IndexedSideChainBlockData indexedSideChainBlockData) { var txRootHashList = indexedSideChainBlockData.SideChainBlockDataList .Select(scb => scb.TransactionStatusMerkleTreeRoot).ToList(); var calculatedSideChainTransactionsRoot = BinaryMerkleTree.FromLeafNodes(txRootHashList).Root; return(new CrossChainExtraData { TransactionStatusMerkleTreeRoot = calculatedSideChainTransactionsRoot } .ToByteString()); }
public static ByteString ExtractCrossChainExtraDataFromCrossChainBlockData(this CrossChainBlockData crossChainBlockData) { if (crossChainBlockData.IsNullOrEmpty() || crossChainBlockData.SideChainBlockDataList.Count == 0) { return(ByteString.Empty); } var indexedSideChainBlockData = new IndexedSideChainBlockData { SideChainBlockDataList = { crossChainBlockData.SideChainBlockDataList } }; return(indexedSideChainBlockData.ExtractCrossChainExtraDataFromCrossChainBlockData()); }
public override void ConfigureServices(ServiceConfigurationContext context) { base.ConfigureServices(context); var dictionary = new Dictionary <long, Hash> { { 1, Hash.FromString("1") }, { 2, Hash.FromString("2") }, { 3, Hash.FromString("3") } }; Configure <GrpcCrossChainConfigOption>(option => { option.ListeningPort = 5001; option.ParentChainServerIp = "127.0.0.1"; option.ParentChainServerPort = 5000; }); Configure <CrossChainConfigOptions>(option => { option.ParentChainId = ChainHelper.ConvertChainIdToBase58(ChainHelper.GetChainId(1)); }); context.Services.AddTransient(provider => { var kernelTestHelper = context.Services.GetRequiredServiceLazy <KernelTestHelper>(); var mockBlockChainService = new Mock <IBlockchainService>(); mockBlockChainService.Setup(m => m.GetChainAsync()).Returns(() => { var chain = new Chain { LastIrreversibleBlockHeight = 10 }; return(Task.FromResult(chain)); }); mockBlockChainService.Setup(m => m.GetBlockHashByHeightAsync(It.IsAny <Chain>(), It.IsAny <long>(), It.IsAny <Hash>())) .Returns <Chain, long, Hash>((chain, height, hash) => { if (height > 0 && height <= 3) { return(Task.FromResult(dictionary[height])); } return(Task.FromResult <Hash>(null)); }); mockBlockChainService.Setup(m => m.GetBlockByHashAsync(It.IsAny <Hash>())).Returns <Hash>(hash => { foreach (var kv in dictionary) { if (kv.Value.Equals(hash)) { var block = kernelTestHelper.Value.GenerateBlock(kv.Key - 1, dictionary[kv.Key - 1]); return(Task.FromResult(block)); } } return(Task.FromResult <Block>(null)); }); return(mockBlockChainService.Object); }); context.Services.AddTransient(provider => { var mockBlockExtraDataService = new Mock <IBlockExtraDataService>(); mockBlockExtraDataService .Setup(m => m.GetExtraDataFromBlockHeader(It.IsAny <string>(), It.IsAny <BlockHeader>())).Returns( () => { var crossExtraData = new CrossChainExtraData() { TransactionStatusMerkleTreeRoot = Hash.FromString("SideChainBlockHeadersRoot"), }; return(ByteString.CopyFrom(crossExtraData.ToByteArray())); }); return(mockBlockExtraDataService.Object); }); context.Services.AddTransient(provider => { var mockCrossChainIndexingDataService = new Mock <ICrossChainIndexingDataService>(); mockCrossChainIndexingDataService .Setup(m => m.GetIndexedCrossChainBlockDataAsync(It.IsAny <Hash>(), It.IsAny <long>())) .Returns(() => { var crossChainBlockData = new CrossChainBlockData { SideChainBlockDataList = { new SideChainBlockData { ChainId = 123, Height = 1, TransactionStatusMerkleTreeRoot = Hash.FromString("fakeTransactionMerkleTree") } } }; return(Task.FromResult(crossChainBlockData)); }); mockCrossChainIndexingDataService .Setup(m => m.GetIndexedSideChainBlockDataAsync(It.IsAny <Hash>(), It.IsAny <long>())).Returns( () => { var indexedSideChainBlockData = new IndexedSideChainBlockData { SideChainBlockDataList = { new SideChainBlockData { ChainId = 123, Height = 1, TransactionStatusMerkleTreeRoot = Hash.FromString("fakeTransactionMerkleTree") } } }; return(Task.FromResult(indexedSideChainBlockData)); }); return(mockCrossChainIndexingDataService.Object); }); context.Services.AddTransient(provider => { var mockCrossChainClientProvider = new Mock <ICrossChainClientProvider>(); mockCrossChainClientProvider.Setup(m => m.CreateCrossChainClient(It.IsAny <CrossChainClientDto>())) .Returns(() => { var mockCrossChainClient = new Mock <ICrossChainClient>(); mockCrossChainClient.Setup(m => m.RequestChainInitializationDataAsync(It.IsAny <int>())).Returns( () => { var chainInitialization = new ChainInitializationData { CreationHeightOnParentChain = 1 }; return(Task.FromResult(chainInitialization)); }); mockCrossChainClient.Setup(m => m.RequestCrossChainDataAsync(It.IsAny <long>(), It.IsAny <Func <IBlockCacheEntity, bool> >())) .Returns(() => { var chainInitialization = new ChainInitializationData { CreationHeightOnParentChain = 1 }; return(Task.FromResult(chainInitialization)); }); return(mockCrossChainClient.Object); }); return(mockCrossChainClientProvider.Object); }); context.Services.AddSingleton <CrossChainPlugin>(); context.Services.AddSingleton <IConsensusExtraDataNameProvider, MockConsensusExtraDataProvider>(); }
public static bool IsNullOrEmpty(this IndexedSideChainBlockData indexedSideChainBlockData) { return(indexedSideChainBlockData == null || indexedSideChainBlockData.SideChainBlockDataList.Count == 0); }
/// <summary> /// Index side chain block data. /// </summary> /// <param name="sideChainBlockDataList">Side chain block data to be indexed.</param> /// <param name="proposer">Charge indexing fee for the one who proposed side chain block data.</param> /// <returns>Valid side chain block data which are indexed.</returns> private IndexedSideChainBlockData IndexSideChainBlockData(IList <SideChainBlockData> sideChainBlockDataList, Address proposer) { var indexedSideChainBlockData = new IndexedSideChainBlockData(); long indexingFeeAmount = 0; var groupResult = sideChainBlockDataList.GroupBy(data => data.ChainId, data => data); var formattedProposerAddress = proposer.ToByteString().ToBase64(); foreach (var group in groupResult) { var chainId = group.Key; var sideChainInfo = State.SideChainInfo[chainId]; if (sideChainInfo == null) { continue; } var currentSideChainHeight = State.CurrentSideChainHeight[chainId]; long arrearsAmount = 0; foreach (var sideChainBlockData in group) { var target = currentSideChainHeight != 0 ? currentSideChainHeight + 1 : Constants.GenesisBlockHeight; var sideChainHeight = sideChainBlockData.Height; if (target != sideChainHeight) { break; } // indexing fee var indexingPrice = sideChainInfo.IndexingPrice; var lockedToken = State.IndexingBalance[chainId]; lockedToken -= indexingPrice; State.IndexingBalance[chainId] = lockedToken; if (lockedToken < 0) { // record arrears arrearsAmount += indexingPrice; } else { indexingFeeAmount += indexingPrice; if (lockedToken < indexingPrice) { sideChainInfo.SideChainStatus = SideChainStatus.InsufficientBalance; } } currentSideChainHeight++; indexedSideChainBlockData.SideChainBlockDataList.Add(sideChainBlockData); } Context.LogDebug(() => $"## [ {State.CurrentSideChainHeight[chainId]} - {currentSideChainHeight} ] from side chain {chainId} indexed by {proposer}, index blocks {currentSideChainHeight - State.CurrentSideChainHeight[chainId] + 1} "); if (arrearsAmount > 0) { if (sideChainInfo.ArrearsInfo.TryGetValue(formattedProposerAddress, out var amount)) { sideChainInfo.ArrearsInfo[formattedProposerAddress] = amount + arrearsAmount; } else { sideChainInfo.ArrearsInfo[formattedProposerAddress] = arrearsAmount; } } State.SideChainInfo[chainId] = sideChainInfo; State.CurrentSideChainHeight[chainId] = currentSideChainHeight; } if (indexingFeeAmount > 0) { Transfer(new TransferInput { To = proposer, Symbol = Context.Variables.NativeSymbol, Amount = indexingFeeAmount, Memo = "Index fee." }); } return(indexedSideChainBlockData); }
private bool ValidateBlockExtraDataAsync(IndexedSideChainBlockData indexedSideChainBlockData, ByteString extraData) { var expected = indexedSideChainBlockData.ExtractCrossChainExtraDataFromCrossChainBlockData(); return(expected.Equals(extraData)); }
public override void ConfigureServices(ServiceConfigurationContext context) { context.Services.AddSingleton <CrossChainTestHelper>(); context.Services.AddTransient(provider => { var mockCrossChainRequestService = new Mock <ICrossChainRequestService>(); mockCrossChainRequestService.Setup(mock => mock.RequestCrossChainDataFromOtherChainsAsync()) .Returns(Task.CompletedTask); return(mockCrossChainRequestService.Object); }); context.Services.AddTransient(provider => { var mockCrossChainIndexingDataService = new Mock <ICrossChainIndexingDataService>(); mockCrossChainIndexingDataService .Setup(m => m.GetIndexedSideChainBlockDataAsync(It.IsAny <Hash>(), It.IsAny <long>())) .Returns <Hash, long>((blockHash, blockHeight) => { var crossChainTestHelper = context.Services.GetRequiredServiceLazy <CrossChainTestHelper>().Value; var crossChainBlockData = crossChainTestHelper.GetIndexedCrossChainExtraData(blockHeight); var indexedSideChainBlockData = new IndexedSideChainBlockData { SideChainBlockDataList = { crossChainBlockData.SideChainBlockDataList }, }; return(Task.FromResult(indexedSideChainBlockData)); }); mockCrossChainIndexingDataService.Setup(m => m.GetCrossChainTransactionInputForNextMiningAsync(It.IsAny <Hash>(), It.IsAny <long>())) .Returns <Hash, long>( (previousHash, height) => { var crossChainTestHelper = context.Services.GetRequiredServiceLazy <CrossChainTestHelper>().Value; return(Task.FromResult(crossChainTestHelper.GetCrossChainBlockData(previousHash))); }); mockCrossChainIndexingDataService.Setup(m => m.PrepareExtraDataForNextMiningAsync(It.IsAny <Hash>(), It.IsAny <long>())) .Returns <Hash, long>( (previousHash, height) => { var crossChainTestHelper = context.Services.GetRequiredServiceLazy <CrossChainTestHelper>().Value; return(Task.FromResult( crossChainTestHelper.GetCrossChainExtraData(previousHash)?.ToByteString() ?? ByteString.Empty)); }); mockCrossChainIndexingDataService.Setup(m => m.GetAllChainIdHeightPairsAtLibAsync()).Returns(() => { var crossChainTestHelper = context.Services.GetRequiredServiceLazy <CrossChainTestHelper>().Value; return(Task.FromResult(crossChainTestHelper.GetAllIndexedCrossChainExtraData())); }); mockCrossChainIndexingDataService .Setup( m => m.CheckExtraDataIsNeededAsync(It.IsAny <Hash>(), It.IsAny <long>(), It.IsAny <Timestamp>())) .Returns <Hash, long, Timestamp>((blockHash, height, timeStamp) => { var crossChainTestHelper = context.Services.GetRequiredServiceLazy <CrossChainTestHelper>().Value; return(Task.FromResult(crossChainTestHelper.GetCrossChainExtraData(blockHash) != null)); }); return(mockCrossChainIndexingDataService.Object); }); context.Services.AddTransient(provider => { var mockService = new Mock <ISmartContractAddressService>(); mockService.Setup(m => m.GetAddressByContractNameAsync(It.IsAny <IChainContext>(), It.IsAny <string>())) .Returns(Task.FromResult(default(Address))); return(mockService.Object); }); context.Services.AddSingleton <ITransactionPackingOptionProvider, MockTransactionPackingOptionProvider>(); }