diff --git a/client/api/src/backend.rs b/client/api/src/backend.rs index 195fc49612ba9..b09995f887c4a 100644 --- a/client/api/src/backend.rs +++ b/client/api/src/backend.rs @@ -149,6 +149,7 @@ pub trait BlockImportOperation { &mut self, header: Block::Header, body: Option>, + indexed_body: Option>>, justifications: Option, state: NewBlockState, ) -> sp_blockchain::Result<()>; diff --git a/client/api/src/in_mem.rs b/client/api/src/in_mem.rs index 916b830f6189d..505b69981694a 100644 --- a/client/api/src/in_mem.rs +++ b/client/api/src/in_mem.rs @@ -572,6 +572,7 @@ impl backend::BlockImportOperation for BlockImportOperatio &mut self, header: ::Header, body: Option::Extrinsic>>, + _indexed_body: Option>>, justifications: Option, state: NewBlockState, ) -> sp_blockchain::Result<()> { diff --git a/client/cli/src/arg_enums.rs b/client/cli/src/arg_enums.rs index 1bca67e782a3b..d9a4210376298 100644 --- a/client/cli/src/arg_enums.rs +++ b/client/cli/src/arg_enums.rs @@ -250,8 +250,14 @@ impl Into for SyncMode { fn into(self) -> sc_network::config::SyncMode { match self { SyncMode::Full => sc_network::config::SyncMode::Full, - SyncMode::Fast => sc_network::config::SyncMode::Fast { skip_proofs: false }, - SyncMode::FastUnsafe => sc_network::config::SyncMode::Fast { skip_proofs: true }, + SyncMode::Fast => sc_network::config::SyncMode::Fast { + skip_proofs: false, + storage_chain_mode: false, + }, + SyncMode::FastUnsafe => sc_network::config::SyncMode::Fast { + skip_proofs: true, + storage_chain_mode: false, + }, } } } diff --git a/client/db/src/changes_tries_storage.rs b/client/db/src/changes_tries_storage.rs index 860ca41730518..3863099a09f96 100644 --- a/client/db/src/changes_tries_storage.rs +++ b/client/db/src/changes_tries_storage.rs @@ -572,7 +572,7 @@ mod tests { }; let mut op = backend.begin_operation().unwrap(); backend.begin_state_operation(&mut op, block_id).unwrap(); - op.set_block_data(header, None, None, NewBlockState::Best).unwrap(); + op.set_block_data(header, None, None, None, NewBlockState::Best).unwrap(); op.update_changes_trie((changes_trie_update, ChangesTrieCacheAction::Clear)).unwrap(); backend.commit_operation(op).unwrap(); @@ -916,7 +916,7 @@ mod tests { backend.begin_state_operation(&mut op, BlockId::Hash(block2)).unwrap(); op.mark_finalized(BlockId::Hash(block1), None).unwrap(); op.mark_finalized(BlockId::Hash(block2), None).unwrap(); - op.set_block_data(header3, None, None, NewBlockState::Final).unwrap(); + op.set_block_data(header3, None, None, None, NewBlockState::Final).unwrap(); backend.commit_operation(op).unwrap(); // insert more unfinalized headers @@ -941,7 +941,7 @@ mod tests { op.mark_finalized(BlockId::Hash(block4), None).unwrap(); op.mark_finalized(BlockId::Hash(block5), None).unwrap(); op.mark_finalized(BlockId::Hash(block6), None).unwrap(); - op.set_block_data(header7, None, None, NewBlockState::Final).unwrap(); + op.set_block_data(header7, None, None, None, NewBlockState::Final).unwrap(); backend.commit_operation(op).unwrap(); } diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index 977d55b3cc674..505c7b9d49ea6 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -74,6 +74,7 @@ use sp_arithmetic::traits::Saturating; use sp_runtime::{generic::{DigestItem, BlockId}, Justification, Justifications, Storage}; use sp_runtime::traits::{ Block as BlockT, Header as HeaderT, NumberFor, Zero, One, SaturatedConversion, HashFor, + Hash, }; use sp_state_machine::{ DBValue, ChangesTrieTransaction, ChangesTrieCacheAction, UsageInfo as StateUsageInfo, @@ -384,6 +385,7 @@ struct PendingBlock { header: Block::Header, justifications: Option, body: Option>, + indexed_body: Option>>, leaf_state: NewBlockState, } @@ -824,6 +826,7 @@ impl sc_client_api::backend::BlockImportOperation for Bloc &mut self, header: Block::Header, body: Option>, + indexed_body: Option>>, justifications: Option, leaf_state: NewBlockState, ) -> ClientResult<()> { @@ -834,6 +837,7 @@ impl sc_client_api::backend::BlockImportOperation for Bloc self.pending_block = Some(PendingBlock { header, body, + indexed_body, justifications, leaf_state, }); @@ -1068,7 +1072,7 @@ impl Backend { /// Create new memory-backed client backend for tests. #[cfg(any(test, feature = "test-helpers"))] - fn new_test_with_tx_storage( + pub fn new_test_with_tx_storage( keep_blocks: u32, canonicalization_delay: u64, transaction_storage: TransactionStorageMode, @@ -1393,6 +1397,16 @@ impl Backend { }, } } + if let Some(body) = pending_block.indexed_body { + match self.transaction_storage { + TransactionStorageMode::BlockBody => { + debug!(target: "db", "Commit: ignored indexed block body"); + }, + TransactionStorageMode::StorageChain => { + apply_indexed_body::(&mut transaction, body); + }, + } + } if let Some(justifications) = pending_block.justifications { transaction.set_from_vec(columns::JUSTIFICATIONS, &lookup_key, justifications.encode()); } @@ -1881,6 +1895,20 @@ fn apply_index_ops( extrinsic_headers.encode() } +fn apply_indexed_body( + transaction: &mut Transaction, + body: Vec>, +) { + for extrinsic in body { + let hash = sp_runtime::traits::BlakeTwo256::hash(&extrinsic); + transaction.store( + columns::TRANSACTION, + DbHash::from_slice(hash.as_ref()), + extrinsic, + ); + } +} + impl sc_client_api::backend::AuxStore for Backend where Block: BlockT { fn insert_aux< 'a, @@ -2439,7 +2467,7 @@ pub(crate) mod tests { }; let mut op = backend.begin_operation().unwrap(); backend.begin_state_operation(&mut op, block_id).unwrap(); - op.set_block_data(header, Some(body), None, NewBlockState::Best).unwrap(); + op.set_block_data(header, Some(body), None, None, NewBlockState::Best).unwrap(); if let Some(index) = transaction_index { op.update_transaction_index(index).unwrap(); } @@ -2481,6 +2509,7 @@ pub(crate) mod tests { header, Some(vec![]), None, + None, NewBlockState::Best, ).unwrap(); db.commit_operation(op).unwrap(); @@ -2537,6 +2566,7 @@ pub(crate) mod tests { header.clone(), Some(vec![]), None, + None, NewBlockState::Best, ).unwrap(); @@ -2579,6 +2609,7 @@ pub(crate) mod tests { header, Some(vec![]), None, + None, NewBlockState::Best, ).unwrap(); @@ -2622,6 +2653,7 @@ pub(crate) mod tests { header, Some(vec![]), None, + None, NewBlockState::Best, ).unwrap(); @@ -2659,6 +2691,7 @@ pub(crate) mod tests { header, Some(vec![]), None, + None, NewBlockState::Best, ).unwrap(); @@ -2695,6 +2728,7 @@ pub(crate) mod tests { header, Some(vec![]), None, + None, NewBlockState::Best, ).unwrap(); @@ -2730,6 +2764,7 @@ pub(crate) mod tests { header, Some(vec![]), None, + None, NewBlockState::Best, ).unwrap(); @@ -3067,6 +3102,7 @@ pub(crate) mod tests { header.clone(), Some(vec![]), None, + None, NewBlockState::Best, ).unwrap(); @@ -3106,6 +3142,7 @@ pub(crate) mod tests { header, Some(vec![]), None, + None, NewBlockState::Normal, ).unwrap(); @@ -3118,7 +3155,7 @@ pub(crate) mod tests { let header = backend.blockchain().header(BlockId::Hash(hash1)).unwrap().unwrap(); let mut op = backend.begin_operation().unwrap(); backend.begin_state_operation(&mut op, BlockId::Hash(hash0)).unwrap(); - op.set_block_data(header, None, None, NewBlockState::Best).unwrap(); + op.set_block_data(header, None, None, None, NewBlockState::Best).unwrap(); backend.commit_operation(op).unwrap(); } diff --git a/client/light/src/backend.rs b/client/light/src/backend.rs index 3e53d3b81cc77..425720c1d7770 100644 --- a/client/light/src/backend.rs +++ b/client/light/src/backend.rs @@ -293,6 +293,7 @@ impl BlockImportOperation for ImportOperation &mut self, header: Block::Header, _body: Option>, + _indexed_body: Option>>, _justifications: Option, state: NewBlockState, ) -> ClientResult<()> { diff --git a/client/network/src/block_request_handler.rs b/client/network/src/block_request_handler.rs index 19367b1104691..ce65e5eca3457 100644 --- a/client/network/src/block_request_handler.rs +++ b/client/network/src/block_request_handler.rs @@ -264,6 +264,7 @@ impl BlockRequestHandler { ) -> Result { let get_header = attributes.contains(BlockAttributes::HEADER); let get_body = attributes.contains(BlockAttributes::BODY); + let get_indexed_body = attributes.contains(BlockAttributes::INDEXED_BODY); let get_justification = attributes.contains(BlockAttributes::JUSTIFICATION); let mut blocks = Vec::new(); @@ -321,6 +322,18 @@ impl BlockRequestHandler { Vec::new() }; + let indexed_body = if get_indexed_body { + match self.client.block_indexed_body(&BlockId::Hash(hash))? { + Some(transactions) => transactions, + None => { + log::trace!(target: LOG_TARGET, "Missing indexed block data for block request."); + break; + } + } + } else { + Vec::new() + }; + let block_data = crate::schema::v1::BlockData { hash: hash.encode(), header: if get_header { @@ -334,6 +347,7 @@ impl BlockRequestHandler { justification, is_empty_justification, justifications, + indexed_body, }; total_size += block_data.body.len(); diff --git a/client/network/src/config.rs b/client/network/src/config.rs index a6aa5feea5bd9..8cc467a7fb9fd 100644 --- a/client/network/src/config.rs +++ b/client/network/src/config.rs @@ -390,7 +390,9 @@ pub enum SyncMode { /// Download blocks and the latest state. Fast { /// Skip state proof download and verification. - skip_proofs: bool + skip_proofs: bool, + /// Download indexed transactions for recent blocks. + storage_chain_mode: bool, }, } diff --git a/client/network/src/lib.rs b/client/network/src/lib.rs index 11e235bb81ae7..b43836cacaa54 100644 --- a/client/network/src/lib.rs +++ b/client/network/src/lib.rs @@ -263,7 +263,6 @@ pub mod light_client_requests; pub mod state_request_handler; pub mod config; pub mod error; -pub mod gossip; pub mod network_state; pub mod transactions; diff --git a/client/network/src/protocol.rs b/client/network/src/protocol.rs index b9a189a0f384f..eaed7ffcccace 100644 --- a/client/network/src/protocol.rs +++ b/client/network/src/protocol.rs @@ -228,7 +228,13 @@ impl ProtocolConfig { } else { match self.sync_mode { config::SyncMode::Full => sync::SyncMode::Full, - config::SyncMode::Fast { skip_proofs } => sync::SyncMode::LightState { skip_proofs }, + config::SyncMode::Fast { + skip_proofs, + storage_chain_mode, + } => sync::SyncMode::LightState { + skip_proofs, + storage_chain_mode + }, } } } @@ -597,6 +603,11 @@ impl Protocol { } else { None }, + indexed_body: if request.fields.contains(message::BlockAttributes::INDEXED_BODY) { + Some(block_data.indexed_body) + } else { + None + }, receipt: if !block_data.message_queue.is_empty() { Some(block_data.receipt) } else { @@ -965,6 +976,7 @@ impl Protocol { hash: header.hash(), header: Some(header), body: None, + indexed_body: None, receipt: None, message_queue: None, justification: None, diff --git a/client/network/src/protocol/message.rs b/client/network/src/protocol/message.rs index dc6beac99aa01..50d0fd7969021 100644 --- a/client/network/src/protocol/message.rs +++ b/client/network/src/protocol/message.rs @@ -77,6 +77,8 @@ bitflags! { const MESSAGE_QUEUE = 0b00001000; /// Include a justification for the block. const JUSTIFICATION = 0b00010000; + /// Include indexed transactions for a block. + const INDEXED_BODY = 0b00100000; } } @@ -248,6 +250,8 @@ pub mod generic { pub header: Option
, /// Block body if requested. pub body: Option>, + /// Block body indexed transactions if requested. + pub indexed_body: Option>>, /// Block receipt if requested. pub receipt: Option>, /// Block message queue if requested. diff --git a/client/network/src/protocol/sync.rs b/client/network/src/protocol/sync.rs index 44fbe64bfcff4..55b64c157c65e 100644 --- a/client/network/src/protocol/sync.rs +++ b/client/network/src/protocol/sync.rs @@ -469,7 +469,8 @@ pub enum SyncMode { Full, // Sync headers and the last finalied state LightState { - skip_proofs: bool + storage_chain_mode: bool, + skip_proofs: bool, }, } @@ -518,8 +519,10 @@ impl ChainSync { match self.mode { SyncMode::Full => BlockAttributes::HEADER | BlockAttributes::JUSTIFICATION | BlockAttributes::BODY, SyncMode::Light => BlockAttributes::HEADER | BlockAttributes::JUSTIFICATION, - SyncMode::LightState { .. } => + SyncMode::LightState { storage_chain_mode: false, .. } => BlockAttributes::HEADER | BlockAttributes::JUSTIFICATION | BlockAttributes::BODY, + SyncMode::LightState { storage_chain_mode: true, .. } => + BlockAttributes::HEADER | BlockAttributes::JUSTIFICATION | BlockAttributes::INDEXED_BODY, } } @@ -914,25 +917,7 @@ impl ChainSync { peer.state = PeerSyncState::Available; validate_blocks::(&blocks, who, Some(request))?; self.blocks.insert(start_block, blocks, who.clone()); - self.blocks - .drain(self.best_queued_number + One::one()) - .into_iter() - .map(|block_data| { - let justifications = block_data.block.justifications.or( - legacy_justification_mapping(block_data.block.justification) - ); - IncomingBlock { - hash: block_data.block.hash, - header: block_data.block.header, - body: block_data.block.body, - justifications, - origin: block_data.origin, - allow_missing_state: true, - import_existing: self.import_existing, - skip_execution: self.skip_execution(), - state: None, - } - }).collect() + self.drain_blocks() } PeerSyncState::DownloadingStale(_) => { peer.state = PeerSyncState::Available; @@ -949,6 +934,7 @@ impl ChainSync { hash: b.hash, header: b.header, body: b.body, + indexed_body: None, justifications, origin: Some(who.clone()), allow_missing_state: true, @@ -1064,6 +1050,7 @@ impl ChainSync { hash: b.hash, header: b.header, body: b.body, + indexed_body: None, justifications, origin: Some(who.clone()), allow_missing_state: true, @@ -1115,6 +1102,7 @@ impl ChainSync { hash, header: Some(header), body: None, + indexed_body: None, justifications: None, origin: None, allow_missing_state: true, @@ -1367,7 +1355,7 @@ impl ChainSync { is_descendent_of(&**client, base, block) }); - if let SyncMode::LightState { skip_proofs } = &self.mode { + if let SyncMode::LightState { skip_proofs, .. } = &self.mode { if self.state_sync.is_none() && !self.peers.is_empty() && self.queue_blocks.is_empty() @@ -1757,24 +1745,7 @@ impl ChainSync { target.peers.remove(who); !target.peers.is_empty() }); - let blocks: Vec<_> = self.blocks - .drain(self.best_queued_number + One::one()) - .into_iter() - .map(|block_data| { - let justifications = - legacy_justification_mapping(block_data.block.justification); - IncomingBlock { - hash: block_data.block.hash, - header: block_data.block.header, - body: block_data.block.body, - justifications, - origin: block_data.origin, - allow_missing_state: true, - import_existing: false, - skip_execution: self.skip_execution(), - state: None, - } - }).collect(); + let blocks = self.drain_blocks(); if !blocks.is_empty() { Some(self.validate_and_queue_blocks(blocks)) } else { @@ -1878,6 +1849,31 @@ impl ChainSync { _priv: () } } + + /// Drain the downloaded block set up to the first gap. + fn drain_blocks(&mut self) -> Vec> { + self.blocks + .drain(self.best_queued_number + One::one()) + .into_iter() + .map(|block_data| { + let justifications = block_data.block.justifications.or( + legacy_justification_mapping(block_data.block.justification) + ); + IncomingBlock { + hash: block_data.block.hash, + header: block_data.block.header, + body: block_data.block.body, + indexed_body: block_data.block.indexed_body, + justifications, + origin: block_data.origin, + allow_missing_state: true, + import_existing: self.import_existing, + skip_execution: self.skip_execution(), + state: None, + } + }).collect() + } + } // This is purely during a backwards compatible transitionary period and should be removed @@ -2383,6 +2379,7 @@ mod test { hash: b.hash(), header: Some(b.header().clone()), body: Some(b.deconstruct().1), + indexed_body: None, receipt: None, message_queue: None, justification: None, diff --git a/client/network/src/protocol/sync/blocks.rs b/client/network/src/protocol/sync/blocks.rs index 81f9cffacaab4..01b5f6016f8a5 100644 --- a/client/network/src/protocol/sync/blocks.rs +++ b/client/network/src/protocol/sync/blocks.rs @@ -225,6 +225,7 @@ mod test { hash: H256::random(), header: None, body: None, + indexed_body: None, message_queue: None, receipt: None, justification: None, diff --git a/client/network/src/schema/api.v1.proto b/client/network/src/schema/api.v1.proto index a16fdbaebc81b..c5333c7dcdbf1 100644 --- a/client/network/src/schema/api.v1.proto +++ b/client/network/src/schema/api.v1.proto @@ -66,6 +66,8 @@ message BlockData { // is because empty justifications, like all justifications, are paired with a non-empty // consensus engine ID. bytes justifications = 8; // optional + // Indexed block body if requestd. + repeated bytes indexed_body = 9; // optional } // Request storage data from a peer. diff --git a/client/network/test/src/block_import.rs b/client/network/test/src/block_import.rs index 05169aba8d730..6d3ceb4a933d8 100644 --- a/client/network/test/src/block_import.rs +++ b/client/network/test/src/block_import.rs @@ -42,6 +42,7 @@ fn prepare_good_block() -> (TestClient, Hash, u64, PeerId, IncomingBlock) hash, header, body: Some(Vec::new()), + indexed_body: None, justifications, origin: Some(peer_id.clone()), allow_missing_state: false, diff --git a/client/network/test/src/lib.rs b/client/network/test/src/lib.rs index b6e8f897bb809..900e05e26a78f 100644 --- a/client/network/test/src/lib.rs +++ b/client/network/test/src/lib.rs @@ -660,6 +660,8 @@ pub struct FullPeerConfig { pub is_authority: bool, /// Syncing mode pub sync_mode: SyncMode, + /// Enable transaction indexing. + pub storage_chain: bool, } pub trait TestNetFactory: Sized where >::Transaction: Send { @@ -715,9 +717,11 @@ pub trait TestNetFactory: Sized where >: /// Add a full peer. fn add_full_peer_with_config(&mut self, config: FullPeerConfig) { - let mut test_client_builder = match config.keep_blocks { - Some(keep_blocks) => TestClientBuilder::with_pruning_window(keep_blocks), - None => TestClientBuilder::with_default_backend(), + let mut test_client_builder = match (config.keep_blocks, config.storage_chain) { + (Some(keep_blocks), true) => TestClientBuilder::with_tx_storage(keep_blocks), + (None, true) => TestClientBuilder::with_tx_storage(u32::MAX), + (Some(keep_blocks), false) => TestClientBuilder::with_pruning_window(keep_blocks), + (None, false) => TestClientBuilder::with_default_backend(), }; if matches!(config.sync_mode, SyncMode::Fast{..}) { test_client_builder = test_client_builder.set_no_genesis(); diff --git a/client/network/test/src/sync.rs b/client/network/test/src/sync.rs index 56cec7e4cdfd9..f998c9ebde757 100644 --- a/client/network/test/src/sync.rs +++ b/client/network/test/src/sync.rs @@ -1095,7 +1095,7 @@ fn syncs_state() { let mut net = TestNet::new(0); net.add_full_peer_with_config(Default::default()); net.add_full_peer_with_config(FullPeerConfig { - sync_mode: SyncMode::Fast { skip_proofs: *skip_proofs }, + sync_mode: SyncMode::Fast { skip_proofs: *skip_proofs, storage_chain_mode: false }, ..Default::default() }); net.peer(0).push_blocks(64, false); @@ -1127,3 +1127,39 @@ fn syncs_state() { } } +#[test] +fn syncs_indexed_blocks() { + use sp_runtime::traits::Hash; + sp_tracing::try_init_simple(); + let mut net = TestNet::new(0); + let mut n: u64 = 0; + net.add_full_peer_with_config(FullPeerConfig { + storage_chain: true, + ..Default::default() + }); + net.add_full_peer_with_config(FullPeerConfig { + storage_chain: true, + sync_mode: SyncMode::Fast { skip_proofs: false, storage_chain_mode: true }, + ..Default::default() + }); + net.peer(0).generate_blocks_at( + BlockId::number(0), + 64, + BlockOrigin::Own, |mut builder| { + let ex = Extrinsic::Store(n.to_le_bytes().to_vec()); + n += 1; + builder.push(ex).unwrap(); + builder.build().unwrap().block + }, + false, + true, + true, + ); + let indexed_key = sp_runtime::traits::BlakeTwo256::hash(&42u64.to_le_bytes()); + assert!(net.peer(0).client().as_full().unwrap().indexed_transaction(&indexed_key).unwrap().is_some()); + assert!(net.peer(1).client().as_full().unwrap().indexed_transaction(&indexed_key).unwrap().is_none()); + + net.block_until_sync(); + assert!(net.peer(1).client().as_full().unwrap().indexed_transaction(&indexed_key).unwrap().is_some()); +} + diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index 6318469a7f0bc..46590ce8e8c6c 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -21,7 +21,7 @@ use crate::{ start_rpc_servers, build_network_future, TransactionPoolAdapter, TaskManager, SpawnTaskHandle, metrics::MetricsService, client::{light, Client, ClientConfig}, - config::{Configuration, KeystoreConfig, PrometheusConfig}, + config::{Configuration, KeystoreConfig, PrometheusConfig, TransactionStorageMode}, }; use sc_client_api::{ light::RemoteBlockchain, ForkBlocks, BadBlocks, UsageProvider, ExecutorProvider, @@ -40,7 +40,7 @@ use futures::{ }; use sc_keystore::LocalKeystore; use log::info; -use sc_network::config::{Role, OnDemand}; +use sc_network::config::{Role, OnDemand, SyncMode}; use sc_network::NetworkService; use sc_network::block_request_handler::{self, BlockRequestHandler}; use sc_network::state_request_handler::{self, StateRequestHandler}; @@ -946,7 +946,7 @@ pub fn build_network( } }; - let network_params = sc_network::config::Params { + let mut network_params = sc_network::config::Params { role: config.role.clone(), executor: { let spawn_handle = Clone::clone(&spawn_handle); @@ -973,6 +973,15 @@ pub fn build_network( light_client_request_protocol_config, }; + // Storage chains don't keep full block history and can't be synced in full mode. + // Force fast sync when storage chain mode is enabled. + if matches!(config.transaction_storage, TransactionStorageMode::StorageChain) { + network_params.network_config.sync_mode = SyncMode::Fast { + storage_chain_mode: true, + skip_proofs: false, + }; + } + let has_bootnodes = !network_params.network_config.boot_nodes.is_empty(); let network_mut = sc_network::NetworkWorker::new(network_params)?; let network = network_mut.service().clone(); diff --git a/client/service/src/chain_ops/import_blocks.rs b/client/service/src/chain_ops/import_blocks.rs index 330aaea4f555b..75ea6670f3525 100644 --- a/client/service/src/chain_ops/import_blocks.rs +++ b/client/service/src/chain_ops/import_blocks.rs @@ -168,6 +168,7 @@ fn import_block_to_queue( hash, header: Some(header), body: Some(extrinsics), + indexed_body: None, justifications: signed_block.justifications, origin: None, allow_missing_state: false, diff --git a/client/service/src/client/client.rs b/client/service/src/client/client.rs index 25957560f4dbd..8e808a3d824b8 100644 --- a/client/service/src/client/client.rs +++ b/client/service/src/client/client.rs @@ -355,6 +355,7 @@ impl Client where genesis_block.deconstruct().0, Some(vec![]), None, + None, block_state, )?; backend.commit_operation(op)?; @@ -657,6 +658,7 @@ impl Client where justifications, post_digests, body, + indexed_body, finalized, auxiliary, fork_choice, @@ -695,6 +697,7 @@ impl Client where import_headers, justifications, body, + indexed_body, storage_changes, new_cache, finalized, @@ -734,6 +737,7 @@ impl Client where import_headers: PrePostHeader, justifications: Option, body: Option>, + indexed_body: Option>>, storage_changes: Option>>, new_cache: HashMap>, finalized: bool, @@ -871,6 +875,7 @@ impl Client where operation.op.set_block_data( import_headers.post().clone(), body, + indexed_body, justifications, leaf_state, )?; diff --git a/client/service/test/src/client/light.rs b/client/service/test/src/client/light.rs index 440e0b4dd0dc3..4d620139fa49e 100644 --- a/client/service/test/src/client/light.rs +++ b/client/service/test/src/client/light.rs @@ -266,7 +266,7 @@ fn local_state_is_created_when_genesis_state_is_available() { Arc::new(DummyBlockchain::new(DummyStorage::new())), ); let mut op = backend.begin_operation().unwrap(); - op.set_block_data(header0, None, None, NewBlockState::Final).unwrap(); + op.set_block_data(header0, None, None, None, NewBlockState::Final).unwrap(); op.set_genesis_state(Default::default(), true).unwrap(); backend.commit_operation(op).unwrap(); diff --git a/primitives/consensus/common/src/block_import.rs b/primitives/consensus/common/src/block_import.rs index 447ea5761f767..a444e15095ef6 100644 --- a/primitives/consensus/common/src/block_import.rs +++ b/primitives/consensus/common/src/block_import.rs @@ -196,6 +196,8 @@ pub struct BlockImportParams { pub post_digests: Vec>, /// The body of the block. pub body: Option>, + /// Indexed transaction body of the block. + pub indexed_body: Option>>, /// Specify how the new state is computed. pub state_action: StateAction, /// Is this block finalized already? @@ -233,6 +235,7 @@ impl BlockImportParams { justifications: None, post_digests: Vec::new(), body: None, + indexed_body: None, state_action: StateAction::Execute, finalized: false, intermediates: HashMap::new(), @@ -286,6 +289,7 @@ impl BlockImportParams { justifications: self.justifications, post_digests: self.post_digests, body: self.body, + indexed_body: self.indexed_body, state_action, finalized: self.finalized, auxiliary: self.auxiliary, diff --git a/primitives/consensus/common/src/import_queue.rs b/primitives/consensus/common/src/import_queue.rs index fba5b51e921ca..6cac6b1ff9201 100644 --- a/primitives/consensus/common/src/import_queue.rs +++ b/primitives/consensus/common/src/import_queue.rs @@ -68,6 +68,8 @@ pub struct IncomingBlock { pub header: Option<::Header>, /// Block body if requested. pub body: Option::Extrinsic>>, + /// Indexed block body if requested. + pub indexed_body: Option>>, /// Justification(s) if requested. pub justifications: Option, /// The peer, we received this from @@ -269,6 +271,7 @@ pub(crate) async fn import_single_block_metered, Trans cache.extend(keys.into_iter()); } import_block.import_existing = block.import_existing; + import_block.indexed_body = block.indexed_body; let mut import_block = import_block.clear_storage_changes_and_mutate(); if let Some(state) = block.state { import_block.state_action = StateAction::ApplyChanges(crate::StorageChanges::Import(state)); diff --git a/primitives/consensus/common/src/import_queue/basic_queue.rs b/primitives/consensus/common/src/import_queue/basic_queue.rs index 5767b72dd8084..8dd40d84df305 100644 --- a/primitives/consensus/common/src/import_queue/basic_queue.rs +++ b/primitives/consensus/common/src/import_queue/basic_queue.rs @@ -560,6 +560,7 @@ mod tests { hash, header: Some(header), body: None, + indexed_body: None, justifications: None, origin: None, allow_missing_state: false, diff --git a/test-utils/client/src/lib.rs b/test-utils/client/src/lib.rs index eb810e0360588..0971c00d78428 100644 --- a/test-utils/client/src/lib.rs +++ b/test-utils/client/src/lib.rs @@ -102,6 +102,16 @@ impl TestClientBuilder Self { + let backend = Arc::new(Backend::new_test_with_tx_storage( + keep_blocks, + 0, + sc_client_db::TransactionStorageMode::StorageChain, + )); + Self::with_backend(backend) + } } impl TestClientBuilder { diff --git a/test-utils/runtime/src/lib.rs b/test-utils/runtime/src/lib.rs index 1023b77939bbc..f4c722ab12c2b 100644 --- a/test-utils/runtime/src/lib.rs +++ b/test-utils/runtime/src/lib.rs @@ -167,6 +167,7 @@ pub enum Extrinsic { ChangesTrieConfigUpdate(Option), OffchainIndexSet(Vec, Vec), OffchainIndexClear(Vec), + Store(Vec), } parity_util_mem::malloc_size_of_is_0!(Extrinsic); // non-opaque extrinsic does not need this @@ -199,6 +200,7 @@ impl BlindCheckable for Extrinsic { Ok(Extrinsic::OffchainIndexSet(key, value)), Extrinsic::OffchainIndexClear(key) => Ok(Extrinsic::OffchainIndexClear(key)), + Extrinsic::Store(data) => Ok(Extrinsic::Store(data)), } } } diff --git a/test-utils/runtime/src/system.rs b/test-utils/runtime/src/system.rs index ae35ded83bfc7..c4b88c09e8d23 100644 --- a/test-utils/runtime/src/system.rs +++ b/test-utils/runtime/src/system.rs @@ -272,6 +272,8 @@ fn execute_transaction_backend(utx: &Extrinsic, extrinsic_index: u32) -> ApplyEx sp_io::offchain_index::clear(&key); Ok(Ok(())) } + Extrinsic::Store(data) => + execute_store(data.clone()), } } @@ -301,6 +303,13 @@ fn execute_transfer_backend(tx: &Transfer) -> ApplyExtrinsicResult { Ok(Ok(())) } +fn execute_store(data: Vec) -> ApplyExtrinsicResult { + let content_hash = sp_io::hashing::blake2_256(&data); + let extrinsic_index: u32 = storage::unhashed::get(well_known_keys::EXTRINSIC_INDEX).unwrap(); + sp_io::transaction_index::index(extrinsic_index, data.len() as u32, content_hash); + Ok(Ok(())) +} + fn execute_new_authorities_backend(new_authorities: &[AuthorityId]) -> ApplyExtrinsicResult { NewAuthorities::put(new_authorities.to_vec()); Ok(Ok(()))