/// <summary> /// Response to indexing request from main chain node. /// One request to many responses. /// </summary> /// <param name="request"></param> /// <param name="responseStream"></param> /// <param name="context"></param> /// <returns></returns> public override async Task IndexServerStreaming(RequestBlockInfo request, IServerStreamWriter <ResponseSideChainBlockInfo> responseStream, ServerCallContext context) { // TODO: verify the from address and the chain _logger?.Debug("Side Chain Server received IndexedInfo message."); try { var height = request.NextHeight; while (height <= await LightChain.GetCurrentBlockHeightAsync()) { var blockHeader = await LightChain.GetHeaderByHeightAsync(height); var res = new ResponseSideChainBlockInfo { Success = blockHeader != null, BlockInfo = blockHeader == null ? null : new SideChainBlockInfo { Height = height, BlockHeaderHash = blockHeader.GetHash(), TransactionMKRoot = blockHeader.MerkleTreeRootOfTransactions, ChainId = blockHeader.ChainId } }; //_logger?.Log(LogLevel.Debug, $"Side Chain Server responsed IndexedInfo message of height {height}"); await responseStream.WriteAsync(res); height++; } } catch (Exception e) { _logger?.Error(e, "Exception while index server streaming."); } }
/// <summary> /// Start to request only once but response many (one by one) /// </summary> /// <param name="next"></param> /// <returns></returns> public async Task StartServerStreamingCall(ulong next) { _next = Math.Max(next, ToBeIndexedInfoQueue.Last()?.Height ?? -1 + 1); try { var request = new RequestBlockInfo { ChainId = Hash.LoadHex(ChainConfig.Instance.ChainId), NextHeight = ToBeIndexedInfoQueue.Count == 0 ? _next : ToBeIndexedInfoQueue.Last().Height + 1 }; using (var call = Call(request)) { while (await call.ResponseStream.MoveNext()) { var response = call.ResponseStream.Current; // request failed or useless response if (!response.Success || response.Height != _next) { continue; } if (ToBeIndexedInfoQueue.TryAdd(response.BlockInfoResult)) { _next++; } } } } catch (RpcException e) { _logger.Error(e); throw; } }
/// <summary> /// Response to recording request from side chain node. /// One request to many responses. /// </summary> /// <param name="request"></param> /// <param name="responseStream"></param> /// <param name="context"></param> /// <returns></returns> public override async Task RecordServerStreaming(RequestBlockInfo request, IServerStreamWriter <ResponseParentChainBlockInfo> responseStream, ServerCallContext context) { _logger?.Trace("Parent Chain Server received IndexedInfo message."); try { var height = request.NextHeight; var sideChainId = request.ChainId; while (height <= await BlockChain.GetCurrentBlockHeightAsync()) { IBlock block = await BlockChain.GetBlockByHeightAsync(height); BlockHeader header = block?.Header; BlockBody body = block?.Body; var res = new ResponseParentChainBlockInfo { Success = block != null }; if (res.Success) { res.BlockInfo = new ParentChainBlockInfo { Root = new ParentChainBlockRootInfo { Height = height, SideChainBlockHeadersRoot = header?.SideChainBlockHeadersRoot, SideChainTransactionsRoot = header?.SideChainTransactionsRoot, ChainId = header?.ChainId } }; var tree = await _binaryMerkleTreeManager .GetSideChainTransactionRootsMerkleTreeByHeightAsync(header?.ChainId, height); //Todo: this is to tell side chain the height of side chain block in this main chain block, which could be removed with subsequent improvement. body?.IndexedInfo.Where(predicate: i => i.ChainId.Equals(sideChainId)) .Select((info, index) => new KeyValuePair <ulong, MerklePath>(info.Height, tree.GenerateMerklePath(index))) .ForEach(kv => res.BlockInfo.IndexedBlockInfo.Add(kv.Key, kv.Value)); } //_logger?.Log(LogLevel.Trace, $"Parent Chain Server responsed IndexedInfo message of height {height}"); await responseStream.WriteAsync(res); height++; } } catch (Exception e) { _logger?.Error(e, "Miner server RecordDuplexStreaming failed."); } }
/// <summary> /// Task to create request in loop. /// </summary> /// <param name="call"></param> /// <param name="cancellationToken"></param> /// <returns></returns> private async Task RequestLoop(AsyncDuplexStreamingCall <RequestBlockInfo, TResponse> call, CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { var request = new RequestBlockInfo { ChainId = Hash.LoadHex(ChainConfig.Instance.ChainId), NextHeight = ToBeIndexedInfoQueue.Count == 0 ? _next : ToBeIndexedInfoQueue.Last().Height + 1 }; //_logger.Trace($"New request for height {request.NextHeight} to chain {_targetChainId.DumpHex()}"); await call.RequestStream.WriteAsync(request); await Task.Delay(_realInterval); } }
protected override AsyncServerStreamingCall <ResponseParentChainBlockInfo> Call(RequestBlockInfo requestBlockInfo) { return(_client.RecordServerStreaming(requestBlockInfo)); }
protected abstract AsyncServerStreamingCall <TResponse> Call(RequestBlockInfo requestBlockInfo);
protected override AsyncServerStreamingCall <ResponseSideChainBlockInfo> Call(RequestBlockInfo requestBlockInfo) { return(_client.IndexServerStreaming(requestBlockInfo)); }