Skip to content

Commit

Permalink
Add the genesis hash to the database (#2928)
Browse files Browse the repository at this point in the history
Close #476

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
tomaka and mergify[bot] authored Oct 27, 2022
1 parent 70681cb commit a413d18
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 10 deletions.
4 changes: 2 additions & 2 deletions bin/light-base/src/json_rpc_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ impl ServicePrototype {
Default::default(),
),
}),
genesis_block: config.genesis_block_hash,
genesis_block_hash: config.genesis_block_hash,
next_subscription_id: atomic::AtomicU64::new(0),
subscriptions: Mutex::new(Subscriptions {
misc: HashMap::with_capacity_and_hasher(
Expand Down Expand Up @@ -466,7 +466,7 @@ struct Background<TPlat: Platform> {
/// Hash of the genesis block.
/// Keeping the genesis block is important, as the genesis block hash is included in
/// transaction signatures, and must therefore be queried by upper-level UIs.
genesis_block: [u8; 32],
genesis_block_hash: [u8; 32],

/// Identifier to use for the next subscription.
///
Expand Down
1 change: 1 addition & 0 deletions bin/light-base/src/json_rpc_service/chain_head.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1441,6 +1441,7 @@ impl<TPlat: Platform> Background<TPlat> {
let response = super::super::encode_database(
&self.network_service.0,
&self.sync_service,
&self.genesis_block_hash,
self.sync_service.block_number_bytes(),
usize::try_from(max_size_bytes.unwrap_or(u64::max_value()))
.unwrap_or(usize::max_value()),
Expand Down
4 changes: 2 additions & 2 deletions bin/light-base/src/json_rpc_service/getters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ impl<TPlat: Platform> Background<TPlat> {
.respond(
state_machine_request_id,
methods::Response::chainHead_unstable_genesisHash(methods::HashHexString(
self.genesis_block,
self.genesis_block_hash,
))
.to_json_response(request_id),
)
Expand Down Expand Up @@ -93,7 +93,7 @@ impl<TPlat: Platform> Background<TPlat> {
.respond(
state_machine_request_id,
methods::Response::chainSpec_unstable_genesisHash(methods::HashHexString(
self.genesis_block,
self.genesis_block_hash,
))
.to_json_response(request_id),
)
Expand Down
2 changes: 1 addition & 1 deletion bin/light-base/src/json_rpc_service/state_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ impl<TPlat: Platform> Background<TPlat> {
let response = {
match height {
Some(0) => methods::Response::chain_getBlockHash(methods::HashHexString(
self.genesis_block,
self.genesis_block_hash,
))
.to_json_response(request_id),
None => {
Expand Down
43 changes: 38 additions & 5 deletions bin/light-base/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -369,8 +369,16 @@ impl<TPlat: platform::Platform, TChain> Client<TPlat, TChain> {
),
) {
// Use the database if it contains a more recent block than the chain spec checkpoint.
(Ok(Ok(genesis_ci)), checkpoint, Ok((database, checkpoint_nodes)))
if checkpoint
(
Ok(Ok(genesis_ci)),
checkpoint,
Ok((genesis_hash, database, checkpoint_nodes)),
) if 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| {
Expand All @@ -386,7 +394,7 @@ impl<TPlat: platform::Platform, TChain> Client<TPlat, TChain> {
(
Err(chain_spec::FromGenesisStorageError::UnknownStorageItems),
checkpoint,
Ok((database, checkpoint_nodes)),
Ok((database_genesis_hash, database, checkpoint_nodes)),
) if checkpoint
.as_ref()
.and_then(|r| r.as_ref().ok())
Expand All @@ -403,7 +411,20 @@ impl<TPlat: platform::Platform, TChain> Client<TPlat, TChain> {
digest: header::DigestRef::empty().into(),
};

(database, genesis_header, checkpoint_nodes)
if database_genesis_hash
== genesis_header.hash(chain_spec.block_number_bytes().into())
{
(database, genesis_header, checkpoint_nodes)
} else if let Some(Ok(checkpoint)) = checkpoint {
// Database is incorrect.
(checkpoint, genesis_header, checkpoint_nodes)
} else {
// TODO: we can in theory support chain specs that have neither a checkpoint nor the genesis storage, but it's complicated
return Err(
"Either a checkpoint or the genesis storage must be provided"
.to_string(),
);
}
}

(Err(chain_spec::FromGenesisStorageError::UnknownStorageItems), None, _) => {
Expand Down Expand Up @@ -1115,11 +1136,13 @@ async fn start_services<TPlat: platform::Platform>(
async fn encode_database<TPlat: platform::Platform>(
network_service: &network_service::NetworkService<TPlat>,
sync_service: &sync_service::SyncService<TPlat>,
genesis_block_hash: &[u8; 32],
block_number_bytes: usize,
max_size: usize,
) -> String {
// 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, block_number_bytes);
Expand Down Expand Up @@ -1188,13 +1211,20 @@ fn decode_database(
block_number_bytes: usize,
) -> Result<
(
[u8; 32],
chain::chain_information::ValidChainInformation,
Vec<(PeerId, Vec<multiaddr::Multiaddr>)>,
),
(),
> {
let decoded: SerdeDatabase = serde_json::from_str(encoded).map_err(|_| ())?;

let genesis_hash = if decoded.genesis_hash.len() == 64 {
<[u8; 32]>::try_from(hex::decode(&decoded.genesis_hash).map_err(|_| ())?).unwrap()
} else {
return Err(());
};

let (chain, _) = finalized_serialize::decode_chain(
&serde_json::to_string(&decoded.chain).unwrap(),
block_number_bytes,
Expand All @@ -1216,11 +1246,14 @@ fn decode_database(
})
.collect::<Vec<_>>();

Ok((chain, nodes))
Ok((genesis_hash, chain, nodes))
}

#[derive(serde::Serialize, serde::Deserialize)]
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>,
nodes: hashbrown::HashMap<String, Vec<String>, fnv::FnvBuildHasher>,
}
1 change: 1 addition & 0 deletions bin/wasm-node/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

- The `payment_queryInfo` JSON-RPC function now works with runtimes that have defined the type of `Balance` to be less than 16 bytes. ([#2914](https://github.com/paritytech/smoldot/pull/2914))
- The parameter of `chainHead_unstable_finalizedDatabase` has been renamed from `max_size_bytes` to `maxSizeBytes`. ([#2923](https://github.com/paritytech/smoldot/pull/2923))
- The database now contains the hash of the genesis block header. This hash is verified when the database is loaded, and the database is ignored if there is a mismatch. This prevents accidents where the wrong database is provided, which would lead to the chain not working and would be hard to debug. ([#2928](https://github.com/paritytech/smoldot/pull/2928))

## 0.7.3 - 2022-10-19

Expand Down

0 comments on commit a413d18

Please sign in to comment.