mirror of
https://github.com/vacp2p/rfc-index.git
synced 2026-01-09 07:38:09 -05:00
1694 lines
97 KiB
HTML
1694 lines
97 KiB
HTML
<!DOCTYPE HTML>
|
|
<html lang="en" class="ayu" dir="ltr">
|
|
<head>
|
|
<!-- Book generated using mdBook -->
|
|
<meta charset="UTF-8">
|
|
<title>Block Exchange - Vac RFC</title>
|
|
|
|
|
|
<!-- Custom HTML head -->
|
|
|
|
<meta name="description" content="">
|
|
<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="../../custom.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 ? "navy" : "ayu";
|
|
</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('ayu')
|
|
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="../../index.html">Introduction</a></li><li class="chapter-item expanded "><a href="../../vac/index.html"><strong aria-hidden="true">1.</strong> Vac</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../vac/1/coss.html"><strong aria-hidden="true">1.1.</strong> 1/COSS</a></li><li class="chapter-item expanded "><a href="../../vac/2/mvds.html"><strong aria-hidden="true">1.2.</strong> 2/MVDS</a></li><li class="chapter-item expanded "><a href="../../vac/3/remote-log.html"><strong aria-hidden="true">1.3.</strong> 3/Remote Log</a></li><li class="chapter-item expanded "><a href="../../vac/4/mvds-meta.html"><strong aria-hidden="true">1.4.</strong> 4/MVDS Meta</a></li><li class="chapter-item expanded "><a href="../../vac/25/libp2p-dns-discovery.html"><strong aria-hidden="true">1.5.</strong> 25/Libp2p DNS Discovery</a></li><li class="chapter-item expanded "><a href="../../vac/32/rln-v1.html"><strong aria-hidden="true">1.6.</strong> 32/RLN-V1</a></li><li class="chapter-item expanded "><a href="../../vac/raw/index.html"><strong aria-hidden="true">1.7.</strong> Raw</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../vac/raw/consensus-hashgraphlike.html"><strong aria-hidden="true">1.7.1.</strong> Consensus Hashgraphlike</a></li><li class="chapter-item expanded "><a href="../../vac/raw/decentralized-messaging-ethereum.html"><strong aria-hidden="true">1.7.2.</strong> Decentralized Messaging Ethereum</a></li><li class="chapter-item expanded "><a href="../../vac/raw/eth-mls-offchain.html"><strong aria-hidden="true">1.7.3.</strong> ETH MLS Offchain</a></li><li class="chapter-item expanded "><a href="../../vac/raw/eth-mls-onchain.html"><strong aria-hidden="true">1.7.4.</strong> ETH MLS Onchain</a></li><li class="chapter-item expanded "><a href="../../vac/raw/deleted/eth-secpm.html"><strong aria-hidden="true">1.7.5.</strong> ETH SecPM</a></li><li class="chapter-item expanded "><a href="../../vac/raw/gossipsub-tor-push.html"><strong aria-hidden="true">1.7.6.</strong> Gossipsub Tor Push</a></li><li class="chapter-item expanded "><a href="../../vac/raw/logos-capability-discovery.html"><strong aria-hidden="true">1.7.7.</strong> Logos Capability Discovery</a></li><li class="chapter-item expanded "><a href="../../vac/raw/mix.html"><strong aria-hidden="true">1.7.8.</strong> Mix</a></li><li class="chapter-item expanded "><a href="../../vac/raw/noise-x3dh-double-ratchet.html"><strong aria-hidden="true">1.7.9.</strong> Noise X3DH Double Ratchet</a></li><li class="chapter-item expanded "><a href="../../vac/raw/rln-interep-spec.html"><strong aria-hidden="true">1.7.10.</strong> RLN Interep Spec</a></li><li class="chapter-item expanded "><a href="../../vac/raw/rln-stealth-commitments.html"><strong aria-hidden="true">1.7.11.</strong> RLN Stealth Commitments</a></li><li class="chapter-item expanded "><a href="../../vac/raw/rln-v2.html"><strong aria-hidden="true">1.7.12.</strong> RLN-V2</a></li><li class="chapter-item expanded "><a href="../../vac/raw/sds.html"><strong aria-hidden="true">1.7.13.</strong> SDS</a></li></ol></li><li class="chapter-item expanded "><a href="../../vac/template.html"><strong aria-hidden="true">1.8.</strong> Template</a></li></ol></li><li class="chapter-item expanded "><a href="../../waku/index.html"><strong aria-hidden="true">2.</strong> Waku</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../waku/standards/core/index.html"><strong aria-hidden="true">2.1.</strong> Standards - Core</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../waku/standards/core/10/waku2.html"><strong aria-hidden="true">2.1.1.</strong> 10/Waku2</a></li><li class="chapter-item expanded "><a href="../../waku/standards/core/11/relay.html"><strong aria-hidden="true">2.1.2.</strong> 11/Relay</a></li><li class="chapter-item expanded "><a href="../../waku/standards/core/12/filter.html"><strong aria-hidden="true">2.1.3.</strong> 12/Filter</a></li><li class="chapter-item expanded "><a href="../../waku/standards/core/13/store.html"><strong aria-hidden="true">2.1.4.</strong> 13/Store</a></li><li class="chapter-item expanded "><a href="../../waku/standards/core/14/message.html"><strong aria-hidden="true">2.1.5.</strong> 14/Message</a></li><li class="chapter-item expanded "><a href="../../waku/standards/core/15/bridge.html"><strong aria-hidden="true">2.1.6.</strong> 15/Bridge</a></li><li class="chapter-item expanded "><a href="../../waku/standards/core/17/rln-relay.html"><strong aria-hidden="true">2.1.7.</strong> 17/RLN Relay</a></li><li class="chapter-item expanded "><a href="../../waku/standards/core/19/lightpush.html"><strong aria-hidden="true">2.1.8.</strong> 19/Lightpush</a></li><li class="chapter-item expanded "><a href="../../waku/standards/core/31/enr.html"><strong aria-hidden="true">2.1.9.</strong> 31/ENR</a></li><li class="chapter-item expanded "><a href="../../waku/standards/core/33/discv5.html"><strong aria-hidden="true">2.1.10.</strong> 33/Discv5</a></li><li class="chapter-item expanded "><a href="../../waku/standards/core/34/peer-exchange.html"><strong aria-hidden="true">2.1.11.</strong> 34/Peer Exchange</a></li><li class="chapter-item expanded "><a href="../../waku/standards/core/36/bindings-api.html"><strong aria-hidden="true">2.1.12.</strong> 36/Bindings API</a></li><li class="chapter-item expanded "><a href="../../waku/standards/core/64/network.html"><strong aria-hidden="true">2.1.13.</strong> 64/Network</a></li><li class="chapter-item expanded "><a href="../../waku/standards/core/66/metadata.html"><strong aria-hidden="true">2.1.14.</strong> 66/Metadata</a></li></ol></li><li class="chapter-item expanded "><a href="../../waku/standards/application/index.html"><strong aria-hidden="true">2.2.</strong> Standards - Application</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../waku/standards/application/20/toy-eth-pm.html"><strong aria-hidden="true">2.2.1.</strong> 20/Toy ETH PM</a></li><li class="chapter-item expanded "><a href="../../waku/standards/application/26/payload.html"><strong aria-hidden="true">2.2.2.</strong> 26/Payload</a></li><li class="chapter-item expanded "><a href="../../waku/standards/application/53/x3dh.html"><strong aria-hidden="true">2.2.3.</strong> 53/X3DH</a></li><li class="chapter-item expanded "><a href="../../waku/standards/application/54/x3dh-sessions.html"><strong aria-hidden="true">2.2.4.</strong> 54/X3DH Sessions</a></li></ol></li><li class="chapter-item expanded "><a href="../../waku/standards/legacy/index.html"><strong aria-hidden="true">2.3.</strong> Standards - Legacy</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../waku/standards/legacy/6/waku1.html"><strong aria-hidden="true">2.3.1.</strong> 6/Waku1</a></li><li class="chapter-item expanded "><a href="../../waku/standards/legacy/7/data.html"><strong aria-hidden="true">2.3.2.</strong> 7/Data</a></li><li class="chapter-item expanded "><a href="../../waku/standards/legacy/8/mail.html"><strong aria-hidden="true">2.3.3.</strong> 8/Mail</a></li><li class="chapter-item expanded "><a href="../../waku/standards/legacy/9/rpc.html"><strong aria-hidden="true">2.3.4.</strong> 9/RPC</a></li></ol></li><li class="chapter-item expanded "><a href="../../waku/informational/index.html"><strong aria-hidden="true">2.4.</strong> Informational</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../waku/informational/22/toy-chat.html"><strong aria-hidden="true">2.4.1.</strong> 22/Toy Chat</a></li><li class="chapter-item expanded "><a href="../../waku/informational/23/topics.html"><strong aria-hidden="true">2.4.2.</strong> 23/Topics</a></li><li class="chapter-item expanded "><a href="../../waku/informational/27/peers.html"><strong aria-hidden="true">2.4.3.</strong> 27/Peers</a></li><li class="chapter-item expanded "><a href="../../waku/informational/29/config.html"><strong aria-hidden="true">2.4.4.</strong> 29/Config</a></li><li class="chapter-item expanded "><a href="../../waku/informational/30/adaptive-nodes.html"><strong aria-hidden="true">2.4.5.</strong> 30/Adaptive Nodes</a></li></ol></li><li class="chapter-item expanded "><a href="../../waku/deprecated/index.html"><strong aria-hidden="true">2.5.</strong> Deprecated</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../waku/deprecated/5/waku0.html"><strong aria-hidden="true">2.5.1.</strong> 5/Waku0</a></li><li class="chapter-item expanded "><a href="../../waku/deprecated/16/rpc.html"><strong aria-hidden="true">2.5.2.</strong> 16/RPC</a></li><li class="chapter-item expanded "><a href="../../waku/deprecated/18/swap.html"><strong aria-hidden="true">2.5.3.</strong> 18/Swap</a></li><li class="chapter-item expanded "><a href="../../waku/deprecated/fault-tolerant-store.html"><strong aria-hidden="true">2.5.4.</strong> Fault Tolerant Store</a></li></ol></li></ol></li><li class="chapter-item expanded "><a href="../../nomos/index.html"><strong aria-hidden="true">3.</strong> Nomos</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../nomos/raw/index.html"><strong aria-hidden="true">3.1.</strong> Raw</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../nomos/raw/nomosda-encoding.html"><strong aria-hidden="true">3.1.1.</strong> NomosDA Encoding</a></li><li class="chapter-item expanded "><a href="../../nomos/raw/nomosda-network.html"><strong aria-hidden="true">3.1.2.</strong> NomosDA Network</a></li><li class="chapter-item expanded "><a href="../../nomos/raw/p2p-hardware-requirements.html"><strong aria-hidden="true">3.1.3.</strong> P2P Hardware Requirements</a></li><li class="chapter-item expanded "><a href="../../nomos/raw/p2p-nat-solution.html"><strong aria-hidden="true">3.1.4.</strong> P2P NAT Solution</a></li><li class="chapter-item expanded "><a href="../../nomos/raw/p2p-network-bootstrapping.html"><strong aria-hidden="true">3.1.5.</strong> P2P Network Bootstrapping</a></li><li class="chapter-item expanded "><a href="../../nomos/raw/p2p-network.html"><strong aria-hidden="true">3.1.6.</strong> P2P Network</a></li><li class="chapter-item expanded "><a href="../../nomos/raw/sdp.html"><strong aria-hidden="true">3.1.7.</strong> SDP</a></li></ol></li><li class="chapter-item expanded "><a href="../../nomos/deprecated/index.html"><strong aria-hidden="true">3.2.</strong> Deprecated</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../nomos/deprecated/claro.html"><strong aria-hidden="true">3.2.1.</strong> Claro</a></li></ol></li></ol></li><li class="chapter-item expanded "><a href="../../codex/index.html"><strong aria-hidden="true">4.</strong> Codex</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../codex/raw/index.html"><strong aria-hidden="true">4.1.</strong> Raw</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../codex/raw/codex-block-exchange.html" class="active"><strong aria-hidden="true">4.1.1.</strong> Block Exchange</a></li><li class="chapter-item expanded "><a href="../../codex/raw/codex-marketplace.html"><strong aria-hidden="true">4.1.2.</strong> Marketplace</a></li></ol></li></ol></li><li class="chapter-item expanded "><a href="../../status/index.html"><strong aria-hidden="true">5.</strong> Status</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../status/24/curation.html"><strong aria-hidden="true">5.1.</strong> 24/Curation</a></li><li class="chapter-item expanded "><a href="../../status/28/featuring.html"><strong aria-hidden="true">5.2.</strong> 28/Featuring</a></li><li class="chapter-item expanded "><a href="../../status/55/1to1-chat.html"><strong aria-hidden="true">5.3.</strong> 55/1-to-1 Chat</a></li><li class="chapter-item expanded "><a href="../../status/56/communities.html"><strong aria-hidden="true">5.4.</strong> 56/Communities</a></li><li class="chapter-item expanded "><a href="../../status/61/community-history-service.html"><strong aria-hidden="true">5.5.</strong> 61/Community History Service</a></li><li class="chapter-item expanded "><a href="../../status/62/payloads.html"><strong aria-hidden="true">5.6.</strong> 62/Payloads</a></li><li class="chapter-item expanded "><a href="../../status/63/keycard-usage.html"><strong aria-hidden="true">5.7.</strong> 63/Keycard Usage</a></li><li class="chapter-item expanded "><a href="../../status/65/account-address.html"><strong aria-hidden="true">5.8.</strong> 65/Account Address</a></li><li class="chapter-item expanded "><a href="../../status/71/push-notification-server.html"><strong aria-hidden="true">5.9.</strong> 71/Push Notification Server</a></li><li class="chapter-item expanded "><a href="../../status/raw/index.html"><strong aria-hidden="true">5.10.</strong> Raw</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../status/raw/simple-scaling.html"><strong aria-hidden="true">5.10.1.</strong> Simple Scaling</a></li><li class="chapter-item expanded "><a href="../../status/raw/status-app-protocols.html"><strong aria-hidden="true">5.10.2.</strong> Status App Protocols</a></li><li class="chapter-item expanded "><a href="../../status/raw/status-mvds.html"><strong aria-hidden="true">5.10.3.</strong> Status MVDS</a></li><li class="chapter-item expanded "><a href="../../status/raw/url-data.html"><strong aria-hidden="true">5.10.4.</strong> URL Data</a></li><li class="chapter-item expanded "><a href="../../status/raw/url-scheme.html"><strong aria-hidden="true">5.10.5.</strong> URL Scheme</a></li></ol></li><li class="chapter-item expanded "><a href="../../status/deprecated/index.html"><strong aria-hidden="true">5.11.</strong> Deprecated</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../status/deprecated/3rd-party.html"><strong aria-hidden="true">5.11.1.</strong> 3rd Party</a></li><li class="chapter-item expanded "><a href="../../status/deprecated/account.html"><strong aria-hidden="true">5.11.2.</strong> Account</a></li><li class="chapter-item expanded "><a href="../../status/deprecated/client.html"><strong aria-hidden="true">5.11.3.</strong> Client</a></li><li class="chapter-item expanded "><a href="../../status/deprecated/dapp-browser-API-usage.html"><strong aria-hidden="true">5.11.4.</strong> Dapp Browser API Usage</a></li><li class="chapter-item expanded "><a href="../../status/deprecated/eips.html"><strong aria-hidden="true">5.11.5.</strong> EIPs</a></li><li class="chapter-item expanded "><a href="../../status/deprecated/ethereum-usage.html"><strong aria-hidden="true">5.11.6.</strong> Ethereum Usage</a></li><li class="chapter-item expanded "><a href="../../status/deprecated/group-chat.html"><strong aria-hidden="true">5.11.7.</strong> Group Chat</a></li><li class="chapter-item expanded "><a href="../../status/deprecated/IPFS-gateway-for-sticker-Pack.html"><strong aria-hidden="true">5.11.8.</strong> IPFS Gateway for Sticker Pack</a></li><li class="chapter-item expanded "><a href="../../status/deprecated/keycard-usage-for-wallet-and-chat-keys.html"><strong aria-hidden="true">5.11.9.</strong> Keycard Usage for Wallet and Chat Keys</a></li><li class="chapter-item expanded "><a href="../../status/deprecated/notifications.html"><strong aria-hidden="true">5.11.10.</strong> Notifications</a></li><li class="chapter-item expanded "><a href="../../status/deprecated/payloads.html"><strong aria-hidden="true">5.11.11.</strong> Payloads</a></li><li class="chapter-item expanded "><a href="../../status/deprecated/push-notification-server.html"><strong aria-hidden="true">5.11.12.</strong> Push Notification Server</a></li><li class="chapter-item expanded "><a href="../../status/deprecated/secure-transport.html"><strong aria-hidden="true">5.11.13.</strong> Secure Transport</a></li><li class="chapter-item expanded "><a href="../../status/deprecated/waku-mailserver.html"><strong aria-hidden="true">5.11.14.</strong> Waku Mailserver</a></li><li class="chapter-item expanded "><a href="../../status/deprecated/waku-usage.html"><strong aria-hidden="true">5.11.15.</strong> Waku Usage</a></li><li class="chapter-item expanded "><a href="../../status/deprecated/whisper-mailserver.html"><strong aria-hidden="true">5.11.16.</strong> Whisper Mailserver</a></li><li class="chapter-item expanded "><a href="../../status/deprecated/whisper-usage.html"><strong aria-hidden="true">5.11.17.</strong> Whisper Usage</a></li></ol></li></ol></li></ol>
|
|
</div>
|
|
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
|
<div class="sidebar-resize-indicator"></div>
|
|
</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="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">Vac RFC</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>
|
|
<a href="https://github.com/vacp2p/rfc-index" title="Git repository" aria-label="Git repository">
|
|
<i id="git-repository-button" class="fa fa-github"></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>
|
|
<h1 id="codex-block-exchange"><a class="header" href="#codex-block-exchange">CODEX-BLOCK-EXCHANGE</a></h1>
|
|
<div class="table-wrapper"><table><thead><tr><th>Field</th><th>Value</th></tr></thead><tbody>
|
|
<tr><td>Name</td><td>Codex Block Exchange Protocol</td></tr>
|
|
<tr><td>Status</td><td>raw</td></tr>
|
|
<tr><td>Category</td><td>Standards Track</td></tr>
|
|
<tr><td>Editor</td><td>Codex Team</td></tr>
|
|
<tr><td>Contributors</td><td>Filip Dimitrijevic <a href="mailto:filip@status.im">filip@status.im</a></td></tr>
|
|
</tbody></table>
|
|
</div><!-- timeline:start -->
|
|
<h2 id="timeline"><a class="header" href="#timeline">Timeline</a></h2>
|
|
<ul>
|
|
<li><strong>2025-12-22</strong> — <a href="https://github.com/vacp2p/rfc-index/blob/0f1855edcf68ef982c4ce478b67d660809aa9830/docs/codex/raw/codex-block-exchange.md"><code>0f1855e</code></a> — Chore/fix headers (#239)</li>
|
|
<li><strong>2025-12-22</strong> — <a href="https://github.com/vacp2p/rfc-index/blob/b1a578393edf8487ccc97a5f25b25af9bf41efb3/docs/codex/raw/codex-block-exchange.md"><code>b1a5783</code></a> — Chore/mdbook updates (#237)</li>
|
|
<li><strong>2025-12-18</strong> — <a href="https://github.com/vacp2p/rfc-index/blob/d03e699084774ebecef9c6d4662498907c5e2080/docs/codex/raw/codex-block-exchange.md"><code>d03e699</code></a> — ci: add mdBook configuration (#233)</li>
|
|
<li><strong>2025-12-12</strong> — <a href="https://github.com/vacp2p/rfc-index/blob/b2f35644a4ffd6ec871d1a0e73c62dbffedb93e2/codex/raw/codex-block-exchange.md"><code>b2f3564</code></a> — Improved codex/raw/codex-block-exchange.md file (#215)</li>
|
|
<li><strong>2025-11-19</strong> — <a href="https://github.com/vacp2p/rfc-index/blob/63107d3830ae5d19c520923fde8fdfa7c89543b5/codex/raw/codex-block-exchange.md"><code>63107d3</code></a> — Created new codex/raw/codex-block-exchange.md file (#211)</li>
|
|
</ul>
|
|
<!-- timeline:end -->
|
|
<h2 id="specification-status"><a class="header" href="#specification-status">Specification Status</a></h2>
|
|
<p>This specification contains a mix of:</p>
|
|
<ul>
|
|
<li><strong>Verified protocol elements</strong>: Core message formats, protobuf structures, and addressing modes confirmed from implementation</li>
|
|
<li><strong>Design specifications</strong>: Payment flows, state machines, and negotiation strategies representing intended behavior</li>
|
|
<li><strong>Recommended values</strong>: Protocol limits and timeouts that serve as guidelines (actual implementations may vary)</li>
|
|
<li><strong>Pending verification</strong>: Some technical details (e.g., multicodec 0xCD02) require further validation</li>
|
|
</ul>
|
|
<p>Sections marked with notes indicate areas where implementation details may differ from this specification.</p>
|
|
<h2 id="abstract"><a class="header" href="#abstract">Abstract</a></h2>
|
|
<p>The Block Exchange (BE) is a core Codex component responsible for
|
|
peer-to-peer content distribution across the network.
|
|
It manages the sending and receiving of data blocks between nodes,
|
|
enabling efficient data sharing and retrieval.
|
|
This specification defines both an internal service interface and a
|
|
network protocol for referring to and providing data blocks.
|
|
Blocks are uniquely identifiable by means of an address and represent
|
|
fixed-length chunks of arbitrary data.</p>
|
|
<h2 id="semantics"><a class="header" href="#semantics">Semantics</a></h2>
|
|
<p>The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
|
|
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
|
|
document are to be interpreted as described in
|
|
<a href="https://www.ietf.org/rfc/rfc2119.txt">RFC 2119</a>.</p>
|
|
<h3 id="definitions"><a class="header" href="#definitions">Definitions</a></h3>
|
|
<div class="table-wrapper"><table><thead><tr><th>Term</th><th>Description</th></tr></thead><tbody>
|
|
<tr><td><strong>Block</strong></td><td>Fixed-length chunk of arbitrary data, uniquely identifiable</td></tr>
|
|
<tr><td><strong>Standalone Block</strong></td><td>Self-contained block addressed by SHA256 hash (CID)</td></tr>
|
|
<tr><td><strong>Dataset Block</strong></td><td>Block in ordered set, addressed by dataset CID + index</td></tr>
|
|
<tr><td><strong>Block Address</strong></td><td>Unique identifier for standalone/dataset addressing</td></tr>
|
|
<tr><td><strong>WantList</strong></td><td>List of block requests sent by a peer</td></tr>
|
|
<tr><td><strong>Block Delivery</strong></td><td>Transmission of block data from one peer to another</td></tr>
|
|
<tr><td><strong>Block Presence</strong></td><td>Indicator of whether peer has requested block</td></tr>
|
|
<tr><td><strong>Merkle Proof</strong></td><td>Proof verifying dataset block position correctness</td></tr>
|
|
<tr><td><strong>CodexProof</strong></td><td>Codex-specific Merkle proof format verifying a block's position within a dataset tree</td></tr>
|
|
<tr><td><strong>Stream</strong></td><td>Bidirectional libp2p communication channel between two peers for exchanging messages</td></tr>
|
|
<tr><td><strong>Peer Context Store</strong></td><td>Internal data structure tracking active peer connections, their WantLists, and exchange state</td></tr>
|
|
<tr><td><strong>CID</strong></td><td>Content Identifier - hash-based identifier for content</td></tr>
|
|
<tr><td><strong>Multicodec</strong></td><td>Self-describing format identifier for data encoding</td></tr>
|
|
<tr><td><strong>Multihash</strong></td><td>Self-describing hash format</td></tr>
|
|
</tbody></table>
|
|
</div>
|
|
<h2 id="motivation"><a class="header" href="#motivation">Motivation</a></h2>
|
|
<p>The Block Exchange module serves as the fundamental layer for content
|
|
distribution in the Codex network.
|
|
It provides primitives for requesting and delivering blocks of data
|
|
between peers, supporting both standalone blocks and blocks that are
|
|
part of larger datasets.
|
|
The protocol is designed to work over libp2p streams and integrates
|
|
with Codex's discovery, storage, and payment systems.</p>
|
|
<p>When a peer wishes to obtain a block, it registers its unique address
|
|
with the Block Exchange, and the Block Exchange will then be in charge
|
|
of procuring it by finding a peer that has the block, if any, and then
|
|
downloading it.
|
|
The Block Exchange will also accept requests from peers which might
|
|
want blocks that the node has, and provide them.</p>
|
|
<p><strong>Discovery Separation:</strong> Throughout this specification we assume that
|
|
if a peer wants a block, then the peer has the means to locate and
|
|
connect to peers which either: (1) have the block; or (2) are
|
|
reasonably expected to obtain the block in the future.
|
|
In practical implementations, the Block Exchange will typically require
|
|
the support of an underlying discovery service, e.g., the Codex DHT,
|
|
to look up such peers, but this is beyond the scope of this document.</p>
|
|
<p>The protocol supports two distinct block types to accommodate different
|
|
use cases: standalone blocks for independent data chunks and dataset
|
|
blocks for ordered collections of data that form larger structures.</p>
|
|
<h2 id="block-format"><a class="header" href="#block-format">Block Format</a></h2>
|
|
<p>The Block Exchange protocol supports two types of blocks:</p>
|
|
<h3 id="standalone-blocks"><a class="header" href="#standalone-blocks">Standalone Blocks</a></h3>
|
|
<p>Standalone blocks are self-contained pieces of data addressed by their
|
|
SHA256 content identifier (CID).
|
|
These blocks are independent and do not reference any larger structure.</p>
|
|
<p><strong>Properties:</strong></p>
|
|
<ul>
|
|
<li>Addressed by content hash (SHA256)</li>
|
|
<li>Default size: 64 KiB</li>
|
|
<li>Self-contained and independently verifiable</li>
|
|
</ul>
|
|
<h3 id="dataset-blocks"><a class="header" href="#dataset-blocks">Dataset Blocks</a></h3>
|
|
<p>Dataset blocks are part of ordered sets and are addressed by a
|
|
<code>(datasetCID, index)</code> tuple.
|
|
The datasetCID refers to the Merkle tree root of the entire dataset,
|
|
and the index indicates the block's position within that dataset.</p>
|
|
<p>Formally, we can define a block as a tuple consisting of raw data and
|
|
its content identifier: <code>(data: seq[byte], cid: Cid)</code>, where standalone
|
|
blocks are addressed by <code>cid</code>, and dataset blocks can be addressed
|
|
either by <code>cid</code> or a <code>(datasetCID, index)</code> tuple.</p>
|
|
<p><strong>Properties:</strong></p>
|
|
<ul>
|
|
<li>Addressed by <code>(treeCID, index)</code> tuple</li>
|
|
<li>Part of a Merkle tree structure</li>
|
|
<li>Require Merkle proof for verification</li>
|
|
<li>Must be uniformly sized within a dataset</li>
|
|
<li>Final blocks MUST be zero-padded if incomplete</li>
|
|
</ul>
|
|
<h3 id="block-specifications"><a class="header" href="#block-specifications">Block Specifications</a></h3>
|
|
<p>All blocks in the Codex Block Exchange protocol adhere to the
|
|
following specifications:</p>
|
|
<div class="table-wrapper"><table><thead><tr><th>Property</th><th>Value</th><th>Description</th></tr></thead><tbody>
|
|
<tr><td>Default Block Size</td><td>64 KiB</td><td>Standard size for data blocks</td></tr>
|
|
<tr><td>Maximum Block Size</td><td>100 MiB</td><td>Upper limit for block data field</td></tr>
|
|
<tr><td>Multicodec</td><td><code>codex-block</code> (0xCD02)*</td><td>Format identifier</td></tr>
|
|
<tr><td>Multihash</td><td><code>sha2-256</code> (0x12)</td><td>Hash algorithm for addressing</td></tr>
|
|
<tr><td>Padding Requirement</td><td>Zero-padding</td><td>Incomplete final blocks padded</td></tr>
|
|
</tbody></table>
|
|
</div>
|
|
<p><strong>Note:</strong> *The multicodec value 0xCD02 is not currently registered in the official <a href="https://github.com/multiformats/multicodec/blob/master/table.csv">multiformats multicodec table</a>. This may be a reserved/private code pending official registration.</p>
|
|
<h3 id="protocol-limits"><a class="header" href="#protocol-limits">Protocol Limits</a></h3>
|
|
<p>To ensure network stability and prevent resource exhaustion, implementations
|
|
SHOULD enforce reasonable limits. The following are <strong>recommended values</strong>
|
|
(actual implementation limits may vary):</p>
|
|
<div class="table-wrapper"><table><thead><tr><th>Limit</th><th>Recommended Value</th><th>Description</th></tr></thead><tbody>
|
|
<tr><td><strong>Maximum Block Size</strong></td><td>100 MiB</td><td>Maximum size of block data in BlockDelivery</td></tr>
|
|
<tr><td><strong>Maximum WantList Size</strong></td><td>1000 entries</td><td>Maximum entries per WantList message</td></tr>
|
|
<tr><td><strong>Maximum Concurrent Requests</strong></td><td>256 per peer</td><td>Maximum simultaneous block requests per peer</td></tr>
|
|
<tr><td><strong>Stream Timeout</strong></td><td>60 seconds</td><td>Idle stream closure timeout</td></tr>
|
|
<tr><td><strong>Request Timeout</strong></td><td>300 seconds</td><td>Maximum time to fulfill a block request</td></tr>
|
|
<tr><td><strong>Maximum Message Size</strong></td><td>105 MiB</td><td>Maximum total message size (protobuf)</td></tr>
|
|
<tr><td><strong>Maximum Pending Bytes</strong></td><td>10 GiB</td><td>Maximum pending data per peer connection</td></tr>
|
|
</tbody></table>
|
|
</div>
|
|
<p><strong>Note:</strong> These values are <strong>not verified from implementation</strong> and serve as
|
|
reasonable guidelines. Actual implementations MAY use different limits based
|
|
on their resource constraints and deployment requirements.</p>
|
|
<p><strong>Enforcement:</strong></p>
|
|
<ul>
|
|
<li>Implementations MUST reject messages exceeding their configured size limits</li>
|
|
<li>Implementations SHOULD track per-peer request counts</li>
|
|
<li>Implementations SHOULD close streams exceeding configured timeout limits</li>
|
|
<li>Implementations MAY implement stricter or more lenient limits based on local resources</li>
|
|
</ul>
|
|
<h2 id="service-interface"><a class="header" href="#service-interface">Service Interface</a></h2>
|
|
<p>The Block Exchange module exposes two core primitives for
|
|
block management:</p>
|
|
<h3 id="requestblock"><a class="header" href="#requestblock"><code>requestBlock</code></a></h3>
|
|
<pre><code class="language-python">async def requestBlock(address: BlockAddress) -> Block
|
|
</code></pre>
|
|
<p>Registers a block address for retrieval and returns the block data
|
|
when available.
|
|
This function can be awaited by the caller until the block is retrieved
|
|
from the network or local storage.</p>
|
|
<p><strong>Parameters:</strong></p>
|
|
<ul>
|
|
<li><code>address</code>: BlockAddress - The unique address identifying the block
|
|
to retrieve</li>
|
|
</ul>
|
|
<p><strong>Returns:</strong></p>
|
|
<ul>
|
|
<li><code>Block</code> - The retrieved block data</li>
|
|
</ul>
|
|
<h3 id="cancelrequest"><a class="header" href="#cancelrequest"><code>cancelRequest</code></a></h3>
|
|
<pre><code class="language-python">async def cancelRequest(address: BlockAddress) -> bool
|
|
</code></pre>
|
|
<p>Cancels a previously registered block request.</p>
|
|
<p><strong>Parameters:</strong></p>
|
|
<ul>
|
|
<li><code>address</code>: BlockAddress - The address of the block request to cancel</li>
|
|
</ul>
|
|
<p><strong>Returns:</strong></p>
|
|
<ul>
|
|
<li><code>bool</code> - True if the cancellation was successful, False otherwise</li>
|
|
</ul>
|
|
<h2 id="dependencies"><a class="header" href="#dependencies">Dependencies</a></h2>
|
|
<p>The Block Exchange module depends on and interacts with several other
|
|
Codex components:</p>
|
|
<div class="table-wrapper"><table><thead><tr><th>Component</th><th>Purpose</th></tr></thead><tbody>
|
|
<tr><td><strong>Discovery Module</strong></td><td>DHT-based peer discovery for locating nodes</td></tr>
|
|
<tr><td><strong>Local Store (Repo)</strong></td><td>Persistent block storage for local blocks</td></tr>
|
|
<tr><td><strong>Advertiser</strong></td><td>Announces block availability to the network</td></tr>
|
|
<tr><td><strong>Network Layer</strong></td><td>libp2p connections and stream management</td></tr>
|
|
</tbody></table>
|
|
</div>
|
|
<h2 id="protocol-specification"><a class="header" href="#protocol-specification">Protocol Specification</a></h2>
|
|
<h3 id="protocol-identifier"><a class="header" href="#protocol-identifier">Protocol Identifier</a></h3>
|
|
<p>The Block Exchange protocol uses the following libp2p protocol
|
|
identifier:</p>
|
|
<pre><code class="language-text">/codex/blockexc/1.0.0
|
|
</code></pre>
|
|
<h3 id="version-negotiation"><a class="header" href="#version-negotiation">Version Negotiation</a></h3>
|
|
<p>The protocol version is negotiated through libp2p's multistream-select
|
|
protocol during connection establishment. The following describes standard
|
|
libp2p version negotiation behavior; actual Codex implementation details
|
|
may vary.</p>
|
|
<h4 id="protocol-versioning"><a class="header" href="#protocol-versioning">Protocol Versioning</a></h4>
|
|
<p><strong>Version Format</strong>: <code>/codex/blockexc/<major>.<minor>.<patch></code></p>
|
|
<ul>
|
|
<li><strong>Major version</strong>: Incompatible protocol changes</li>
|
|
<li><strong>Minor version</strong>: Backward-compatible feature additions</li>
|
|
<li><strong>Patch version</strong>: Backward-compatible bug fixes</li>
|
|
</ul>
|
|
<p><strong>Current Version</strong>: <code>1.0.0</code></p>
|
|
<h4 id="version-negotiation-process"><a class="header" href="#version-negotiation-process">Version Negotiation Process</a></h4>
|
|
<pre><code class="language-text">1. Initiator opens stream
|
|
2. Initiator proposes: "/codex/blockexc/1.0.0"
|
|
3. Responder checks supported versions
|
|
4. If supported:
|
|
Responder accepts: "/codex/blockexc/1.0.0"
|
|
→ Connection established
|
|
5. If not supported:
|
|
Responder rejects with: "na" (not available)
|
|
→ Try fallback version or close connection
|
|
</code></pre>
|
|
<h4 id="compatibility-rules"><a class="header" href="#compatibility-rules">Compatibility Rules</a></h4>
|
|
<p><strong>Major Version Compatibility:</strong></p>
|
|
<ul>
|
|
<li>Major version <code>1.x.x</code> is incompatible with <code>2.x.x</code></li>
|
|
<li>Nodes MUST support only their major version</li>
|
|
<li>Cross-major-version communication requires protocol upgrade</li>
|
|
</ul>
|
|
<p><strong>Minor Version Compatibility:</strong></p>
|
|
<ul>
|
|
<li>Version <code>1.1.0</code> MUST be backward compatible with <code>1.0.0</code></li>
|
|
<li>Newer minors MAY include optional features</li>
|
|
<li>Older nodes ignore unknown message fields (protobuf semantics)</li>
|
|
</ul>
|
|
<p><strong>Patch Version Compatibility:</strong></p>
|
|
<ul>
|
|
<li>All patches within same minor version are fully compatible</li>
|
|
<li>Patches fix bugs without changing protocol behavior</li>
|
|
</ul>
|
|
<h4 id="multi-version-support"><a class="header" href="#multi-version-support">Multi-Version Support</a></h4>
|
|
<p>Implementations MAY support multiple protocol versions simultaneously:</p>
|
|
<pre><code class="language-text">Supported protocols (in preference order):
|
|
1. /codex/blockexc/1.2.0 (preferred, latest features)
|
|
2. /codex/blockexc/1.1.0 (fallback, stable)
|
|
3. /codex/blockexc/1.0.0 (legacy support)
|
|
</code></pre>
|
|
<p><strong>Negotiation Strategy:</strong></p>
|
|
<ol>
|
|
<li>Propose highest supported version first</li>
|
|
<li>If rejected, try next lower version</li>
|
|
<li>If all rejected, connection fails</li>
|
|
<li>Track peer's supported version for future connections</li>
|
|
</ol>
|
|
<h4 id="feature-detection"><a class="header" href="#feature-detection">Feature Detection</a></h4>
|
|
<p>For optional features within same major.minor version:</p>
|
|
<pre><code class="language-text">Method 1: Message field presence
|
|
- Send message with optional field
|
|
- Peer ignores if not supported (protobuf default)
|
|
|
|
Method 2: Capability exchange (future extension)
|
|
- Exchange capability bitmask in initial message
|
|
- Enable features only if both peers support
|
|
</code></pre>
|
|
<h4 id="version-upgrade-path"><a class="header" href="#version-upgrade-path">Version Upgrade Path</a></h4>
|
|
<p><strong>Backward Compatibility:</strong></p>
|
|
<ul>
|
|
<li>New versions MUST handle messages from older versions</li>
|
|
<li>Unknown message fields silently ignored</li>
|
|
<li>Unknown WantList flags ignored</li>
|
|
<li>Unknown BlockPresence types treated as DontHave</li>
|
|
</ul>
|
|
<p><strong>Forward Compatibility:</strong></p>
|
|
<ul>
|
|
<li>Older versions MAY ignore new message types</li>
|
|
<li>Critical features require major version bump</li>
|
|
<li>Optional features use minor version bump</li>
|
|
</ul>
|
|
<h3 id="connection-model"><a class="header" href="#connection-model">Connection Model</a></h3>
|
|
<p>The protocol operates over libp2p streams.
|
|
When a node wants to communicate with a peer:</p>
|
|
<ol>
|
|
<li>The initiating node dials the peer using the protocol identifier</li>
|
|
<li>A bidirectional stream is established</li>
|
|
<li>Both sides can send and receive messages on this stream</li>
|
|
<li>Messages are encoded using Protocol Buffers</li>
|
|
<li>The stream remains open for the duration of the exchange session</li>
|
|
<li>Peers track active connections in a peer context store</li>
|
|
</ol>
|
|
<p>The protocol handles peer lifecycle events:</p>
|
|
<ul>
|
|
<li><strong>Peer Joined</strong>: When a peer connects, it is added to the active
|
|
peer set</li>
|
|
<li><strong>Peer Departed</strong>: When a peer disconnects gracefully, its context
|
|
is cleaned up</li>
|
|
<li><strong>Peer Dropped</strong>: When a peer connection fails, it is removed from
|
|
the active set</li>
|
|
</ul>
|
|
<h3 id="message-flow-examples"><a class="header" href="#message-flow-examples">Message Flow Examples</a></h3>
|
|
<p>This section illustrates typical message exchange sequences for common
|
|
block exchange scenarios.</p>
|
|
<h4 id="example-1-standalone-block-request"><a class="header" href="#example-1-standalone-block-request">Example 1: Standalone Block Request</a></h4>
|
|
<p><strong>Scenario</strong>: Node A requests a standalone block from Node B</p>
|
|
<pre><code class="language-text">Node A Node B
|
|
| |
|
|
|--- Message(wantlist) ------------------>|
|
|
| wantlist.entries[0]: |
|
|
| address.cid = QmABC123 |
|
|
| wantType = wantBlock |
|
|
| priority = 0 |
|
|
| |
|
|
|<-- Message(blockPresences, payload) ----|
|
|
| blockPresences[0]: |
|
|
| address.cid = QmABC123 |
|
|
| type = presenceHave |
|
|
| payload[0]: |
|
|
| cid = QmABC123 |
|
|
| data = <64 KiB block data> |
|
|
| address.cid = QmABC123 |
|
|
| |
|
|
</code></pre>
|
|
<p><strong>Steps:</strong></p>
|
|
<ol>
|
|
<li>Node A sends WantList requesting block with <code>wantType = wantBlock</code></li>
|
|
<li>Node B checks local storage, finds block</li>
|
|
<li>Node B responds with BlockPresence confirming availability</li>
|
|
<li>Node B includes BlockDelivery with actual block data</li>
|
|
<li>Node A verifies CID matches SHA256(data)</li>
|
|
<li>Node A stores block locally</li>
|
|
</ol>
|
|
<h4 id="example-2-dataset-block-request-with-merkle-proof"><a class="header" href="#example-2-dataset-block-request-with-merkle-proof">Example 2: Dataset Block Request with Merkle Proof</a></h4>
|
|
<p><strong>Scenario</strong>: Node A requests a dataset block from Node B</p>
|
|
<pre><code class="language-text">Node A Node B
|
|
| |
|
|
|--- Message(wantlist) ------------------>|
|
|
| wantlist.entries[0]: |
|
|
| address.leaf = true |
|
|
| address.treeCid = QmTree456 |
|
|
| address.index = 42 |
|
|
| wantType = wantBlock |
|
|
| |
|
|
|<-- Message(payload) ---------------------|
|
|
| payload[0]: |
|
|
| cid = QmBlock789 |
|
|
| data = <64 KiB zero-padded data> |
|
|
| address.leaf = true |
|
|
| address.treeCid = QmTree456 |
|
|
| address.index = 42 |
|
|
| proof = <CodexProof bytes> |
|
|
| |
|
|
</code></pre>
|
|
<p><strong>Steps:</strong></p>
|
|
<ol>
|
|
<li>Node A sends WantList for dataset block at specific index</li>
|
|
<li>Node B locates block in dataset</li>
|
|
<li>Node B generates CodexProof for block position in Merkle tree</li>
|
|
<li>Node B delivers block with proof</li>
|
|
<li>Node A verifies proof against treeCid</li>
|
|
<li>Node A verifies block data integrity</li>
|
|
<li>Node A stores block with dataset association</li>
|
|
</ol>
|
|
<h4 id="example-3-block-presence-check-wanthave"><a class="header" href="#example-3-block-presence-check-wanthave">Example 3: Block Presence Check (wantHave)</a></h4>
|
|
<p><strong>Scenario</strong>: Node A checks if Node B has a block without requesting full data</p>
|
|
<pre><code class="language-text">Node A Node B
|
|
| |
|
|
|--- Message(wantlist) ------------------>|
|
|
| wantlist.entries[0]: |
|
|
| address.cid = QmCheck999 |
|
|
| wantType = wantHave |
|
|
| sendDontHave = true |
|
|
| |
|
|
|<-- Message(blockPresences) -------------|
|
|
| blockPresences[0]: |
|
|
| address.cid = QmCheck999 |
|
|
| type = presenceHave |
|
|
| price = 0x00 (free) |
|
|
| |
|
|
</code></pre>
|
|
<p><strong>Steps:</strong></p>
|
|
<ol>
|
|
<li>Node A sends WantList with <code>wantType = wantHave</code></li>
|
|
<li>Node B checks local storage without loading block data</li>
|
|
<li>Node B responds with BlockPresence only (no payload)</li>
|
|
<li>Node A updates peer availability map</li>
|
|
<li>If Node A decides to request, sends new WantList with <code>wantType = wantBlock</code></li>
|
|
</ol>
|
|
<h4 id="example-4-block-not-available"><a class="header" href="#example-4-block-not-available">Example 4: Block Not Available</a></h4>
|
|
<p><strong>Scenario</strong>: Node A requests block Node B doesn't have</p>
|
|
<pre><code class="language-text">Node A Node B
|
|
| |
|
|
|--- Message(wantlist) ------------------>|
|
|
| wantlist.entries[0]: |
|
|
| address.cid = QmMissing111 |
|
|
| wantType = wantBlock |
|
|
| sendDontHave = true |
|
|
| |
|
|
|<-- Message(blockPresences) -------------|
|
|
| blockPresences[0]: |
|
|
| address.cid = QmMissing111 |
|
|
| type = presenceDontHave |
|
|
| |
|
|
</code></pre>
|
|
<p><strong>Steps:</strong></p>
|
|
<ol>
|
|
<li>Node A requests block with <code>sendDontHave = true</code></li>
|
|
<li>Node B checks storage, block not found</li>
|
|
<li>Node B sends BlockPresence with <code>presenceDontHave</code></li>
|
|
<li>Node A removes Node B from candidates for this block</li>
|
|
<li>Node A queries discovery service for alternative peers</li>
|
|
</ol>
|
|
<h4 id="example-5-wantlist-cancellation"><a class="header" href="#example-5-wantlist-cancellation">Example 5: WantList Cancellation</a></h4>
|
|
<p><strong>Scenario</strong>: Node A cancels a previous block request</p>
|
|
<pre><code class="language-text">Node A Node B
|
|
| |
|
|
|--- Message(wantlist) ------------------>|
|
|
| wantlist.entries[0]: |
|
|
| address.cid = QmCancel222 |
|
|
| cancel = true |
|
|
| |
|
|
</code></pre>
|
|
<p><strong>Steps:</strong></p>
|
|
<ol>
|
|
<li>Node A sends WantList entry with <code>cancel = true</code></li>
|
|
<li>Node B removes block request from peer's want queue</li>
|
|
<li>Node B stops any pending block transfer for this address</li>
|
|
<li>No response message required for cancellation</li>
|
|
</ol>
|
|
<h4 id="example-6-delta-wantlist-update"><a class="header" href="#example-6-delta-wantlist-update">Example 6: Delta WantList Update</a></h4>
|
|
<p><strong>Scenario</strong>: Node A adds requests to existing WantList</p>
|
|
<pre><code class="language-text">Node A Node B
|
|
| |
|
|
|--- Message(wantlist) ------------------>|
|
|
| wantlist.full = false |
|
|
| wantlist.entries[0]: |
|
|
| address.cid = QmNew1 |
|
|
| wantType = wantBlock |
|
|
| wantlist.entries[1]: |
|
|
| address.cid = QmNew2 |
|
|
| wantType = wantBlock |
|
|
| |
|
|
</code></pre>
|
|
<p><strong>Steps:</strong></p>
|
|
<ol>
|
|
<li>Node A sends WantList with <code>full = false</code> (delta update)</li>
|
|
<li>Node B merges entries with existing WantList for Node A</li>
|
|
<li>Node B begins processing new requests</li>
|
|
<li>Previous WantList entries remain active</li>
|
|
</ol>
|
|
<h3 id="sequence-diagrams"><a class="header" href="#sequence-diagrams">Sequence Diagrams</a></h3>
|
|
<p>These diagrams illustrate the complete flow of block exchange operations
|
|
including service interface, peer discovery, and network protocol interactions.</p>
|
|
<h4 id="complete-block-request-flow"><a class="header" href="#complete-block-request-flow">Complete Block Request Flow</a></h4>
|
|
<p>The protocol supports two strategies for WantBlock requests,
|
|
each with different trade-offs.
|
|
Implementations may choose the strategy based on network conditions,
|
|
peer availability, and resource constraints.</p>
|
|
<h5 id="strategy-1-parallel-request-low-latency"><a class="header" href="#strategy-1-parallel-request-low-latency">Strategy 1: Parallel Request (Low Latency)</a></h5>
|
|
<p>In this strategy, the requester sends <code>wantType = wantBlock</code> to all
|
|
discovered peers simultaneously.
|
|
This minimizes latency as the first peer to respond with the block
|
|
data wins, but it wastes bandwidth since multiple peers may send
|
|
the same block data.</p>
|
|
<p><strong>Trade-offs:</strong></p>
|
|
<ul>
|
|
<li><strong>Pro</strong>: Lowest latency - block arrives as soon as any peer can deliver it</li>
|
|
<li><strong>Pro</strong>: More resilient to slow or unresponsive peers</li>
|
|
<li><strong>Con</strong>: Bandwidth-wasteful - multiple peers may send duplicate data</li>
|
|
<li><strong>Con</strong>: Higher network overhead for the requester</li>
|
|
<li><strong>Best for</strong>: Time-critical data retrieval, unreliable networks</li>
|
|
</ul>
|
|
<pre><code class="language-mermaid">sequenceDiagram
|
|
participant Client
|
|
participant BlockExchange
|
|
participant LocalStore
|
|
participant Discovery
|
|
participant PeerA
|
|
participant PeerB
|
|
participant PeerC
|
|
|
|
Client->>BlockExchange: requestBlock(address)
|
|
BlockExchange->>LocalStore: checkBlock(address)
|
|
LocalStore-->>BlockExchange: Not found
|
|
|
|
BlockExchange->>Discovery: findPeers(address)
|
|
Discovery-->>BlockExchange: [PeerA, PeerB, PeerC]
|
|
|
|
par Send wantBlock to all peers
|
|
BlockExchange->>PeerA: Message(wantlist: wantBlock)
|
|
BlockExchange->>PeerB: Message(wantlist: wantBlock)
|
|
BlockExchange->>PeerC: Message(wantlist: wantBlock)
|
|
end
|
|
|
|
Note over PeerA,PeerC: All peers start preparing block data
|
|
|
|
PeerB-->>BlockExchange: Message(payload: BlockDelivery)
|
|
Note over BlockExchange: First response wins
|
|
|
|
BlockExchange->>BlockExchange: Verify block
|
|
BlockExchange->>LocalStore: Store block
|
|
|
|
par Cancel requests to other peers
|
|
BlockExchange->>PeerA: Message(wantlist: cancel)
|
|
BlockExchange->>PeerC: Message(wantlist: cancel)
|
|
end
|
|
|
|
Note over PeerA,PeerC: May have already sent data (wasted bandwidth)
|
|
|
|
BlockExchange-->>Client: Return block
|
|
</code></pre>
|
|
<h5 id="strategy-2-two-phase-discovery-bandwidth-efficient"><a class="header" href="#strategy-2-two-phase-discovery-bandwidth-efficient">Strategy 2: Two-Phase Discovery (Bandwidth Efficient)</a></h5>
|
|
<p>In this strategy, the requester first sends <code>wantType = wantHave</code> to
|
|
discover which peers have the block, then sends <code>wantType = wantBlock</code>
|
|
only to a single selected peer.
|
|
This conserves bandwidth but adds an extra round-trip of latency.</p>
|
|
<p><strong>Trade-offs:</strong></p>
|
|
<ul>
|
|
<li><strong>Pro</strong>: Bandwidth-efficient - only one peer sends block data</li>
|
|
<li><strong>Pro</strong>: Enables price comparison before committing to a peer</li>
|
|
<li><strong>Pro</strong>: Allows selection based on peer reputation or proximity</li>
|
|
<li><strong>Con</strong>: Higher latency due to extra round-trip for presence check</li>
|
|
<li><strong>Con</strong>: Selected peer may become unavailable between phases</li>
|
|
<li><strong>Best for</strong>: Large blocks, paid content, bandwidth-constrained networks</li>
|
|
</ul>
|
|
<pre><code class="language-mermaid">sequenceDiagram
|
|
participant Client
|
|
participant BlockExchange
|
|
participant LocalStore
|
|
participant Discovery
|
|
participant PeerA
|
|
participant PeerB
|
|
participant PeerC
|
|
|
|
Client->>BlockExchange: requestBlock(address)
|
|
BlockExchange->>LocalStore: checkBlock(address)
|
|
LocalStore-->>BlockExchange: Not found
|
|
|
|
BlockExchange->>Discovery: findPeers(address)
|
|
Discovery-->>BlockExchange: [PeerA, PeerB, PeerC]
|
|
|
|
Note over BlockExchange: Phase 1: Discovery
|
|
|
|
par Send wantHave to all peers
|
|
BlockExchange->>PeerA: Message(wantlist: wantHave)
|
|
BlockExchange->>PeerB: Message(wantlist: wantHave)
|
|
BlockExchange->>PeerC: Message(wantlist: wantHave)
|
|
end
|
|
|
|
PeerA-->>BlockExchange: BlockPresence(presenceDontHave)
|
|
PeerB-->>BlockExchange: BlockPresence(presenceHave, price=X)
|
|
PeerC-->>BlockExchange: BlockPresence(presenceHave, price=Y)
|
|
|
|
BlockExchange->>BlockExchange: Select best peer (PeerB: lower price)
|
|
|
|
Note over BlockExchange: Phase 2: Retrieval
|
|
|
|
BlockExchange->>PeerB: Message(wantlist: wantBlock)
|
|
PeerB-->>BlockExchange: Message(payload: BlockDelivery)
|
|
|
|
BlockExchange->>BlockExchange: Verify block
|
|
BlockExchange->>LocalStore: Store block
|
|
BlockExchange-->>Client: Return block
|
|
</code></pre>
|
|
<h5 id="hybrid-approach"><a class="header" href="#hybrid-approach">Hybrid Approach</a></h5>
|
|
<p>Implementations MAY combine both strategies:</p>
|
|
<ol>
|
|
<li>Use two-phase discovery for large blocks or paid content</li>
|
|
<li>Use parallel requests for small blocks or time-critical data</li>
|
|
<li>Adaptively switch strategies based on network conditions</li>
|
|
</ol>
|
|
<pre><code class="language-mermaid">flowchart TD
|
|
A[Block Request] --> B{Block Size?}
|
|
B -->|Small < 64 KiB| C[Parallel Strategy]
|
|
B -->|Large >= 64 KiB| D{Paid Content?}
|
|
D -->|Yes| E[Two-Phase Discovery]
|
|
D -->|No| F{Network Condition?}
|
|
F -->|Reliable| E
|
|
F -->|Unreliable| C
|
|
C --> G[Return Block]
|
|
E --> G
|
|
</code></pre>
|
|
<h4 id="dataset-block-verification-flow"><a class="header" href="#dataset-block-verification-flow">Dataset Block Verification Flow</a></h4>
|
|
<pre><code class="language-mermaid">sequenceDiagram
|
|
participant Requester
|
|
participant Provider
|
|
participant Verifier
|
|
|
|
Requester->>Provider: WantList(leaf=true, treeCid, index)
|
|
Provider->>Provider: Load block at index
|
|
Provider->>Provider: Generate CodexProof
|
|
Provider->>Requester: BlockDelivery(data, proof)
|
|
|
|
Requester->>Verifier: Verify proof
|
|
|
|
alt Proof valid
|
|
Verifier-->>Requester: Valid
|
|
Requester->>Requester: Verify CID
|
|
alt CID matches
|
|
Requester->>Requester: Store block
|
|
Requester-->>Requester: Success
|
|
else CID mismatch
|
|
Requester->>Requester: Reject block
|
|
Requester->>Provider: Disconnect
|
|
end
|
|
else Proof invalid
|
|
Verifier-->>Requester: Invalid
|
|
Requester->>Requester: Reject block
|
|
Requester->>Provider: Disconnect
|
|
end
|
|
</code></pre>
|
|
<h4 id="payment-flow-with-state-channels"><a class="header" href="#payment-flow-with-state-channels">Payment Flow with State Channels</a></h4>
|
|
<pre><code class="language-mermaid">sequenceDiagram
|
|
participant Buyer
|
|
participant Seller
|
|
participant StateChannel
|
|
|
|
Buyer->>Seller: Message(wantlist)
|
|
Seller->>Seller: Check block availability
|
|
Seller->>Buyer: BlockPresence(price)
|
|
|
|
alt Buyer accepts price
|
|
Buyer->>StateChannel: Create update
|
|
StateChannel-->>Buyer: Signed state
|
|
Buyer->>Seller: Message(payment: StateChannelUpdate)
|
|
Seller->>StateChannel: Verify update
|
|
|
|
alt Payment valid
|
|
StateChannel-->>Seller: Valid
|
|
Seller->>Buyer: BlockDelivery(data)
|
|
Buyer->>Buyer: Verify block
|
|
Buyer->>StateChannel: Finalize
|
|
else Payment invalid
|
|
StateChannel-->>Seller: Invalid
|
|
Seller->>Buyer: BlockPresence(price)
|
|
end
|
|
else Buyer rejects price
|
|
Buyer->>Seller: Message(wantlist.cancel)
|
|
end
|
|
</code></pre>
|
|
<h4 id="peer-lifecycle-management"><a class="header" href="#peer-lifecycle-management">Peer Lifecycle Management</a></h4>
|
|
<pre><code class="language-mermaid">sequenceDiagram
|
|
participant Network
|
|
participant BlockExchange
|
|
participant PeerStore
|
|
participant Peer
|
|
|
|
Network->>BlockExchange: PeerJoined(Peer)
|
|
BlockExchange->>PeerStore: AddPeer(Peer)
|
|
BlockExchange->>Peer: Open stream
|
|
|
|
loop Active exchange
|
|
BlockExchange->>Peer: Message(wantlist/payload)
|
|
Peer->>BlockExchange: Message(payload/presence)
|
|
end
|
|
|
|
alt Graceful disconnect
|
|
Peer->>BlockExchange: Close stream
|
|
BlockExchange->>PeerStore: RemovePeer(Peer)
|
|
else Connection failure
|
|
Network->>BlockExchange: PeerDropped(Peer)
|
|
BlockExchange->>PeerStore: RemovePeer(Peer)
|
|
BlockExchange->>BlockExchange: Requeue pending requests
|
|
end
|
|
</code></pre>
|
|
<h3 id="message-format"><a class="header" href="#message-format">Message Format</a></h3>
|
|
<p>All messages use Protocol Buffers encoding for serialization.
|
|
The main message structure supports multiple operation types in a
|
|
single message.</p>
|
|
<h4 id="main-message-structure"><a class="header" href="#main-message-structure">Main Message Structure</a></h4>
|
|
<pre><code class="language-protobuf">message Message {
|
|
Wantlist wantlist = 1;
|
|
// Field 2 reserved for future use
|
|
repeated BlockDelivery payload = 3;
|
|
repeated BlockPresence blockPresences = 4;
|
|
int32 pendingBytes = 5;
|
|
AccountMessage account = 6;
|
|
StateChannelUpdate payment = 7;
|
|
}
|
|
</code></pre>
|
|
<p><strong>Fields:</strong></p>
|
|
<ul>
|
|
<li><code>wantlist</code>: Block requests from the sender</li>
|
|
<li>Field 2: Reserved (unused, see note below)</li>
|
|
<li><code>payload</code>: Block deliveries (actual block data)</li>
|
|
<li><code>blockPresences</code>: Availability indicators for requested blocks</li>
|
|
<li><code>pendingBytes</code>: Number of bytes pending delivery</li>
|
|
<li><code>account</code>: Account information for micropayments</li>
|
|
<li><code>payment</code>: State channel update for payment processing</li>
|
|
</ul>
|
|
<p><strong>Note on Missing Field 2:</strong></p>
|
|
<p>Field number 2 is intentionally skipped in the Message protobuf definition.
|
|
This is a common protobuf practice for several reasons:</p>
|
|
<ul>
|
|
<li><strong>Protocol Evolution</strong>: Field 2 may have been used in earlier versions and
|
|
removed, with the field number reserved to prevent reuse</li>
|
|
<li><strong>Forward Compatibility</strong>: Reserving field numbers ensures old clients can
|
|
safely ignore new fields</li>
|
|
<li><strong>Implementation History</strong>: May have been used during development and removed
|
|
before final release</li>
|
|
</ul>
|
|
<p>The gap does not affect protocol operation. Protobuf field numbers need not be
|
|
sequential, and skipping numbers is standard practice for protocol evolution.</p>
|
|
<h4 id="block-address"><a class="header" href="#block-address">Block Address</a></h4>
|
|
<p>The BlockAddress structure supports both standalone and dataset
|
|
block addressing:</p>
|
|
<pre><code class="language-protobuf">message BlockAddress {
|
|
bool leaf = 1;
|
|
bytes treeCid = 2; // Present when leaf = true
|
|
uint64 index = 3; // Present when leaf = true
|
|
bytes cid = 4; // Present when leaf = false
|
|
}
|
|
</code></pre>
|
|
<p><strong>Fields:</strong></p>
|
|
<ul>
|
|
<li><code>leaf</code>: Indicates if this is dataset block (true) or standalone
|
|
(false)</li>
|
|
<li><code>treeCid</code>: Merkle tree root CID (present when <code>leaf = true</code>)</li>
|
|
<li><code>index</code>: Position of block within dataset (present when <code>leaf = true</code>)</li>
|
|
<li><code>cid</code>: Content identifier of the block (present when <code>leaf = false</code>)</li>
|
|
</ul>
|
|
<p><strong>Addressing Modes:</strong></p>
|
|
<ul>
|
|
<li><strong>Standalone Block</strong> (<code>leaf = false</code>): Direct CID reference to a
|
|
standalone content block</li>
|
|
<li><strong>Dataset Block</strong> (<code>leaf = true</code>): Reference to a block within an
|
|
ordered set, identified by a Merkle tree root and an index.
|
|
The Merkle root may refer to either a regular dataset, or a dataset
|
|
that has undergone erasure-coding</li>
|
|
</ul>
|
|
<h4 id="wantlist"><a class="header" href="#wantlist">WantList</a></h4>
|
|
<p>The WantList communicates which blocks a peer desires to receive:</p>
|
|
<pre><code class="language-protobuf">message Wantlist {
|
|
enum WantType {
|
|
wantBlock = 0;
|
|
wantHave = 1;
|
|
}
|
|
|
|
message Entry {
|
|
BlockAddress address = 1;
|
|
int32 priority = 2;
|
|
bool cancel = 3;
|
|
WantType wantType = 4;
|
|
bool sendDontHave = 5;
|
|
}
|
|
|
|
repeated Entry entries = 1;
|
|
bool full = 2;
|
|
}
|
|
</code></pre>
|
|
<p><strong>WantType Values:</strong></p>
|
|
<ul>
|
|
<li><code>wantBlock (0)</code>: Request full block delivery</li>
|
|
<li><code>wantHave (1)</code>: Request availability information only (presence check)</li>
|
|
</ul>
|
|
<p><strong>Entry Fields:</strong></p>
|
|
<ul>
|
|
<li><code>address</code>: The block being requested</li>
|
|
<li><code>priority</code>: Request priority (currently always 0, reserved for future use)</li>
|
|
<li><code>cancel</code>: If true, cancels a previous want for this block</li>
|
|
<li><code>wantType</code>: Specifies whether full block or presence is desired
|
|
<ul>
|
|
<li><code>wantHave (1)</code>: Only check if peer has the block</li>
|
|
<li><code>wantBlock (0)</code>: Request full block data</li>
|
|
</ul>
|
|
</li>
|
|
<li><code>sendDontHave</code>: If true, peer should respond even if it doesn't have
|
|
the block</li>
|
|
</ul>
|
|
<p><strong>Priority Field Clarification:</strong></p>
|
|
<p>The <code>priority</code> field is currently fixed at <code>0</code> in all implementations and is
|
|
reserved for future protocol extensions. Originally intended for request
|
|
prioritization, this feature is not yet implemented.</p>
|
|
<p><strong>Current Behavior:</strong></p>
|
|
<ul>
|
|
<li>All WantList entries use <code>priority = 0</code></li>
|
|
<li>Implementations MUST accept priority values but MAY ignore them</li>
|
|
<li>Blocks are processed in order received, not by priority</li>
|
|
</ul>
|
|
<p><strong>Future Extensions:</strong></p>
|
|
<p>The priority field is reserved for:</p>
|
|
<ul>
|
|
<li><strong>Bandwidth Management</strong>: Higher priority blocks served first during congestion</li>
|
|
<li><strong>Time-Critical Data</strong>: Urgent blocks (e.g., recent dataset indices) prioritized</li>
|
|
<li><strong>Fair Queueing</strong>: Priority-based scheduling across multiple peers</li>
|
|
<li><strong>QoS Tiers</strong>: Different service levels based on payment/reputation</li>
|
|
</ul>
|
|
<p><strong>Implementation Notes:</strong></p>
|
|
<ul>
|
|
<li>Senders SHOULD set <code>priority = 0</code> for compatibility</li>
|
|
<li>Receivers MUST NOT reject messages with non-zero priority</li>
|
|
<li>Future protocol versions may activate priority-based scheduling</li>
|
|
<li>When activated, higher priority values = higher priority (0 = lowest)</li>
|
|
</ul>
|
|
<p><strong>WantList Fields:</strong></p>
|
|
<ul>
|
|
<li><code>entries</code>: List of block requests</li>
|
|
<li><code>full</code>: If true, replaces all previous entries; if false, delta update</li>
|
|
</ul>
|
|
<p><strong>Delta Updates:</strong></p>
|
|
<p>WantLists support delta updates for efficiency.
|
|
When <code>full = false</code>, entries represent additions or modifications to
|
|
the existing WantList rather than a complete replacement.</p>
|
|
<h4 id="block-delivery"><a class="header" href="#block-delivery">Block Delivery</a></h4>
|
|
<p>Block deliveries contain the actual block data along with verification
|
|
information:</p>
|
|
<pre><code class="language-protobuf">message BlockDelivery {
|
|
bytes cid = 1;
|
|
bytes data = 2;
|
|
BlockAddress address = 3;
|
|
bytes proof = 4;
|
|
}
|
|
</code></pre>
|
|
<p><strong>Fields:</strong></p>
|
|
<ul>
|
|
<li><code>cid</code>: Content identifier of the block</li>
|
|
<li><code>data</code>: Raw block data (up to 100 MiB)</li>
|
|
<li><code>address</code>: The BlockAddress identifying this block</li>
|
|
<li><code>proof</code>: Merkle proof (CodexProof) verifying block correctness
|
|
(required for dataset blocks)</li>
|
|
</ul>
|
|
<p><strong>Merkle Proof Verification:</strong></p>
|
|
<p>When delivering dataset blocks (<code>address.leaf = true</code>):</p>
|
|
<ul>
|
|
<li>The delivery MUST include a Merkle proof (CodexProof)</li>
|
|
<li>The proof verifies that the block at the given index is correctly
|
|
part of the Merkle tree identified by the tree CID</li>
|
|
<li>This applies to all datasets, irrespective of whether they have been
|
|
erasure-coded or not</li>
|
|
<li>Recipients MUST verify the proof before accepting the block</li>
|
|
<li>Invalid proofs result in block rejection</li>
|
|
</ul>
|
|
<h4 id="block-presence"><a class="header" href="#block-presence">Block Presence</a></h4>
|
|
<p>Block presence messages indicate whether a peer has or does not have a
|
|
requested block:</p>
|
|
<pre><code class="language-protobuf">enum BlockPresenceType {
|
|
presenceHave = 0;
|
|
presenceDontHave = 1;
|
|
}
|
|
|
|
message BlockPresence {
|
|
BlockAddress address = 1;
|
|
BlockPresenceType type = 2;
|
|
bytes price = 3;
|
|
}
|
|
</code></pre>
|
|
<p><strong>Fields:</strong></p>
|
|
<ul>
|
|
<li><code>address</code>: The block address being referenced</li>
|
|
<li><code>type</code>: Whether the peer has the block or not</li>
|
|
<li><code>price</code>: Price in wei (UInt256 format, see below)</li>
|
|
</ul>
|
|
<p><strong>UInt256 Price Format:</strong></p>
|
|
<p>The <code>price</code> field encodes a 256-bit unsigned integer representing the cost in
|
|
wei (the smallest Ethereum denomination, where 1 ETH = 10^18 wei).</p>
|
|
<p><strong>Encoding Specification:</strong></p>
|
|
<ul>
|
|
<li><strong>Format</strong>: 32 bytes, big-endian byte order</li>
|
|
<li><strong>Type</strong>: Unsigned 256-bit integer</li>
|
|
<li><strong>Range</strong>: 0 to 2^256 - 1</li>
|
|
<li><strong>Zero Price</strong>: <code>0x0000000000000000000000000000000000000000000000000000000000000000</code>
|
|
(block is free)</li>
|
|
</ul>
|
|
<p><strong>Examples:</strong></p>
|
|
<pre><code class="language-text">Free (0 wei):
|
|
0x0000000000000000000000000000000000000000000000000000000000000000
|
|
|
|
1 wei:
|
|
0x0000000000000000000000000000000000000000000000000000000000000001
|
|
|
|
1 gwei (10^9 wei):
|
|
0x000000000000000000000000000000000000000000000000000000003b9aca00
|
|
|
|
0.001 ETH (10^15 wei):
|
|
0x00000000000000000000000000000000000000000000000000038d7ea4c68000
|
|
|
|
1 ETH (10^18 wei):
|
|
0x0000000000000000000000000000000000000000000000000de0b6b3a7640000
|
|
|
|
Maximum (2^256 - 1):
|
|
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
|
</code></pre>
|
|
<p><strong>Conversion Logic:</strong></p>
|
|
<pre><code class="language-python"># Wei to bytes (big-endian)
|
|
def wei_to_bytes(amount_wei: int) -> bytes:
|
|
return amount_wei.to_bytes(32, byteorder='big')
|
|
|
|
# Bytes to wei
|
|
def bytes_to_wei(price_bytes: bytes) -> int:
|
|
return int.from_bytes(price_bytes, byteorder='big')
|
|
|
|
# ETH to wei to bytes
|
|
def eth_to_price_bytes(amount_eth: float) -> bytes:
|
|
amount_wei = int(amount_eth * 10**18)
|
|
return wei_to_bytes(amount_wei)
|
|
</code></pre>
|
|
<h4 id="payment-messages"><a class="header" href="#payment-messages">Payment Messages</a></h4>
|
|
<p>Payment-related messages for micropayments using Nitro state channels.</p>
|
|
<p><strong>Account Message:</strong></p>
|
|
<pre><code class="language-protobuf">message AccountMessage {
|
|
bytes address = 1; // Ethereum address to which payments should be made
|
|
}
|
|
</code></pre>
|
|
<p><strong>Fields:</strong></p>
|
|
<ul>
|
|
<li><code>address</code>: Ethereum address for receiving payments</li>
|
|
</ul>
|
|
<h3 id="concrete-message-examples"><a class="header" href="#concrete-message-examples">Concrete Message Examples</a></h3>
|
|
<p>This section provides real-world examples of protobuf messages for different
|
|
block exchange scenarios.</p>
|
|
<h4 id="example-1-simple-standalone-block-request"><a class="header" href="#example-1-simple-standalone-block-request">Example 1: Simple Standalone Block Request</a></h4>
|
|
<p><strong>Scenario</strong>: Request a single standalone block</p>
|
|
<p><strong>Protobuf (wire format representation):</strong></p>
|
|
<pre><code class="language-protobuf">Message {
|
|
wantlist: Wantlist {
|
|
entries: [
|
|
Entry {
|
|
address: BlockAddress {
|
|
leaf: false
|
|
cid: 0x0155a0e40220b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9 // CID bytes
|
|
}
|
|
priority: 0
|
|
cancel: false
|
|
wantType: wantBlock // 0
|
|
sendDontHave: true
|
|
}
|
|
]
|
|
full: true
|
|
}
|
|
}
|
|
</code></pre>
|
|
<p><strong>Hex representation (sample):</strong></p>
|
|
<pre><code class="language-text">0a2e 0a2c 0a24 0001 5512 2012 20b9 4d27
|
|
b993 4d3e 08a5 2e52 d7da 7dab fac4 84ef
|
|
e37a 5380 ee90 88f7 ace2 efcd e910 0018
|
|
0020 0028 011201 01
|
|
</code></pre>
|
|
<h4 id="example-2-dataset-block-request"><a class="header" href="#example-2-dataset-block-request">Example 2: Dataset Block Request</a></h4>
|
|
<p><strong>Scenario</strong>: Request block at index 100 from dataset</p>
|
|
<p><strong>Protobuf:</strong></p>
|
|
<pre><code class="language-protobuf">Message {
|
|
wantlist: Wantlist {
|
|
entries: [
|
|
Entry {
|
|
address: BlockAddress {
|
|
leaf: true
|
|
treeCid: 0x0155a0e40220c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 // Tree CID
|
|
index: 100
|
|
}
|
|
priority: 0
|
|
cancel: false
|
|
wantType: wantBlock
|
|
sendDontHave: true
|
|
}
|
|
]
|
|
full: false // Delta update
|
|
}
|
|
}
|
|
</code></pre>
|
|
<h4 id="example-3-block-delivery-with-proof"><a class="header" href="#example-3-block-delivery-with-proof">Example 3: Block Delivery with Proof</a></h4>
|
|
<p><strong>Scenario</strong>: Provider sends dataset block with Merkle proof</p>
|
|
<p><strong>Protobuf:</strong></p>
|
|
<pre><code class="language-protobuf">Message {
|
|
payload: [
|
|
BlockDelivery {
|
|
cid: 0x0155a0e40220a1b2c3d4e5f6071829... // Block CID
|
|
data: <65536 bytes of block data> // 64 KiB
|
|
address: BlockAddress {
|
|
leaf: true
|
|
treeCid: 0x0155a0e40220c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470
|
|
index: 100
|
|
}
|
|
proof: <CodexProof bytes> // Merkle proof data
|
|
// Contains: path indices, sibling hashes, tree height
|
|
// Format: Implementation-specific (e.g., [height][index][hash1][hash2]...[hashN])
|
|
// Size varies by tree depth (illustrative: ~1KB for depth-10 tree)
|
|
}
|
|
]
|
|
}
|
|
</code></pre>
|
|
<h4 id="example-4-block-presence-response"><a class="header" href="#example-4-block-presence-response">Example 4: Block Presence Response</a></h4>
|
|
<p><strong>Scenario</strong>: Provider indicates block availability with price</p>
|
|
<p><strong>Protobuf:</strong></p>
|
|
<pre><code class="language-protobuf">Message {
|
|
blockPresences: [
|
|
BlockPresence {
|
|
address: BlockAddress {
|
|
leaf: false
|
|
cid: 0x0155a0e40220b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
|
|
}
|
|
type: presenceHave // 0
|
|
price: 0x00000000000000000000000000000000000000000000000000038d7ea4c68000 // 0.001 ETH in wei
|
|
}
|
|
]
|
|
}
|
|
</code></pre>
|
|
<h4 id="example-5-payment-message"><a class="header" href="#example-5-payment-message">Example 5: Payment Message</a></h4>
|
|
<p><strong>Scenario</strong>: Send payment via state channel update</p>
|
|
<p><strong>Protobuf:</strong></p>
|
|
<pre><code class="language-protobuf">Message {
|
|
account: AccountMessage {
|
|
address: 0x742d35Cc6634C0532925a3b844200a717C48D6d9 // 20 bytes Ethereum address
|
|
}
|
|
payment: StateChannelUpdate {
|
|
update: <JSON bytes>
|
|
// Contains signed Nitro state as UTF-8 JSON string
|
|
// Example: {"channelId":"0x1234...","nonce":42,...}
|
|
}
|
|
}
|
|
</code></pre>
|
|
<h4 id="example-6-multiple-operations-in-one-message"><a class="header" href="#example-6-multiple-operations-in-one-message">Example 6: Multiple Operations in One Message</a></h4>
|
|
<p><strong>Scenario</strong>: Combined WantList, BlockPresence, and Delivery</p>
|
|
<p><strong>Protobuf:</strong></p>
|
|
<pre><code class="language-protobuf">Message {
|
|
wantlist: Wantlist {
|
|
entries: [
|
|
Entry {
|
|
address: BlockAddress {
|
|
leaf: false
|
|
cid: 0x0155a0e40220... // Requesting new block
|
|
}
|
|
wantType: wantBlock
|
|
priority: 0
|
|
cancel: false
|
|
sendDontHave: true
|
|
}
|
|
]
|
|
full: false
|
|
}
|
|
blockPresences: [
|
|
BlockPresence {
|
|
address: BlockAddress {
|
|
leaf: false
|
|
cid: 0x0155a0e40220... // Response to previous request
|
|
}
|
|
type: presenceHave
|
|
price: 0x00 // Free
|
|
}
|
|
]
|
|
payload: [
|
|
BlockDelivery {
|
|
cid: 0x0155a0e40220... // Delivering another block
|
|
data: <65536 bytes>
|
|
address: BlockAddress {
|
|
leaf: false
|
|
cid: 0x0155a0e40220...
|
|
}
|
|
}
|
|
]
|
|
pendingBytes: 131072 // 128 KiB more data pending
|
|
}
|
|
</code></pre>
|
|
<h4 id="example-7-wantlist-cancellation"><a class="header" href="#example-7-wantlist-cancellation">Example 7: WantList Cancellation</a></h4>
|
|
<p><strong>Scenario</strong>: Cancel multiple pending requests</p>
|
|
<p><strong>Protobuf:</strong></p>
|
|
<pre><code class="language-protobuf">Message {
|
|
wantlist: Wantlist {
|
|
entries: [
|
|
Entry {
|
|
address: BlockAddress {
|
|
leaf: false
|
|
cid: 0x0155a0e40220abc123...
|
|
}
|
|
cancel: true // Cancellation flag
|
|
},
|
|
Entry {
|
|
address: BlockAddress {
|
|
leaf: true
|
|
treeCid: 0x0155a0e40220def456...
|
|
index: 50
|
|
}
|
|
cancel: true
|
|
}
|
|
]
|
|
full: false
|
|
}
|
|
}
|
|
</code></pre>
|
|
<h4 id="cid-format-details"><a class="header" href="#cid-format-details">CID Format Details</a></h4>
|
|
<p><strong>CID Structure:</strong></p>
|
|
<pre><code class="language-text">CID v1 format (multibase + multicodec + multihash):
|
|
[0x01] [0x55] [0xa0] [0xe4] [0x02] [0x20] [<32 bytes SHA256 hash>]
|
|
│ │ │ │ │ │ │
|
|
│ │ │ │ │ │ └─ Hash digest
|
|
│ │ │ │ │ └───────── Hash length (32)
|
|
│ │ │ │ └──────────────── Hash algorithm (SHA2-256)
|
|
│ │ │ └─────────────────────── Codec size
|
|
│ │ └────────────────────────────── Codec (raw = 0x55)
|
|
│ └───────────────────────────────────── Multicodec prefix
|
|
└──────────────────────────────────────────── CID version (1)
|
|
|
|
Actual: 0x01 55 a0 e4 02 20 <hash bytes>
|
|
</code></pre>
|
|
<p><strong>Example Block CID Breakdown:</strong></p>
|
|
<pre><code class="language-text">Full CID: 0x0155a0e40220b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
|
|
|
|
Parts:
|
|
Version: 0x01 (CID v1)
|
|
Multicodec: 0x55 (raw)
|
|
Codec Size: 0xa0e402 (codex-block = 0xCD02, varint encoded)*
|
|
Hash Type: 0x20 (SHA2-256)
|
|
Hash Len: 0x12 (20) (32 bytes)
|
|
Hash: b94d27b993... (32 bytes SHA256)
|
|
</code></pre>
|
|
<p><strong>State Channel Update:</strong></p>
|
|
<pre><code class="language-protobuf">message StateChannelUpdate {
|
|
bytes update = 1; // Signed Nitro state, serialized as JSON
|
|
}
|
|
</code></pre>
|
|
<p><strong>Fields:</strong></p>
|
|
<ul>
|
|
<li><code>update</code>: Nitro state channel update containing payment information</li>
|
|
</ul>
|
|
<h3 id="payment-flow-and-price-negotiation"><a class="header" href="#payment-flow-and-price-negotiation">Payment Flow and Price Negotiation</a></h3>
|
|
<p>The Block Exchange protocol integrates with Nitro state channels to enable
|
|
micropayments for block delivery.</p>
|
|
<h4 id="payment-requirements"><a class="header" href="#payment-requirements">Payment Requirements</a></h4>
|
|
<p><strong>When Payment is Required:</strong></p>
|
|
<ul>
|
|
<li>Blocks marked as paid content by the provider</li>
|
|
<li>Provider's local policy requires payment for specific blocks</li>
|
|
<li>Block size exceeds free tier threshold (implementation-defined)</li>
|
|
<li>Requester has insufficient credit with provider</li>
|
|
</ul>
|
|
<p><strong>Free Blocks:</strong></p>
|
|
<ul>
|
|
<li>Blocks explicitly marked as free (<code>price = 0x00</code>)</li>
|
|
<li>Blocks exchanged between trusted peers</li>
|
|
<li>Small metadata blocks (implementation-defined)</li>
|
|
</ul>
|
|
<h4 id="price-discovery"><a class="header" href="#price-discovery">Price Discovery</a></h4>
|
|
<p><strong>Initial Price Advertisement:</strong></p>
|
|
<ol>
|
|
<li>Requester sends WantList with <code>wantType = wantHave</code></li>
|
|
<li>Provider responds with BlockPresence including <code>price</code> field</li>
|
|
<li>Price encoded as UInt256 in wei (smallest Ethereum unit)</li>
|
|
<li>Requester evaluates price against local policy</li>
|
|
</ol>
|
|
<p><strong>Price Format:</strong></p>
|
|
<pre><code class="language-text">price: bytes (32 bytes, big-endian UInt256)
|
|
Example: 0x0000000000000000000000000000000000000000000000000de0b6b3a7640000
|
|
represents 1 ETH = 10^18 wei
|
|
</code></pre>
|
|
<h4 id="payment-negotiation-process"><a class="header" href="#payment-negotiation-process">Payment Negotiation Process</a></h4>
|
|
<h5 id="step-1-price-quote"><a class="header" href="#step-1-price-quote">Step 1: Price Quote</a></h5>
|
|
<pre><code class="language-text">Requester → Provider: Message(wantlist: wantHave)
|
|
Provider → Requester: BlockPresence(type=presenceHave, price=<amount>)
|
|
</code></pre>
|
|
<h5 id="step-2-payment-decision"><a class="header" href="#step-2-payment-decision">Step 2: Payment Decision</a></h5>
|
|
<p>Requester evaluates price:</p>
|
|
<ul>
|
|
<li><strong>Accept</strong>: Proceed to payment</li>
|
|
<li><strong>Reject</strong>: Send cancellation</li>
|
|
<li><strong>Counter</strong>: Not supported in current protocol (future extension)</li>
|
|
</ul>
|
|
<h5 id="step-3-state-channel-update"><a class="header" href="#step-3-state-channel-update">Step 3: State Channel Update</a></h5>
|
|
<p>If accepted:</p>
|
|
<pre><code class="language-text">Requester:
|
|
1. Load existing state channel with Provider
|
|
2. Create new state with updated balance
|
|
3. Sign state update
|
|
4. Encode as JSON
|
|
|
|
Requester → Provider: Message(payment: StateChannelUpdate(update=<signed JSON>))
|
|
</code></pre>
|
|
<h5 id="step-4-payment-verification"><a class="header" href="#step-4-payment-verification">Step 4: Payment Verification</a></h5>
|
|
<pre><code class="language-text">Provider:
|
|
1. Decode state channel update
|
|
2. Verify signatures
|
|
3. Check balance increase matches price
|
|
4. Verify state channel validity
|
|
5. Check nonce/sequence number
|
|
|
|
If valid:
|
|
Provider → Requester: BlockDelivery(data, proof)
|
|
Else:
|
|
Provider → Requester: BlockPresence(price) // Retry with correct payment
|
|
</code></pre>
|
|
<h5 id="step-5-delivery-and-finalization"><a class="header" href="#step-5-delivery-and-finalization">Step 5: Delivery and Finalization</a></h5>
|
|
<pre><code class="language-text">Requester:
|
|
1. Receive and verify block
|
|
2. Store block locally
|
|
3. Finalize state channel update
|
|
4. Update peer credit balance
|
|
</code></pre>
|
|
<h4 id="payment-state-machine"><a class="header" href="#payment-state-machine">Payment State Machine</a></h4>
|
|
<p><strong>Note:</strong> The following state machine represents a <strong>design specification</strong> for
|
|
payment flow logic. Actual implementation may differ.</p>
|
|
<pre><code class="language-text">State: INIT
|
|
→ Send wantHave
|
|
→ Transition to PRICE_DISCOVERY
|
|
|
|
State: PRICE_DISCOVERY
|
|
← Receive BlockPresence(price)
|
|
→ If price acceptable: Transition to PAYMENT_CREATION
|
|
→ If price rejected: Transition to CANCELLED
|
|
|
|
State: PAYMENT_CREATION
|
|
→ Create state channel update
|
|
→ Send payment message
|
|
→ Transition to PAYMENT_PENDING
|
|
|
|
State: PAYMENT_PENDING
|
|
← Receive BlockDelivery: Transition to DELIVERY_VERIFICATION
|
|
← Receive BlockPresence(price): Transition to PAYMENT_FAILED
|
|
|
|
State: PAYMENT_FAILED
|
|
→ Retry with corrected payment: Transition to PAYMENT_CREATION
|
|
→ Abort: Transition to CANCELLED
|
|
|
|
State: DELIVERY_VERIFICATION
|
|
→ Verify block
|
|
→ If valid: Transition to COMPLETED
|
|
→ If invalid: Transition to DISPUTE
|
|
|
|
State: COMPLETED
|
|
→ Finalize state channel
|
|
→ End
|
|
|
|
State: CANCELLED
|
|
→ Send cancellation
|
|
→ End
|
|
|
|
State: DISPUTE
|
|
→ Reject block
|
|
→ Dispute state channel update
|
|
→ End
|
|
</code></pre>
|
|
<h4 id="state-channel-integration"><a class="header" href="#state-channel-integration">State Channel Integration</a></h4>
|
|
<p><strong>Account Message Usage:</strong></p>
|
|
<p>Sent early in connection to establish payment address:</p>
|
|
<pre><code class="language-protobuf">Message {
|
|
account: AccountMessage {
|
|
address: 0x742d35Cc6634C0532925a3b8... // Ethereum address
|
|
}
|
|
}
|
|
</code></pre>
|
|
<p><strong>State Channel Update Format:</strong></p>
|
|
<pre><code class="language-json">{
|
|
"channelId": "0x1234...",
|
|
"nonce": 42,
|
|
"balances": {
|
|
"0x742d35Cc...": "1000000000000000000", // Seller balance
|
|
"0x8ab5d2F3...": "500000000000000000" // Buyer balance
|
|
},
|
|
"signatures": [
|
|
"0x789abc...", // Buyer signature
|
|
"0x456def..." // Seller signature
|
|
]
|
|
}
|
|
</code></pre>
|
|
<h4 id="error-scenarios"><a class="header" href="#error-scenarios">Error Scenarios</a></h4>
|
|
<p><strong>Insufficient Funds:</strong></p>
|
|
<ul>
|
|
<li>State channel balance < block price</li>
|
|
<li>Response: BlockPresence with price (retry after funding)</li>
|
|
</ul>
|
|
<p><strong>Invalid Signature:</strong></p>
|
|
<ul>
|
|
<li>State update signature verification fails</li>
|
|
<li>Response: Reject payment, close stream if repeated</li>
|
|
</ul>
|
|
<p><strong>Nonce Mismatch:</strong></p>
|
|
<ul>
|
|
<li>State update nonce doesn't match expected sequence</li>
|
|
<li>Response: Request state sync, retry with correct nonce</li>
|
|
</ul>
|
|
<p><strong>Channel Expired:</strong></p>
|
|
<ul>
|
|
<li>State channel past expiration time</li>
|
|
<li>Response: Refuse payment, request new channel creation</li>
|
|
</ul>
|
|
<h2 id="error-handling"><a class="header" href="#error-handling">Error Handling</a></h2>
|
|
<p>The Block Exchange protocol defines error handling for common failure scenarios:</p>
|
|
<h3 id="verification-failures"><a class="header" href="#verification-failures">Verification Failures</a></h3>
|
|
<p><strong>Merkle Proof Verification Failure:</strong></p>
|
|
<ul>
|
|
<li><strong>Condition</strong>: CodexProof validation fails for dataset block</li>
|
|
<li><strong>Action</strong>: Reject block delivery, do NOT store block</li>
|
|
<li><strong>Response</strong>: Send BlockPresence with <code>presenceDontHave</code> for the address</li>
|
|
<li><strong>Logging</strong>: Log verification failure with peer ID and block address</li>
|
|
<li><strong>Peer Management</strong>: Track repeated failures; disconnect after threshold</li>
|
|
</ul>
|
|
<p><strong>CID Mismatch:</strong></p>
|
|
<ul>
|
|
<li><strong>Condition</strong>: SHA256 hash of block data doesn't match provided CID</li>
|
|
<li><strong>Action</strong>: Reject block delivery immediately</li>
|
|
<li><strong>Response</strong>: Close stream and mark peer as potentially malicious</li>
|
|
<li><strong>Logging</strong>: Log CID mismatch with peer ID and expected/actual CIDs</li>
|
|
</ul>
|
|
<h3 id="network-failures"><a class="header" href="#network-failures">Network Failures</a></h3>
|
|
<p><strong>Stream Disconnection:</strong></p>
|
|
<ul>
|
|
<li><strong>Condition</strong>: libp2p stream closes unexpectedly during transfer</li>
|
|
<li><strong>Action</strong>: Cancel pending block requests for that peer</li>
|
|
<li><strong>Recovery</strong>: Attempt to request blocks from alternative peers</li>
|
|
<li><strong>Timeout</strong>: Wait for stream timeout (60s) before peer cleanup</li>
|
|
</ul>
|
|
<p><strong>Missing Blocks:</strong></p>
|
|
<ul>
|
|
<li><strong>Condition</strong>: Peer responds with <code>presenceDontHave</code> for requested block</li>
|
|
<li><strong>Action</strong>: Remove peer from candidates for this block</li>
|
|
<li><strong>Recovery</strong>: Query discovery service for alternative peers</li>
|
|
<li><strong>Fallback</strong>: If no peers have block, return error to <code>requestBlock</code> caller</li>
|
|
</ul>
|
|
<p><strong>Request Timeout:</strong></p>
|
|
<ul>
|
|
<li><strong>Condition</strong>: Block not received within request timeout (300s)</li>
|
|
<li><strong>Action</strong>: Cancel request with that peer</li>
|
|
<li><strong>Recovery</strong>: Retry with different peer if available</li>
|
|
<li><strong>User Notification</strong>: If all retry attempts exhausted, <code>requestBlock</code> returns timeout error</li>
|
|
</ul>
|
|
<h3 id="protocol-violations"><a class="header" href="#protocol-violations">Protocol Violations</a></h3>
|
|
<p><strong>Oversized Messages:</strong></p>
|
|
<ul>
|
|
<li><strong>Condition</strong>: Message exceeds maximum size limits</li>
|
|
<li><strong>Action</strong>: Close stream immediately</li>
|
|
<li><strong>Peer Management</strong>: Mark peer as non-compliant</li>
|
|
<li><strong>No Response</strong>: Do not send error message (message may be malicious)</li>
|
|
</ul>
|
|
<p><strong>Invalid WantList:</strong></p>
|
|
<ul>
|
|
<li><strong>Condition</strong>: WantList exceeds entry limit or contains malformed addresses</li>
|
|
<li><strong>Action</strong>: Ignore malformed entries, process valid ones</li>
|
|
<li><strong>Response</strong>: Continue processing stream</li>
|
|
<li><strong>Logging</strong>: Log validation errors for debugging</li>
|
|
</ul>
|
|
<p><strong>Payment Failures:</strong></p>
|
|
<ul>
|
|
<li><strong>Condition</strong>: State channel update invalid or payment insufficient</li>
|
|
<li><strong>Action</strong>: Do not deliver blocks requiring payment</li>
|
|
<li><strong>Response</strong>: Send BlockPresence with price indicating payment needed</li>
|
|
<li><strong>Stream</strong>: Keep stream open for payment retry</li>
|
|
</ul>
|
|
<h3 id="recovery-strategies"><a class="header" href="#recovery-strategies">Recovery Strategies</a></h3>
|
|
<h4 id="retry-responsibility-model"><a class="header" href="#retry-responsibility-model">Retry Responsibility Model</a></h4>
|
|
<p>The protocol defines a clear separation between system-level and caller-level
|
|
retry responsibilities:</p>
|
|
<p><strong>System-Level Retry (Automatic):</strong></p>
|
|
<p>The Block Exchange module automatically retries in these scenarios:</p>
|
|
<ul>
|
|
<li><strong>Peer failure</strong>: If a peer disconnects or times out, the system
|
|
transparently tries alternative peers from the discovery set</li>
|
|
<li><strong>Transient errors</strong>: Network glitches, temporary unavailability</li>
|
|
<li><strong>Peer rotation</strong>: Automatic failover to next available peer</li>
|
|
</ul>
|
|
<p>The caller's <code>requestBlock</code> call remains pending during system-level retries.
|
|
This is transparent to the caller.</p>
|
|
<p><strong>Caller-Level Retry (Manual):</strong></p>
|
|
<p>The caller is responsible for retry decisions when:</p>
|
|
<ul>
|
|
<li><strong>All peers exhausted</strong>: No more peers available from discovery</li>
|
|
<li><strong>Permanent failures</strong>: Block doesn't exist in the network</li>
|
|
<li><strong>Timeout exceeded</strong>: Request timeout (300s) expired</li>
|
|
<li><strong>Verification failures</strong>: All peers provided invalid data</li>
|
|
</ul>
|
|
<p>In these cases, <code>requestBlock</code> returns an error and the caller decides
|
|
whether to retry, perhaps after waiting or refreshing the peer list
|
|
via discovery.</p>
|
|
<p><strong>Retry Flow:</strong></p>
|
|
<pre><code class="language-text">requestBlock(address)
|
|
│
|
|
├─► System tries Peer A ──► Fails
|
|
│ │
|
|
│ └─► System tries Peer B ──► Fails (automatic, transparent)
|
|
│ │
|
|
│ └─► System tries Peer C ──► Success ──► Return block
|
|
│
|
|
└─► All peers failed ──► Return error to caller
|
|
│
|
|
└─► Caller decides: retry? wait? abort?
|
|
</code></pre>
|
|
<p><strong>Peer Rotation:</strong></p>
|
|
<p>When a peer fails to deliver blocks:</p>
|
|
<ol>
|
|
<li>Mark peer as temporarily unavailable for this block</li>
|
|
<li>Query discovery service for alternative peers</li>
|
|
<li>Send WantList to new peers</li>
|
|
<li>Implement exponential backoff before retrying failed peer</li>
|
|
</ol>
|
|
<p><strong>Graceful Degradation:</strong></p>
|
|
<ul>
|
|
<li>If verification fails, request block from alternative peer</li>
|
|
<li>If all peers fail, propagate error to caller</li>
|
|
<li>Clean up resources (memory, pending requests) on unrecoverable failures</li>
|
|
</ul>
|
|
<p><strong>Error Propagation:</strong></p>
|
|
<ul>
|
|
<li>Service interface functions (<code>requestBlock</code>, <code>cancelRequest</code>) return errors
|
|
to callers only after system-level retries are exhausted</li>
|
|
<li>Internal errors logged for debugging</li>
|
|
<li>Network errors trigger automatic peer rotation before surfacing to caller</li>
|
|
<li>Verification errors result in block rejection and peer reputation impact</li>
|
|
</ul>
|
|
<h2 id="security-considerations"><a class="header" href="#security-considerations">Security Considerations</a></h2>
|
|
<h3 id="block-verification"><a class="header" href="#block-verification">Block Verification</a></h3>
|
|
<ul>
|
|
<li>All dataset blocks MUST include and verify Merkle proofs before acceptance</li>
|
|
<li>Standalone blocks MUST verify CID matches the SHA256 hash of the data</li>
|
|
<li>Peers SHOULD reject blocks that fail verification immediately</li>
|
|
</ul>
|
|
<h3 id="dos-protection"><a class="header" href="#dos-protection">DoS Protection</a></h3>
|
|
<ul>
|
|
<li>Implementations SHOULD limit the number of concurrent block requests per peer</li>
|
|
<li>Implementations SHOULD implement rate limiting for WantList updates</li>
|
|
<li>Large WantLists MAY be rejected to prevent resource exhaustion</li>
|
|
</ul>
|
|
<h3 id="data-integrity"><a class="header" href="#data-integrity">Data Integrity</a></h3>
|
|
<ul>
|
|
<li>All blocks MUST be validated before being stored or forwarded</li>
|
|
<li>Zero-padding in dataset blocks MUST be verified to prevent data corruption</li>
|
|
<li>Block sizes MUST be validated against protocol limits</li>
|
|
</ul>
|
|
<h3 id="privacy-considerations"><a class="header" href="#privacy-considerations">Privacy Considerations</a></h3>
|
|
<ul>
|
|
<li>Block requests reveal information about what data a peer is seeking</li>
|
|
<li>Implementations MAY implement request obfuscation strategies</li>
|
|
<li>Presence information can leak storage capacity details</li>
|
|
</ul>
|
|
<h2 id="rationale"><a class="header" href="#rationale">Rationale</a></h2>
|
|
<h3 id="design-decisions"><a class="header" href="#design-decisions">Design Decisions</a></h3>
|
|
<p><strong>Two-Tier Block Addressing:</strong>
|
|
The protocol supports both standalone and dataset blocks to accommodate
|
|
different use cases.
|
|
Standalone blocks are simpler and don't require Merkle proofs, while
|
|
dataset blocks enable efficient verification of large datasets without
|
|
requiring the entire dataset.</p>
|
|
<p><strong>WantList Delta Updates:</strong>
|
|
Supporting delta updates reduces bandwidth consumption when peers only
|
|
need to modify a small portion of their wants, which is common in
|
|
long-lived connections.</p>
|
|
<p><strong>Separate Presence Messages:</strong>
|
|
Decoupling presence information from block delivery allows peers to
|
|
quickly assess availability without waiting for full block transfers.</p>
|
|
<p><strong>Fixed Block Size:</strong>
|
|
The 64 KiB default block size balances efficient network transmission
|
|
with manageable memory overhead.</p>
|
|
<p><strong>Zero-Padding Requirement:</strong>
|
|
Requiring zero-padding for incomplete dataset blocks ensures uniform
|
|
block sizes within datasets, simplifying Merkle tree construction and
|
|
verification.</p>
|
|
<p><strong>Protocol Buffers:</strong>
|
|
Using Protocol Buffers provides efficient serialization, forward
|
|
compatibility, and wide language support.</p>
|
|
<h2 id="copyright"><a class="header" href="#copyright">Copyright</a></h2>
|
|
<p>Copyright and related rights waived via
|
|
<a href="https://creativecommons.org/publicdomain/zero/1.0/">CC0</a>.</p>
|
|
<h2 id="references"><a class="header" href="#references">References</a></h2>
|
|
<h3 id="normative"><a class="header" href="#normative">Normative</a></h3>
|
|
<ul>
|
|
<li><a href="https://www.ietf.org/rfc/rfc2119.txt">RFC 2119</a> - Key words for use
|
|
in RFCs to Indicate Requirement Levels</li>
|
|
<li><strong>libp2p</strong>: <a href="https://libp2p.io">https://libp2p.io</a></li>
|
|
<li><strong>Protocol Buffers</strong>: <a href="https://protobuf.dev">https://protobuf.dev</a></li>
|
|
<li><strong>Multihash</strong>: <a href="https://multiformats.io/multihash/">https://multiformats.io/multihash/</a></li>
|
|
<li><strong>Multicodec</strong>: <a href="https://github.com/multiformats/multicodec">https://github.com/multiformats/multicodec</a></li>
|
|
</ul>
|
|
<h3 id="informative"><a class="header" href="#informative">Informative</a></h3>
|
|
<ul>
|
|
<li><strong>Codex Documentation</strong>: <a href="https://docs.codex.storage">https://docs.codex.storage</a></li>
|
|
<li><strong>Codex Block Exchange Module Spec</strong>:
|
|
<a href="https://github.com/codex-storage/codex-docs-obsidian/blob/main/10%20Notes/Specs/Block%20Exchange%20Module%20Spec.md">https://github.com/codex-storage/codex-docs-obsidian/blob/main/10%20Notes/Specs/Block%20Exchange%20Module%20Spec.md</a></li>
|
|
<li><strong>Merkle Trees</strong>: <a href="https://en.wikipedia.org/wiki/Merkle_tree">https://en.wikipedia.org/wiki/Merkle_tree</a></li>
|
|
<li><strong>Content Addressing</strong>:
|
|
<a href="https://en.wikipedia.org/wiki/Content-addressable_storage">https://en.wikipedia.org/wiki/Content-addressable_storage</a></li>
|
|
</ul>
|
|
|
|
</main>
|
|
|
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
|
<!-- Mobile navigation buttons -->
|
|
<a rel="prev" href="../../codex/raw/index.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="../../codex/raw/codex-marketplace.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="../../codex/raw/index.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="../../codex/raw/codex-marketplace.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 -->
|
|
<script src="../../scripts/rfc-index.js"></script>
|
|
|
|
|
|
</div>
|
|
</body>
|
|
</html>
|