polkadot_sdk_docs/guides/enable_metadata_hash.rs
1//! # Enable metadata hash verification
2//!
3//! This guide will teach you how to enable the metadata hash verification in your runtime.
4//!
5//! ## What is metadata hash verification?
6//!
7//! Each FRAME based runtime exposes metadata about itself. This metadata is used by consumers of
8//! the runtime to interpret the state, to construct transactions etc. Part of this metadata are the
9//! type information. These type information can be used to e.g. decode storage entries or to decode
10//! a transaction. So, the metadata is quite useful for wallets to interact with a FRAME based
11//! chain. Online wallets can fetch the metadata directly from any node of the chain they are
12//! connected to, but offline wallets can not do this. So, for the offline wallet to have access to
13//! the metadata it needs to be transferred and stored on the device. The problem is that the
14//! metadata has a size of several hundreds of kilobytes, which takes quite a while to transfer to
15//! these offline wallets and the internal storage of these devices is also not big enough to store
16//! the metadata for one or more networks. The next problem is that the offline wallet/user can not
17//! trust the metadata to be correct. It is very important for the metadata to be correct or
18//! otherwise an attacker could change them in a way that the offline wallet decodes a transaction
19//! in a different way than what it will be decoded to on chain. So, the user may sign an incorrect
20//! transaction leading to unexpected behavior.
21//!
22//! The metadata hash verification circumvents the issues of the huge metadata and the need to trust
23//! some metadata blob to be correct. To generate a hash for the metadata, the metadata is chunked,
24//! these chunks are put into a merkle tree and then the root of this merkle tree is the "metadata
25//! hash". For a more technical explanation on how it works, see
26//! [RFC78](https://polkadot-fellows.github.io/RFCs/approved/0078-merkleized-metadata.html). At compile
27//! time the metadata hash is generated and "baked" into the runtime. This makes it extremely cheap
28//! for the runtime to verify on chain that the metadata hash is correct. By having the runtime
29//! verify the hash on chain, the user also doesn't need to trust the offchain metadata. If the
30//! metadata hash doesn't match the on chain metadata hash the transaction will be rejected. The
31//! metadata hash itself is added to the data of the transaction that is signed, this means the
32//! actual hash does not appear in the transaction. On chain the same procedure is repeated with the
33//! metadata hash that is known by the runtime and if the metadata hash doesn't match the signature
34//! verification will fail. As the metadata hash is actually the root of a merkle tree, the offline
35//! wallet can get proofs of individual types to decode a transaction. This means that the offline
36//! wallet does not require the entire metadata to be present on the device.
37//!
38//! ## Integrating metadata hash verification into your runtime
39//!
40//! The integration of the metadata hash verification is split into two parts, first the actual
41//! integration into the runtime and secondly the enabling of the metadata hash generation at
42//! compile time.
43//!
44//! ### Runtime integration
45//!
46//! From the runtime side only the
47//! [`CheckMetadataHash`](frame_metadata_hash_extension::CheckMetadataHash) needs to be added to the
48//! list of signed extension:
49#![doc = docify::embed!("../../templates/parachain/runtime/src/lib.rs", template_signed_extra)]
50//!
51//! > **Note:**
52//! >
53//! > Adding the signed extension changes the encoding of the transaction and adds one extra byte
54//! > per transaction!
55//!
56//! This signed extension will make sure to decode the requested `mode` and will add the metadata
57//! hash to the signed data depending on the requested `mode`. The `mode` gives the user/wallet
58//! control over deciding if the metadata hash should be verified or not. The metadata hash itself
59//! is drawn from the `RUNTIME_METADATA_HASH` environment variable. If the environment variable is
60//! not set, any transaction that requires the metadata hash is rejected with the error
61//! `CannotLookup`. This is a security measurement to prevent including invalid transactions.
62//!
63//! <div class="warning">
64//!
65//! The extension does not work with the native runtime, because the
66//! `RUNTIME_METADATA_HASH` environment variable is not set when building the
67//! `frame-metadata-hash-extension` crate.
68//!
69//! </div>
70//!
71//! ### Enable metadata hash generation
72//!
73//! The metadata hash generation needs to be enabled when building the wasm binary. The
74//! `substrate-wasm-builder` supports this out of the box:
75#![doc = docify::embed!("../../templates/parachain/runtime/build.rs", template_enable_metadata_hash)]
76//!
77//! > **Note:**
78//! >
79//! > The `metadata-hash` feature needs to be enabled for the `substrate-wasm-builder` to enable the
80//! > code for being able to generate the metadata hash. It is also recommended to put the metadata
81//! > hash generation behind a feature in the runtime as shown above. The reason behind is that it
82//! > adds a lot of code which increases the compile time and the generation itself also increases
83//! > the compile time. Thus, it is recommended to enable the feature only when the metadata hash is
84//! > required (e.g. for an on-chain build).
85//!
86//! The two parameters to `enable_metadata_hash` are the token symbol and the number of decimals of
87//! the primary token of the chain. These information are included for the wallets to show token
88//! related operations in a more user friendly way.