Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parachains now also have a database #1018

Merged
merged 2 commits into from
Aug 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 22 additions & 27 deletions light-base/src/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<chain::chain_information::ValidChainInformation>,

/// 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<multiaddr::Multiaddr>)>,

/// Known valid Merkle value and storage value combination for the `:code` key.
///
/// Does **not** necessarily match the finalized block found in
Expand All @@ -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`].
Expand Down Expand Up @@ -93,23 +96,10 @@ pub async fn encode_database<TPlat: platform::PlatformRef>(
// 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 = "<unknown>";
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
Expand Down Expand Up @@ -190,13 +180,17 @@ pub fn decode_database(encoded: &str, block_number_bytes: usize) -> Result<Datab
return Err(());
};

let finalized_serialize::Decoded {
chain_information, ..
} = finalized_serialize::decode_chain(
&serde_json::to_string(&decoded.chain).unwrap(),
block_number_bytes,
)
.map_err(|_| ())?;
let chain_information = match &decoded.chain {
Some(chain) => 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
Expand Down Expand Up @@ -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_json::value::RawValue>,
#[serde(default = "Default::default", skip_serializing_if = "Option::is_none")]
chain: Option<Box<serde_json::value::RawValue>>,
nodes: hashbrown::HashMap<String, Vec<String>, fnv::FnvBuildHasher>,
#[serde(
rename = "runtimeCode",
Expand Down
137 changes: 92 additions & 45 deletions light-base/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -385,48 +385,48 @@ impl<TPlat: platform::PlatformRef, TChain> Client<TPlat, TChain> {
),
) {
// 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 {
Expand All @@ -437,23 +437,18 @@ impl<TPlat: platform::PlatformRef, TChain> Client<TPlat, TChain> {
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?
Expand All @@ -470,7 +465,7 @@ impl<TPlat: platform::PlatformRef, TChain> Client<TPlat, TChain> {
(
Err(chain_spec::FromGenesisStorageError::UnknownStorageItems),
Some(Ok(checkpoint)),
_,
database,
) => {
let genesis_header = header::Header {
parent_hash: [0; 32],
Expand All @@ -480,14 +475,49 @@ impl<TPlat: platform::PlatformRef, TChain> Client<TPlat, TChain> {
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,
)
}

(
Expand All @@ -496,11 +526,28 @@ impl<TPlat: platform::PlatformRef, TChain> Client<TPlat, TChain> {
| 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)), _) => {
Expand Down
1 change: 1 addition & 0 deletions wasm-node/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down