mirror of
https://github.com/pezkuwichain/pezkuwi-fellows.git
synced 2026-05-30 07:01:01 +00:00
480 lines
37 KiB
HTML
480 lines
37 KiB
HTML
|
|
<!DOCTYPE HTML>
|
|
<html lang="en" class="polkadot" dir="ltr">
|
|
<head>
|
|
<!-- Book generated using mdBook -->
|
|
<meta charset="UTF-8">
|
|
<title>RFC-0000: Metadata for offline signers - Polkadot Fellowship RFCs</title>
|
|
|
|
|
|
<!-- Custom HTML head -->
|
|
|
|
<meta name="description" content="An online book of RFCs approved or proposed within the Polkadot Fellowship.">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<meta name="theme-color" content="#ffffff">
|
|
|
|
<link rel="icon" href="../favicon.svg">
|
|
<link rel="shortcut icon" href="../favicon.png">
|
|
<link rel="stylesheet" href="../css/variables.css">
|
|
<link rel="stylesheet" href="../css/general.css">
|
|
<link rel="stylesheet" href="../css/chrome.css">
|
|
<link rel="stylesheet" href="../css/print.css" media="print">
|
|
|
|
<!-- Fonts -->
|
|
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
|
<link rel="stylesheet" href="../fonts/fonts.css">
|
|
|
|
<!-- Highlight.js Stylesheets -->
|
|
<link rel="stylesheet" href="../highlight.css">
|
|
<link rel="stylesheet" href="../tomorrow-night.css">
|
|
<link rel="stylesheet" href="../ayu-highlight.css">
|
|
|
|
<!-- Custom theme stylesheets -->
|
|
<link rel="stylesheet" href="../theme/polkadot.css">
|
|
|
|
</head>
|
|
<body class="sidebar-visible no-js">
|
|
<div id="body-container">
|
|
<!-- Provide site root to javascript -->
|
|
<script>
|
|
var path_to_root = "../";
|
|
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "polkadot" : "polkadot";
|
|
</script>
|
|
|
|
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
|
<script>
|
|
try {
|
|
var theme = localStorage.getItem('mdbook-theme');
|
|
var sidebar = localStorage.getItem('mdbook-sidebar');
|
|
|
|
if (theme.startsWith('"') && theme.endsWith('"')) {
|
|
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
|
}
|
|
|
|
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
|
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
|
}
|
|
} catch (e) { }
|
|
</script>
|
|
|
|
<!-- Set the theme before any content is loaded, prevents flash -->
|
|
<script>
|
|
var theme;
|
|
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
|
if (theme === null || theme === undefined) { theme = default_theme; }
|
|
var html = document.querySelector('html');
|
|
html.classList.remove('polkadot')
|
|
html.classList.add(theme);
|
|
var body = document.querySelector('body');
|
|
body.classList.remove('no-js')
|
|
body.classList.add('js');
|
|
</script>
|
|
|
|
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
|
|
|
<!-- Hide / unhide sidebar before it is displayed -->
|
|
<script>
|
|
var body = document.querySelector('body');
|
|
var sidebar = null;
|
|
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
|
if (document.body.clientWidth >= 1080) {
|
|
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
|
sidebar = sidebar || 'visible';
|
|
} else {
|
|
sidebar = 'hidden';
|
|
}
|
|
sidebar_toggle.checked = sidebar === 'visible';
|
|
body.classList.remove('sidebar-visible');
|
|
body.classList.add("sidebar-" + sidebar);
|
|
</script>
|
|
|
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
|
<div class="sidebar-scrollbox">
|
|
<ol class="chapter"><li class="chapter-item expanded affix "><a href="../introduction.html">Introduction</a></li><li class="spacer"></li><li class="chapter-item expanded affix "><li class="part-title">Approved</li><li class="chapter-item expanded "><a href="../approved/0001-agile-coretime.html">RFC-1: Agile Coretime</a></li><li class="chapter-item expanded "><a href="../approved/0005-coretime-interface.html">RFC-5: Coretime Interface</a></li><li class="chapter-item expanded "><a href="../approved/0007-system-collator-selection.html">RFC-0007: System Collator Selection</a></li><li class="chapter-item expanded "><a href="../approved/0008-parachain-bootnodes-dht.html">RFC-0008: Store parachain bootnodes in relay chain DHT</a></li><li class="chapter-item expanded "><a href="../approved/0012-process-for-adding-new-collectives.html">RFC-0012: Process for Adding New System Collectives</a></li><li class="chapter-item expanded "><a href="../approved/0014-improve-locking-mechanism-for-parachains.html">RFC-0014: Improve locking mechanism for parachains</a></li><li class="chapter-item expanded "><a href="../approved/0022-adopt-encointer-runtime.html">RFC-0022: Adopt Encointer Runtime</a></li><li class="chapter-item expanded "><a href="../approved/0032-minimal-relay.html">RFC-0032: Minimal Relay</a></li><li class="chapter-item expanded "><a href="../approved/0050-fellowship-salaries.html">RFC-0050: Fellowship Salaries</a></li><li class="chapter-item expanded "><a href="../approved/0056-one-transaction-per-notification.html">RFC-0056: Enforce only one transaction per notification</a></li><li class="spacer"></li><li class="chapter-item expanded affix "><li class="part-title">Newly Proposed</li><li class="spacer"></li><li class="chapter-item expanded affix "><li class="part-title">Proposed</li><li class="chapter-item expanded "><a href="../proposed/0004-remove-unnecessary-allocator-usage.html">RFC-0004: Remove the host-side runtime memory allocator</a></li><li class="chapter-item expanded "><a href="../proposed/0026-sassafras-consensus.html">RFC-0026: Sassafras Consensus Protocol</a></li><li class="chapter-item expanded "><a href="../proposed/0034-xcm-absolute-location-account-derivation.html">RFC-34: XCM Absolute Location Account Derivation</a></li><li class="chapter-item expanded "><a href="../proposed/0042-extrinsics-state-version.html">RFC-0042: Add System version that replaces StateVersion on RuntimeVersion</a></li><li class="chapter-item expanded "><a href="../proposed/0044-rent-based-registration.html">RFC-0044: Rent based registration model</a></li><li class="chapter-item expanded "><a href="../proposed/0046-metadata-for-offline-signers.html" class="active">RFC-0000: Metadata for offline signers</a></li><li class="chapter-item expanded "><a href="../proposed/0047-assignment-of-availability-chunks.html">RFC-0047: Assignment of availability chunks to validators</a></li><li class="chapter-item expanded "><a href="../proposed/0059-nodes-capabilities-discovery.html">RFC-0059: Add a discovery mechanism for nodes based on their capabilities</a></li><li class="chapter-item expanded "><a href="../proposed/0061-allocator-inside-of-runtime.html">RFC-0061: Support allocator inside of runtime</a></li><li class="chapter-item expanded "><a href="../proposed/0062-lowering-existential-deposit-on-assethub.html">RFC-0062: Lowering Existential Deposit on Asset Hub for Polkadot</a></li><li class="spacer"></li><li class="chapter-item expanded affix "><li class="part-title">Stale</li><li class="chapter-item expanded "><a href="../stale/0006-dynamic-pricing-for-bulk-coretime-sales.html">RFC-0006: Dynamic Pricing for Bulk Coretime Sales</a></li><li class="chapter-item expanded "><a href="../stale/0009-improved-net-light-client-requests.html">RFC-0009: Improved light client requests networking protocol</a></li><li class="chapter-item expanded "><a href="../stale/000x-assethub.html">RFC-0000: Lowering NFT Deposits on Polkadot and Kusama Asset Hubs</a></li><li class="chapter-item expanded "><a href="../stale/0010-burn-coretime-revenue.html">RFC-0010: Burn Coretime Revenue</a></li><li class="chapter-item expanded "><a href="../stale/0011-add-new-path-to-account-creation-on-asset-hubs.html">RFC-0011: Add New Path to Account Creation on Asset Hubs</a></li><li class="chapter-item expanded "><a href="../stale/0013-prepare-blockbuilder-and-core-runtime-apis-for-mbms.html">RFC-0013: Prepare BlockBuilder and Core runtime APIs for MBMs</a></li><li class="chapter-item expanded "><a href="../stale/0015-market-design-revisit.html">RFC-0015: Market Design Revisit</a></li><li class="chapter-item expanded "><a href="../stale/0020-treasurer-track-confirmation-period-duration-modification.html">RFC-0020: Treasurer Track Confirmation Period Duration Modification</a></li><li class="chapter-item expanded "><a href="../stale/0035-conviction-voting-delegation-modifications.html"> RFC-0035: Conviction Voting Delegation Modifications</a></li><li class="chapter-item expanded "><a href="../stale/0043-storage-proof-size-hostfunction.html">RFC-0043: Introduce storage_proof_size Host Function for Improved Parachain Block Utilization</a></li><li class="chapter-item expanded "><a href="../stale/0048-session-keys-runtime-api.html">RFC-0048: Generate ownership proof for SessionKeys</a></li><li class="chapter-item expanded "><a href="../stale/0054-remove-heap-pages.html">RFC-0054: Remove the concept of "heap pages" from the client</a></li></ol>
|
|
</div>
|
|
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
|
</nav>
|
|
|
|
<!-- Track and set sidebar scroll position -->
|
|
<script>
|
|
var sidebarScrollbox = document.querySelector('#sidebar .sidebar-scrollbox');
|
|
sidebarScrollbox.addEventListener('click', function(e) {
|
|
if (e.target.tagName === 'A') {
|
|
sessionStorage.setItem('sidebar-scroll', sidebarScrollbox.scrollTop);
|
|
}
|
|
}, { passive: true });
|
|
var sidebarScrollTop = sessionStorage.getItem('sidebar-scroll');
|
|
sessionStorage.removeItem('sidebar-scroll');
|
|
if (sidebarScrollTop) {
|
|
// preserve sidebar scroll position when navigating via links within sidebar
|
|
sidebarScrollbox.scrollTop = sidebarScrollTop;
|
|
} else {
|
|
// scroll sidebar to current active section when navigating via "next/previous chapter" buttons
|
|
var activeSection = document.querySelector('#sidebar .active');
|
|
if (activeSection) {
|
|
activeSection.scrollIntoView({ block: 'center' });
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<div id="page-wrapper" class="page-wrapper">
|
|
|
|
<div class="page">
|
|
<div id="menu-bar-hover-placeholder"></div>
|
|
<div id="menu-bar" class="menu-bar sticky">
|
|
<div class="left-buttons">
|
|
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
|
<i class="fa fa-bars"></i>
|
|
</label>
|
|
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
|
<i class="fa fa-paint-brush"></i>
|
|
</button>
|
|
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
|
<li role="none"><button role="menuitem" class="theme" id="polkadot">Polkadot</button></li>
|
|
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
|
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
|
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
|
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
|
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
|
</ul>
|
|
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
|
<i class="fa fa-search"></i>
|
|
</button>
|
|
</div>
|
|
|
|
<h1 class="menu-title">Polkadot Fellowship RFCs</h1>
|
|
|
|
<div class="right-buttons">
|
|
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
|
<i id="print-button" class="fa fa-print"></i>
|
|
</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<div id="search-wrapper" class="hidden">
|
|
<form id="searchbar-outer" class="searchbar-outer">
|
|
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
|
</form>
|
|
<div id="searchresults-outer" class="searchresults-outer hidden">
|
|
<div id="searchresults-header" class="searchresults-header"></div>
|
|
<ul id="searchresults">
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
|
<script>
|
|
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
|
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
|
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
|
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
|
});
|
|
</script>
|
|
|
|
<div id="content" class="content">
|
|
<main>
|
|
<p><a href="https://github.com/polkadot-fellows/RFCs/pull/46">(source)</a></p>
|
|
<p><strong>Table of Contents</strong></p>
|
|
<ul>
|
|
<li><a href="#rfc-0000-metadata-for-offline-signers">RFC-0000: Metadata for offline signers</a>
|
|
<ul>
|
|
<li><a href="#summary">Summary</a></li>
|
|
<li><a href="#motivation">Motivation</a>
|
|
<ul>
|
|
<li><a href="#background">Background</a></li>
|
|
<li><a href="#solution-requirements">Solution requirements</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#stakeholders">Stakeholders</a></li>
|
|
<li><a href="#explanation">Explanation</a>
|
|
<ul>
|
|
<li><a href="#definitions">Definitions</a></li>
|
|
<li><a href="#general-flow">General flow</a></li>
|
|
<li><a href="#metadata-modularization">Metadata modularization</a></li>
|
|
<li><a href="#merging-protocol">Merging protocol</a></li>
|
|
<li><a href="#complete-binary-merkle-tree-construction-protocol">Complete Binary Merkle Tree construction protocol</a></li>
|
|
<li><a href="#digest">Digest</a></li>
|
|
<li><a href="#shortening">Shortening</a></li>
|
|
<li><a href="#transmission">Transmission</a></li>
|
|
<li><a href="#offline-verification">Offline verification</a></li>
|
|
<li><a href="#chain-verification">Chain verification</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#drawbacks">Drawbacks</a>
|
|
<ul>
|
|
<li><a href="#increased-transaction-size">Increased transaction size</a></li>
|
|
<li><a href="#transition-overhead">Transition overhead</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#testing-security-and-privacy">Testing, Security, and Privacy</a></li>
|
|
<li><a href="#performance-ergonomics-and-compatibility">Performance, Ergonomics, and Compatibility</a>
|
|
<ul>
|
|
<li><a href="#performance">Performance</a></li>
|
|
<li><a href="#ergonomics">Ergonomics</a></li>
|
|
<li><a href="#compatibility">Compatibility</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#prior-art-and-references">Prior Art and References</a></li>
|
|
<li><a href="#unresolved-questions">Unresolved Questions</a></li>
|
|
<li><a href="#future-directions-and-related-material">Future Directions and Related Material</a></li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
<h1 id="rfc-0000-metadata-for-offline-signers"><a class="header" href="#rfc-0000-metadata-for-offline-signers">RFC-0000: Metadata for offline signers</a></h1>
|
|
<div class="table-wrapper"><table><thead><tr><th></th><th></th></tr></thead><tbody>
|
|
<tr><td><strong>Start Date</strong></td><td>2023-10-31</td></tr>
|
|
<tr><td><strong>Description</strong></td><td>Add SignedExtension to check Metadata Root Hash</td></tr>
|
|
<tr><td><strong>Authors</strong></td><td>Alzymologist Oy, Zondax LLC, Parity GmbH</td></tr>
|
|
</tbody></table>
|
|
</div>
|
|
<h2 id="summary"><a class="header" href="#summary">Summary</a></h2>
|
|
<p>Add a metadata digest value (33-byte constant within fixed <code>spec_version</code>) to Signed Extensions to supplement signer party with proof of correct extrinsic interpretation. The digest value is generated once before release and is well-known and deterministic. The digest mechanism is designed to be modular and flexible. It also supports partial metadata transfer as needed by the signing party's extrinsic decoding mechanism. This considers signing devices potentially limited communication bandwidth and/or memory capacity.</p>
|
|
<h2 id="motivation"><a class="header" href="#motivation">Motivation</a></h2>
|
|
<h3 id="background"><a class="header" href="#background">Background</a></h3>
|
|
<p>While all blockchain systems support (at least in some sense) offline signing used in air-gapped wallets and lightweight embedded devices, only few allow simultaneously complex upgradeable logic and full message decoding on the cold off-line signer side; Substrate is one of these heartening few, and therefore - we should build on this feature to greatly improve transaction security, and thus in general, network resilience.</p>
|
|
<p>As a starting point, it is important to recognise that prudence and due care are naturally required. As we build further reliance on this feature we should be very careful to make sure it works correctly every time so as not to create false sense of security.</p>
|
|
<p>In order to enable decoding that is small and optimized for chain storage transactions, a metadata entity is used, which is not at all small in itself (on the order of half-MB for most networks). This is a dynamic data chunk which completely describes chain interfaces and properties that could be made into a portable scale-encoded string for any given network version and passed along into an off-chain device to familiarize it with latest network updates. Of course, compromising this metadata anywhere in the path could result in differences between what user sees and signs, thus it is essential that we protect it.</p>
|
|
<p>Therefore, we have 2 problems to be solved:</p>
|
|
<ol>
|
|
<li>Metadata is large, takes long time to be passed into a cold storage device with memory insufficient for its storage; metadata SHOULD be shortened and transmission SHOULD be optimized.</li>
|
|
<li>Metadata authenticity SHOULD be ensured.</li>
|
|
</ol>
|
|
<p>As of now, there is no working solution for (1), as the whole metadata has to be passed to the device. On top of this, the solution for (2) heavily relies on a trusted party managing keys and ensuring metadata is indeed authentic: creating poorly decentralized points of potential failure.</p>
|
|
<h3 id="solution-requirements"><a class="header" href="#solution-requirements">Solution requirements</a></h3>
|
|
<h4 id="include-metadata-digest-into-signature"><a class="header" href="#include-metadata-digest-into-signature">Include metadata digest into signature</a></h4>
|
|
<p>Some cryptographically strong digest of metadata MAY be included into signable blob. There SHALL NOT be storage overhead for this blob, nor computational overhead, on the node side; thus MUST be a constant within given runtime version, deterministically defined by metadata.</p>
|
|
<ul>
|
|
<li>Metadata information that could be used in signable extrinsic decoding MAY be included in digest, its inclusion MUST be indicated in signed extensions;</li>
|
|
<li>Digest MUST be deterministic with respect to metadata;</li>
|
|
<li>Digest MUST be cryptographically strong against pre-image, both first and second;</li>
|
|
<li>Extra-metadata information necessary for extrinsic decoding and constant within runtime version MUST be included in digest;</li>
|
|
<li>It SHOULD be possible to quickly withdraw offline signing mechanism without access to cold signing devices;</li>
|
|
<li>Digest format SHOULD be versioned.</li>
|
|
<li>Work necessary for proving metadata authenticity MAY be omitted at discretion of signer device design (to support automation tools).</li>
|
|
</ul>
|
|
<h4 id="reduce-metadata-size"><a class="header" href="#reduce-metadata-size">Reduce metadata size</a></h4>
|
|
<p>Metadata should be stripped from parts that are not necessary to parse a signable extrinsic, then it should be separated into a finite set of self-descriptive chunks. Thus, a subset of chunks necessary for signable extrinsic decoding and rendering could be sent, possibly in small portions (ultimately - one at a time), to cold device together with proof.</p>
|
|
<ul>
|
|
<li>Single chunk with proof payload size SHOULD fit within few kB;</li>
|
|
<li>Chunks handling mechanism SHOULD support chunks being sent in any order without memory utilization overhead;</li>
|
|
<li>Unused enum variants MUST be stripped (this has great impact on transmitted metadata size; examples: era enum, enum with all calls for call batching).</li>
|
|
</ul>
|
|
<h2 id="stakeholders"><a class="header" href="#stakeholders">Stakeholders</a></h2>
|
|
<p>All chain teams are stakeholders, as implementing this feature would require timely effort on their side and would impact compatibility with older tools.</p>
|
|
<p>This feature is essential for <strong>all</strong> offline signer tools; many regular signing tools might make use of it. In general, this RFC greatly improves security of any network implementing it, as many governing keys are used with offline signers.</p>
|
|
<p>Implementing this RFC would remove requirement to maintain metadata portals manually, as task of metadata verification would be effectively moved to consensus mechanism of the chain.</p>
|
|
<h2 id="explanation"><a class="header" href="#explanation">Explanation</a></h2>
|
|
<p>Detailed description of metadata shortening and digest process is provided in <a href="https://github.com/Alzymologist/metadata-shortener">metadata-shortener</a> crate (see <code>cargo doc --open</code> and examples). Below are presented algorithms of the process.</p>
|
|
<h3 id="definitions"><a class="header" href="#definitions">Definitions</a></h3>
|
|
<h4 id="metadata-structure"><a class="header" href="#metadata-structure">Metadata structure</a></h4>
|
|
<p>Metadata in general consists of four sections:</p>
|
|
<ol>
|
|
<li>Types registry</li>
|
|
<li>Pallets</li>
|
|
<li>Extrinsic metadata</li>
|
|
<li>Runtime type</li>
|
|
</ol>
|
|
<p>Of these, only sections 1-3 contain information required for extrinsic decoding. The most important section is (1) Types registry, that is mostly used in extrinsic decoding. It is also the largest part, thus it is modularized for fractional transmission. Part (2) contains runtime version and is otherwise useless for transaction decoding; thus its contents are reduced to this parameter and included into Metadata Descriptor. Part (3) is included into Metadata Descriptor verbatim.</p>
|
|
<h4 id="metadata-descriptor"><a class="header" href="#metadata-descriptor">Metadata descriptor</a></h4>
|
|
<p>Values for:</p>
|
|
<ol>
|
|
<li><code>u8</code> metadata shortening protocol version, </li>
|
|
<li>SCALE-encoded <code>ExtrinsicMetadata</code>,</li>
|
|
<li>SCALE-encoded <code>spec_version</code> <code>String</code>,</li>
|
|
<li>SCALE-encoded <code>spec_name</code> <code>String</code>,</li>
|
|
<li><code>u16</code> base58 prefix,</li>
|
|
<li><code>u8</code> decimals value or <code>0u8</code> if no units are defined,</li>
|
|
<li>SCALE-encoded <code>tokenSymbol</code> <code>String</code> defined on chain to identify the name of currency (available for example through <code>system.properties()</code> RPC call) or empty string if no base units are defined,</li>
|
|
</ol>
|
|
<pre><code>struct MetadataDescriptor { // really a scale-encoded enum, thus first field is enum value - only 0x01 currently supported.
|
|
protocol_version: u8,
|
|
extrinsic_metadata: Vec<u8>, // SCALE from `ExtrinsicMetadata
|
|
spec_version: Vec<u8>, // SCALE form `String`
|
|
spec_name: Vec<u8>, // SCALE from `String`
|
|
base58_prefix: u16,
|
|
decimals: u8,
|
|
token_symbol: Vec<u8>, // SCALE from `String`
|
|
}
|
|
</code></pre>
|
|
<p>constitute metadata descriptor. This is minimal information that is, together with (shortened) types registry, sufficient to decode any signable transaction.</p>
|
|
<h4 id="merkle-tree"><a class="header" href="#merkle-tree">Merkle tree</a></h4>
|
|
<p>A <strong>Complete Binary Merkle Tree</strong> (<strong>CBMT</strong>) is proposed as digest structure.</p>
|
|
<p>Every node of the proposed tree has a 32-bit value.</p>
|
|
<p>A terminal node of the tree we call <strong>leaf</strong>. Its value is input for digest.</p>
|
|
<p>The top node of the tree we call <strong>root</strong>.</p>
|
|
<p>All node values for non-leave nodes are not terminal are computed through non-commutative <strong>merge</strong> procedure of child nodes.</p>
|
|
<p>In CBMT, all layers must be populated, except for the last one, that must have complete filling from the left.</p>
|
|
<p>Nodes are numbered top-down and left-to-right starting with 0 at the top of tree.</p>
|
|
<pre><code>Example 8-node tree
|
|
|
|
0
|
|
/ \
|
|
1 2
|
|
/ \ / \
|
|
3 4 5 6
|
|
/ \
|
|
7 8
|
|
|
|
Nodes 4, 5, 6, 7, 8 are leaves
|
|
Node 0 is root
|
|
|
|
</code></pre>
|
|
<h3 id="general-flow"><a class="header" href="#general-flow">General flow</a></h3>
|
|
<ol>
|
|
<li>The metadata is converted into lean modular form (vector of chunks)</li>
|
|
<li>A Merkle tree is constructed from the metadata chunks</li>
|
|
<li>A root of tree (as a left element) is merged with Metadata Descriptor (as a right element)</li>
|
|
<li>Resulting value is a constant to be included in <code>additionalSigned</code> to prove that the metadata seen by cold device is genuine</li>
|
|
</ol>
|
|
<h3 id="metadata-modularization"><a class="header" href="#metadata-modularization">Metadata modularization</a></h3>
|
|
<ol>
|
|
<li>Types registry is stripped from <code>docs</code> fields.</li>
|
|
<li>Types records are separated into chunks, with enum variants being individual chunks differing by variant index; each chunk consisting of <code>id</code> (same as in full metadata registry) and SCALE-encoded 'Type' description (reduced to 1-variant enum for enum variants). Enums with 0 variants are treated as regular types.</li>
|
|
<li>Chunks are sorted by <code>id</code> in ascending order; chunks with same <code>id</code> are sorted by enum variant index in ascending order.</li>
|
|
</ol>
|
|
<pre><code>types_registry = metadataV14.types
|
|
modularized_registry = EmptyVector<id, type>
|
|
for (id, type) in types.registry.iterate_enumerate {
|
|
type.doc = Null
|
|
if (type is ReduceableEnum) { // false for 0-variant enums
|
|
for variant in type.variants.iterate {
|
|
variant_type = Type {
|
|
path: type.path
|
|
type_params: Null
|
|
type_def: TypeDef::Variant(variants: [variant])
|
|
}
|
|
modularized_registry.push(id, variant_type)
|
|
}
|
|
} else {
|
|
modularized_registry.push(id, type)
|
|
}
|
|
}
|
|
|
|
modularized_registry.sort(|a, b| {
|
|
if a.id == b.id { //only possible for variants
|
|
a.variant_index > b.variant_index
|
|
} else { a.id > b.id }
|
|
}
|
|
)
|
|
|
|
</code></pre>
|
|
<h3 id="merging-protocol"><a class="header" href="#merging-protocol">Merging protocol</a></h3>
|
|
<p><code>blake3</code> transformation of concatenated child nodes (<code>blake3(left + right)</code>) as merge procedure;</p>
|
|
<h3 id="complete-binary-merkle-tree-construction-protocol"><a class="header" href="#complete-binary-merkle-tree-construction-protocol">Complete Binary Merkle Tree construction protocol</a></h3>
|
|
<ol>
|
|
<li>Leaves are numbered in ascending order. Leaf index is associated with corresponding chunk.</li>
|
|
<li>Merge is performed using the leaf with highest index as right and node with second to highest index as left children; result is pushed to the end of nodes queue and leaves are discarded.</li>
|
|
<li>Step (2) is repeated until no leaves or just one leaf remains; in latter case, the last leaf is pushed to the front of the nodes queue.</li>
|
|
<li>Right node and then left node is popped from the front of the nodes queue and merged; the result is sent to the end of the queue.</li>
|
|
<li>Step (4) is repeated until only one node remains; this is tree root.</li>
|
|
</ol>
|
|
<pre><code>Resulting tree for metadata consisting of 5 nodes (numbered from 0 to 4):
|
|
|
|
root
|
|
/ \
|
|
* *
|
|
/ \ / \
|
|
* 0 1 2
|
|
/ \
|
|
3 4
|
|
</code></pre>
|
|
<h3 id="digest"><a class="header" href="#digest">Digest</a></h3>
|
|
<ol>
|
|
<li>Blake3 hash is computed for each chunk of modular short metadata registry.</li>
|
|
<li>Complete Binary Merkle Tree is constructed as described above.</li>
|
|
<li>Root hash of this tree (left) is merged with metadata descriptor blake3 hash (right); this is metadata digest.</li>
|
|
</ol>
|
|
<p>Version number and corresponding resulting metadata digest MUST be included into Signed Extensions as specified in Chain Verification section below.</p>
|
|
<h3 id="shortening"><a class="header" href="#shortening">Shortening</a></h3>
|
|
<p>For shortening, an attempt to decode transaction completely using provided metadata is performed with the same algorithm that would be used on the cold side. All chunks are associated with their leaf indices. An example of this protocol is proposed in <a href="https://github.com/Alzymologist/metadata-shortener">metadata-shortener</a> that is based on <a href="https://github.com/Alzymologist/substrate-parser">substrate-parser</a> decoding protocol; any decoding protocol could be used here as long as cold signer's design finds it appropriate for given security model.</p>
|
|
<h3 id="transmission"><a class="header" href="#transmission">Transmission</a></h3>
|
|
<p>Shortened metadata chunks MAY be trasmitted into cold device together with Merkle proof in its entirety or in parts, depending on memory capabilities of the cold device and it ability to reconstruct larger fraction of tree. This document does not specify the manner of transmission. The order of metadata chunks MAY be arbitrary, the only requirement is that indices of leaf nodes in Merkle tree corresponding to chunks MUST be communicated. Community MAY handle proof format standartization independently.</p>
|
|
<h3 id="offline-verification"><a class="header" href="#offline-verification">Offline verification</a></h3>
|
|
<p>The transmitted metadata chunks are hashed together with proof lemmas to obtain root that MAY be transmitted along with the rest of payload. Verification that the root transmitted with message matches with calculated root is optional; the transmitted root SHOULD NOT be used in signature, calculated root MUST be used; however, there is no mechanism to enforce this - it should be done during cold signers code audit.</p>
|
|
<h3 id="chain-verification"><a class="header" href="#chain-verification">Chain verification</a></h3>
|
|
<p>The root of metadata computed by cold device MAY be included into Signed Extensions; this way the transaction will pass as valid iff hash of metadata as seen by cold storage device is identical to consensus hash of metadata, ensuring fair signing protocol.</p>
|
|
<p>The Signed Extension representing metadata digest is a single byte representing both digest vaule inclusion and shortening protocol version; this MUST be included in Signed Extensions set. Depending on its value, a digest value is included as <code>additionalSigned</code> to signature computation according to following specification:</p>
|
|
<div class="table-wrapper"><table><thead><tr><th>signed extension value</th><th>digest value</th><th>comment</th></tr></thead><tbody>
|
|
<tr><td><code>0x00</code></td><td></td><td>digest is not included</td></tr>
|
|
<tr><td><code>0x01</code></td><td>32-byte digest</td><td>this represents protocol version 1</td></tr>
|
|
<tr><td><code>0x02</code> - <code>0xFF</code></td><td><em>reserved</em></td><td>reserved for future use</td></tr>
|
|
</tbody></table>
|
|
</div>
|
|
<h2 id="drawbacks"><a class="header" href="#drawbacks">Drawbacks</a></h2>
|
|
<h3 id="increased-transaction-size"><a class="header" href="#increased-transaction-size">Increased transaction size</a></h3>
|
|
<p>A 1-byte increase in transaction size due to signed extension value. Digest is not included in transferred transaction, only in signing process.</p>
|
|
<h3 id="transition-overhead"><a class="header" href="#transition-overhead">Transition overhead</a></h3>
|
|
<p>Some slightly out of spec systems might experience breaking changes as new content of signed extensions is added. It is important to note, that there is no real overhead in processing time nor complexity, as the metadata checking mechanism is voluntary. The only drawbacks are expected for tools that do not implement MetadataV14 self-descripting features.</p>
|
|
<h2 id="testing-security-and-privacy"><a class="header" href="#testing-security-and-privacy">Testing, Security, and Privacy</a></h2>
|
|
<p>The metadata shortening protocol should be extensively tested on all available examples of metadata before releasing changes to either metadata or shortener. Careful code review should be performed on shortener implementation code to ensure security. The main metadata tree would inevitably be constructed on runtime build which would also ensure correctness.</p>
|
|
<p>To be able to recall shortener protocol in case of vulnerability issues, a version byte is included.</p>
|
|
<h2 id="performance-ergonomics-and-compatibility"><a class="header" href="#performance-ergonomics-and-compatibility">Performance, Ergonomics, and Compatibility</a></h2>
|
|
<h3 id="performance"><a class="header" href="#performance">Performance</a></h3>
|
|
<p>This is negligibly short pessimization during build time on the chain side. Cold wallets performance would improve mostly as metadata validity mechanism that was taking most of effort in cold wallet support would become trivial.</p>
|
|
<h3 id="ergonomics"><a class="header" href="#ergonomics">Ergonomics</a></h3>
|
|
<p>The proposal was optimized for cold storage wallets usage with minimal impact on all other parts of the ecosystem</p>
|
|
<h3 id="compatibility"><a class="header" href="#compatibility">Compatibility</a></h3>
|
|
<p>Proposal in this form is not compatible with older tools that do not implement proper MetadataV14 self-descriptive features; those would have to be upgraded to include a new signed extensions field.</p>
|
|
<h2 id="prior-art-and-references"><a class="header" href="#prior-art-and-references">Prior Art and References</a></h2>
|
|
<p>This project was developed upon a Polkadot Treasury grant; relevant development links are located in <a href="https://github.com/Alzymologist/metadata-offline-project">metadata-offline-project</a> repository.</p>
|
|
<h2 id="unresolved-questions"><a class="header" href="#unresolved-questions">Unresolved Questions</a></h2>
|
|
<ol start="2">
|
|
<li>How would polkadot-js handle the transition?</li>
|
|
<li>Where would non-rust tools like Ledger apps get shortened metadata content?</li>
|
|
</ol>
|
|
<h2 id="future-directions-and-related-material"><a class="header" href="#future-directions-and-related-material">Future Directions and Related Material</a></h2>
|
|
<p>Changes to code of all cold signers to implement this mechanism SHOULD be done when this is enabled; non-cold signers may perform extra metadata check for better security. Ultimately, signing anything without decoding it with verifiable metadata should become discouraged in all situations where a decision-making mechanism is involved (that is, outside of fully automated blind signers like trade bots or staking rewards payout tools).</p>
|
|
|
|
</main>
|
|
|
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
|
<!-- Mobile navigation buttons -->
|
|
<a rel="prev" href="../proposed/0044-rent-based-registration.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
|
<i class="fa fa-angle-left"></i>
|
|
</a>
|
|
|
|
<a rel="next prefetch" href="../proposed/0047-assignment-of-availability-chunks.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
|
<i class="fa fa-angle-right"></i>
|
|
</a>
|
|
|
|
<div style="clear: both"></div>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
|
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
|
<a rel="prev" href="../proposed/0044-rent-based-registration.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
|
<i class="fa fa-angle-left"></i>
|
|
</a>
|
|
|
|
<a rel="next prefetch" href="../proposed/0047-assignment-of-availability-chunks.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
|
<i class="fa fa-angle-right"></i>
|
|
</a>
|
|
</nav>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
window.playground_copyable = true;
|
|
</script>
|
|
|
|
|
|
<script src="../elasticlunr.min.js"></script>
|
|
<script src="../mark.min.js"></script>
|
|
<script src="../searcher.js"></script>
|
|
|
|
<script src="../clipboard.min.js"></script>
|
|
<script src="../highlight.js"></script>
|
|
<script src="../book.js"></script>
|
|
|
|
<!-- Custom JS scripts -->
|
|
|
|
|
|
</div>
|
|
</body>
|
|
</html>
|