async Task RetrieveBlocks(IBlocksRetrieverHandler handler) { var cancellationToken = this.retrieveBlocksCancelSource.Token; var height = await handler.GetBlockHintAsync(cancellationToken); while (true) { // Get block. Block block; using (var rpc = await this.rpc.CreateChainInformationRpcAsync(cancellationToken)) { try { block = await rpc.GetBlockAsync(height, cancellationToken); } catch (RPCException ex) when(ex.RPCCode == RPCErrorCode.RPC_INVALID_PARAMETER) { // Invalid block height. await WaitNewBlockAsync(cancellationToken); continue; } } // Execute handler. height = await handler.ProcessBlockAsync(block, height, cancellationToken); } }
async Task RetrieveBlocks(IBlocksRetrieverHandler handler) { var cancellationToken = this.retrieveBlocksCancelSource.Token; var height = await handler.GetStartBlockAsync(cancellationToken); while (true) { // Get block. Block block; using (var rpc = await this.rpc.CreateChainInformationRpcAsync(cancellationToken)) { try { block = await rpc.GetBlockAsync(height, cancellationToken); } catch (RPCException ex) when(ex.RPCCode == RPCErrorCode.RPC_INVALID_PARAMETER) // Invalid height. { var info = await rpc.GetChainInfoAsync(cancellationToken); var blocks = (int)info.Blocks; if (blocks >= height) { // There is a new block already. continue; } if (blocks == height - 1) { // A block of the target height is not available right now. await WaitNewBlockAsync(cancellationToken); continue; } // There is a re-org happened. await handler.DiscardBlocksAsync(blocks, cancellationToken); height = blocks; continue; } } // Execute handler. height = await handler.ProcessBlockAsync(block, height, cancellationToken); } }
public BlocksRetrieverTests() { // Start ZeroMQ publisher. this.publisher = new PublisherSocket(); try { var publisherPort = this.publisher.BindRandomPort("tcp://localhost"); // Mock config. var builder = new ConfigurationBuilder(); builder.AddInMemoryCollection(new Dictionary <string, string>() { { "Zcoin:Rpc:Address", "http://127.0.0.1:8888" }, { "Zcoin:Rpc:UserName", "root" }, { "Zcoin:Rpc:Password", "abc" }, { "Zcoin:ZeroMq:Address", "tcp://localhost:" + publisherPort } }); this.config = builder.Build(); // Mock RPC. this.rpc = new Mock <IChainInformationRpc>(); this.rpcFactory = new Mock <IRpcFactory>(); this.rpcFactory.Setup(f => f.CreateChainInformationRpcAsync(It.IsAny <CancellationToken>())) .ReturnsAsync(this.rpc.Object); // Mock handler. this.handler = Substitute.For <IBlocksRetrieverHandler>(); // Create test subject. this.subject = new BlocksRetriever(this.config, this.rpcFactory.Object); } catch { this.publisher.Dispose(); throw; } }
public Task <Task> StartAsync(IBlocksRetrieverHandler handler, CancellationToken cancellationToken) { if (handler == null) { throw new ArgumentNullException(nameof(handler)); } ThrowIfAlreadyDisposed(); ThrowIfAlreadyRunning(); Debug.Assert(this.subscriber == null); Debug.Assert(this.poller == null); Debug.Assert(this.newBlockNotification == null); Debug.Assert(this.retrieveBlocksCancelSource == null); Debug.Assert(this.retrieveBlocksTask == null); // Subscribe to ZeroMQ. this.subscriber = new SubscriberSocket(); try { this.poller = new NetMQPoller(); try { this.subscriber.ReceiveReady += (sender, e) => NotifyNewBlock(); this.subscriber.Connect(this.config.ZeroMq.Address); this.subscriber.Subscribe("hashblock"); this.poller.Add(this.subscriber); this.poller.RunAsync(); // Start background tasks to retrieve blocks. this.retrieveBlocksCancelSource = new CancellationTokenSource(); try { this.retrieveBlocksTask = Task.Run(() => RetrieveBlocks(handler)); } catch { this.retrieveBlocksCancelSource.Dispose(); this.retrieveBlocksCancelSource = null; throw; } } catch { this.poller.Dispose(); this.poller = null; throw; } } catch { this.subscriber.Dispose(); this.subscriber = null; throw; } return(Task.FromResult(this.retrieveBlocksTask)); }