Skip to content

Commit 3aabd6d

Browse files
authored
Add subxt-historic crate for accesing historic (non head-of-chain) blocks (#2040)
* WIP subxt-historic * WIP subxt-historic * WIP subxt-historic; flesh out basic foundations * WIP filling in extrinsic decoding functionality * iter and decode transaction extensions * Fill in the Online/OfflineClient APIs and move more things to be part of the chain Config * WIP storage * clippy, fmt, finish extrinsics example * prep for 0.0.1 release to claim crate name * fix README link * fmt * WIP thinking about storage APIs * WIP working out storage APIs * Storage plain value fetching first pass * WIP storage: first pass iterating over values done * First apss finishing storage APIs * fmt and clippy * Create a storage example showing fetch and iteration * Bump to frame-decode 0.9.0 * Bump subxt-historic to 0.0.3 for preview release * Remove unused deps * fix import * clippy * doc fixes * tweak CI and fix some cargo hack findings * Update README: subxt-historic is prerelease
1 parent 23f3ebe commit 3aabd6d

33 files changed

+3220
-55
lines changed

.github/workflows/rust.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,9 @@ jobs:
187187
- name: Cargo hack; check each subxt feature
188188
run: cargo hack -p subxt --each-feature check --exclude-no-default-features --exclude-all-features --exclude-features web --features native
189189

190+
- name: Cargo hack; check each subxt feature
191+
run: cargo hack -p subxt-historic --each-feature check --exclude-no-default-features --exclude-all-features --exclude-features web --features native
192+
190193
# Subxt-signer has the "subxt" features enabled in the "check all targets" test. Run it on its own to
191194
# check it without. We can't enable subxt or web features here, so no cargo hack.
192195
- name: Cargo check subxt-signer
@@ -213,7 +216,7 @@ jobs:
213216

214217
# Next, check each other package in isolation.
215218
- name: Cargo hack; check each feature/crate on its own
216-
run: cargo hack --exclude subxt --exclude subxt-signer --exclude subxt-lightclient --exclude subxt-rpcs --exclude-all-features --each-feature check --workspace
219+
run: cargo hack --exclude subxt --exclude subxt-historic --exclude subxt-signer --exclude subxt-lightclient --exclude subxt-rpcs --exclude-all-features --each-feature check --workspace
217220

218221
# Check the full examples, which aren't a part of the workspace so are otherwise ignored.
219222
- name: Cargo check parachain-example

Cargo.lock

Lines changed: 73 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ members = [
44
"codegen",
55
"core",
66
"lightclient",
7+
"historic",
78
"testing/substrate-runner",
89
"testing/test-runtime",
910
"testing/integration-tests",
@@ -80,8 +81,8 @@ darling = "0.20.10"
8081
derive-where = "1.2.7"
8182
either = { version = "1.13.0", default-features = false }
8283
finito = { version = "0.1.0", default-features = false }
83-
frame-decode = { version = "0.8.0", default-features = false }
84-
frame-metadata = { version = "23.0.0", default-features = false, features = ["unstable"] }
84+
frame-decode = { version = "0.9.0", default-features = false }
85+
frame-metadata = { version = "23.0.0", default-features = false }
8586
futures = { version = "0.3.31", default-features = false, features = ["std"] }
8687
getrandom = { version = "0.2", default-features = false }
8788
hashbrown = "0.14.5"
@@ -101,6 +102,8 @@ scale-value = { version = "0.18.0", default-features = false }
101102
scale-bits = { version = "0.7.0", default-features = false }
102103
scale-decode = { version = "0.16.0", default-features = false }
103104
scale-encode = { version = "0.10.0", default-features = false }
105+
scale-type-resolver = { version = "0.2.0" }
106+
scale-info-legacy = { version = "0.2.3" }
104107
scale-typegen = "0.11.1"
105108
scale-typegen-description = "0.11.0"
106109
serde = { version = "1.0.210", default-features = false, features = ["derive"] }

historic/Cargo.toml

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
[package]
2+
name = "subxt-historic"
3+
version = "0.0.3"
4+
authors.workspace = true
5+
edition.workspace = true
6+
rust-version.workspace = true
7+
publish = true
8+
9+
license.workspace = true
10+
readme = "README.md"
11+
repository.workspace = true
12+
documentation.workspace = true
13+
homepage.workspace = true
14+
description = "Download non head-of-chain blocks and state from Substrate based nodes"
15+
keywords = ["parity", "substrate", "blockchain"]
16+
17+
[lints]
18+
workspace = true
19+
20+
[features]
21+
default = ["jsonrpsee", "native"]
22+
23+
# Enable this for native (ie non web/wasm builds).
24+
# Exactly 1 of "web" and "native" is expected.
25+
native = [
26+
"subxt-rpcs/native",
27+
]
28+
29+
# Enable this for web/wasm builds.
30+
# Exactly 1 of "web" and "native" is expected.
31+
web = [
32+
"subxt-rpcs/web",
33+
]
34+
35+
# Enable this to use the reconnecting rpc client
36+
reconnecting-rpc-client = ["subxt-rpcs/reconnecting-rpc-client"]
37+
38+
# Enable this to use jsonrpsee, which enables the jsonrpsee RPC client, and
39+
# a couple of util functions which rely on jsonrpsee.
40+
jsonrpsee = [
41+
"subxt-rpcs/jsonrpsee",
42+
]
43+
44+
[dependencies]
45+
subxt-rpcs = { workspace = true }
46+
frame-decode = { workspace = true, features = ["legacy", "legacy-types"] }
47+
frame-metadata = { workspace = true, features = ["std", "legacy"] }
48+
scale-type-resolver = { workspace = true }
49+
codec = { workspace = true }
50+
primitive-types = { workspace = true }
51+
scale-info = { workspace = true }
52+
scale-info-legacy = { workspace = true }
53+
scale-decode = { workspace = true }
54+
thiserror = { workspace = true }
55+
sp-crypto-hashing = { workspace = true }
56+
url = { workspace = true }
57+
futures = { workspace = true }
58+
59+
[dev-dependencies]
60+
tokio = { workspace = true, features = ["full"] }
61+
scale-value = { workspace = true }
62+
scale-decode = { workspace = true, features = ["derive"] }
63+
hex = { workspace = true }

historic/README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# subxt-historic
2+
3+
**This create is a work in progress and currently is released only as a preview.**
4+
5+
While `subxt` is a library for working at the head of a chain (submitting transactions and obtaining the current state), `subxt-historic` is a library for decoding blocks and state that are anywhere in a chain. To broadly summarize the differences:
6+
7+
| Feature | subxt | subxt-historic |
8+
|-----------------------------------------|------------------------------|-------------------------------|
9+
| Block access | Head of chain | Any block in chain |
10+
| Connection to chain | Light client or RPC node | Archive RPC nodes only |
11+
| Transaction submission | Yes | No |
12+
| Metadata compatibility | V14 and newer | Any version |
13+
14+
# Examples
15+
16+
See the [examples](https://github.com/paritytech/subxt/tree/master/historic/examples) folder for examples of how to use `subxt-historic`.

historic/examples/extrinsics.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#![allow(missing_docs)]
2+
use subxt_historic::{Error, OnlineClient, PolkadotConfig};
3+
4+
#[tokio::main]
5+
async fn main() -> Result<(), Error> {
6+
// Configuration for the Polkadot relay chain.
7+
let config = PolkadotConfig::new();
8+
9+
// Create an online client for the Polkadot relay chain, pointed at a Polkadot archive node.
10+
let client = OnlineClient::from_url(config, "wss://rpc.polkadot.io").await?;
11+
12+
// Iterate through some randomly selected old blocks to show how to fetch and decode extrinsics.
13+
for block_number in 123456.. {
14+
println!("=== Block {block_number} ===");
15+
16+
// Point the client at a specific block number. By default this will download and cache
17+
// metadata for the required spec version (so it's cheaper to instantiate again), if it
18+
// hasn't already, and borrow the relevant legacy types from the client.
19+
let client_at_block = client.at(block_number).await?;
20+
21+
// Fetch the extrinsics at that block.
22+
let extrinsics = client_at_block.extrinsics().fetch().await?;
23+
24+
// Now, we have various operations to work with them. Here we print out various details
25+
// about each extrinsic.
26+
for extrinsic in extrinsics.iter() {
27+
println!(
28+
"{}.{}",
29+
extrinsic.call().pallet_name(),
30+
extrinsic.call().name()
31+
);
32+
33+
if let Some(signature) = extrinsic.signature_bytes() {
34+
println!(" Signature: 0x{}", hex::encode(signature));
35+
}
36+
37+
println!(" Call Data:");
38+
39+
// We can decode each of the fields (in this example we decode everything into a
40+
// scale_value::Value type, which can represent any SCALE encoded data, but if you
41+
// have an idea of the type then you can try to decode into that type instead):
42+
for field in extrinsic.call().fields().iter() {
43+
println!(
44+
" {}: {}",
45+
field.name(),
46+
field.decode::<scale_value::Value>().unwrap()
47+
);
48+
}
49+
50+
// Or, all of them at once:
51+
println!(
52+
" All: {}",
53+
extrinsic
54+
.call()
55+
.fields()
56+
.decode::<scale_value::Composite<_>>()
57+
.unwrap()
58+
);
59+
60+
// We can also look at things like the transaction extensions:
61+
if let Some(extensions) = extrinsic.transaction_extensions() {
62+
println!(" Transaction Extensions:");
63+
64+
// We can decode each of them:
65+
for extension in extensions.iter() {
66+
println!(
67+
" {}: {}",
68+
extension.name(),
69+
extension.decode::<scale_value::Value>().unwrap()
70+
);
71+
}
72+
73+
// Or all of them at once:
74+
println!(
75+
" All: {}",
76+
extensions.decode::<scale_value::Composite<_>>().unwrap()
77+
);
78+
}
79+
}
80+
}
81+
82+
Ok(())
83+
}

0 commit comments

Comments
 (0)