diff --git a/light-base/src/database.rs b/light-base/src/database.rs index 2e866444be..1f5be30823 100644 --- a/light-base/src/database.rs +++ b/light-base/src/database.rs @@ -49,11 +49,14 @@ pub use smoldot::trie::Nibble; pub struct DatabaseContent { /// Hash of the genesis block, as provided to [`encode_database`]. pub genesis_block_hash: [u8; 32], + /// Information about the finalized chain. - pub chain_information: chain::chain_information::ValidChainInformation, + pub chain_information: Option, + /// List of nodes that were known to be part of the peer-to-peer network when the database /// was encoded. pub known_nodes: Vec<(PeerId, Vec)>, + /// Known valid Merkle value and storage value combination for the `:code` key. /// /// Does **not** necessarily match the finalized block found in @@ -62,7 +65,7 @@ pub struct DatabaseContent { } /// See [`DatabaseContent::runtime_code_hint`]. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct DatabaseContentRuntimeCodeHint { /// Storage value of the `:code` trie node corresponding to /// [`DatabaseContentRuntimeCodeHint::code_merkle_value`]. @@ -93,23 +96,10 @@ pub async fn encode_database( // Craft the structure containing all the data that we would like to include. let mut database_draft = SerdeDatabase { genesis_hash: hex::encode(genesis_block_hash), - chain: match sync_service.serialize_chain_information().await { - Some(ci) => { - let encoded = - finalized_serialize::encode_chain(&ci, sync_service.block_number_bytes()); - serde_json::from_str(&encoded).unwrap() - } - None => { - // If the chain information can't be obtained, we just return a dummy value that - // will intentionally fail to decode if passed back. - let dummy_message = ""; - return if dummy_message.len() > max_size { - String::new() - } else { - dummy_message.to_owned() - }; - } - }, + chain: sync_service.serialize_chain_information().await.map(|ci| { + let encoded = finalized_serialize::encode_chain(&ci, sync_service.block_number_bytes()); + serde_json::from_str(&encoded).unwrap() + }), nodes: network_service .discovered_nodes(0) // TODO: hacky chain_index .await @@ -190,13 +180,17 @@ pub fn decode_database(encoded: &str, block_number_bytes: usize) -> Result Some( + finalized_serialize::decode_chain( + &serde_json::to_string(chain).unwrap(), + block_number_bytes, + ) + .map_err(|_| ())? + .chain_information, + ), + None => None, + }; // Nodes that fail to decode are simply ignored. This is especially important for // multiaddresses, as the definition of a valid or invalid multiaddress might change across @@ -246,7 +240,8 @@ struct SerdeDatabase { /// Hexadecimal-encoded hash of the genesis block header. Has no `0x` prefix. #[serde(rename = "genesisHash")] genesis_hash: String, - chain: Box, + #[serde(default = "Default::default", skip_serializing_if = "Option::is_none")] + chain: Option>, nodes: hashbrown::HashMap, fnv::FnvBuildHasher>, #[serde( rename = "runtimeCode", diff --git a/light-base/src/lib.rs b/light-base/src/lib.rs index 623c47b29e..dd604ec6c5 100644 --- a/light-base/src/lib.rs +++ b/light-base/src/lib.rs @@ -385,48 +385,48 @@ impl Client { ), ) { // Use the database if it contains a more recent block than the chain spec checkpoint. - (Ok(genesis_ci), checkpoint, Ok(database_content)) - if database_content.genesis_block_hash - == genesis_ci - .as_ref() - .finalized_block_header - .hash(chain_spec.block_number_bytes().into()) - && checkpoint.as_ref().and_then(|r| r.as_ref().ok()).map_or( - true, - |cp| { - cp.as_ref().finalized_block_header.number - < database_content - .chain_information - .as_ref() - .finalized_block_header - .number - }, - ) => + ( + Ok(genesis_ci), + checkpoint, + Ok(database::DatabaseContent { + chain_information: Some(db_ci), + genesis_block_hash: db_genesis_hash, + known_nodes, + runtime_code_hint, + }), + ) if db_genesis_hash + == genesis_ci + .as_ref() + .finalized_block_header + .hash(chain_spec.block_number_bytes().into()) + && checkpoint + .as_ref() + .and_then(|r| r.as_ref().ok()) + .map_or(true, |cp| { + cp.as_ref().finalized_block_header.number + < db_ci.as_ref().finalized_block_header.number + }) => { let genesis_header = genesis_ci.as_ref().finalized_block_header.clone(); - ( - database_content.chain_information, - genesis_header.into(), - database_content.known_nodes, - database_content.runtime_code_hint, - ) + (db_ci, genesis_header.into(), known_nodes, runtime_code_hint) } // Use the database if it contains a more recent block than the chain spec checkpoint. ( Err(chain_spec::FromGenesisStorageError::UnknownStorageItems), checkpoint, - Ok(database_content), + Ok(database::DatabaseContent { + chain_information: Some(chain_information), + genesis_block_hash, + known_nodes, + runtime_code_hint, + }), ) if checkpoint .as_ref() .and_then(|r| r.as_ref().ok()) .map_or(true, |cp| { cp.as_ref().finalized_block_header.number - < database_content - .chain_information - .as_ref() - .finalized_block_header - .number + < chain_information.as_ref().finalized_block_header.number }) => { let genesis_header = header::Header { @@ -437,23 +437,18 @@ impl Client { digest: header::DigestRef::empty().into(), }; - if database_content.genesis_block_hash + if genesis_block_hash == genesis_header.hash(chain_spec.block_number_bytes().into()) { ( - database_content.chain_information, + chain_information, genesis_header, - database_content.known_nodes, - database_content.runtime_code_hint, + known_nodes, + runtime_code_hint, ) } else if let Some(Ok(checkpoint)) = checkpoint { // Database is incorrect. - ( - checkpoint, - genesis_header, - database_content.known_nodes, - None, - ) + (checkpoint, genesis_header, known_nodes, None) } else { // TODO: we can in theory support chain specs that have neither a checkpoint nor the genesis storage, but it's complicated // TODO: is this relevant for parachains? @@ -470,7 +465,7 @@ impl Client { ( Err(chain_spec::FromGenesisStorageError::UnknownStorageItems), Some(Ok(checkpoint)), - _, + database, ) => { let genesis_header = header::Header { parent_hash: [0; 32], @@ -480,14 +475,49 @@ impl Client { digest: header::DigestRef::empty().into(), }; - (checkpoint, genesis_header, Default::default(), None) + let mut checkpoint_nodes = Vec::new(); + let mut runtime_cache_hint = None; + + if let Ok(database) = database { + if database.genesis_block_hash + == genesis_header.hash(chain_spec.block_number_bytes().into()) + { + checkpoint_nodes = database.known_nodes.clone(); + runtime_cache_hint = database.runtime_code_hint.clone(); + } + } + + ( + checkpoint, + genesis_header, + checkpoint_nodes, + runtime_cache_hint, + ) } (Err(err), _, _) => return Err(AddChainError::InvalidGenesisStorage(err)), - (Ok(genesis_ci), Some(Ok(checkpoint)), _) => { + (Ok(genesis_ci), Some(Ok(checkpoint)), database) => { let genesis_header = genesis_ci.as_ref().finalized_block_header.clone(); - (checkpoint, genesis_header.into(), Default::default(), None) + + let mut checkpoint_nodes = Vec::new(); + let mut runtime_cache_hint = None; + + if let Ok(database) = database { + if database.genesis_block_hash + == genesis_header.hash(chain_spec.block_number_bytes().into()) + { + checkpoint_nodes = database.known_nodes.clone(); + runtime_cache_hint = database.runtime_code_hint.clone(); + } + } + + ( + checkpoint, + genesis_header.into(), + checkpoint_nodes, + runtime_cache_hint, + ) } ( @@ -496,11 +526,28 @@ impl Client { | Some(Err( chain_spec::CheckpointToChainInformationError::GenesisBlockCheckpoint, )), - _, + database, ) => { let genesis_header = header::Header::from(genesis_ci.as_ref().finalized_block_header.clone()); - (genesis_ci, genesis_header, Default::default(), None) + let mut checkpoint_nodes = Vec::new(); + let mut runtime_cache_hint = None; + + if let Ok(database) = database { + if database.genesis_block_hash + == genesis_header.hash(chain_spec.block_number_bytes().into()) + { + checkpoint_nodes = database.known_nodes.clone(); + runtime_cache_hint = database.runtime_code_hint.clone(); + } + } + + ( + genesis_ci, + genesis_header, + checkpoint_nodes, + runtime_cache_hint, + ) } (_, Some(Err(err)), _) => { diff --git a/wasm-node/CHANGELOG.md b/wasm-node/CHANGELOG.md index 47ffaaef07..aab34e9ae8 100644 --- a/wasm-node/CHANGELOG.md +++ b/wasm-node/CHANGELOG.md @@ -5,6 +5,7 @@ ### Changed - Removed the `chainHead_unstable_genesisHash` JSON-RPC function, in accordance with the latest changes in the JSON-RPC API specification. ([#1010](https://github.com/smol-dot/smoldot/pull/1010)) +- The database of a parachain now contains a list of known peer-to-peer nodes and a cache of the runtime code of the parachain, similar to the database of a relay chain. The database of a parachain was previously always empty. ([#1018](https://github.com/smol-dot/smoldot/pull/1018)) ### Fixed