Skip to content

Commit 1866a5d

Browse files
Publish indexed transactions with BLAKE2b hashes to IPFS DHT (#10468)
Add `--ipfs-bootnodes` flag for specifying IPFS bootnodes. If passed along with `--ipfs-server`, the node will register as a content provider in IPFS DHT of indexed transactions with BLAKE2b hashes of the last two weeks (or pruning depth if smaller). ## Follow-ups - Support other hashes (sha2-256 specifically) and CID codecs - Adjust `IPFS_MAX_BLOCKS` for chains with elastic scaling - Speedup DHT publishing in litep2p (should aim at 10s single provider publish time) --------- Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 44f9788 commit 1866a5d

File tree

27 files changed

+742
-80
lines changed

27 files changed

+742
-80
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cumulus/client/pov-recovery/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ sc-consensus = { workspace = true, default-features = true }
2424
sc-network = { workspace = true, default-features = true }
2525
sp-api = { workspace = true, default-features = true }
2626
sp-consensus = { workspace = true, default-features = true }
27+
sp-core = { workspace = true, default-features = true }
2728
sp-maybe-compressed-blob = { workspace = true, default-features = true }
2829
sp-runtime = { workspace = true, default-features = true }
2930
sp-version = { workspace = true, default-features = true }

cumulus/client/pov-recovery/src/tests.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ use sc_consensus::import_queue::RuntimeOrigin;
4242
use sc_utils::mpsc::{TracingUnboundedReceiver, TracingUnboundedSender};
4343
use sp_api::RuntimeApiInfo;
4444
use sp_blockchain::Info;
45+
use sp_core::H256;
4546
use sp_runtime::{generic::SignedBlock, Justifications};
4647
use sp_version::RuntimeVersion;
4748
use std::{
@@ -198,11 +199,15 @@ impl<Block: BlockT> BlockBackend<Block> for ParachainClient<Block> {
198199
unimplemented!()
199200
}
200201

201-
fn indexed_transaction(&self, _: Block::Hash) -> sp_blockchain::Result<Option<Vec<u8>>> {
202+
fn indexed_transaction(&self, _: H256) -> sp_blockchain::Result<Option<Vec<u8>>> {
202203
unimplemented!()
203204
}
204205

205-
fn has_indexed_transaction(&self, _: Block::Hash) -> sp_blockchain::Result<bool> {
206+
fn has_indexed_transaction(&self, _: H256) -> sp_blockchain::Result<bool> {
207+
unimplemented!()
208+
}
209+
210+
fn block_indexed_hashes(&self, _: Block::Hash) -> sp_blockchain::Result<Option<Vec<H256>>> {
206211
unimplemented!()
207212
}
208213

cumulus/client/relay-chain-minimal-node/src/network.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ pub(crate) fn build_collator_network<Network: NetworkBackend<Block, Hash>>(
7676
protocol_id,
7777
metrics_registry: config.prometheus_config.as_ref().map(|config| config.registry.clone()),
7878
block_announce_config,
79-
bitswap_config: None,
79+
ipfs_config: None,
8080
notification_metrics,
8181
};
8282

prdoc/pr_10468.prdoc

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
title: Publish indexed transactions with BLAKE2b hashes to IPFS DHT
2+
doc:
3+
- audience: [Node Dev, Node Operator]
4+
description: Add `--ipfs-bootnodes` flag for specifying IPFS bootnodes. If passed
5+
along with `--ipfs-server`, the node will register as a content provider in IPFS
6+
DHT of indexed transactions with BLAKE2b hashes of the last two weeks (or pruning
7+
depth if smaller).
8+
crates:
9+
- name: sc-network
10+
bump: major
11+
- name: sc-network-types
12+
bump: minor
13+
- name: cumulus-client-pov-recovery
14+
bump: minor
15+
- name: sc-client-api
16+
bump: major
17+
- name: sc-client-db
18+
bump: minor
19+
- name: sc-rpc-spec-v2
20+
bump: patch
21+
- name: sc-service
22+
bump: major
23+
- name: sp-blockchain
24+
bump: minor
25+
- name: cumulus-relay-chain-minimal-node
26+
bump: patch
27+
- name: sc-cli
28+
bump: major

substrate/client/api/src/client.rs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
//! A set of APIs supported by the client along with their primitives.
2020
2121
use sp_consensus::BlockOrigin;
22-
use sp_core::storage::StorageKey;
22+
use sp_core::{storage::StorageKey, H256};
2323
use sp_runtime::{
2424
generic::SignedBlock,
2525
traits::{Block as BlockT, NumberFor},
@@ -132,13 +132,19 @@ pub trait BlockBackend<Block: BlockT> {
132132
hash: Block::Hash,
133133
) -> sp_blockchain::Result<Option<Vec<<Block as BlockT>::Extrinsic>>>;
134134

135-
/// Get all indexed transactions for a block,
136-
/// including renewed transactions.
135+
/// Get all indexed transactions for a block, including renewed transactions.
137136
///
138-
/// Note that this will only fetch transactions
139-
/// that are indexed by the runtime with `storage_index_transaction`.
137+
/// Note that this will only fetch transactions that are indexed by the runtime with
138+
/// `storage_index_transaction`.
140139
fn block_indexed_body(&self, hash: Block::Hash) -> sp_blockchain::Result<Option<Vec<Vec<u8>>>>;
141140

141+
/// Get the BLAKE2b-256 hashes of all indexed transactions in a block, including renewed
142+
/// transactions.
143+
///
144+
/// Note that this will only fetch transactions that are indexed by the runtime with
145+
/// `storage_index_transaction`.
146+
fn block_indexed_hashes(&self, hash: Block::Hash) -> sp_blockchain::Result<Option<Vec<H256>>>;
147+
142148
/// Get full block by hash.
143149
fn block(&self, hash: Block::Hash) -> sp_blockchain::Result<Option<SignedBlock<Block>>>;
144150

@@ -151,14 +157,14 @@ pub trait BlockBackend<Block: BlockT> {
151157
/// Get block hash by number.
152158
fn block_hash(&self, number: NumberFor<Block>) -> sp_blockchain::Result<Option<Block::Hash>>;
153159

154-
/// Get single indexed transaction by content hash.
160+
/// Get single indexed transaction by content hash (BLAKE2b-256).
155161
///
156162
/// Note that this will only fetch transactions
157163
/// that are indexed by the runtime with `storage_index_transaction`.
158-
fn indexed_transaction(&self, hash: Block::Hash) -> sp_blockchain::Result<Option<Vec<u8>>>;
164+
fn indexed_transaction(&self, hash: H256) -> sp_blockchain::Result<Option<Vec<u8>>>;
159165

160-
/// Check if transaction index exists.
161-
fn has_indexed_transaction(&self, hash: Block::Hash) -> sp_blockchain::Result<bool> {
166+
/// Check if transaction index exists given its BLAKE2b-256 hash.
167+
fn has_indexed_transaction(&self, hash: H256) -> sp_blockchain::Result<bool> {
162168
Ok(self.indexed_transaction(hash)?.is_some())
163169
}
164170

substrate/client/api/src/in_mem.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
use parking_lot::RwLock;
2222
use sp_blockchain::{CachedHeaderMetadata, HeaderMetadata};
2323
use sp_core::{
24-
offchain::storage::InMemOffchainStorage as OffchainStorage, storage::well_known_keys,
24+
offchain::storage::InMemOffchainStorage as OffchainStorage, storage::well_known_keys, H256,
2525
};
2626
use sp_runtime::{
2727
generic::BlockId,
@@ -427,7 +427,11 @@ impl<Block: BlockT> blockchain::Backend<Block> for Blockchain<Block> {
427427
unimplemented!()
428428
}
429429

430-
fn indexed_transaction(&self, _hash: Block::Hash) -> sp_blockchain::Result<Option<Vec<u8>>> {
430+
fn indexed_transaction(&self, _hash: H256) -> sp_blockchain::Result<Option<Vec<u8>>> {
431+
unimplemented!("Not supported by the in-mem backend.")
432+
}
433+
434+
fn block_indexed_hashes(&self, _hash: Block::Hash) -> sp_blockchain::Result<Option<Vec<H256>>> {
431435
unimplemented!("Not supported by the in-mem backend.")
432436
}
433437

substrate/client/cli/src/params/network_params.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,10 @@ pub struct NetworkParams {
153153
#[arg(long)]
154154
pub ipfs_server: bool,
155155

156+
/// Specify a list of IPFS bootstrap nodes.
157+
#[arg(long, value_name = "ADDR", num_args = 1.., requires = "ipfs_server")]
158+
pub ipfs_bootnodes: Vec<MultiaddrWithPeerId>,
159+
156160
/// Blockchain syncing mode.
157161
#[arg(
158162
long,
@@ -284,6 +288,7 @@ impl NetworkParams {
284288
kademlia_disjoint_query_paths: self.kademlia_disjoint_query_paths,
285289
kademlia_replication_factor: self.kademlia_replication_factor,
286290
ipfs_server: self.ipfs_server,
291+
ipfs_bootnodes: self.ipfs_bootnodes.clone(),
287292
sync_mode: self.sync.into(),
288293
network_backend: self.network_backend.into(),
289294
}

substrate/client/db/src/lib.rs

Lines changed: 43 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,30 @@ impl<Block: BlockT> BlockchainDb<Block> {
697697
}
698698
Ok(None)
699699
}
700+
701+
fn block_indexed_hashes_iter(
702+
&self,
703+
hash: Block::Hash,
704+
) -> ClientResult<Option<impl Iterator<Item = DbHash>>> {
705+
let Some(body) = read_db(
706+
&*self.db,
707+
columns::KEY_LOOKUP,
708+
columns::BODY_INDEX,
709+
BlockId::<Block>::Hash(hash),
710+
)?
711+
else {
712+
return Ok(None);
713+
};
714+
match Vec::<DbExtrinsic<Block>>::decode(&mut &body[..]) {
715+
Ok(index) => Ok(Some(index.into_iter().flat_map(|ex| match ex {
716+
DbExtrinsic::Indexed { hash, .. } => Some(hash),
717+
_ => None,
718+
}))),
719+
Err(err) => {
720+
Err(sp_blockchain::Error::Backend(format!("Error decoding body list: {err}")))
721+
},
722+
}
723+
}
700724
}
701725

702726
impl<Block: BlockT> sc_client_api::blockchain::HeaderBackend<Block> for BlockchainDb<Block> {
@@ -782,44 +806,32 @@ impl<Block: BlockT> sc_client_api::blockchain::Backend<Block> for BlockchainDb<B
782806
children::read_children(&*self.db, columns::META, meta_keys::CHILDREN_PREFIX, parent_hash)
783807
}
784808

785-
fn indexed_transaction(&self, hash: Block::Hash) -> ClientResult<Option<Vec<u8>>> {
809+
fn indexed_transaction(&self, hash: DbHash) -> ClientResult<Option<Vec<u8>>> {
786810
Ok(self.db.get(columns::TRANSACTION, hash.as_ref()))
787811
}
788812

789-
fn has_indexed_transaction(&self, hash: Block::Hash) -> ClientResult<bool> {
813+
fn has_indexed_transaction(&self, hash: DbHash) -> ClientResult<bool> {
790814
Ok(self.db.contains(columns::TRANSACTION, hash.as_ref()))
791815
}
792816

817+
fn block_indexed_hashes(&self, hash: Block::Hash) -> ClientResult<Option<Vec<DbHash>>> {
818+
self.block_indexed_hashes_iter(hash).map(|hashes| hashes.map(Iterator::collect))
819+
}
820+
793821
fn block_indexed_body(&self, hash: Block::Hash) -> ClientResult<Option<Vec<Vec<u8>>>> {
794-
let body = match read_db(
795-
&*self.db,
796-
columns::KEY_LOOKUP,
797-
columns::BODY_INDEX,
798-
BlockId::<Block>::Hash(hash),
799-
)? {
800-
Some(body) => body,
801-
None => return Ok(None),
802-
};
803-
match Vec::<DbExtrinsic<Block>>::decode(&mut &body[..]) {
804-
Ok(index) => {
805-
let mut transactions = Vec::new();
806-
for ex in index.into_iter() {
807-
if let DbExtrinsic::Indexed { hash, .. } = ex {
808-
match self.db.get(columns::TRANSACTION, hash.as_ref()) {
809-
Some(t) => transactions.push(t),
810-
None => {
811-
return Err(sp_blockchain::Error::Backend(format!(
812-
"Missing indexed transaction {hash:?}",
813-
)))
814-
},
815-
}
816-
}
817-
}
818-
Ok(Some(transactions))
819-
},
820-
Err(err) => {
821-
Err(sp_blockchain::Error::Backend(format!("Error decoding body list: {err}")))
822-
},
822+
match self.block_indexed_hashes_iter(hash) {
823+
Ok(Some(hashes)) => Ok(Some(
824+
hashes
825+
.map(|hash| match self.db.get(columns::TRANSACTION, hash.as_ref()) {
826+
Some(t) => Ok(t),
827+
None => Err(sp_blockchain::Error::Backend(format!(
828+
"Missing indexed transaction {hash:?}",
829+
))),
830+
})
831+
.collect::<Result<_, _>>()?,
832+
)),
833+
Ok(None) => Ok(None),
834+
Err(err) => Err(err),
823835
}
824836
}
825837
}

substrate/client/network/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ async-channel = { workspace = true }
3030
async-trait = { workspace = true }
3131
asynchronous-codec = { workspace = true }
3232
bytes = { workspace = true, default-features = true }
33+
cid = { workspace = true }
3334
codec = { features = ["derive"], workspace = true, default-features = true }
3435
either = { workspace = true, default-features = true }
3536
fnv = { workspace = true }
@@ -69,7 +70,6 @@ zeroize = { workspace = true, default-features = true }
6970

7071
[dev-dependencies]
7172
assert_matches = { workspace = true }
72-
cid = { workspace = true }
7373
multistream-select = { workspace = true }
7474
sc-block-builder = { workspace = true, default-features = true }
7575
sp-consensus = { workspace = true, default-features = true }

0 commit comments

Comments
 (0)