Implement BlocksClient for working with blocks (#671)

* rpc: Fill in any missing finalized blocks

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* tests: Move fill blocks test to RPC location

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* events: Remove the fill in strategy

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* blocks: Introduce blocks client

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* client: Enable the block API

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* blocks: Simplify `subscribe_finalized_headers` method

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* tests: Add tests for `subscribe_finalized_headers`

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* blocks: Implement `subscribe_headers`

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* tests: Add tests for `subscribe_headers`

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* tests: Move `missing_block_headers_will_be_filled_in` to blocks

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* events: Use the new subscribe to blocks

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* blocks: Change API to return future similar to events

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* events: Use blocks API for subscribing to blocks

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Update subxt/src/blocks/blocks_client.rs

Co-authored-by: James Wilson <james@jsdw.me>

* blocks: Simplify docs for `subscribe_finalized_headers`

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* blocks: Use `PhantomDataSendSync` to avoid other bounds on `T: Config`

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* blocks: Add docs for best blocks

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* blocks: Avoid one clone for the `client.rpc()`

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Update testing/integration-tests/src/blocks/mod.rs

Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com>

* blocks: Improve `subscribe_headers` doc

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
Co-authored-by: James Wilson <james@jsdw.me>
Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com>
This commit is contained in:
Alexandru Vasile
2022-10-10 12:31:54 +03:00
committed by GitHub
parent 10def3c2c4
commit 95e6aa9dda
11 changed files with 278 additions and 137 deletions
@@ -0,0 +1,89 @@
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use crate::test_context;
use futures::StreamExt;
// Check that we can subscribe to non-finalized blocks.
#[tokio::test]
async fn non_finalized_headers_subscription() -> Result<(), subxt::Error> {
let ctx = test_context().await;
let api = ctx.client();
let mut sub = api.blocks().subscribe_headers().await?;
// Wait for the next set of headers, and check that the
// associated block hash is the one we just finalized.
// (this can be a bit slow as we have to wait for finalization)
let header = sub.next().await.unwrap()?;
let block_hash = header.hash();
let current_block_hash = api.rpc().block_hash(None).await?.unwrap();
assert_eq!(block_hash, current_block_hash);
Ok(())
}
// Check that we can subscribe to finalized blocks.
#[tokio::test]
async fn finalized_headers_subscription() -> Result<(), subxt::Error> {
let ctx = test_context().await;
let api = ctx.client();
let mut sub = api.blocks().subscribe_finalized_headers().await?;
// Wait for the next set of headers, and check that the
// associated block hash is the one we just finalized.
// (this can be a bit slow as we have to wait for finalization)
let header = sub.next().await.unwrap()?;
let finalized_hash = api.rpc().finalized_head().await?;
assert_eq!(header.hash(), finalized_hash);
Ok(())
}
#[tokio::test]
async fn missing_block_headers_will_be_filled_in() -> Result<(), subxt::Error> {
let ctx = test_context().await;
let api = ctx.client();
// Manually subscribe to the next 6 finalized block headers, but deliberately
// filter out some in the middle so we get back b _ _ b _ b. This guarantees
// that there will be some gaps, even if there aren't any from the subscription.
let some_finalized_blocks = api
.rpc()
.subscribe_finalized_blocks()
.await?
.enumerate()
.take(6)
.filter(|(n, _)| {
let n = *n;
async move { n == 0 || n == 3 || n == 5 }
})
.map(|(_, h)| h);
// This should spot any gaps in the middle and fill them back in.
let all_finalized_blocks = subxt::blocks::subscribe_to_block_headers_filling_in_gaps(
ctx.client(),
None,
some_finalized_blocks,
);
futures::pin_mut!(all_finalized_blocks);
// Iterate the block headers, making sure we get them all in order.
let mut last_block_number = None;
while let Some(header) = all_finalized_blocks.next().await {
let header = header?;
use sp_runtime::traits::Header;
let block_number: u128 = (*header.number()).into();
if let Some(last) = last_block_number {
assert_eq!(last + 1, block_number);
}
last_block_number = Some(block_number);
}
assert!(last_block_number.is_some());
Ok(())
}
@@ -169,57 +169,6 @@ async fn balance_transfer_subscription() -> Result<(), subxt::Error> {
Ok(())
}
#[tokio::test]
async fn missing_block_headers_will_be_filled_in() -> Result<(), subxt::Error> {
let ctx = test_context().await;
let api = ctx.client();
// This function is not publically available to use, but contains
// the key logic for filling in missing blocks, so we want to test it.
// This is used in `subscribe_finalized` to ensure no block headers are
// missed.
use subxt::events::subscribe_to_block_headers_filling_in_gaps;
// Manually subscribe to the next 6 finalized block headers, but deliberately
// filter out some in the middle so we get back b _ _ b _ b. This guarantees
// that there will be some gaps, even if there aren't any from the subscription.
let some_finalized_blocks = api
.rpc()
.subscribe_finalized_blocks()
.await?
.enumerate()
.take(6)
.filter(|(n, _)| {
let n = *n;
async move { n == 0 || n == 3 || n == 5 }
})
.map(|(_, h)| h);
// This should spot any gaps in the middle and fill them back in.
let all_finalized_blocks = subscribe_to_block_headers_filling_in_gaps(
ctx.client(),
None,
some_finalized_blocks,
);
futures::pin_mut!(all_finalized_blocks);
// Iterate the block headers, making sure we get them all in order.
let mut last_block_number = None;
while let Some(header) = all_finalized_blocks.next().await {
let header = header?;
use sp_runtime::traits::Header;
let block_number: u128 = (*header.number()).into();
if let Some(last) = last_block_number {
assert_eq!(last + 1, block_number);
}
last_block_number = Some(block_number);
}
Ok(())
}
// This is just a compile-time check that we can subscribe to events in
// a context that requires the event subscription/filtering to be Send-able.
// We test a typical use of EventSubscription and FilterEvents. We don't need
+2
View File
@@ -9,6 +9,8 @@ mod codegen;
#[cfg(test)]
mod utils;
#[cfg(test)]
mod blocks;
#[cfg(test)]
mod client;
#[cfg(test)]