diff --git a/.editorconfig b/.editorconfig index 6bc7f03c7..06c502b01 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,7 +2,7 @@ root = true [*] indent_style = space -indent_size = 4 +indent_size = 2 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true diff --git a/.fleek.json b/.fleek.json index c246a0fa5..0dae6993b 100644 --- a/.fleek.json +++ b/.fleek.json @@ -1,7 +1,7 @@ { - "build": { - "image": "hugomrdias/puppeteer", - "command": "npm install && npm run build", - "publicDir": "public" - } + "build": { + "image": "hugomrdias/puppeteer", + "command": "npm install && npm run build", + "publicDir": "public" + } } diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 80f455366..e734bf95c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -3,10 +3,10 @@ name: CI on: push: branches: - - master + - master pull_request: branches: - - master + - master jobs: test: diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..6b15975f4 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,12 @@ +.vscode +.idea +public/ +.DS_Store +yarn.lock +package-lock.json +node_modules +resources +static/_gen +data/toc.json +themes +layouts diff --git a/.remarkrc.yaml b/.remarkrc.yaml index 1aed20544..e98b4a0a9 100644 --- a/.remarkrc.yaml +++ b/.remarkrc.yaml @@ -11,6 +11,3 @@ plugins: remark-lint-no-hr-after-heading: # disable some of the sensible defaults given where we are today. remark-lint-list-item-indent: false - remark-lint-final-newline: false - remark-lint-no-literal-urls: false - remark-lint-list-item-bullet-indent: false diff --git a/README.md b/README.md index c2a4d7775..a64d8d03e 100644 --- a/README.md +++ b/README.md @@ -8,31 +8,31 @@ This is the [Filecoin Specification](https://github.com/filecoin-project/specs), ## Table of Contents -- [Install](#install) -- [Writing the spec](#writing-the-spec) -- [Check your markdown](#check-your-markdown) -- [Page Template](#page-template) -- [Code](#code) -- [Images](#images) -- [Links](#links) -- [Shortcodes](#shortcodes) - - [`embed`](#embed) - - [`listing`](#listing) - - [`mermaid`](#mermaid) - - [`hint`](#hint) - - [`katex`](#katex) -- [Math mode](#math-mode) - - [Wrap `def`, `gdef`, etc.](#wrap-def-gdef-etc) - - [Wrap inline math text with code blocks](#wrap-inline-math-text-with-code-blocks) - - [Wrap math blocks with code fences](#wrap-math-blocks-with-code-fences) -- [Front-matter](#front-matter) -- [References](#references) +- [Install](#install) +- [Writing the spec](#writing-the-spec) +- [Check your markdown](#check-your-markdown) +- [Page Template](#page-template) +- [Code](#code) +- [Images](#images) +- [Links](#links) +- [Shortcodes](#shortcodes) + - [`embed`](#embed) + - [`listing`](#listing) + - [`mermaid`](#mermaid) + - [`hint`](#hint) + - [`katex`](#katex) +- [Math mode](#math-mode) + - [Wrap `def`, `gdef`, etc.](#wrap-def-gdef-etc) + - [Wrap inline math text with code blocks](#wrap-inline-math-text-with-code-blocks) + - [Wrap math blocks with code fences](#wrap-math-blocks-with-code-fences) +- [Front-matter](#front-matter) +- [References](#references) ## Install To build the spec website you need -- [`node` & `npm`](https://nodejs.org/en/download) +- [`node` & `npm`](https://nodejs.org/en/download) On macOS you can get node from Homebrew @@ -57,9 +57,9 @@ Then open in the browser ## Writing the spec -The spec is written in markdown. Each section is markdown document in the `content` directory. The first level of the directory structure denotes the top level sections of the spec; (Introduction, Systems, etc.) The `_index.md` file in each folder is used as the starting point for each section. For example the **Introduction** starts in `content/intro/_index.md`. +The spec is written in markdown. Each section is markdown document in the `content` directory. The first level of the directory structure denotes the top level sections of the spec; (Introduction, Systems, etc.) The `_index.md` file in each folder is used as the starting point for each section. For example the **Introduction** starts in `content/intro/_index.md`. -Sections can be split out into multiple markdown documents. The build process combines them into a single html page. The sections are ordered by the `weight` front-matter property. The introduction appears at the start of the html page because `content/intro/_index.md` has `weight: 1`, while `content/systems/_index.html` has `weight: 2` so it appears as the second section. +Sections can be split out into multiple markdown documents. The build process combines them into a single html page. The sections are ordered by the `weight` front-matter property. The introduction appears at the start of the html page because `content/intro/_index.md` has `weight: 1`, while `content/systems/_index.html` has `weight: 2` so it appears as the second section. You can split out sub-sections by adding additional pages to a section directory. The `content/intro/concepts.md` defines the Key Concepts sub-section of the the Introduction. The order of sub-sections within a section is again controlled by setting the `weight` property. This pattern repeats for sub sub folders which represent sub sub sections. @@ -121,19 +121,21 @@ For `dot` and `mermaid` diagrams you link to the source file and the pipelines w ```md # relative to the markdown file + ![Alt text](picture.jpg) # relative to the content folder + ![Alt text](/content/intro/diagram1.mmd) -![Alt text](graph.dot "Graph title") +![Alt text](graph.dot 'Graph title') ``` > The alt text is used as the title if not provided. ## Links -Use markdown syntax `[text](markdown-document-name)`. +Use markdown syntax `[text](markdown-document-name)`. These links use "portable links" just like `relref`. Just give it the name of the file and it will fetch the correct relative path and title automatically. You can override the title by passing a second `string` in the link definition. @@ -143,20 +145,20 @@ These links use "portable links" just like `relref`. Just give it the name of th [](storage_power_consensus) # Renders to -Storage Power Consensus +Storage Power Consensus -[Storage Power](storage_power_consensus "Title to override the page original title") +[Storage Power](storage_power_consensus 'Title to override the page original title') # Renders to -Storage Power +Storage Power -[Tickets](storage_power_consensus#the-ticket-chain-and-drawing-randomness "The Ticket chain and drawing randomness") +[Tickets](storage_power_consensus#the-ticket-chain-and-drawing-randomness 'The Ticket chain and drawing randomness') # Renders to -Tickets +Tickets ``` ## Shortcodes @@ -167,19 +169,25 @@ hugo shortcodes you can add to your markdown. ```md # src relative to the page + {{}} # src relative to content folder + {{}} # can just embed a markdown file + {{}} # can embed symbols from Go files + # extracts comments and symbol body + {{}} # can embed from external sources like github + {{}} ``` @@ -191,12 +199,15 @@ The listing shortcode creates tables from externals sources, supports Go `struct ```md # src relative to the page + {{}} # src relative to content folder + {{}} # src can also be from the externals repos + {{}} ``` @@ -212,7 +223,7 @@ graph TD C -->|One| D[Laptop] C -->|Two| E[iPhone] C -->|Three| F[fa:fa-car Car] - + {{}} ``` @@ -220,6 +231,7 @@ graph TD ```md + {{< hint info >}} **Markdown content** Lorem markdownum insigne. Olympo signis Delphis! Retexi Nereius nova develat @@ -233,11 +245,13 @@ We should **only** use `inline` mode for now! Display mode has a bug and is not ```md + {{}} $SectorInitialConsensusPledge = \\[0.2cm] 30\% \times FILCirculatingSupply \times \frac{SectorQAP}{max(NetworkBaseline, NetworkQAP)}$ {{}} + {{}} $$SectorInitialConsensusPledge = \\[0.2cm] 30\% \times FILCirculatingSupply \times \frac{SectorQAP}{max(NetworkBaseline, NetworkQAP)}$$ {{}} @@ -268,6 +282,7 @@ Math text needs to be wrapped to avoid Hugo's Markdown parser. When wrapping def ```md {{}} +$$ ``` ### Wrap inline math text with code blocks @@ -311,7 +327,7 @@ Description for all the available frontmatter properties ```yaml # Page Title to be used in the navigation -title: Libraries +title: Libraries # Small description for html metadata, if not present the first couple of paragraphs will be used instead description: Libraries used from Filecoin # This will be used to order the ToC, navigation and any other listings of pages @@ -329,17 +345,17 @@ dashboardAudit: wip # When dashboardAudit is stable we should have a report url dashboardAuditURL: https://url.to.the.report # The date that the report at dashboardAuditURL was completed -dashboardAuditDate: "2020-08-01" +dashboardAuditDate: '2020-08-01' # This is used in the dashboard to describe if the page content has compliance tests, options are 0 or numbers of tests dashboardTests: 0 ``` ## References -- [hugo theme book](https://themes.gohugo.io//theme/hugo-book/docs/shortcodes/columns/) -- [Katex](https://katex.org/) -- [Mermaid](https://mermaid-js.github.io/mermaid/#/) - - [config](https://github.com/mermaid-js/mermaid/blob/master/docs/mermaidAPI.md#mermaidapi-configuration-defaults) - - [editor](https://mermaid-js.github.io/mermaid-live-editor) -- [Pan/Zoom for SVG](https://github.com/anvaka/panzoom) -- [Icons](https://css.gg/) +- [hugo theme book](https://themes.gohugo.io//theme/hugo-book/docs/shortcodes/columns/) +- [Katex](https://katex.org/) +- [Mermaid](https://mermaid-js.github.io/mermaid/#/) + - [config](https://github.com/mermaid-js/mermaid/blob/master/docs/mermaidAPI.md#mermaidapi-configuration-defaults) + - [editor](https://mermaid-js.github.io/mermaid-live-editor) +- [Pan/Zoom for SVG](https://github.com/anvaka/panzoom) +- [Icons](https://css.gg/) diff --git a/assets/_code.scss b/assets/_code.scss index ea09b5cf2..c62e629ac 100644 --- a/assets/_code.scss +++ b/assets/_code.scss @@ -1,13 +1,14 @@ .markdown pre { - max-height: 450px; - overflow: auto; + max-height: 450px; + overflow: auto; } -.markdown code, code { - margin: 0 1px; - padding: 3px 6px; - font-size: 85%; - border-radius: 3px; - line-height: 16px; - box-decoration-break: clone; -} \ No newline at end of file +.markdown code, +code { + margin: 0 1px; + padding: 3px 6px; + font-size: 85%; + border-radius: 3px; + line-height: 16px; + box-decoration-break: clone; +} diff --git a/assets/_colors.scss b/assets/_colors.scss index f8b1974a7..ef13c1725 100644 --- a/assets/_colors.scss +++ b/assets/_colors.scss @@ -1,60 +1,60 @@ // Colors .text-missing { - color: var(--cl-red); + color: var(--cl-red); } .text-incorrect { - color: var(--cl-orange); + color: var(--cl-orange); } .text-wip { - color: var(--cl-yellow); + color: var(--cl-yellow); } .text-coming { - color: var(--cl-yellow); + color: var(--cl-yellow); } .text-reliable { - color: var(--cl-green); + color: var(--cl-green); } .text-stable { - color: var(--cl-blue); + color: var(--cl-blue); } .text-transparent { - color: transparent; + color: transparent; } .text-black { - color: black !important; + color: black !important; } .bg-na { - background-color: var(--cl-gray) !important; + background-color: var(--cl-gray) !important; } .bg-missing { - background-color: var(--cl-red) !important; + background-color: var(--cl-red) !important; } .bg-incorrect { - background-color: var(--cl-orange) !important; + background-color: var(--cl-orange) !important; } .bg-wip { - background-color: var(--cl-yellow) !important; + background-color: var(--cl-yellow) !important; } .bg-coming { - background-color: var(--cl-yellow) !important; + background-color: var(--cl-yellow) !important; } .bg-done { - background-color: var(--cl-green) !important; + background-color: var(--cl-green) !important; } .bg-reliable { - background-color: var(--cl-green) !important; + background-color: var(--cl-green) !important; } .bg-stable { - background-color: var(--cl-blue) !important; - a { - color: black; - } + background-color: var(--cl-blue) !important; + a { + color: black; + } } .bg-transparent { - background-color: transparent; + background-color: transparent; } .bg-black { - background-color: black; + background-color: black; } diff --git a/assets/_custom.scss b/assets/_custom.scss index 5756141aa..23a719acc 100644 --- a/assets/_custom.scss +++ b/assets/_custom.scss @@ -2,181 +2,181 @@ // Addons -@import "plugins/numbered"; -@import "plugins/scrollbars"; -@import "plugins/lightbox"; -@import "plugins/bagdes"; -@import "plugins/meter"; -@import "plugins/toc"; -@import "plugins/table-sort"; -@import "plugins/diagrams"; -@import "colors"; -@import "dashboard"; -@import "katex"; -@import "icons"; -@import "code"; -@import "utils-custom"; +@import 'plugins/numbered'; +@import 'plugins/scrollbars'; +@import 'plugins/lightbox'; +@import 'plugins/bagdes'; +@import 'plugins/meter'; +@import 'plugins/toc'; +@import 'plugins/table-sort'; +@import 'plugins/diagrams'; +@import 'colors'; +@import 'dashboard'; +@import 'katex'; +@import 'icons'; +@import 'code'; +@import 'utils-custom'; input.toggle { - height: 20px; - width: 20px; - overflow: hidden; - opacity: 1; - position: fixed; - visibility: hidden; + height: 20px; + width: 20px; + overflow: hidden; + opacity: 1; + position: fixed; + visibility: hidden; } .markdown { - h1, - h2, - h3, - h4, - h5, - h6 { - display: flex; - align-items: center; - flex-wrap: nowrap; - a:first-child { - flex-grow: 2; - color: inherit; - text-decoration: none; - &:visited { - color: inherit; - } - &:hover { - text-decoration: none; - &::after { - font-size: 70%; - padding-left: 0.3em; - color: var(--color-link); - content: " #"; - } - } + h1, + h2, + h3, + h4, + h5, + h6 { + display: flex; + align-items: center; + flex-wrap: nowrap; + a:first-child { + flex-grow: 2; + color: inherit; + text-decoration: none; + &:visited { + color: inherit; + } + &:hover { + text-decoration: none; + &::after { + font-size: 70%; + padding-left: 0.3em; + color: var(--color-link); + content: ' #'; } + } } + } } .markdown .permalink { + color: inherit; + text-decoration: none; + &:visited { color: inherit; + } + &:hover { text-decoration: none; - &:visited { - color: inherit; - } - &:hover { - text-decoration: none; - &::after { - font-size: 90%; - padding-left: 0.3em; - color: var(--color-link); - content: " #"; - } + &::after { + font-size: 90%; + padding-left: 0.3em; + color: var(--color-link); + content: ' #'; } + } } // Tweaks html { - scroll-behavior: auto; - touch-action: auto; + scroll-behavior: auto; + touch-action: auto; } .container { - margin-left: 20rem; - overflow-x: hidden; + margin-left: 20rem; + overflow-x: hidden; } summary { - outline: none; + outline: none; } blockquote { - overflow: auto; + overflow: auto; } .book-page { - max-width: 52rem; - margin: 0 auto; + max-width: 52rem; + margin: 0 auto; } .book-menu nav { - width: 20rem; - overflow: hidden; + width: 20rem; + overflow: hidden; } .book-menu > nav > ul { - height: 90vh; - overflow: auto; + height: 90vh; + overflow: auto; } .book-brand img { - height: 40px; - width: auto; - vertical-align: middle; - margin-inline-end: 0.5rem; + height: 40px; + width: auto; + vertical-align: middle; + margin-inline-end: 0.5rem; } .book-header label { - height: 43px; - width: 43px; - display: flex; - align-items: center; + height: 43px; + width: 43px; + display: flex; + align-items: center; } .book-menu { - position: fixed; - z-index: 3; - top: 0; - left: 0; - height: 100%; - width: 20rem; - visibility: initial; - margin-inline-start: initial; - transition: 0.2s ease-in-out; - transition-property: transform; - will-change: transform; + position: fixed; + z-index: 3; + top: 0; + left: 0; + height: 100%; + width: 20rem; + visibility: initial; + margin-inline-start: initial; + transition: 0.2s ease-in-out; + transition-property: transform; + will-change: transform; } @media screen and (max-width: 56rem) { - .container { - margin-left: 0; - } - .book-menu { - transform: translateX(-20rem); - } - #menu-control:checked ~ main .book-page { - opacity: initial; - } - - #menu-control:checked ~ .book-menu { - transform: translateX(0); - box-shadow: 0 0 $padding-8 rgba(0, 0, 0, 0.1); - } - - #menu-control:checked ~ .book-menu-overlay { - display: block; - position: fixed; - z-index: 2; - background-color: rgba(0, 0, 0, 0.4); - top: 0; - bottom: 0; - left: 0; - right: 0; - } - - .toc-label { - font-weight: 700; - font-size: 10px; - .gg-menu-motion { - display: none; - } - } - .book-header { - margin-bottom: 2rem; - position: fixed; - width: 100%; - background: white; - z-index: 1; - padding: 5px 0.6rem; - box-shadow: 1px 0 2px 0px rgba(0, 0, 0, 0.2); - } - #toc-depth-slider { - display: none; + .container { + margin-left: 0; + } + .book-menu { + transform: translateX(-20rem); + } + #menu-control:checked ~ main .book-page { + opacity: initial; + } + + #menu-control:checked ~ .book-menu { + transform: translateX(0); + box-shadow: 0 0 $padding-8 rgba(0, 0, 0, 0.1); + } + + #menu-control:checked ~ .book-menu-overlay { + display: block; + position: fixed; + z-index: 2; + background-color: rgba(0, 0, 0, 0.4); + top: 0; + bottom: 0; + left: 0; + right: 0; + } + + .toc-label { + font-weight: 700; + font-size: 10px; + .gg-menu-motion { + display: none; } + } + .book-header { + margin-bottom: 2rem; + position: fixed; + width: 100%; + background: white; + z-index: 1; + padding: 5px 0.6rem; + box-shadow: 1px 0 2px 0px rgba(0, 0, 0, 0.2); + } + #toc-depth-slider { + display: none; + } } diff --git a/assets/_dashboard.scss b/assets/_dashboard.scss index b849888dc..be46ae34e 100644 --- a/assets/_dashboard.scss +++ b/assets/_dashboard.scss @@ -1,40 +1,39 @@ #dashboard-container { - // the dashboard is generated on the client, so it throws out in page anchor scrolling, so we set the heigt manually here. - // TODO: Fix properly. perhaps generating the table earlier in the page render could fix it... otherwise writing it into the dom as a post process. - height: 2850px; - overflow: auto + // the dashboard is generated on the client, so it throws out in page anchor scrolling, so we set the heigt manually here. + // TODO: Fix properly. perhaps generating the table earlier in the page render could fix it... otherwise writing it into the dom as a post process. + height: 2850px; + overflow: auto; } table.Dashboard { - display: table; - table-layout: fixed; - width: 100%; - font-size: 12px; - margin: 0; + display: table; + table-layout: fixed; + width: 100%; + font-size: 12px; + margin: 0; } table.Dashboard { - th:first-child { - width: 50%; - } - .gg-external { - display: inline-block; - margin-left: 3px; - vertical-align: text-top; - } + th:first-child { + width: 50%; + } + .gg-external { + display: inline-block; + margin-left: 3px; + vertical-align: text-top; + } } .Dashboard tr th, .Dashboard tr td { - padding: 0 1rem !important; - text-align: center; + padding: 0 1rem !important; + text-align: center; } .Dashboard tr td:first-child { - text-align: left; + text-align: left; } .Dashboard-section { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } - diff --git a/assets/_fonts.scss b/assets/_fonts.scss index 4e6be72d3..09663d328 100644 --- a/assets/_fonts.scss +++ b/assets/_fonts.scss @@ -2,41 +2,28 @@ * Use the default user interface font in all browsers (opinionated). */ - html { - font-family: - system-ui, - /* macOS 10.11-10.12 */ -apple-system, - /* Windows 6+ */ "Segoe UI", - /* Android 4+ */ "Roboto", - /* Ubuntu 10.10+ */ "Ubuntu", - /* Gnome 3+ */ "Cantarell", - /* KDE Plasma 5+ */ "Noto Sans", - /* fallback */ sans-serif, - /* macOS emoji */ "Apple Color Emoji", - /* Windows emoji */ "Segoe UI Emoji", - /* Windows emoji */ "Segoe UI Symbol", - /* Linux emoji */ "Noto Color Emoji"; - } +html { + font-family: system-ui, /* macOS 10.11-10.12 */ -apple-system, + /* Windows 6+ */ 'Segoe UI', /* Android 4+ */ 'Roboto', + /* Ubuntu 10.10+ */ 'Ubuntu', /* Gnome 3+ */ 'Cantarell', + /* KDE Plasma 5+ */ 'Noto Sans', /* fallback */ sans-serif, + /* macOS emoji */ 'Apple Color Emoji', /* Windows emoji */ 'Segoe UI Emoji', + /* Windows emoji */ 'Segoe UI Symbol', /* Linux emoji */ 'Noto Color Emoji'; +} - /** +/** * Use the default monospace user interface font in all browsers (opinionated). */ - code, - kbd, - samp, - pre { - font-family: - /* macOS 10.10+ */ "Menlo", - /* Windows 6+ */ "Consolas", - /* Android 4+ */ "Roboto Mono", - /* Ubuntu 10.10+ */ "Ubuntu Monospace", - /* KDE Plasma 5+ */ "Noto Mono", - /* KDE Plasma 4+ */ "Oxygen Mono", - /* Linux/OpenOffice fallback */ "Liberation Mono", - /* fallback */ monospace, - /* macOS emoji */ "Apple Color Emoji", - /* Windows emoji */ "Segoe UI Emoji", - /* Windows emoji */ "Segoe UI Symbol", - /* Linux emoji */ "Noto Color Emoji"; - } +code, +kbd, +samp, +pre { + font-family: + /* macOS 10.10+ */ 'Menlo', /* Windows 6+ */ 'Consolas', + /* Android 4+ */ 'Roboto Mono', /* Ubuntu 10.10+ */ 'Ubuntu Monospace', + /* KDE Plasma 5+ */ 'Noto Mono', /* KDE Plasma 4+ */ 'Oxygen Mono', + /* Linux/OpenOffice fallback */ 'Liberation Mono', /* fallback */ monospace, + /* macOS emoji */ 'Apple Color Emoji', /* Windows emoji */ 'Segoe UI Emoji', + /* Windows emoji */ 'Segoe UI Symbol', /* Linux emoji */ 'Noto Color Emoji'; +} diff --git a/assets/_icons.scss b/assets/_icons.scss index cb5294359..6c23d41be 100644 --- a/assets/_icons.scss +++ b/assets/_icons.scss @@ -1,10 +1,10 @@ .icon { - display: inline-block; - width: 1em; - height: 1em; - stroke-width: 0; - stroke: currentColor; - fill: currentColor; - vertical-align: text-bottom; - margin: 1px; + display: inline-block; + width: 1em; + height: 1em; + stroke-width: 0; + stroke: currentColor; + fill: currentColor; + vertical-align: text-bottom; + margin: 1px; } diff --git a/assets/_katex.scss b/assets/_katex.scss index c45611dcd..61466a767 100644 --- a/assets/_katex.scss +++ b/assets/_katex.scss @@ -1,360 +1,360 @@ /* stylelint-disable font-family-no-missing-generic-family-keyword */ @font-face { - font-family: "KaTeX_AMS"; - src: url(fonts/KaTeX_AMS-Regular.woff2) format("woff2"), - url(fonts/KaTeX_AMS-Regular.woff) format("woff"), - url(fonts/KaTeX_AMS-Regular.ttf) format("truetype"); - font-weight: normal; - font-style: normal; - font-display: swap; + font-family: 'KaTeX_AMS'; + src: url(fonts/KaTeX_AMS-Regular.woff2) format('woff2'), + url(fonts/KaTeX_AMS-Regular.woff) format('woff'), + url(fonts/KaTeX_AMS-Regular.ttf) format('truetype'); + font-weight: normal; + font-style: normal; + font-display: swap; } @font-face { - font-family: "KaTeX_Caligraphic"; - src: url(fonts/KaTeX_Caligraphic-Bold.woff2) format("woff2"), - url(fonts/KaTeX_Caligraphic-Bold.woff) format("woff"), - url(fonts/KaTeX_Caligraphic-Bold.ttf) format("truetype"); - font-weight: bold; - font-style: normal; - font-display: swap; + font-family: 'KaTeX_Caligraphic'; + src: url(fonts/KaTeX_Caligraphic-Bold.woff2) format('woff2'), + url(fonts/KaTeX_Caligraphic-Bold.woff) format('woff'), + url(fonts/KaTeX_Caligraphic-Bold.ttf) format('truetype'); + font-weight: bold; + font-style: normal; + font-display: swap; } @font-face { - font-family: "KaTeX_Caligraphic"; - src: url(fonts/KaTeX_Caligraphic-Regular.woff2) format("woff2"), - url(fonts/KaTeX_Caligraphic-Regular.woff) format("woff"), - url(fonts/KaTeX_Caligraphic-Regular.ttf) format("truetype"); - font-weight: normal; - font-style: normal; - font-display: swap; + font-family: 'KaTeX_Caligraphic'; + src: url(fonts/KaTeX_Caligraphic-Regular.woff2) format('woff2'), + url(fonts/KaTeX_Caligraphic-Regular.woff) format('woff'), + url(fonts/KaTeX_Caligraphic-Regular.ttf) format('truetype'); + font-weight: normal; + font-style: normal; + font-display: swap; } @font-face { - font-family: "KaTeX_Fraktur"; - src: url(fonts/KaTeX_Fraktur-Bold.woff2) format("woff2"), - url(fonts/KaTeX_Fraktur-Bold.woff) format("woff"), - url(fonts/KaTeX_Fraktur-Bold.ttf) format("truetype"); - font-weight: bold; - font-style: normal; - font-display: swap; + font-family: 'KaTeX_Fraktur'; + src: url(fonts/KaTeX_Fraktur-Bold.woff2) format('woff2'), + url(fonts/KaTeX_Fraktur-Bold.woff) format('woff'), + url(fonts/KaTeX_Fraktur-Bold.ttf) format('truetype'); + font-weight: bold; + font-style: normal; + font-display: swap; } @font-face { - font-family: "KaTeX_Fraktur"; - src: url(fonts/KaTeX_Fraktur-Regular.woff2) format("woff2"), - url(fonts/KaTeX_Fraktur-Regular.woff) format("woff"), - url(fonts/KaTeX_Fraktur-Regular.ttf) format("truetype"); - font-weight: normal; - font-style: normal; - font-display: swap; + font-family: 'KaTeX_Fraktur'; + src: url(fonts/KaTeX_Fraktur-Regular.woff2) format('woff2'), + url(fonts/KaTeX_Fraktur-Regular.woff) format('woff'), + url(fonts/KaTeX_Fraktur-Regular.ttf) format('truetype'); + font-weight: normal; + font-style: normal; + font-display: swap; } @font-face { - font-family: "KaTeX_Main"; - src: url(fonts/KaTeX_Main-Bold.woff2) format("woff2"), - url(fonts/KaTeX_Main-Bold.woff) format("woff"), - url(fonts/KaTeX_Main-Bold.ttf) format("truetype"); - font-weight: bold; - font-style: normal; - font-display: swap; + font-family: 'KaTeX_Main'; + src: url(fonts/KaTeX_Main-Bold.woff2) format('woff2'), + url(fonts/KaTeX_Main-Bold.woff) format('woff'), + url(fonts/KaTeX_Main-Bold.ttf) format('truetype'); + font-weight: bold; + font-style: normal; + font-display: swap; } @font-face { - font-family: "KaTeX_Main"; - src: url(fonts/KaTeX_Main-BoldItalic.woff2) format("woff2"), - url(fonts/KaTeX_Main-BoldItalic.woff) format("woff"), - url(fonts/KaTeX_Main-BoldItalic.ttf) format("truetype"); - font-weight: bold; - font-style: italic; - font-display: swap; + font-family: 'KaTeX_Main'; + src: url(fonts/KaTeX_Main-BoldItalic.woff2) format('woff2'), + url(fonts/KaTeX_Main-BoldItalic.woff) format('woff'), + url(fonts/KaTeX_Main-BoldItalic.ttf) format('truetype'); + font-weight: bold; + font-style: italic; + font-display: swap; } @font-face { - font-family: "KaTeX_Main"; - src: url(fonts/KaTeX_Main-Italic.woff2) format("woff2"), - url(fonts/KaTeX_Main-Italic.woff) format("woff"), - url(fonts/KaTeX_Main-Italic.ttf) format("truetype"); - font-weight: normal; - font-style: italic; - font-display: swap; + font-family: 'KaTeX_Main'; + src: url(fonts/KaTeX_Main-Italic.woff2) format('woff2'), + url(fonts/KaTeX_Main-Italic.woff) format('woff'), + url(fonts/KaTeX_Main-Italic.ttf) format('truetype'); + font-weight: normal; + font-style: italic; + font-display: swap; } @font-face { - font-family: "KaTeX_Main"; - src: url(fonts/KaTeX_Main-Regular.woff2) format("woff2"), - url(fonts/KaTeX_Main-Regular.woff) format("woff"), - url(fonts/KaTeX_Main-Regular.ttf) format("truetype"); - font-weight: normal; - font-style: normal; - font-display: swap; + font-family: 'KaTeX_Main'; + src: url(fonts/KaTeX_Main-Regular.woff2) format('woff2'), + url(fonts/KaTeX_Main-Regular.woff) format('woff'), + url(fonts/KaTeX_Main-Regular.ttf) format('truetype'); + font-weight: normal; + font-style: normal; + font-display: swap; } @font-face { - font-family: "KaTeX_Math"; - src: url(fonts/KaTeX_Math-BoldItalic.woff2) format("woff2"), - url(fonts/KaTeX_Math-BoldItalic.woff) format("woff"), - url(fonts/KaTeX_Math-BoldItalic.ttf) format("truetype"); - font-weight: bold; - font-style: italic; - font-display: swap; + font-family: 'KaTeX_Math'; + src: url(fonts/KaTeX_Math-BoldItalic.woff2) format('woff2'), + url(fonts/KaTeX_Math-BoldItalic.woff) format('woff'), + url(fonts/KaTeX_Math-BoldItalic.ttf) format('truetype'); + font-weight: bold; + font-style: italic; + font-display: swap; } @font-face { - font-family: "KaTeX_Math"; - src: url(fonts/KaTeX_Math-Italic.woff2) format("woff2"), - url(fonts/KaTeX_Math-Italic.woff) format("woff"), - url(fonts/KaTeX_Math-Italic.ttf) format("truetype"); - font-weight: normal; - font-style: italic; - font-display: swap; + font-family: 'KaTeX_Math'; + src: url(fonts/KaTeX_Math-Italic.woff2) format('woff2'), + url(fonts/KaTeX_Math-Italic.woff) format('woff'), + url(fonts/KaTeX_Math-Italic.ttf) format('truetype'); + font-weight: normal; + font-style: italic; + font-display: swap; } @font-face { - font-family: "KaTeX_SansSerif"; - src: url(fonts/KaTeX_SansSerif-Bold.woff2) format("woff2"), - url(fonts/KaTeX_SansSerif-Bold.woff) format("woff"), - url(fonts/KaTeX_SansSerif-Bold.ttf) format("truetype"); - font-weight: bold; - font-style: normal; - font-display: swap; + font-family: 'KaTeX_SansSerif'; + src: url(fonts/KaTeX_SansSerif-Bold.woff2) format('woff2'), + url(fonts/KaTeX_SansSerif-Bold.woff) format('woff'), + url(fonts/KaTeX_SansSerif-Bold.ttf) format('truetype'); + font-weight: bold; + font-style: normal; + font-display: swap; } @font-face { - font-family: "KaTeX_SansSerif"; - src: url(fonts/KaTeX_SansSerif-Italic.woff2) format("woff2"), - url(fonts/KaTeX_SansSerif-Italic.woff) format("woff"), - url(fonts/KaTeX_SansSerif-Italic.ttf) format("truetype"); - font-weight: normal; - font-style: italic; - font-display: swap; + font-family: 'KaTeX_SansSerif'; + src: url(fonts/KaTeX_SansSerif-Italic.woff2) format('woff2'), + url(fonts/KaTeX_SansSerif-Italic.woff) format('woff'), + url(fonts/KaTeX_SansSerif-Italic.ttf) format('truetype'); + font-weight: normal; + font-style: italic; + font-display: swap; } @font-face { - font-family: "KaTeX_SansSerif"; - src: url(fonts/KaTeX_SansSerif-Regular.woff2) format("woff2"), - url(fonts/KaTeX_SansSerif-Regular.woff) format("woff"), - url(fonts/KaTeX_SansSerif-Regular.ttf) format("truetype"); - font-weight: normal; - font-style: normal; - font-display: swap; + font-family: 'KaTeX_SansSerif'; + src: url(fonts/KaTeX_SansSerif-Regular.woff2) format('woff2'), + url(fonts/KaTeX_SansSerif-Regular.woff) format('woff'), + url(fonts/KaTeX_SansSerif-Regular.ttf) format('truetype'); + font-weight: normal; + font-style: normal; + font-display: swap; } @font-face { - font-family: "KaTeX_Script"; - src: url(fonts/KaTeX_Script-Regular.woff2) format("woff2"), - url(fonts/KaTeX_Script-Regular.woff) format("woff"), - url(fonts/KaTeX_Script-Regular.ttf) format("truetype"); - font-weight: normal; - font-style: normal; - font-display: swap; + font-family: 'KaTeX_Script'; + src: url(fonts/KaTeX_Script-Regular.woff2) format('woff2'), + url(fonts/KaTeX_Script-Regular.woff) format('woff'), + url(fonts/KaTeX_Script-Regular.ttf) format('truetype'); + font-weight: normal; + font-style: normal; + font-display: swap; } @font-face { - font-family: "KaTeX_Size1"; - src: url(fonts/KaTeX_Size1-Regular.woff2) format("woff2"), - url(fonts/KaTeX_Size1-Regular.woff) format("woff"), - url(fonts/KaTeX_Size1-Regular.ttf) format("truetype"); - font-weight: normal; - font-style: normal; - font-display: swap; + font-family: 'KaTeX_Size1'; + src: url(fonts/KaTeX_Size1-Regular.woff2) format('woff2'), + url(fonts/KaTeX_Size1-Regular.woff) format('woff'), + url(fonts/KaTeX_Size1-Regular.ttf) format('truetype'); + font-weight: normal; + font-style: normal; + font-display: swap; } @font-face { - font-family: "KaTeX_Size2"; - src: url(fonts/KaTeX_Size2-Regular.woff2) format("woff2"), - url(fonts/KaTeX_Size2-Regular.woff) format("woff"), - url(fonts/KaTeX_Size2-Regular.ttf) format("truetype"); - font-weight: normal; - font-style: normal; - font-display: swap; + font-family: 'KaTeX_Size2'; + src: url(fonts/KaTeX_Size2-Regular.woff2) format('woff2'), + url(fonts/KaTeX_Size2-Regular.woff) format('woff'), + url(fonts/KaTeX_Size2-Regular.ttf) format('truetype'); + font-weight: normal; + font-style: normal; + font-display: swap; } @font-face { - font-family: "KaTeX_Size3"; - src: url(fonts/KaTeX_Size3-Regular.woff2) format("woff2"), - url(fonts/KaTeX_Size3-Regular.woff) format("woff"), - url(fonts/KaTeX_Size3-Regular.ttf) format("truetype"); - font-weight: normal; - font-style: normal; - font-display: swap; + font-family: 'KaTeX_Size3'; + src: url(fonts/KaTeX_Size3-Regular.woff2) format('woff2'), + url(fonts/KaTeX_Size3-Regular.woff) format('woff'), + url(fonts/KaTeX_Size3-Regular.ttf) format('truetype'); + font-weight: normal; + font-style: normal; + font-display: swap; } @font-face { - font-family: "KaTeX_Size4"; - src: url(fonts/KaTeX_Size4-Regular.woff2) format("woff2"), - url(fonts/KaTeX_Size4-Regular.woff) format("woff"), - url(fonts/KaTeX_Size4-Regular.ttf) format("truetype"); - font-weight: normal; - font-style: normal; - font-display: swap; + font-family: 'KaTeX_Size4'; + src: url(fonts/KaTeX_Size4-Regular.woff2) format('woff2'), + url(fonts/KaTeX_Size4-Regular.woff) format('woff'), + url(fonts/KaTeX_Size4-Regular.ttf) format('truetype'); + font-weight: normal; + font-style: normal; + font-display: swap; } @font-face { - font-family: "KaTeX_Typewriter"; - src: url(fonts/KaTeX_Typewriter-Regular.woff2) format("woff2"), - url(fonts/KaTeX_Typewriter-Regular.woff) format("woff"), - url(fonts/KaTeX_Typewriter-Regular.ttf) format("truetype"); - font-weight: normal; - font-style: normal; - font-display: swap; + font-family: 'KaTeX_Typewriter'; + src: url(fonts/KaTeX_Typewriter-Regular.woff2) format('woff2'), + url(fonts/KaTeX_Typewriter-Regular.woff) format('woff'), + url(fonts/KaTeX_Typewriter-Regular.ttf) format('truetype'); + font-weight: normal; + font-style: normal; + font-display: swap; } .katex { - font: normal 1.21em KaTeX_Main, Times New Roman, serif; - line-height: 1.2; - text-indent: 0; - text-rendering: auto; - border-color: currentColor; + font: normal 1.21em KaTeX_Main, Times New Roman, serif; + line-height: 1.2; + text-indent: 0; + text-rendering: auto; + border-color: currentColor; } .katex * { - -ms-high-contrast-adjust: none !important; + -ms-high-contrast-adjust: none !important; } .katex .katex-version::after { - content: "0.12.0"; + content: '0.12.0'; } .katex .katex-mathml { - position: absolute; - clip: rect(1px, 1px, 1px, 1px); - padding: 0; - border: 0; - height: 1px; - width: 1px; - overflow: hidden; + position: absolute; + clip: rect(1px, 1px, 1px, 1px); + padding: 0; + border: 0; + height: 1px; + width: 1px; + overflow: hidden; } .katex .katex-html { - /* \newline is an empty block at top level, between .base elements */ + /* \newline is an empty block at top level, between .base elements */ } .katex .katex-html > .newline { - display: block; + display: block; } .katex .base { - position: relative; - display: inline-block; - white-space: nowrap; - width: min-content; + position: relative; + display: inline-block; + white-space: nowrap; + width: min-content; } .katex .strut { - display: inline-block; + display: inline-block; } .katex .textbf { - font-weight: bold; + font-weight: bold; } .katex .textit { - font-style: italic; + font-style: italic; } .katex .textrm { - font-family: KaTeX_Main; + font-family: KaTeX_Main; } .katex .textsf { - font-family: KaTeX_SansSerif; + font-family: KaTeX_SansSerif; } .katex .texttt { - font-family: KaTeX_Typewriter; + font-family: KaTeX_Typewriter; } .katex .mathnormal { - font-family: KaTeX_Math; - font-style: italic; + font-family: KaTeX_Math; + font-style: italic; } .katex .mathit { - font-family: KaTeX_Main; - font-style: italic; + font-family: KaTeX_Main; + font-style: italic; } .katex .mathrm { - font-style: normal; + font-style: normal; } .katex .mathbf { - font-family: KaTeX_Main; - font-weight: bold; + font-family: KaTeX_Main; + font-weight: bold; } .katex .boldsymbol { - font-family: KaTeX_Math; - font-weight: bold; - font-style: italic; + font-family: KaTeX_Math; + font-weight: bold; + font-style: italic; } .katex .amsrm { - font-family: KaTeX_AMS; + font-family: KaTeX_AMS; } .katex .mathbb, .katex .textbb { - font-family: KaTeX_AMS; + font-family: KaTeX_AMS; } .katex .mathcal { - font-family: KaTeX_Caligraphic; + font-family: KaTeX_Caligraphic; } .katex .mathfrak, .katex .textfrak { - font-family: KaTeX_Fraktur; + font-family: KaTeX_Fraktur; } .katex .mathtt { - font-family: KaTeX_Typewriter; + font-family: KaTeX_Typewriter; } .katex .mathscr, .katex .textscr { - font-family: KaTeX_Script; + font-family: KaTeX_Script; } .katex .mathsf, .katex .textsf { - font-family: KaTeX_SansSerif; + font-family: KaTeX_SansSerif; } .katex .mathboldsf, .katex .textboldsf { - font-family: KaTeX_SansSerif; - font-weight: bold; + font-family: KaTeX_SansSerif; + font-weight: bold; } .katex .mathitsf, .katex .textitsf { - font-family: KaTeX_SansSerif; - font-style: italic; + font-family: KaTeX_SansSerif; + font-style: italic; } .katex .mainrm { - font-family: KaTeX_Main; - font-style: normal; + font-family: KaTeX_Main; + font-style: normal; } .katex .vlist-t { - display: inline-table; - table-layout: fixed; - border-collapse: collapse; + display: inline-table; + table-layout: fixed; + border-collapse: collapse; } .katex .vlist-r { - display: table-row; + display: table-row; } .katex .vlist { - display: table-cell; - vertical-align: bottom; - position: relative; + display: table-cell; + vertical-align: bottom; + position: relative; } .katex .vlist > span { - display: block; - height: 0; - position: relative; + display: block; + height: 0; + position: relative; } .katex .vlist > span > span { - display: inline-block; + display: inline-block; } .katex .vlist > span > .pstrut { - overflow: hidden; - width: 0; + overflow: hidden; + width: 0; } .katex .vlist-t2 { - margin-right: -2px; + margin-right: -2px; } .katex .vlist-s { - display: table-cell; - vertical-align: bottom; - font-size: 1px; - width: 2px; - min-width: 2px; + display: table-cell; + vertical-align: bottom; + font-size: 1px; + width: 2px; + min-width: 2px; } .katex .vbox { - display: -ms-inline-flexbox; - display: inline-flex; - -ms-flex-direction: column; - flex-direction: column; - align-items: baseline; + display: -ms-inline-flexbox; + display: inline-flex; + -ms-flex-direction: column; + flex-direction: column; + align-items: baseline; } .katex .hbox { - display: -ms-inline-flexbox; - display: inline-flex; - -ms-flex-direction: row; - flex-direction: row; - width: 100%; + display: -ms-inline-flexbox; + display: inline-flex; + -ms-flex-direction: row; + flex-direction: row; + width: 100%; } .katex .thinbox { - display: inline-flex; - flex-direction: row; - width: 0; - max-width: 0; + display: inline-flex; + flex-direction: row; + width: 0; + max-width: 0; } .katex .msupsub { - text-align: left; + text-align: left; } .katex .mfrac > span > span { - text-align: center; + text-align: center; } .katex .mfrac .frac-line { - display: inline-block; - width: 100%; - border-bottom-style: solid; + display: inline-block; + width: 100%; + border-bottom-style: solid; } .katex .mfrac .frac-line, .katex .overline .overline-line, @@ -362,733 +362,733 @@ .katex .hline, .katex .hdashline, .katex .rule { - min-height: 1px; + min-height: 1px; } .katex .mspace { - display: inline-block; + display: inline-block; } .katex .llap, .katex .rlap, .katex .clap { - width: 0; - position: relative; + width: 0; + position: relative; } .katex .llap > .inner, .katex .rlap > .inner, .katex .clap > .inner { - position: absolute; + position: absolute; } .katex .llap > .fix, .katex .rlap > .fix, .katex .clap > .fix { - display: inline-block; + display: inline-block; } .katex .llap > .inner { - right: 0; + right: 0; } .katex .rlap > .inner, .katex .clap > .inner { - left: 0; + left: 0; } .katex .clap > .inner > span { - margin-left: -50%; - margin-right: 50%; + margin-left: -50%; + margin-right: 50%; } .katex .rule { - display: inline-block; - border: solid 0; - position: relative; + display: inline-block; + border: solid 0; + position: relative; } .katex .overline .overline-line, .katex .underline .underline-line, .katex .hline { - display: inline-block; - width: 100%; - border-bottom-style: solid; + display: inline-block; + width: 100%; + border-bottom-style: solid; } .katex .hdashline { - display: inline-block; - width: 100%; - border-bottom-style: dashed; + display: inline-block; + width: 100%; + border-bottom-style: dashed; } .katex .sqrt > .root { - margin-left: 0.27777778em; - margin-right: -0.55555556em; + margin-left: 0.27777778em; + margin-right: -0.55555556em; } .katex .sizing.reset-size1.size1, .katex .fontsize-ensurer.reset-size1.size1 { - font-size: 1em; + font-size: 1em; } .katex .sizing.reset-size1.size2, .katex .fontsize-ensurer.reset-size1.size2 { - font-size: 1.2em; + font-size: 1.2em; } .katex .sizing.reset-size1.size3, .katex .fontsize-ensurer.reset-size1.size3 { - font-size: 1.4em; + font-size: 1.4em; } .katex .sizing.reset-size1.size4, .katex .fontsize-ensurer.reset-size1.size4 { - font-size: 1.6em; + font-size: 1.6em; } .katex .sizing.reset-size1.size5, .katex .fontsize-ensurer.reset-size1.size5 { - font-size: 1.8em; + font-size: 1.8em; } .katex .sizing.reset-size1.size6, .katex .fontsize-ensurer.reset-size1.size6 { - font-size: 2em; + font-size: 2em; } .katex .sizing.reset-size1.size7, .katex .fontsize-ensurer.reset-size1.size7 { - font-size: 2.4em; + font-size: 2.4em; } .katex .sizing.reset-size1.size8, .katex .fontsize-ensurer.reset-size1.size8 { - font-size: 2.88em; + font-size: 2.88em; } .katex .sizing.reset-size1.size9, .katex .fontsize-ensurer.reset-size1.size9 { - font-size: 3.456em; + font-size: 3.456em; } .katex .sizing.reset-size1.size10, .katex .fontsize-ensurer.reset-size1.size10 { - font-size: 4.148em; + font-size: 4.148em; } .katex .sizing.reset-size1.size11, .katex .fontsize-ensurer.reset-size1.size11 { - font-size: 4.976em; + font-size: 4.976em; } .katex .sizing.reset-size2.size1, .katex .fontsize-ensurer.reset-size2.size1 { - font-size: 0.83333333em; + font-size: 0.83333333em; } .katex .sizing.reset-size2.size2, .katex .fontsize-ensurer.reset-size2.size2 { - font-size: 1em; + font-size: 1em; } .katex .sizing.reset-size2.size3, .katex .fontsize-ensurer.reset-size2.size3 { - font-size: 1.16666667em; + font-size: 1.16666667em; } .katex .sizing.reset-size2.size4, .katex .fontsize-ensurer.reset-size2.size4 { - font-size: 1.33333333em; + font-size: 1.33333333em; } .katex .sizing.reset-size2.size5, .katex .fontsize-ensurer.reset-size2.size5 { - font-size: 1.5em; + font-size: 1.5em; } .katex .sizing.reset-size2.size6, .katex .fontsize-ensurer.reset-size2.size6 { - font-size: 1.66666667em; + font-size: 1.66666667em; } .katex .sizing.reset-size2.size7, .katex .fontsize-ensurer.reset-size2.size7 { - font-size: 2em; + font-size: 2em; } .katex .sizing.reset-size2.size8, .katex .fontsize-ensurer.reset-size2.size8 { - font-size: 2.4em; + font-size: 2.4em; } .katex .sizing.reset-size2.size9, .katex .fontsize-ensurer.reset-size2.size9 { - font-size: 2.88em; + font-size: 2.88em; } .katex .sizing.reset-size2.size10, .katex .fontsize-ensurer.reset-size2.size10 { - font-size: 3.45666667em; + font-size: 3.45666667em; } .katex .sizing.reset-size2.size11, .katex .fontsize-ensurer.reset-size2.size11 { - font-size: 4.14666667em; + font-size: 4.14666667em; } .katex .sizing.reset-size3.size1, .katex .fontsize-ensurer.reset-size3.size1 { - font-size: 0.71428571em; + font-size: 0.71428571em; } .katex .sizing.reset-size3.size2, .katex .fontsize-ensurer.reset-size3.size2 { - font-size: 0.85714286em; + font-size: 0.85714286em; } .katex .sizing.reset-size3.size3, .katex .fontsize-ensurer.reset-size3.size3 { - font-size: 1em; + font-size: 1em; } .katex .sizing.reset-size3.size4, .katex .fontsize-ensurer.reset-size3.size4 { - font-size: 1.14285714em; + font-size: 1.14285714em; } .katex .sizing.reset-size3.size5, .katex .fontsize-ensurer.reset-size3.size5 { - font-size: 1.28571429em; + font-size: 1.28571429em; } .katex .sizing.reset-size3.size6, .katex .fontsize-ensurer.reset-size3.size6 { - font-size: 1.42857143em; + font-size: 1.42857143em; } .katex .sizing.reset-size3.size7, .katex .fontsize-ensurer.reset-size3.size7 { - font-size: 1.71428571em; + font-size: 1.71428571em; } .katex .sizing.reset-size3.size8, .katex .fontsize-ensurer.reset-size3.size8 { - font-size: 2.05714286em; + font-size: 2.05714286em; } .katex .sizing.reset-size3.size9, .katex .fontsize-ensurer.reset-size3.size9 { - font-size: 2.46857143em; + font-size: 2.46857143em; } .katex .sizing.reset-size3.size10, .katex .fontsize-ensurer.reset-size3.size10 { - font-size: 2.96285714em; + font-size: 2.96285714em; } .katex .sizing.reset-size3.size11, .katex .fontsize-ensurer.reset-size3.size11 { - font-size: 3.55428571em; + font-size: 3.55428571em; } .katex .sizing.reset-size4.size1, .katex .fontsize-ensurer.reset-size4.size1 { - font-size: 0.625em; + font-size: 0.625em; } .katex .sizing.reset-size4.size2, .katex .fontsize-ensurer.reset-size4.size2 { - font-size: 0.75em; + font-size: 0.75em; } .katex .sizing.reset-size4.size3, .katex .fontsize-ensurer.reset-size4.size3 { - font-size: 0.875em; + font-size: 0.875em; } .katex .sizing.reset-size4.size4, .katex .fontsize-ensurer.reset-size4.size4 { - font-size: 1em; + font-size: 1em; } .katex .sizing.reset-size4.size5, .katex .fontsize-ensurer.reset-size4.size5 { - font-size: 1.125em; + font-size: 1.125em; } .katex .sizing.reset-size4.size6, .katex .fontsize-ensurer.reset-size4.size6 { - font-size: 1.25em; + font-size: 1.25em; } .katex .sizing.reset-size4.size7, .katex .fontsize-ensurer.reset-size4.size7 { - font-size: 1.5em; + font-size: 1.5em; } .katex .sizing.reset-size4.size8, .katex .fontsize-ensurer.reset-size4.size8 { - font-size: 1.8em; + font-size: 1.8em; } .katex .sizing.reset-size4.size9, .katex .fontsize-ensurer.reset-size4.size9 { - font-size: 2.16em; + font-size: 2.16em; } .katex .sizing.reset-size4.size10, .katex .fontsize-ensurer.reset-size4.size10 { - font-size: 2.5925em; + font-size: 2.5925em; } .katex .sizing.reset-size4.size11, .katex .fontsize-ensurer.reset-size4.size11 { - font-size: 3.11em; + font-size: 3.11em; } .katex .sizing.reset-size5.size1, .katex .fontsize-ensurer.reset-size5.size1 { - font-size: 0.55555556em; + font-size: 0.55555556em; } .katex .sizing.reset-size5.size2, .katex .fontsize-ensurer.reset-size5.size2 { - font-size: 0.66666667em; + font-size: 0.66666667em; } .katex .sizing.reset-size5.size3, .katex .fontsize-ensurer.reset-size5.size3 { - font-size: 0.77777778em; + font-size: 0.77777778em; } .katex .sizing.reset-size5.size4, .katex .fontsize-ensurer.reset-size5.size4 { - font-size: 0.88888889em; + font-size: 0.88888889em; } .katex .sizing.reset-size5.size5, .katex .fontsize-ensurer.reset-size5.size5 { - font-size: 1em; + font-size: 1em; } .katex .sizing.reset-size5.size6, .katex .fontsize-ensurer.reset-size5.size6 { - font-size: 1.11111111em; + font-size: 1.11111111em; } .katex .sizing.reset-size5.size7, .katex .fontsize-ensurer.reset-size5.size7 { - font-size: 1.33333333em; + font-size: 1.33333333em; } .katex .sizing.reset-size5.size8, .katex .fontsize-ensurer.reset-size5.size8 { - font-size: 1.6em; + font-size: 1.6em; } .katex .sizing.reset-size5.size9, .katex .fontsize-ensurer.reset-size5.size9 { - font-size: 1.92em; + font-size: 1.92em; } .katex .sizing.reset-size5.size10, .katex .fontsize-ensurer.reset-size5.size10 { - font-size: 2.30444444em; + font-size: 2.30444444em; } .katex .sizing.reset-size5.size11, .katex .fontsize-ensurer.reset-size5.size11 { - font-size: 2.76444444em; + font-size: 2.76444444em; } .katex .sizing.reset-size6.size1, .katex .fontsize-ensurer.reset-size6.size1 { - font-size: 0.5em; + font-size: 0.5em; } .katex .sizing.reset-size6.size2, .katex .fontsize-ensurer.reset-size6.size2 { - font-size: 0.6em; + font-size: 0.6em; } .katex .sizing.reset-size6.size3, .katex .fontsize-ensurer.reset-size6.size3 { - font-size: 0.7em; + font-size: 0.7em; } .katex .sizing.reset-size6.size4, .katex .fontsize-ensurer.reset-size6.size4 { - font-size: 0.8em; + font-size: 0.8em; } .katex .sizing.reset-size6.size5, .katex .fontsize-ensurer.reset-size6.size5 { - font-size: 0.9em; + font-size: 0.9em; } .katex .sizing.reset-size6.size6, .katex .fontsize-ensurer.reset-size6.size6 { - font-size: 1em; + font-size: 1em; } .katex .sizing.reset-size6.size7, .katex .fontsize-ensurer.reset-size6.size7 { - font-size: 1.2em; + font-size: 1.2em; } .katex .sizing.reset-size6.size8, .katex .fontsize-ensurer.reset-size6.size8 { - font-size: 1.44em; + font-size: 1.44em; } .katex .sizing.reset-size6.size9, .katex .fontsize-ensurer.reset-size6.size9 { - font-size: 1.728em; + font-size: 1.728em; } .katex .sizing.reset-size6.size10, .katex .fontsize-ensurer.reset-size6.size10 { - font-size: 2.074em; + font-size: 2.074em; } .katex .sizing.reset-size6.size11, .katex .fontsize-ensurer.reset-size6.size11 { - font-size: 2.488em; + font-size: 2.488em; } .katex .sizing.reset-size7.size1, .katex .fontsize-ensurer.reset-size7.size1 { - font-size: 0.41666667em; + font-size: 0.41666667em; } .katex .sizing.reset-size7.size2, .katex .fontsize-ensurer.reset-size7.size2 { - font-size: 0.5em; + font-size: 0.5em; } .katex .sizing.reset-size7.size3, .katex .fontsize-ensurer.reset-size7.size3 { - font-size: 0.58333333em; + font-size: 0.58333333em; } .katex .sizing.reset-size7.size4, .katex .fontsize-ensurer.reset-size7.size4 { - font-size: 0.66666667em; + font-size: 0.66666667em; } .katex .sizing.reset-size7.size5, .katex .fontsize-ensurer.reset-size7.size5 { - font-size: 0.75em; + font-size: 0.75em; } .katex .sizing.reset-size7.size6, .katex .fontsize-ensurer.reset-size7.size6 { - font-size: 0.83333333em; + font-size: 0.83333333em; } .katex .sizing.reset-size7.size7, .katex .fontsize-ensurer.reset-size7.size7 { - font-size: 1em; + font-size: 1em; } .katex .sizing.reset-size7.size8, .katex .fontsize-ensurer.reset-size7.size8 { - font-size: 1.2em; + font-size: 1.2em; } .katex .sizing.reset-size7.size9, .katex .fontsize-ensurer.reset-size7.size9 { - font-size: 1.44em; + font-size: 1.44em; } .katex .sizing.reset-size7.size10, .katex .fontsize-ensurer.reset-size7.size10 { - font-size: 1.72833333em; + font-size: 1.72833333em; } .katex .sizing.reset-size7.size11, .katex .fontsize-ensurer.reset-size7.size11 { - font-size: 2.07333333em; + font-size: 2.07333333em; } .katex .sizing.reset-size8.size1, .katex .fontsize-ensurer.reset-size8.size1 { - font-size: 0.34722222em; + font-size: 0.34722222em; } .katex .sizing.reset-size8.size2, .katex .fontsize-ensurer.reset-size8.size2 { - font-size: 0.41666667em; + font-size: 0.41666667em; } .katex .sizing.reset-size8.size3, .katex .fontsize-ensurer.reset-size8.size3 { - font-size: 0.48611111em; + font-size: 0.48611111em; } .katex .sizing.reset-size8.size4, .katex .fontsize-ensurer.reset-size8.size4 { - font-size: 0.55555556em; + font-size: 0.55555556em; } .katex .sizing.reset-size8.size5, .katex .fontsize-ensurer.reset-size8.size5 { - font-size: 0.625em; + font-size: 0.625em; } .katex .sizing.reset-size8.size6, .katex .fontsize-ensurer.reset-size8.size6 { - font-size: 0.69444444em; + font-size: 0.69444444em; } .katex .sizing.reset-size8.size7, .katex .fontsize-ensurer.reset-size8.size7 { - font-size: 0.83333333em; + font-size: 0.83333333em; } .katex .sizing.reset-size8.size8, .katex .fontsize-ensurer.reset-size8.size8 { - font-size: 1em; + font-size: 1em; } .katex .sizing.reset-size8.size9, .katex .fontsize-ensurer.reset-size8.size9 { - font-size: 1.2em; + font-size: 1.2em; } .katex .sizing.reset-size8.size10, .katex .fontsize-ensurer.reset-size8.size10 { - font-size: 1.44027778em; + font-size: 1.44027778em; } .katex .sizing.reset-size8.size11, .katex .fontsize-ensurer.reset-size8.size11 { - font-size: 1.72777778em; + font-size: 1.72777778em; } .katex .sizing.reset-size9.size1, .katex .fontsize-ensurer.reset-size9.size1 { - font-size: 0.28935185em; + font-size: 0.28935185em; } .katex .sizing.reset-size9.size2, .katex .fontsize-ensurer.reset-size9.size2 { - font-size: 0.34722222em; + font-size: 0.34722222em; } .katex .sizing.reset-size9.size3, .katex .fontsize-ensurer.reset-size9.size3 { - font-size: 0.40509259em; + font-size: 0.40509259em; } .katex .sizing.reset-size9.size4, .katex .fontsize-ensurer.reset-size9.size4 { - font-size: 0.46296296em; + font-size: 0.46296296em; } .katex .sizing.reset-size9.size5, .katex .fontsize-ensurer.reset-size9.size5 { - font-size: 0.52083333em; + font-size: 0.52083333em; } .katex .sizing.reset-size9.size6, .katex .fontsize-ensurer.reset-size9.size6 { - font-size: 0.5787037em; + font-size: 0.5787037em; } .katex .sizing.reset-size9.size7, .katex .fontsize-ensurer.reset-size9.size7 { - font-size: 0.69444444em; + font-size: 0.69444444em; } .katex .sizing.reset-size9.size8, .katex .fontsize-ensurer.reset-size9.size8 { - font-size: 0.83333333em; + font-size: 0.83333333em; } .katex .sizing.reset-size9.size9, .katex .fontsize-ensurer.reset-size9.size9 { - font-size: 1em; + font-size: 1em; } .katex .sizing.reset-size9.size10, .katex .fontsize-ensurer.reset-size9.size10 { - font-size: 1.20023148em; + font-size: 1.20023148em; } .katex .sizing.reset-size9.size11, .katex .fontsize-ensurer.reset-size9.size11 { - font-size: 1.43981481em; + font-size: 1.43981481em; } .katex .sizing.reset-size10.size1, .katex .fontsize-ensurer.reset-size10.size1 { - font-size: 0.24108004em; + font-size: 0.24108004em; } .katex .sizing.reset-size10.size2, .katex .fontsize-ensurer.reset-size10.size2 { - font-size: 0.28929605em; + font-size: 0.28929605em; } .katex .sizing.reset-size10.size3, .katex .fontsize-ensurer.reset-size10.size3 { - font-size: 0.33751205em; + font-size: 0.33751205em; } .katex .sizing.reset-size10.size4, .katex .fontsize-ensurer.reset-size10.size4 { - font-size: 0.38572806em; + font-size: 0.38572806em; } .katex .sizing.reset-size10.size5, .katex .fontsize-ensurer.reset-size10.size5 { - font-size: 0.43394407em; + font-size: 0.43394407em; } .katex .sizing.reset-size10.size6, .katex .fontsize-ensurer.reset-size10.size6 { - font-size: 0.48216008em; + font-size: 0.48216008em; } .katex .sizing.reset-size10.size7, .katex .fontsize-ensurer.reset-size10.size7 { - font-size: 0.57859209em; + font-size: 0.57859209em; } .katex .sizing.reset-size10.size8, .katex .fontsize-ensurer.reset-size10.size8 { - font-size: 0.69431051em; + font-size: 0.69431051em; } .katex .sizing.reset-size10.size9, .katex .fontsize-ensurer.reset-size10.size9 { - font-size: 0.83317261em; + font-size: 0.83317261em; } .katex .sizing.reset-size10.size10, .katex .fontsize-ensurer.reset-size10.size10 { - font-size: 1em; + font-size: 1em; } .katex .sizing.reset-size10.size11, .katex .fontsize-ensurer.reset-size10.size11 { - font-size: 1.19961427em; + font-size: 1.19961427em; } .katex .sizing.reset-size11.size1, .katex .fontsize-ensurer.reset-size11.size1 { - font-size: 0.20096463em; + font-size: 0.20096463em; } .katex .sizing.reset-size11.size2, .katex .fontsize-ensurer.reset-size11.size2 { - font-size: 0.24115756em; + font-size: 0.24115756em; } .katex .sizing.reset-size11.size3, .katex .fontsize-ensurer.reset-size11.size3 { - font-size: 0.28135048em; + font-size: 0.28135048em; } .katex .sizing.reset-size11.size4, .katex .fontsize-ensurer.reset-size11.size4 { - font-size: 0.32154341em; + font-size: 0.32154341em; } .katex .sizing.reset-size11.size5, .katex .fontsize-ensurer.reset-size11.size5 { - font-size: 0.36173633em; + font-size: 0.36173633em; } .katex .sizing.reset-size11.size6, .katex .fontsize-ensurer.reset-size11.size6 { - font-size: 0.40192926em; + font-size: 0.40192926em; } .katex .sizing.reset-size11.size7, .katex .fontsize-ensurer.reset-size11.size7 { - font-size: 0.48231511em; + font-size: 0.48231511em; } .katex .sizing.reset-size11.size8, .katex .fontsize-ensurer.reset-size11.size8 { - font-size: 0.57877814em; + font-size: 0.57877814em; } .katex .sizing.reset-size11.size9, .katex .fontsize-ensurer.reset-size11.size9 { - font-size: 0.69453376em; + font-size: 0.69453376em; } .katex .sizing.reset-size11.size10, .katex .fontsize-ensurer.reset-size11.size10 { - font-size: 0.83360129em; + font-size: 0.83360129em; } .katex .sizing.reset-size11.size11, .katex .fontsize-ensurer.reset-size11.size11 { - font-size: 1em; + font-size: 1em; } .katex .delimsizing.size1 { - font-family: KaTeX_Size1; + font-family: KaTeX_Size1; } .katex .delimsizing.size2 { - font-family: KaTeX_Size2; + font-family: KaTeX_Size2; } .katex .delimsizing.size3 { - font-family: KaTeX_Size3; + font-family: KaTeX_Size3; } .katex .delimsizing.size4 { - font-family: KaTeX_Size4; + font-family: KaTeX_Size4; } .katex .delimsizing.mult .delim-size1 > span { - font-family: KaTeX_Size1; + font-family: KaTeX_Size1; } .katex .delimsizing.mult .delim-size4 > span { - font-family: KaTeX_Size4; + font-family: KaTeX_Size4; } .katex .nulldelimiter { - display: inline-block; - width: 0.12em; + display: inline-block; + width: 0.12em; } .katex .delimcenter { - position: relative; + position: relative; } .katex .op-symbol { - position: relative; + position: relative; } .katex .op-symbol.small-op { - font-family: KaTeX_Size1; + font-family: KaTeX_Size1; } .katex .op-symbol.large-op { - font-family: KaTeX_Size2; + font-family: KaTeX_Size2; } .katex .op-limits > .vlist-t { - text-align: center; + text-align: center; } .katex .accent > .vlist-t { - text-align: center; + text-align: center; } .katex .accent .accent-body { - position: relative; + position: relative; } .katex .accent .accent-body:not(.accent-full) { - width: 0; + width: 0; } .katex .overlay { - display: block; + display: block; } .katex .mtable .vertical-separator { - display: inline-block; - min-width: 1px; + display: inline-block; + min-width: 1px; } .katex .mtable .arraycolsep { - display: inline-block; + display: inline-block; } .katex .mtable .col-align-c > .vlist-t { - text-align: center; + text-align: center; } .katex .mtable .col-align-l > .vlist-t { - text-align: left; + text-align: left; } .katex .mtable .col-align-r > .vlist-t { - text-align: right; + text-align: right; } .katex .svg-align { - text-align: left; + text-align: left; } .katex svg { - display: block; - position: absolute; - width: 100%; - height: inherit; - fill: currentColor; - stroke: currentColor; - fill-rule: nonzero; - fill-opacity: 1; - stroke-width: 1; - stroke-linecap: butt; - stroke-linejoin: miter; - stroke-miterlimit: 4; - stroke-dasharray: none; - stroke-dashoffset: 0; - stroke-opacity: 1; + display: block; + position: absolute; + width: 100%; + height: inherit; + fill: currentColor; + stroke: currentColor; + fill-rule: nonzero; + fill-opacity: 1; + stroke-width: 1; + stroke-linecap: butt; + stroke-linejoin: miter; + stroke-miterlimit: 4; + stroke-dasharray: none; + stroke-dashoffset: 0; + stroke-opacity: 1; } .katex svg path { - stroke: none; + stroke: none; } .katex img { - border-style: none; - min-width: 0; - min-height: 0; - max-width: none; - max-height: none; + border-style: none; + min-width: 0; + min-height: 0; + max-width: none; + max-height: none; } .katex .stretchy { - width: 100%; - display: block; - position: relative; - overflow: hidden; + width: 100%; + display: block; + position: relative; + overflow: hidden; } .katex .stretchy::before, .katex .stretchy::after { - content: ""; + content: ''; } .katex .hide-tail { - width: 100%; - position: relative; - overflow: hidden; + width: 100%; + position: relative; + overflow: hidden; } .katex .halfarrow-left { - position: absolute; - left: 0; - width: 50.2%; - overflow: hidden; + position: absolute; + left: 0; + width: 50.2%; + overflow: hidden; } .katex .halfarrow-right { - position: absolute; - right: 0; - width: 50.2%; - overflow: hidden; + position: absolute; + right: 0; + width: 50.2%; + overflow: hidden; } .katex .brace-left { - position: absolute; - left: 0; - width: 25.1%; - overflow: hidden; + position: absolute; + left: 0; + width: 25.1%; + overflow: hidden; } .katex .brace-center { - position: absolute; - left: 25%; - width: 50%; - overflow: hidden; + position: absolute; + left: 25%; + width: 50%; + overflow: hidden; } .katex .brace-right { - position: absolute; - right: 0; - width: 25.1%; - overflow: hidden; + position: absolute; + right: 0; + width: 25.1%; + overflow: hidden; } .katex .x-arrow-pad { - padding: 0 0.5em; + padding: 0 0.5em; } .katex .x-arrow, .katex .mover, .katex .munder { - text-align: center; + text-align: center; } .katex .boxpad { - padding: 0 0.3em 0 0.3em; + padding: 0 0.3em 0 0.3em; } .katex .fbox, .katex .fcolorbox { - box-sizing: border-box; - border: 0.04em solid; + box-sizing: border-box; + border: 0.04em solid; } .katex .cancel-pad { - padding: 0 0.2em 0 0.2em; + padding: 0 0.2em 0 0.2em; } .katex .cancel-lap { - margin-left: -0.2em; - margin-right: -0.2em; + margin-left: -0.2em; + margin-right: -0.2em; } .katex .sout { - border-bottom-style: solid; - border-bottom-width: 0.08em; + border-bottom-style: solid; + border-bottom-width: 0.08em; } .katex-display { - display: block; - margin: 1em 0; - text-align: center; + display: block; + margin: 1em 0; + text-align: center; } .katex-display > .katex { - display: block; - text-align: center; - white-space: nowrap; + display: block; + text-align: center; + white-space: nowrap; } .katex-display > .katex > .katex-html { - display: block; - position: relative; + display: block; + position: relative; } .katex-display > .katex > .katex-html > .tag { - position: absolute; - right: 0; + position: absolute; + right: 0; } .katex-display.leqno > .katex > .katex-html > .tag { - left: 0; - right: auto; + left: 0; + right: auto; } .katex-display.fleqn > .katex { - text-align: left; - padding-left: 2em; + text-align: left; + padding-left: 2em; } diff --git a/assets/_utils-custom.scss b/assets/_utils-custom.scss index 77afcf3fe..f00898f8a 100644 --- a/assets/_utils-custom.scss +++ b/assets/_utils-custom.scss @@ -1,3 +1,3 @@ .u-VaMiddle { - vertical-align: middle; + vertical-align: middle; } diff --git a/assets/_variables.scss b/assets/_variables.scss index bf1766788..7b43cb279 100644 --- a/assets/_variables.scss +++ b/assets/_variables.scss @@ -1,12 +1,11 @@ - :root { - --cl-red: #ff0010; - --cl-orange: #ff6f00; - --cl-yellow: #ffef00; - --cl-green: #90ff00; - --cl-blue: #0090ff; - --cl-gray: #6c8293; + --cl-red: #ff0010; + --cl-orange: #ff6f00; + --cl-yellow: #ffef00; + --cl-green: #90ff00; + --cl-blue: #0090ff; + --cl-gray: #6c8293; - --color-link: var(--cl-blue); - --color-visited-link: var(--cl-blue); + --color-link: var(--cl-blue); + --color-visited-link: var(--cl-blue); } diff --git a/assets/js/katex.js b/assets/js/katex.js index a7f520062..bfd5bfe5e 100644 --- a/assets/js/katex.js +++ b/assets/js/katex.js @@ -1,18 +1,16 @@ import renderMathInElement from 'katex/dist/contrib/auto-render.mjs' -function renderKatex (target) { +function renderKatex(target) { renderMathInElement(target, { - ignoredTags: ["script", "noscript", "style", "textarea"], + ignoredTags: ['script', 'noscript', 'style', 'textarea'], throwOnError: false, delimiters: [ - {left: "$$", right: "$$", display: true}, - {left: "$", right: "$", display: false}, - {left: "\\(", right: "\\)", display: false}, - {left: "\\[", right: "\\]", display: true} - ] + { left: '$$', right: '$$', display: true }, + { left: '$', right: '$', display: false }, + { left: '\\(', right: '\\)', display: false }, + { left: '\\[', right: '\\]', display: true }, + ], }) } -export { - renderKatex -} +export { renderKatex } diff --git a/assets/js/lightbox.js b/assets/js/lightbox.js index 2842357c1..a48bd0c7f 100644 --- a/assets/js/lightbox.js +++ b/assets/js/lightbox.js @@ -1,71 +1,69 @@ -import zoomable from 'd3-zoomable'; +import zoomable from 'd3-zoomable' -function lightbox () { - const transitionSpeedInMilliseconds = 250; +function lightbox() { + const transitionSpeedInMilliseconds = 250 - // template - const fragment = new DocumentFragment() - const container = document.createElement('div') - container.classList.add('lightbox-container') - - const zoom = document.createElement('div') - zoom.classList.add('lightbox-zoom') - - const img = document.createElement('img') - img.src = '' - img.classList.add('lightbox-image') - - container.appendChild(zoom) - zoom.appendChild(img) - fragment.appendChild(container) - document.body.appendChild(fragment) - - // init zoomable in the template - const myZoom = zoomable() - myZoom(container).htmlEl(zoom) - - // hook events - const elements = document.querySelectorAll('.zoomable img') - elements.forEach((element) => { - element.addEventListener('click', () => { - handleElementClick(element); - }); - }); - container.addEventListener('click', hideLightbox); - window.addEventListener('keyup', (event) => { - if (event.key === 'Escape') { - hideLightbox(); - } - }); - - function handleElementClick(htmlElement) { - img.attributes['src'].value = htmlElement.attributes['src'].value; - if (!isVisible()) { - container.classList.remove('hidden'); - container.classList.add('visible'); - document.body.classList.add('lightbox-body-scroll-stop') - } + // template + const fragment = new DocumentFragment() + const container = document.createElement('div') + container.classList.add('lightbox-container') + + const zoom = document.createElement('div') + zoom.classList.add('lightbox-zoom') + + const img = document.createElement('img') + img.src = '' + img.classList.add('lightbox-image') + + container.appendChild(zoom) + zoom.appendChild(img) + fragment.appendChild(container) + document.body.appendChild(fragment) + + // init zoomable in the template + const myZoom = zoomable() + myZoom(container).htmlEl(zoom) + + // hook events + const elements = document.querySelectorAll('.zoomable img') + elements.forEach((element) => { + element.addEventListener('click', () => { + handleElementClick(element) + }) + }) + container.addEventListener('click', hideLightbox) + window.addEventListener('keyup', (event) => { + if (event.key === 'Escape') { + hideLightbox() } - - function hideLightbox() { - if (isVisible()) { - container.classList.add('hidden'); - - setTimeout(() => { - container.classList.remove('visible'); - container.classList.remove('hidden'); - document.body.classList.remove('lightbox-body-scroll-stop') - img.attributes['src'].value = ''; - myZoom.zoomReset() - }, transitionSpeedInMilliseconds); - } + }) + + function handleElementClick(htmlElement) { + img.attributes['src'].value = htmlElement.attributes['src'].value + if (!isVisible()) { + container.classList.remove('hidden') + container.classList.add('visible') + document.body.classList.add('lightbox-body-scroll-stop') } - - function isVisible() { - return container.classList.contains('visible'); + } + + function hideLightbox() { + if (isVisible()) { + container.classList.add('hidden') + + setTimeout(() => { + container.classList.remove('visible') + container.classList.remove('hidden') + document.body.classList.remove('lightbox-body-scroll-stop') + img.attributes['src'].value = '' + myZoom.zoomReset() + }, transitionSpeedInMilliseconds) } } - export { - lightbox - } \ No newline at end of file + function isVisible() { + return container.classList.contains('visible') + } +} + +export { lightbox } diff --git a/assets/js/main.js b/assets/js/main.js index d44923d47..b3b62a12d 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -1,34 +1,34 @@ import '@pwabuilder/pwaupdate' import tablesort from 'tablesort' import Gumshoe from 'gumshoejs' -import { renderKatex } from './katex'; +import { renderKatex } from './katex' import { lightbox } from './lightbox' // Note: the tablesort lib is not ESM friendly, and the sorts expect `Tablesort` to be available on the global window.Tablesort = tablesort require('tablesort/dist/sorts/tablesort.number.min.js') -function initTableSort () { - var elements = document.querySelectorAll(".tablesort") +function initTableSort() { + var elements = document.querySelectorAll('.tablesort') elements.forEach(function (el) { - tablesort(el); + tablesort(el) }) } -function initTocDepthSlider () { +function initTocDepthSlider() { var slider = document.getElementById('toc-depth-slider') var toc = document.querySelector('.toc') - if(slider) { - slider.addEventListener('change', (event) => { - handleSliderChange(Number(event.target.value)) - }) - // init to the current value - handleSliderChange(slider.value) + if (slider) { + slider.addEventListener('change', (event) => { + handleSliderChange(Number(event.target.value)) + }) + // init to the current value + handleSliderChange(slider.value) } - function handleSliderChange (depth) { + function handleSliderChange(depth) { for (let i = 0; i < 6; i++) { - toc.querySelectorAll(`.depth-${i}`).forEach(el => { + toc.querySelectorAll(`.depth-${i}`).forEach((el) => { if (i < depth) { el.classList.remove('maybe-hide') } else { @@ -39,14 +39,14 @@ function initTocDepthSlider () { } } -function initTocScrollSpy () { - const toc = document.querySelector('.toc a') - if(toc) { - new Gumshoe('.toc a', { - nested: true, - nestedClass: 'active-parent' - }) - } +function initTocScrollSpy() { + const toc = document.querySelector('.toc a') + if (toc) { + new Gumshoe('.toc a', { + nested: true, + nestedClass: 'active-parent', + }) + } } window.addEventListener('DOMContentLoaded', () => { @@ -56,23 +56,24 @@ window.addEventListener('DOMContentLoaded', () => { lightbox() // load katex when math-mode page intersect with the viewport let observer = new IntersectionObserver((entries, observer) => { - entries.forEach(entry => { - if(entry.isIntersecting){ - renderKatex(entry.target) - observer.unobserve(entry.target); - } - }); - }); - document.querySelectorAll('.math-mode').forEach(img => { observer.observe(img) }); + entries.forEach((entry) => { + if (entry.isIntersecting) { + renderKatex(entry.target) + observer.unobserve(entry.target) + } + }) + }) + document.querySelectorAll('.math-mode').forEach((img) => { + observer.observe(img) + }) const toggle = document.querySelector('#menu-control') toggle.addEventListener('click', (e) => { - if(e.target.checked) { + if (e.target.checked) { document.body.classList.add('lightbox-body-scroll-stop') } else { document.body.classList.remove('lightbox-body-scroll-stop') } }) - -}); +}) diff --git a/assets/plugins/_bagdes.scss b/assets/plugins/_bagdes.scss index b808c743f..8cb5b00f0 100644 --- a/assets/plugins/_bagdes.scss +++ b/assets/plugins/_bagdes.scss @@ -1,48 +1,48 @@ .section-badges { - text-align: right; - font-size: 20px; - position: relative; + text-align: right; + font-size: 20px; + position: relative; } .section-badges .toggle { - cursor: pointer; - padding: 8px 0 8px 16px; + cursor: pointer; + padding: 8px 0 8px 16px; } .section-badges .dropdown { - position: absolute; - right: 0; - background-color: white; - border: 1px solid #e9ecef; - padding: 0.75rem 1rem; - list-style-type: none; - margin: 0; - z-index: 10; - display: none; - text-align: left; - font-size: 14px; - font-weight: initial; - line-height: 24px; - max-width: 400px; + position: absolute; + right: 0; + background-color: white; + border: 1px solid #e9ecef; + padding: 0.75rem 1rem; + list-style-type: none; + margin: 0; + z-index: 10; + display: none; + text-align: left; + font-size: 14px; + font-weight: initial; + line-height: 24px; + max-width: 400px; } .section-badges .dropdown li { - white-space: nowrap; + white-space: nowrap; } .section-badges .dropdown code.label { - max-width: 200px; - white-space: nowrap; - overflow: auto; - display: inline-block; + max-width: 200px; + white-space: nowrap; + overflow: auto; + display: inline-block; } .section-badges .dropdown code.label::-webkit-scrollbar { - width: 3px; - height: 3px; + width: 3px; + height: 3px; } .section-badges .dropdown code.label::-webkit-scrollbar-thumb { - border-radius: 12px; + border-radius: 12px; } .section-badges input.toggle:checked ~ .dropdown { - display: block; + display: block; } diff --git a/assets/plugins/_dark.scss b/assets/plugins/_dark.scss index 5131ee6cd..7949b8041 100644 --- a/assets/plugins/_dark.scss +++ b/assets/plugins/_dark.scss @@ -3,11 +3,11 @@ $gray-200: #282a36; // https://www.colorhexa.com/090909 $body-background: #090909; -$body-font-color: rgba(255, 255, 255, 0.80); +$body-font-color: rgba(255, 255, 255, 0.8); $color-link: #0090ff; $color-visited-link: #0090ff; $icon-filter: brightness(0) invert(1); -$book-search-results-bg: rgba(255,255,255,.1); +$book-search-results-bg: rgba(255, 255, 255, 0.1); diff --git a/assets/plugins/_diagrams.scss b/assets/plugins/_diagrams.scss index 984403185..c3a032027 100644 --- a/assets/plugins/_diagrams.scss +++ b/assets/plugins/_diagrams.scss @@ -1,46 +1,45 @@ - // SVG Diagrams .diagrams-container { - margin: 20px 0px; - border: 1px solid var(--gray-200); - border-radius: $border-radius; + margin: 20px 0px; + border: 1px solid var(--gray-200); + border-radius: $border-radius; } .diagrams { - height: 400px; - overflow: hidden; - outline: none; - cursor: pointer; - display: flex; - justify-content: center; - align-items: center; + height: 400px; + overflow: hidden; + outline: none; + cursor: pointer; + display: flex; + justify-content: center; + align-items: center; } .diagrams img { - max-height: 100%; - max-width: 100%; - display: flex; - justify-content: center; - align-items: center; + max-height: 100%; + max-width: 100%; + display: flex; + justify-content: center; + align-items: center; } .diagrams:active { - cursor: grabbing; + cursor: grabbing; } .diagrams-caption { - border-bottom: 1px solid #e9ecef; - padding: 0.5rem; - font-size: 12px; - font-weight: 500; - .diagrams-link { - text-decoration: none !important; - float: right; - font-weight: 400; - } + border-bottom: 1px solid #e9ecef; + padding: 0.5rem; + font-size: 12px; + font-weight: 500; + .diagrams-link { + text-decoration: none !important; + float: right; + font-weight: 400; + } } .diagrams-container .highlight pre { - margin: 0; - border-top-left-radius: 0; - border-top-right-radius: 0; - // margin: 0 -1px; + margin: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; + // margin: 0 -1px; } .diagrams-container .embed-comment { - margin: 0.5rem 1rem; + margin: 0.5rem 1rem; } diff --git a/assets/plugins/_lightbox.scss b/assets/plugins/_lightbox.scss index d5c36edef..72fe63a5f 100644 --- a/assets/plugins/_lightbox.scss +++ b/assets/plugins/_lightbox.scss @@ -1,88 +1,88 @@ .lightbox-container { - transition: all 250ms ease-in-out; - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: #fff; - z-index: 1000; - justify-content: center; - align-items: center; - display: flex; - transform: scale(0); - opacity: 1; - cursor: zoom-out; - overflow: hidden; - will-change: transform; + transition: all 250ms ease-in-out; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: #fff; + z-index: 1000; + justify-content: center; + align-items: center; + display: flex; + transform: scale(0); + opacity: 1; + cursor: zoom-out; + overflow: hidden; + will-change: transform; } .lightbox-body-scroll-stop { - overflow: hidden; - // position:fixed; - width: 100%; + overflow: hidden; + // position:fixed; + width: 100%; } .lightbox-container.visible { - animation-name: animate-in; - animation-duration: 250ms; - animation-fill-mode: forwards; + animation-name: animate-in; + animation-duration: 250ms; + animation-fill-mode: forwards; } .lightbox-container.hidden { - animation: animate-out; - animation-duration: 250ms; - animation-fill-mode: forwards; + animation: animate-out; + animation-duration: 250ms; + animation-fill-mode: forwards; } .lightbox-zoom { - transform-origin: top left; - width: 100%; - height: 100%; - display: flex; - justify-content: center; - align-items: center; + transform-origin: top left; + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; } .lightbox-image { - max-width: 100%; - max-height: 100%; - width: auto; - height: 100%; - object-fit: contain; + max-width: 100%; + max-height: 100%; + width: auto; + height: 100%; + object-fit: contain; } @media (min-width: 768px) { - .lightbox-image { - max-width: 95%; - max-height: 95%; - } + .lightbox-image { + max-width: 95%; + max-height: 95%; + } } @keyframes animate-in { - 0% { - transform: scale(0); - opacity: 0; - } - 1% { - transform: scale(0.9); - opacity: 0; - } - 100% { - transform: scale(1); - opacity: 1; - } + 0% { + transform: scale(0); + opacity: 0; + } + 1% { + transform: scale(0.9); + opacity: 0; + } + 100% { + transform: scale(1); + opacity: 1; + } } @keyframes animate-out { - 0% { - transform: scale(1); - opacity: 1; - } - 99% { - transform: scale(0.9); - opacity: 0; - } - 100% { - transform: scale(0); - opacity: 0; - } + 0% { + transform: scale(1); + opacity: 1; + } + 99% { + transform: scale(0.9); + opacity: 0; + } + 100% { + transform: scale(0); + opacity: 0; + } } diff --git a/assets/plugins/_meter.scss b/assets/plugins/_meter.scss index 42f840cb6..95294a876 100644 --- a/assets/plugins/_meter.scss +++ b/assets/plugins/_meter.scss @@ -1,6 +1,6 @@ -.meter { - height: 24px; /* Can be anything */ - background: #EBEBEB; +.meter { + height: 24px; /* Can be anything */ + background: #ebebeb; border-radius: 4px; color: #222; border-radius: 4px; @@ -14,4 +14,4 @@ font-size: 12px; padding-right: 1em; vertical-align: top; -} \ No newline at end of file +} diff --git a/assets/plugins/_numbered.scss b/assets/plugins/_numbered.scss index 814d404c5..174105c66 100644 --- a/assets/plugins/_numbered.scss +++ b/assets/plugins/_numbered.scss @@ -11,13 +11,13 @@ $endLevel: 6; counter-reset: h#{$currentLevel + 1} 0; counter-increment: h#{$currentLevel}; } - $content: ""; + $content: ''; @for $n from $startLevel through $currentLevel { $content: $content + 'counter(h#{$n})"."'; } h#{$currentLevel}::before { - content: unquote($content) " "; + content: unquote($content) ' '; } } } @@ -31,14 +31,14 @@ $endLevel: 6; } a:before { - content: counters(item, ".") " "; + content: counters(item, '.') ' '; float: left; margin-inline-end: $padding-4; color: var(--gray-600); } &.active { &::before { - content: "⨎"; + content: '⨎'; color: var(--cl-blue); } > a::before { diff --git a/assets/plugins/_scrollbars.scss b/assets/plugins/_scrollbars.scss index 43f8562a2..c84754668 100644 --- a/assets/plugins/_scrollbars.scss +++ b/assets/plugins/_scrollbars.scss @@ -1,5 +1,5 @@ -@import "defaults"; -@import "variables"; +@import 'defaults'; +@import 'variables'; ::-webkit-scrollbar { width: 6.5px; @@ -20,7 +20,9 @@ ::-webkit-scrollbar-button, ::-webkit-scrollbar-track-piece, ::-webkit-scrollbar-corner, -::-webkit-resizer { display: none; } +::-webkit-resizer { + display: none; +} // code blocks .markdown pre::-webkit-scrollbar { @@ -28,7 +30,7 @@ } .markdown pre { - scrollbar-color: transparent transparent ; + scrollbar-color: transparent transparent; } .markdown pre:hover { scrollbar-color: rgba(255, 255, 255, 0.3) transparent; @@ -37,7 +39,6 @@ scrollbar-color: rgba(255, 255, 255, 0.4) transparent; } - .markdown pre::-webkit-scrollbar-thumb { background-color: transparent; } @@ -61,4 +62,4 @@ } .toc > ol:hover::-webkit-scrollbar { display: initial; -} \ No newline at end of file +} diff --git a/assets/plugins/_table-sort.scss b/assets/plugins/_table-sort.scss index 79c9b2119..f6c1426ff 100644 --- a/assets/plugins/_table-sort.scss +++ b/assets/plugins/_table-sort.scss @@ -1,37 +1,37 @@ -th[role=columnheader]:not(.no-sort) { - cursor: pointer; - position: relative; - min-width: 110px; +th[role='columnheader']:not(.no-sort) { + cursor: pointer; + position: relative; + min-width: 110px; } -th[role=columnheader]:not(.no-sort):after { - content: ''; - position: absolute; - top: -3px; - right: 10px; - margin-top: 10px; - border-width: 0 4px 4px; - border-style: solid; - border-color: #404040 transparent; - visibility: hidden; - opacity: 0; - -ms-user-select: none; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; +th[role='columnheader']:not(.no-sort):after { + content: ''; + position: absolute; + top: -3px; + right: 10px; + margin-top: 10px; + border-width: 0 4px 4px; + border-style: solid; + border-color: #404040 transparent; + visibility: hidden; + opacity: 0; + -ms-user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; } -th[aria-sort=ascending]:not(.no-sort):after { - border-bottom: none; - border-width: 4px 4px 0; +th[aria-sort='ascending']:not(.no-sort):after { + border-bottom: none; + border-width: 4px 4px 0; } th[aria-sort]:not(.no-sort):after { - visibility: visible; - opacity: 0.4; + visibility: visible; + opacity: 0.4; } -th[role=columnheader]:not(.no-sort):hover:after { - visibility: visible; - opacity: 1; -} \ No newline at end of file +th[role='columnheader']:not(.no-sort):hover:after { + visibility: visible; + opacity: 1; +} diff --git a/assets/plugins/_toc.scss b/assets/plugins/_toc.scss index 969693d69..f25c922b1 100644 --- a/assets/plugins/_toc.scss +++ b/assets/plugins/_toc.scss @@ -1,99 +1,99 @@ .toc { + height: 100%; + > ol { + height: 90%; + overflow: auto; + position: relative; + > li { + font-weight: 500; + } + } + > ol li { + list-style: none; + } + > ol > li > ol { + padding-bottom: 1rem; + font-weight: 400; + } + ol { + margin: 0; + padding-left: 10px; + } + a { + color: currentColor; height: 100%; - > ol { - height: 90%; - overflow: auto; - position: relative; - > li { - font-weight: 500; - } - } - > ol li { - list-style: none; - } - > ol > li > ol { - padding-bottom: 1rem; - font-weight: 400; - } - ol { - margin: 0; - padding-left: 10px; - } - a { - color: currentColor; - height: 100%; - } - li::before { - content: " "; - display: inline-block; - height: inherit; - left: 0; - margin-top: -1px; - position: absolute; - } - li:not(.active-parent) > .maybe-hide > li:not(.active) { - height: 0; - } - li.active, - li.active-parent, - .maybe-hide li.active-parent, - li.active-parent > .maybe-hide > li { - height: auto; - } - ol li { - padding: 0 0; - line-height: 2; - font-size: 14px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - a.toc-link { - height: 100%; - } - } - .label { - font-size: 10px; - font-weight: 700; - display: block; - color: var(--gray-600); - } - input[type="range"] { - -webkit-appearance: none; - margin: 18px 0; - width: 100%; - } - input[type="range"]:focus { - outline: none; - } - input[type="range"]::-webkit-slider-runnable-track { - width: 100%; - height: 2px; - cursor: pointer; - background: var(--cl-gray); - border-radius: 1.3px; - } - input[type="range"]::-webkit-slider-thumb { - height: 16px; - width: 16px; - border-radius: 50%; - background: var(--cl-blue); - cursor: pointer; - -webkit-appearance: none; - margin-top: -7px; - } - input[type="range"]::-moz-range-track { - width: 100%; - height: 2px; - cursor: pointer; - background: #444; - border-radius: 1.3px; - } - input[type="range"]::-moz-range-thumb { - height: 16px; - width: 16px; - border-radius: 50%; - background: var(--cl-blue); - cursor: pointer; - border: 0; - } + } + li::before { + content: ' '; + display: inline-block; + height: inherit; + left: 0; + margin-top: -1px; + position: absolute; + } + li:not(.active-parent) > .maybe-hide > li:not(.active) { + height: 0; + } + li.active, + li.active-parent, + .maybe-hide li.active-parent, + li.active-parent > .maybe-hide > li { + height: auto; + } + ol li { + padding: 0 0; + line-height: 2; + font-size: 14px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + a.toc-link { + height: 100%; + } + } + .label { + font-size: 10px; + font-weight: 700; + display: block; + color: var(--gray-600); + } + input[type='range'] { + -webkit-appearance: none; + margin: 18px 0; + width: 100%; + } + input[type='range']:focus { + outline: none; + } + input[type='range']::-webkit-slider-runnable-track { + width: 100%; + height: 2px; + cursor: pointer; + background: var(--cl-gray); + border-radius: 1.3px; + } + input[type='range']::-webkit-slider-thumb { + height: 16px; + width: 16px; + border-radius: 50%; + background: var(--cl-blue); + cursor: pointer; + -webkit-appearance: none; + margin-top: -7px; + } + input[type='range']::-moz-range-track { + width: 100%; + height: 2px; + cursor: pointer; + background: #444; + border-radius: 1.3px; + } + input[type='range']::-moz-range-thumb { + height: 16px; + width: 16px; + border-radius: 50%; + background: var(--cl-blue); + cursor: pointer; + border: 0; + } } diff --git a/assets/site.webmanifest b/assets/site.webmanifest index fe02f282d..6064eefcf 100644 --- a/assets/site.webmanifest +++ b/assets/site.webmanifest @@ -1,21 +1,21 @@ { - "name": "{{ .Site.Title }}", - "short_name": "{{ .Site.Title }}", - "start_url": "{{ "/" | relURL }}", - "scope": "{{ "/" | relURL }}", - "icons": [ - { - "src": "/android-chrome-192x192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "/android-chrome-512x512.png", - "sizes": "512x512", - "type": "image/png" - } - ], - "theme_color": "#0090ff", - "background_color": "#0090ff", - "display": "standalone" + "name": "{{ .Site.Title }}", + "short_name": "{{ .Site.Title }}", + "start_url": "{{ " / " | relURL }}", + "scope": "{{ " / " | relURL }}", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#0090ff", + "background_color": "#0090ff", + "display": "standalone" } diff --git a/content/_index.md b/content/_index.md index ce843ca10..1cee06bf0 100644 --- a/content/_index.md +++ b/content/_index.md @@ -1,4 +1,4 @@ --- title: Home type: single ---- \ No newline at end of file +--- diff --git a/content/algorithms/_index.md b/content/algorithms/_index.md index 210b7c786..44b92205c 100644 --- a/content/algorithms/_index.md +++ b/content/algorithms/_index.md @@ -8,4 +8,4 @@ dashboardAudit: n/a dashboardTests: 0 --- -# Algorithms \ No newline at end of file +# Algorithms diff --git a/content/algorithms/block_reception.md b/content/algorithms/block_reception.md index 54f9640df..2398e3d4f 100644 --- a/content/algorithms/block_reception.md +++ b/content/algorithms/block_reception.md @@ -1,9 +1,8 @@ --- -title: "Block Reception" +title: 'Block Reception' draft: true --- - ```go func (g *BlockValidationGraph_I) ConsiderBlock(block Block) { panic("TODO") @@ -33,4 +32,4 @@ func (g *BlockValidationGraph_I) tryConnectBlockToFringe(block Block) { // blocks.EnqueueAll(children) // } } -``` \ No newline at end of file +``` diff --git a/content/algorithms/block_sync.md b/content/algorithms/block_sync.md index 2d41e7e5a..6abbd8ea8 100644 --- a/content/algorithms/block_sync.md +++ b/content/algorithms/block_sync.md @@ -1,5 +1,5 @@ --- -title: "BlockSync" +title: 'BlockSync' weight: 5 dashboardWeight: 1 dashboardState: stable @@ -84,13 +84,18 @@ type Status enum { For the set of arrays in the following TipSetBundle, the corresponding messages per block are as shown below: **TipSetBundle** + ```js Blocks: [b0, b1] secpMsgs: [mA, mB, mC, mD] -secpMsgIncludes: [[0, 1, 3], [1, 2, 0]] +secpMsgIncludes: [ + [0, 1, 3], + [1, 2, 0], +] ``` **Messages corresponding to each Block** + ```js Block 'b0': [mA, mB, mD] Block 'b1': [mB, mC, mA] diff --git a/content/algorithms/crypto/_index.md b/content/algorithms/crypto/_index.md index 7eb604bfb..97daedada 100644 --- a/content/algorithms/crypto/_index.md +++ b/content/algorithms/crypto/_index.md @@ -1,5 +1,5 @@ --- -title: "Cryptographic Primitives" +title: 'Cryptographic Primitives' bookCollapseSection: true weight: 7 dashboardWeight: 2 @@ -15,5 +15,4 @@ dashboardTests: 0 - zkSNARK - Reliable broadcast channel (libp2p) - -- TODO: Add more detail and include references to relevant papers. \ No newline at end of file +- TODO: Add more detail and include references to relevant papers. diff --git a/content/algorithms/crypto/randomness.md b/content/algorithms/crypto/randomness.md index 5b17e8b7a..5007bd217 100644 --- a/content/algorithms/crypto/randomness.md +++ b/content/algorithms/crypto/randomness.md @@ -1,5 +1,5 @@ --- -title: "Randomness" +title: 'Randomness' weight: 3 dashboardWeight: 2 dashboardState: reliable @@ -27,14 +27,14 @@ type BeaconEntry struct { ``` The BeaconEntry is then combined with other values to generate necessary randomness that can be -specific to (eg) a given miner address or epoch. To be used as part of entropy, these values are combined in +specific to (eg) a given miner address or epoch. To be used as part of entropy, these values are combined in objects that can then be CBOR-serialized according to their algebraic datatypes. ## Domain Separation Tags Further, we define Domain Separation Tags with which we prepend random inputs when creating entropy. -All randomness used in the protocol must be generated in conjunction with a unique DST, as well as +All randomness used in the protocol must be generated in conjunction with a unique DST, as well as certain [Signatures](signatures) and [Verifiable Random Function](vrf) usage. ## Forming Randomness Seeds @@ -42,10 +42,10 @@ certain [Signatures](signatures) and [Verifiable Random Function](vrf) usage. The beacon entry is combined with a few elements for use as part of the protocol as follows: - a DST (domain separation tag) - - Different uses of randomness are distinguished by this type of personalization which ensures that randomness used for different purposes will not conflict with randomness used elsewhere in the protocol + - Different uses of randomness are distinguished by this type of personalization which ensures that randomness used for different purposes will not conflict with randomness used elsewhere in the protocol - the epoch number, ensuring - - liveness for leader election -- in the case no one is elected in a round and no new beacon entry has appeared (i.e. if the beacon frequency is slower than that of block production in Filecoin), the new epoch number will output new randomness for LE (note that Filecoin uses liveness during a beacon outage). - - other entropy, ensuring that randomness is modified as needed by other context-dependent entropy (e.g. a miner address if we want the randomness to be different for each miner). + - liveness for leader election -- in the case no one is elected in a round and no new beacon entry has appeared (i.e. if the beacon frequency is slower than that of block production in Filecoin), the new epoch number will output new randomness for LE (note that Filecoin uses liveness during a beacon outage). + - other entropy, ensuring that randomness is modified as needed by other context-dependent entropy (e.g. a miner address if we want the randomness to be different for each miner). While all elements are not needed for every use of entropy (e.g. the inclusion of the round number is not necessary prior to genesis or outside of leader election, other entropy is only used sometimes, etc), we draw randomness as follows for the sake of uniformity/simplicity in the overall protocol. @@ -66,13 +66,13 @@ GetRandomness(dst, l, s): return H(buffer) ``` - ## Drawing tickets from the VRF-chain for proof inclusion In some places, the protocol needs randomness drawn from the Filecoin blockchain's VRF-chain (which generates [tickets](storage_power_consensus#tickets) with each new block) rather than from the random beacon, in order to tie certain proofs to a particular set of Filecoin blocks (i.e. a given chain or fork). In particular, `SealRandomness` must be taken from the VRF chain, in order to ensure that no other fork can replay the Seal (see [sealing](sealing) for more). A ticket is drawn from the chain for randomness as follows, for a given epoch `n`, and ticket sought at epoch `e`: + ```text GetRandomnessFromVRFChain(e): While ticket is not set: @@ -95,7 +95,6 @@ In plain language, this means: This ticket is then combined with a Domain Separation Tag, the round number sought and appropriate entropy to form randomness for various uses in the protocol. - ## Entropy to be used with randomness As stated above, different uses of randomness may require added entropy. The CBOR-serialization of the inputs to this entropy must be used. diff --git a/content/algorithms/crypto/signatures.md b/content/algorithms/crypto/signatures.md index 757ef825c..d24aae785 100644 --- a/content/algorithms/crypto/signatures.md +++ b/content/algorithms/crypto/signatures.md @@ -1,5 +1,5 @@ --- -title: "Signatures" +title: 'Signatures' weight: 1 dashboardWeight: 2 dashboardState: wip @@ -17,13 +17,13 @@ generate a signed message that appears to have been generated by j, with j != i. Filecoin uses signatures to associate an action to a given party. For example, Filecoin uses signatures in order to validate deal messages which represent an -action like a storage deal. +action like a storage deal. Filecoin uses signatures to verify the authenticity of the following objects (non exhaustive list): - Messages: Users authenticate their transactions to the blockchain. - Tickets: Miner authenticates its ticket (see [Storage Miner](filecoin_mining)). -- Blocks: Block leader signs over all data in the block. +- Blocks: Block leader signs over all data in the block. ## Messages @@ -34,12 +34,13 @@ Read more about this in [Randomness](randomness). ## Signature Types -Filecoin currently uses two types of signatures: +Filecoin currently uses two types of signatures: + - ECDSA signatures over the Secp256k1 elliptic curve to authenticate user transactions, mainly for compatibility with external blockchain systems. -- BLS signatures over the BLS12-381 group of curves +- BLS signatures over the BLS12-381 group of curves -Both signature types fulfill the `Signature` interface +Both signature types fulfill the `Signature` interface and each type have additional functionality as explained below. {{}} @@ -75,10 +76,8 @@ SignatureBytes = [0x30][len][0x02][r][indicator][s][indicator][recovery] `indicator` = a 2 byte formatting indicator - **External References**: [Elliptic Curve Cryptography Paper](http://www.secg.org/sec1-v2.pdf) - ### BLS Signatures Filecoin uses the [BLS signature scheme](https://datatracker.ietf.org/doc/draft-boneh-bls-signature/) over the [BLS12-381](https://electriccoin.co/blog/new-snark-curve/) group of elliptic curves. You can find the default Rust implementation in [Filecoin's repo](https://github.com/filecoin-project/bls-signatures/). @@ -101,14 +100,14 @@ signatures**. **Wire Format**: Filecoin uses the standard way to serialize BLS signatures as explained in the [RFC Section -2.6.1](https://tools.ietf.org/html/draft-boneh-bls-signature-00#section-2.6.1). +2.6.1](https://tools.ietf.org/html/draft-boneh-bls-signature-00#section-2.6.1). -**Rationale**: +**Rationale**: BLS signatures have two main characteristics that are making them ideal candidates in recent blockchain systems: - BLS signatures are deterministic: for a given message and a given secret key, - the signature is always the same. That feature removes an important security + the signature is always the same. That feature removes an important security weakness of most randomized signature schemes: signer must never re-use the same randomness twice otherwise this reveals its private key. As well, deterministic signatures are an ideal candidate to reduce the attack surface in @@ -118,9 +117,8 @@ candidates in recent blockchain systems: space on the blockchain, especially when aggregating user transactions. **Aggregation Functionality**: The aggregation functionality is commutative and -associative, enabling *partial* aggregation. For example, given -`(PK1, sig1), (PK2, sig2), (PK3, sig3)`, one can first aggregate `(PK12 = PK1 + -PK2, sig12 = sig1 + sig2)` then aggregate with the third tuple to produce +associative, enabling _partial_ aggregation. For example, given +`(PK1, sig1), (PK2, sig2), (PK3, sig3)`, one can first aggregate `(PK12 = PK1 + PK2, sig12 = sig1 + sig2)` then aggregate with the third tuple to produce `(PK123 = PK12 + PK3, sig123 = sig12 + sig3)`. **Aggregation Security**: The naive BLS signature aggregation scheme is @@ -138,7 +136,7 @@ Filecoin uses aggregation only for aggregating the transaction's signature of a block. Since Filecoin uses the account model to represent the state of the chain, each message for a given signer is used in combination with a nonce to avoid replay attacks. As a direct consequence, every transaction's message is -unique thereby the aggregation is done on distinct messages. Obviously, the +unique thereby the aggregation is done on distinct messages. Obviously, the **assumption** here is that the block producer **enforces that distinction** and the other miners will **check all transactions** to make sure they are valid. The validity of a transaction in Filecoin's context implies that the signature diff --git a/content/algorithms/crypto/vrf.md b/content/algorithms/crypto/vrf.md index d9eacb69e..800bf9c42 100644 --- a/content/algorithms/crypto/vrf.md +++ b/content/algorithms/crypto/vrf.md @@ -1,5 +1,5 @@ --- -title: "Verifiable Random Function" +title: 'Verifiable Random Function' weight: 2 dashboardWeight: 2 dashboardState: incorrect diff --git a/content/algorithms/cryptoecon/_index.md b/content/algorithms/cryptoecon/_index.md index d638c97b0..3e748bb47 100644 --- a/content/algorithms/cryptoecon/_index.md +++ b/content/algorithms/cryptoecon/_index.md @@ -23,28 +23,26 @@ Economic analyses and models were developed to design, validate, and parameteriz The following table summarizes initial parameter recommendations for Filecoin. Monitoring, testing, validation and recommendations will continue to evolve and adapt. When changes to these parameters are due they will be announced and applied through FIPs. - -| **Parameter** | **Value** | -| :------------- | :---------- | -| Baseline Storage Amount Initial Value | 2.88888888 EB | -| Baseline Storage Amount Function | 100% | -| Percent simple minting vs baseline minting | 30% / 70% | -| Reward delay and linear vesting period | 0 days | -| Linear vesting period | 180 days | -| Sector quality multipliers | Committed Capacity: 1x
Regular Deals: 1x
Verified Client Deals: 10x | -| Initial pledge function | 20 days worth of block reward +
share of 30% qa power-normalized circulating supply | -| Initial Pledge Cap | 1FIL/32GiB QA Power | -| Minimum sector lifetime | 180 days | -| Maximum sector lifetime | 540 days | -| Minimum deal duration | 180 days | -| Maximum deal duration | 540 days | -| Sector Fault Fee | 2.14 days worth of block reward| -| Sector Fault Detection Fee | 1.5 days worth of estimated block reward | -| Sector Termination Fee | Estimated number of days of block reward that a sector has earned; capped at 90 days | -| Minimum Client Deal Collateral | 0 | -| Minimum Provider Deal Collateral | share of 1% raw byte-normalised circulating supply | -| Network Transaction Fee | Dynamic fee structure based on network congestion | - +| **Parameter** | **Value** | +| :----------------------------------------- | :--------------------------------------------------------------------------------------- | +| Baseline Storage Amount Initial Value | 2.88888888 EB | +| Baseline Storage Amount Function | 100% | +| Percent simple minting vs baseline minting | 30% / 70% | +| Reward delay and linear vesting period | 0 days | +| Linear vesting period | 180 days | +| Sector quality multipliers | Committed Capacity: 1x
Regular Deals: 1x
Verified Client Deals: 10x | +| Initial pledge function | 20 days worth of block reward +
share of 30% qa power-normalized circulating supply | +| Initial Pledge Cap | 1FIL/32GiB QA Power | +| Minimum sector lifetime | 180 days | +| Maximum sector lifetime | 540 days | +| Minimum deal duration | 180 days | +| Maximum deal duration | 540 days | +| Sector Fault Fee | 2.14 days worth of block reward | +| Sector Fault Detection Fee | 1.5 days worth of estimated block reward | +| Sector Termination Fee | Estimated number of days of block reward that a sector has earned; capped at 90 days | +| Minimum Client Deal Collateral | 0 | +| Minimum Provider Deal Collateral | share of 1% raw byte-normalised circulating supply | +| Network Transaction Fee | Dynamic fee structure based on network congestion | ## Design Principles Justification diff --git a/content/algorithms/expected_consensus/_index.md b/content/algorithms/expected_consensus/_index.md index a90d66bda..370587aeb 100644 --- a/content/algorithms/expected_consensus/_index.md +++ b/content/algorithms/expected_consensus/_index.md @@ -24,10 +24,10 @@ The [Storage Power Consensus](storage_power_consensus) subsystem uses access to - Access to a weighting function enabling [Chain Selection](expected_consensus#chain-selection) by the chain manager. - Access to the most recently [finalized tipset](expected_consensus#finality-in-ec) available to all protocol participants. - ## Tickets in EC There are two kinds of tickets: + 1. ElectionProof ticket: which is the VRF that runs based on DRAND input. In particular, the miner gets the DRAND randomness beacon and gives it as input to the VRF together with the miner's worker's key. 2. the ticket is generated using the VRF as above, but the input includes the concatenation of the previous ticket. This means that the new ticket is generated running the VRF on the old ticket concatenated with the new DRAND value (and the key as before). @@ -70,7 +70,7 @@ A miner will use the ElectionProof ticket to uniformly draw a value from 0 to 1 **Step 1:** Check for leader election -A miner checks if they are elected for the current epoch by running `GenerateElectionProof`. +A miner checks if they are elected for the current epoch by running `GenerateElectionProof`. Recall that a miner is elected proportionally to their quality adjusted power at `ElectionPowerTableLookback`. @@ -89,7 +89,7 @@ An elected miner gets the randomness value through the DRAND randomness generato WinningPoSt uses the randomness to select a sector for which the miner must generate a proof. If the miner is not able to generate this proof within some predefined amount of time, then they will not be able to create a block. The sector is chosen from the power table `WinningPoStSectorSetLookback` epochs in the past. -Similarly to `ElectionPowerTableLookback`, a requirement for setting `WinningPoStSectorSetLookback` is that it must be larger than finality. This is to enforce that a miner cannot play with the power table and change which sector is challenged for WinningPoSt (i.e., set the challenged sector to one of their preference). +Similarly to `ElectionPowerTableLookback`, a requirement for setting `WinningPoStSectorSetLookback` is that it must be larger than finality. This is to enforce that a miner cannot play with the power table and change which sector is challenged for WinningPoSt (i.e., set the challenged sector to one of their preference). If `WinningPoStSectorSetLookback` is not longer than finality, a miner could try to create forks to change their sectors allocation to get a more favourable sector to be challenged for example. A simple attack could unfold as follows: @@ -107,6 +107,7 @@ Note that there are variants of this attack in which the miner introduces a new An expired sector will not be challenged during WindowPoSt (hence not penalized after its expiration). However, an edge case around the fact that `WinningPoStSectorSetLookback` is longer than finality is that due to the lookback, a miner can be challenged for an expired sector between `expirationEpoch` and `expirationEpoch + WinningPoStSectorSetLookback - 1`. Therefore, it is important that miners keep an expired sector for `WinningPoStSectorSetLookback` more epochs after expiration, or they will not be able to generate a WinningPoSt (and get the corresponding reward). Example: + - At epoch `X`: - Sector expires and miner deletes the sector. - At epoch `X+WinningPoStSectorSetLookback-1`: @@ -137,7 +138,7 @@ The "WinCount" is an integer that is used for weight and block reward calculatio - The probability of winning one block is `1-P[X=0]`, where `X` is a Poisson random variable following Poisson distribution with parameter `lambda = MinerPowerShare*ExpectedLeadersPerEpoch`. Thus, if `h_n` is less than `1-P[X=0]`, the miner wins at least one block. - Similarly if `h_n` is less than `1-P[X=0]-P[X=1]` we have at least two blocks and so on. - While it is not permitted for a single miner to publish two distinct blocks, in this case, the miner produces a single block which earns two block rewards - + #### Explanations - Poisson Sortition Filecoin is building on the principle that a miner possessing _X%_ of network power should win as many times as X miners with 1% of network power in the election algorithm. @@ -148,20 +149,18 @@ Despite this finding, we wanted to assess the difference between the two distrib Using `lambda = MinerPower*ExepectedLeader` as the parameter for the Poisson distribution, and assuming `TotalPower =10000`, `minerPower = 3300` and `ExpectedLeaderPerEpoch = 5`, we find (see table) that the probability mass function for the Binomial and the Poisson distributions do not differ much anyway. - -k | Binomial | Poisson -|---|---|---| -0 | 0.19197 | 0.19205 -1 | 0.31691 | 0.31688 -2 | 0.26150 | 0.26143 -3 | 0.14381 | 0.14379 -4 | 0.05930 | 0.05931 -5 | 0.01955 | 0.01957 -6 | 0.00537 | 0.00538 -7 | 0.00126 | 0.00127 -8 | 0.00026 | 0.00026 -9 | 0.00005 | 0.00005 - +| k | Binomial | Poisson | +| --- | -------- | ------- | +| 0 | 0.19197 | 0.19205 | +| 1 | 0.31691 | 0.31688 | +| 2 | 0.26150 | 0.26143 | +| 3 | 0.14381 | 0.14379 | +| 4 | 0.05930 | 0.05931 | +| 5 | 0.01955 | 0.01957 | +| 6 | 0.00537 | 0.00538 | +| 7 | 0.00126 | 0.00127 | +| 8 | 0.00026 | 0.00026 | +| 9 | 0.00005 | 0.00005 | **Justification for the need of _WinCount_** @@ -182,11 +181,11 @@ Bernouli, Binomial and Poisson distributions have been considered for the _WinCo - Option 4: WinCount(p,N) ~ Binomial(p/M, ME/N) - Option 5: WinCount(p,N) ~ Poisson(pE/N) -Note that in Options 2-5 the expectation of the win-count grows linearly with the miner's power `p`. That is, `𝔼[WinCount(p,N)] = pE/N`. For Option 1 this property does not hold when p/N > 1/E. +Note that in Options 2-5 the expectation of the win-count grows linearly with the miner's power `p`. That is, `𝔼[WinCount(p,N)] = pE/N`. For Option 1 this property does not hold when p/N > 1/E. -Furthermore, in Options 1, 3 and 5 the _WinCount distribution is invariant to the number of Sybils in the system. In particular WinCount(p,N)=2WinCount(p/2,N), which is a desirable property. +Furthermore, in Options 1, 3 and 5 the \_WinCount distribution is invariant to the number of Sybils in the system. In particular WinCount(p,N)=2WinCount(p/2,N), which is a desirable property. -In Option 5 (the one used in Filecoin Leader Election), the ticket targets for each _WinCount k that range from 1 to mE (with m=2 or 3) shall approximate the upside-down CDF of a Poisson distribution with rate λ=pE/N, or explicitly, 2²⁵⁶(1-exp(-pE/N)∑ᵏ⁻¹ᵢ₌₀(pE/N)ⁱ/(i!)). +In Option 5 (the one used in Filecoin Leader Election), the ticket targets for each \_WinCount k that range from 1 to mE (with m=2 or 3) shall approximate the upside-down CDF of a Poisson distribution with rate λ=pE/N, or explicitly, 2²⁵⁶(1-exp(-pE/N)∑ᵏ⁻¹ᵢ₌₀(pE/N)ⁱ/(i!)). **Rationale for the Poisson Sortition choice** @@ -319,13 +318,13 @@ w[r+1] = w[r] + (wPowerFactor[r+1] + wBlocksFactor[r+1]) * 2^8 For a given tipset `ts` in round `r+1`, we define: -- `wPowerFactor[r+1] = wFunction(totalPowerAtTipset(ts))` -- `wBlocksFactor[r+1] = wPowerFactor[r+1] * wRatio * t / e` +- `wPowerFactor[r+1] = wFunction(totalPowerAtTipset(ts))` +- `wBlocksFactor[r+1] = wPowerFactor[r+1] * wRatio * t / e` - with `t = |ticketsInTipset(ts)|` - `e = expected number of tickets per round in the protocol` - and `wRatio in ]0, 1[` -Thus, for stability of weight across implementations, we take: -- `wBlocksFactor[r+1] = (wPowerFactor[r+1] * b * wRatio_num) / (e * wRatio_den)` + Thus, for stability of weight across implementations, we take: +- `wBlocksFactor[r+1] = (wPowerFactor[r+1] * b * wRatio_num) / (e * wRatio_den)` We get: @@ -337,8 +336,8 @@ Using the 2^8 here to prevent precision loss ahead of the division in the wBlock The exact value for these parameters remain to be determined, but for testing purposes, you may use: - - `e = 5` - - `wRatio = .5, or wRatio_num = 1, wRatio_den = 2` +- `e = 5` +- `wRatio = .5, or wRatio_num = 1, wRatio_den = 2` - `wFunction = log2b` with - `log2b(X) = floor(log2(x)) = (binary length of X) - 1` and `log2b(0) = 0`. Note that that special case should never be used (given it would mean an empty power table). @@ -381,21 +380,23 @@ This is detectable when a given miner submits two blocks that satisfy any of the ### Types of faults 1. **Double-Fork Mining Fault**: two blocks mined at the same epoch (even if they have the same tipset). - - `B4.Epoch == B5.Epoch` -![Double-Fork Mining Fault](diagrams/double_fork.dot) + - `B4.Epoch == B5.Epoch` + ![Double-Fork Mining Fault](diagrams/double_fork.dot) 2. **Time-Offset Mining Fault**: two blocks mined off of the same Tipset at different epochs. + - `B3.Parents == B4.Parents && B3.Epoch != B4.Epoch` -![Time-Offset Mining Fault](diagrams/time_offset.dot) + ![Time-Offset Mining Fault](diagrams/time_offset.dot) 3. **Parent-Grinding Fault**: one block's parent is a Tipset that provably should have included a given block but does not. While it cannot be proven that a missing block was willfully omitted in general (i.e. network latency could simply mean the miner did not receive a particular block), it can when a miner has successfully mined a block two epochs in a row and omitted one. That is, this condition should be evoked when a miner omits their own prior block. -Specifically, this can be proven with a "witness" block, that is by submitting blocks B2, B3, B4 where B2 is B4's parent and B3's sibling but B3 is not B4's parent. - - `!B4.Parents.Include(B3) && B4.Parents.Include(B2) && B3.Parents == B2.Parents && B3.Epoch == B2.Epoch` -![Parent-Grinding fault](diagrams/parent_grinding.dot) + Specifically, this can be proven with a "witness" block, that is by submitting blocks B2, B3, B4 where B2 is B4's parent and B3's sibling but B3 is not B4's parent. - `!B4.Parents.Include(B3) && B4.Parents.Include(B2) && B3.Parents == B2.Parents && B3.Epoch == B2.Epoch` + ![Parent-Grinding fault](diagrams/parent_grinding.dot) ### Penalization for faults + A single consensus fault results into: + - miner termination and removal of power from the power table, - loss of all pledge collateral (which includes the initial pledge and blocks rewards yet to be vested) diff --git a/content/algorithms/gossip_sub.md b/content/algorithms/gossip_sub.md index af01818d0..dd8a72809 100644 --- a/content/algorithms/gossip_sub.md +++ b/content/algorithms/gossip_sub.md @@ -1,5 +1,5 @@ --- -title: "GossipSub" +title: 'GossipSub' weight: 6 dashboardWeight: 2.0 dashboardState: stable diff --git a/content/algorithms/porep-old/_index.md b/content/algorithms/porep-old/_index.md index 61e9f4055..7106510c2 100644 --- a/content/algorithms/porep-old/_index.md +++ b/content/algorithms/porep-old/_index.md @@ -7,6 +7,6 @@ bookhidden: true # Proof-of-Replication -_Proof-of-Replication(PoRep)_, is a new kind of _Proof-of-Storage_, that can be used to prove that some data _D_ has been replicated to its own uniquely dedicated physical storage. Enforcing unique physical copies enables a verifier to check that a prover is not deduplicating multiple copies of _D_ into the same storage space. This construction is particularly useful in Cloud Computing and Decentralized Storage Networks, which must be transparently verifiable, resistant to Sybil attacks, and unfriendly to outsourcing. +_Proof-of-Replication(PoRep)_, is a new kind of _Proof-of-Storage_, that can be used to prove that some data _D_ has been replicated to its own uniquely dedicated physical storage. Enforcing unique physical copies enables a verifier to check that a prover is not deduplicating multiple copies of _D_ into the same storage space. This construction is particularly useful in Cloud Computing and Decentralized Storage Networks, which must be transparently verifiable, resistant to Sybil attacks, and unfriendly to outsourcing. Section 3.2 of the [Filecoin Paper](https://filecoin.io/filecoin.pdf) provides the original introduction to Proof-of-Replication. diff --git a/content/algorithms/porep-old/porep_commitments/_index.md b/content/algorithms/porep-old/porep_commitments/_index.md index 8092aee34..41c4de1d3 100644 --- a/content/algorithms/porep-old/porep_commitments/_index.md +++ b/content/algorithms/porep-old/porep_commitments/_index.md @@ -1,5 +1,5 @@ --- -title: "PoRep Commitments" +title: 'PoRep Commitments' weight: 2 --- @@ -7,4 +7,4 @@ weight: 2 {{< hint warning >}} TODO: Description -{{< /hint >}} \ No newline at end of file +{{< /hint >}} diff --git a/content/algorithms/porep-old/stacked_drg.md b/content/algorithms/porep-old/stacked_drg.md index 48a72f5d0..b3db798b6 100644 --- a/content/algorithms/porep-old/stacked_drg.md +++ b/content/algorithms/porep-old/stacked_drg.md @@ -5,42 +5,41 @@ weight: 1 # Stacked DRG PoRep - -This section describes *Stacked DRG PoRep* (SDR), the specific Proof-of-Replication (PoRep) used in Filecoin. In this construction, the prover encodes the original data into a replica and commits to it. An offline PoRep proves that the commitment to the replica is a valid commitment of the encoded original data. +This section describes _Stacked DRG PoRep_ (SDR), the specific Proof-of-Replication (PoRep) used in Filecoin. In this construction, the prover encodes the original data into a replica and commits to it. An offline PoRep proves that the commitment to the replica is a valid commitment of the encoded original data. SDR has been presented by [Ben Fisch at EUROCRYPT19](https://eprint.iacr.org/2018/702.pdf). # Introduction ## Background on Proof-of-Replication -*Proof-of-Replication* enables a prover *P* to convince a verifier *V* that *P* is storing a replica *R*, a physically independent copy of some data *D*, unique to *P*. The scheme is defined by a tuple of polynomial time algorithms (_Setup_, Replication, _Prove_, _Verify_). The assumption is that generation of a replica after _Replicate_ must be difficult (if not impossible) to generate. -- *Setup*: On setup, the public parameters of the proving systems are set. -- *Replicate*: On replication, either a party or both (depending on the scheme, in our case the prover only!) generate a unique permutation of the original data _D_, which we call replica _R_. +_Proof-of-Replication_ enables a prover _P_ to convince a verifier _V_ that _P_ is storing a replica _R_, a physically independent copy of some data _D_, unique to _P_. The scheme is defined by a tuple of polynomial time algorithms (_Setup_, Replication, _Prove_, _Verify_). The assumption is that generation of a replica after _Replicate_ must be difficult (if not impossible) to generate. + +- _Setup_: On setup, the public parameters of the proving systems are set. +- _Replicate_: On replication, either a party or both (depending on the scheme, in our case the prover only!) generate a unique permutation of the original data _D_, which we call replica _R_. - _Prove_: On receiving a challenge, the prover must generate a proof that it is in possession of the replica and that it was derived from data _D_. The prover must only be able to respond to the challenge successfully if it is in possession of the replica, since would be difficult (if not impossible) to generate a replica that can be used to generate the proof at this stage - _Verify_: On receiving the proof, the verifier checks the validity of the proof and accepts or rejects the proof. {{% mermaid %}} sequenceDiagram - Note right of Prover: CommD - Prover-->>Prover: R, CommR ← Replicate(D) - Prover->>Verifier: CommR - Verifier-->>Verifier: Generate random challenge - Verifier->>Prover: challenge - Prover-->>Prover: proof ← Prove(D, R, challenge) - Prover->>Verifier: proof +Note right of Prover: CommD +Prover-->>Prover: R, CommR ← Replicate(D) +Prover->>Verifier: CommR +Verifier-->>Verifier: Generate random challenge +Verifier->>Prover: challenge +Prover-->>Prover: proof ← Prove(D, R, challenge) +Prover->>Verifier: proof {{% /mermaid %}} ## Time-bounded Proof-of-Replication -**Timing assumption**. *Time-bounded Proof-of-Replication* are constructions of PoRep with timing assumptions. The assumption is that generation of the replica (hence the _Replication_) takes some time _t_ that is substantially larger than the time it takes to produce a proof (hence _time(Prove)_) and the round-trip time (_RTT_) for sending a challenge and receiving a proof. +**Timing assumption**. _Time-bounded Proof-of-Replication_ are constructions of PoRep with timing assumptions. The assumption is that generation of the replica (hence the _Replication_) takes some time _t_ that is substantially larger than the time it takes to produce a proof (hence _time(Prove)_) and the round-trip time (_RTT_) for sending a challenge and receiving a proof. **Distinguishing Malicious provers**. A malicious prover that does not have _R_, must obtain it (or generate it), before the _Prove_ step. A verifier can distinguish an honest prover from a malicious prover, since the malicious one will take too long to answer the challenge. A verifier will reject if receiving the proof from the prover takes longer than a timeout (bounded between proving time and replication time). ## Background on Stacked DRG PoRep -*Stacked DRG PoRep* (SDR) is a specific Proof-of-Replication construction that we use in Filecoin. SDR has been designed by [Ben Fisch at EUROCRYPT19](https://eprint.iacr.org/2018/702.pdf). At a high level, SDR ensures that the *Replicate* step is a slow non-parallelizable sequential process by using a special type of graph called Depth Robust Graphs (DRG). - +_Stacked DRG PoRep_ (SDR) is a specific Proof-of-Replication construction that we use in Filecoin. SDR has been designed by [Ben Fisch at EUROCRYPT19](https://eprint.iacr.org/2018/702.pdf). At a high level, SDR ensures that the _Replicate_ step is a slow non-parallelizable sequential process by using a special type of graph called Depth Robust Graphs (DRG). **Encoding using DRGs**. A key is generated by sequentially labeling nodes in the graph such that each label depends on the labels of its parents. The depth robustness property of these graphs ensure that the sequential labeling steps are not parallelizable. The final labels are used as a key to encode the original data. @@ -56,7 +55,6 @@ TODO: This probably needs a more thorough rewrite. - `CommRLast` is the Merkle tree root hash of the replica. - `CommR` is the on-chain commitment to the replica, dervied as the hash of the concatenation of `CommC` and `CommRLast`. - An SDR proof proves that some data whose committment is `CommD` has been used to run a `Replicate` algorithm and generated some data. `CommR` is the on-chain commitment to both the replicated data and to intermediate stages required to prove `Replicate` was performed correctly. An SDR proof consists of a set of challenged DRG nodes for each layer, a set of parent nodes for each challenged node and a Merkle tree inclusion proof for each node provided. The verifier can then verify the correct labeling of each node and that the nodes given were consistent with the prover's commitments. @@ -67,16 +65,16 @@ The SNARK circuit proves that given Merkle roots `CommD`, and `CommR`, the prove ## PoRep in Filecoin -Proof-of-Replication proves that a Storage Miner is dedicating unique storage for each ***sector***. Filecoin Storage Miners collect new clients' data in a sector, run a slow encoding process (called `Seal`) and generate a proof (`SealProof`) that the encoding was generated correctly. +Proof-of-Replication proves that a Storage Miner is dedicating unique storage for each **_sector_**. Filecoin Storage Miners collect new clients' data in a sector, run a slow encoding process (called `Seal`) and generate a proof (`SealProof`) that the encoding was generated correctly. -In Filecoin, PoRep provides two guarantees: (1) *space-hardness*: Storage Miners cannot lie about the amount of space they are dedicating to Filecoin in order to gain more power in the consensus; (2) *replication*: Storage Miners are dedicating unique storage for each copy of their clients data. +In Filecoin, PoRep provides two guarantees: (1) _space-hardness_: Storage Miners cannot lie about the amount of space they are dedicating to Filecoin in order to gain more power in the consensus; (2) _replication_: Storage Miners are dedicating unique storage for each copy of their clients data. Glossary: -- __*sector:*__ a fixed-size block of data of `SECTOR_SIZE` bytes which generally contains clients' data. -- __*unsealed sector:*__ a concrete representation (on disk or in memory) of a sector's that follows the "Storage Format" described in Client Data Processing (currently `paddedfr32v1` is the required default). -- __*sealed sector:*__ a concrete representation (on disk or in memory) of the unique replica generated by `Seal` from an __*unsealed sector*__. A sector contains one or more ***pieces***. -- __*piece:*__ a block of data of at most `SECTOR_SIZE` bytes which is generally a client's file or part of. +- **_sector:_** a fixed-size block of data of `SECTOR_SIZE` bytes which generally contains clients' data. +- **_unsealed sector:_** a concrete representation (on disk or in memory) of a sector's that follows the "Storage Format" described in Client Data Processing (currently `paddedfr32v1` is the required default). +- **_sealed sector:_** a concrete representation (on disk or in memory) of the unique replica generated by `Seal` from an **_unsealed sector_**. A sector contains one or more **_pieces_**. +- **_piece:_** a block of data of at most `SECTOR_SIZE` bytes which is generally a client's file or part of. # Stacked DRG Construction @@ -86,51 +84,45 @@ The following public parameters are used in the Stacked DRG Replication and Proo {{< hint warning >}} TODO: -The Appendix should explain why we picked those values +The Appendix should explain why we picked those values Just interpolate a table of the Orient parameters and reconcile naming. {{< /hint >}} - - - -| name | type | description | value | -| --- | --- | --- | ---: | -| `SECTOR_SIZE` | `uint` | Number of nodes in the DRG in bytes | `68,719,476,736` | -| `LAYERS` | `uint` | Number of Depth Robust Graph stacked layers. | `10` | -| `BASE_DEGREE` | `uint` | In-Degree of each Depth Robust Graph. | `6` | -| `EXPANSION_DEGREE` | `uint` | Degree of each Bipartite Expander Graph to extend dependencies between layers. | `8` | -| `GRAPH_SEED` | `uint` | Seed used for random number generation in `baseParents`. | `TODO` | -| `NODE_SIZE` | `uint` | Size of each node in bytes.| `32B` | - +| name | type | description | value | +| ------------------ | ------ | ------------------------------------------------------------------------------ | ---------------: | +| `SECTOR_SIZE` | `uint` | Number of nodes in the DRG in bytes | `68,719,476,736` | +| `LAYERS` | `uint` | Number of Depth Robust Graph stacked layers. | `10` | +| `BASE_DEGREE` | `uint` | In-Degree of each Depth Robust Graph. | `6` | +| `EXPANSION_DEGREE` | `uint` | Degree of each Bipartite Expander Graph to extend dependencies between layers. | `8` | +| `GRAPH_SEED` | `uint` | Seed used for random number generation in `baseParents`. | `TODO` | +| `NODE_SIZE` | `uint` | Size of each node in bytes. | `32B` | The following constants are computed from the public parameters: - -| name | type | description | computation | value | -| --- | --- | --- | ---: | ---- | -| `PARENTS_COUNT` | `uint` | Total number of parent nodes |`EXPANSION_DEGREE + BASE_DEGREE` | `13` | -| `GRAPH_SIZE` | `uint` | Number of nodes in the graph | `SECTOR_SIZE / NODE_SIZE` | `2,147,483,648` | -| `TREE_DEPTH` | `uint` | Height of the Merkle Tree of a sector | `LOG_2(GRAPH_SIZE)` | `31` | - +| name | type | description | computation | value | +| --------------- | ------ | ------------------------------------- | -------------------------------: | --------------- | +| `PARENTS_COUNT` | `uint` | Total number of parent nodes | `EXPANSION_DEGREE + BASE_DEGREE` | `13` | +| `GRAPH_SIZE` | `uint` | Number of nodes in the graph | `SECTOR_SIZE / NODE_SIZE` | `2,147,483,648` | +| `TREE_DEPTH` | `uint` | Height of the Merkle Tree of a sector | `LOG_2(GRAPH_SIZE)` | `31` | The following additional public parameters are required: - `TAPER` : `Float`: Fraction of each layer's challenges by which to reduce next-lowest layer's challenge count. - `TAPER_LAYERS`: `uint`: Number of layers which should be tapered. FIXME: update for current tapering. - `Data` is a byte array initialized to the content of __*unsealed sector*__ and will be mutated in-place by the replication process. + `Data` is a byte array initialized to the content of **_unsealed sector_** and will be mutated in-place by the replication process. ## Hash Functions We have describe three hash functions: -| name | description | size of input | size of output | construction | -| ------------- | ------------------------------------------------------------ | ------------- | -------------- | --------------------- | -| `KDFHash` | Hash function used as a KDF to derive the key used to label a single node. | TODO | `32B` | `SHA256` | -| `ColumnHash` | Hash function used to hash the labeled leaves of each layer (see SDR Column Commitments). | TODO | `32B` | `JubjubPedersen` | -| `RepCompress` | Collision Resistant Hash function used for the Merkle tree. | 2 x `32B` + integer height | `32B` | `JubjubPedersen` | -| `RepHash` | Balanced binary Merkle tree based used to generate commitments to sealed sectors, unsealed sectors, piece commitments, and intermediate parts of the Proof-of-Replication. | TODO | `32B` | Uses `RepCompress` | -| | | | | | +| name | description | size of input | size of output | construction | +| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------- | -------------- | ------------------ | +| `KDFHash` | Hash function used as a KDF to derive the key used to label a single node. | TODO | `32B` | `SHA256` | +| `ColumnHash` | Hash function used to hash the labeled leaves of each layer (see SDR Column Commitments). | TODO | `32B` | `JubjubPedersen` | +| `RepCompress` | Collision Resistant Hash function used for the Merkle tree. | 2 x `32B` + integer height | `32B` | `JubjubPedersen` | +| `RepHash` | Balanced binary Merkle tree based used to generate commitments to sealed sectors, unsealed sectors, piece commitments, and intermediate parts of the Proof-of-Replication. | TODO | `32B` | Uses `RepCompress` | +| | | | | | ### RepHash @@ -150,7 +142,7 @@ TODO: this isn't quite right. {{< /hint >}} -We provide an algorithm (`SDR`) which computes the parents of a node. In high level, the parents of a node are computed by combining two algorithms: some parents (`BASE_DEGREE` of them) are computed via the `BucketSample` algorithm extended with a direct ordering of nodes, others (`EXPANSION_DEGREE` of them) are computed via the `Chung` algorithm. +We provide an algorithm (`SDR`) which computes the parents of a node. In high level, the parents of a node are computed by combining two algorithms: some parents (`BASE_DEGREE` of them) are computed via the `BucketSample` algorithm extended with a direct ordering of nodes, others (`EXPANSION_DEGREE` of them) are computed via the `Chung` algorithm. ### `SDRGraph`: SDR Graph algorithm @@ -165,13 +157,14 @@ Overview: Compute the DRG and Bipartite Expander parents using respectively `Buc #### Outputs -| name | description | Type | -| --------- | ------------------------------------------- | --------------------- | +| name | description | Type | +| --------- | --------------------------------------------------- | --------------------- | | `parents` | The ordered parents of node `node` on layer `layer` | `[PARENTS_COUNT]uint` | #### Algorithm - If `layer = 1`: + - Compute `drgParents = BucketSample(node)` - Set `parents` to be `drgParents`. @@ -183,7 +176,7 @@ Overview: Compute the DRG and Bipartite Expander parents using respectively `Buc We provide below a more succinct representation of the algorithm: {{< hint warning >}} -TODO: Reference to code in filproofs/algorithms.go — or restructure all this. +TODO: Reference to code in filproofs/algorithms.go — or restructure all this. {{< /hint >}} #### Time-space tradeoff @@ -198,7 +191,7 @@ The properties of DRG graphs guarantee that a sector has been encoded with a slo `BucketSample` DRG graphs are random graphs that can be deterministically generated from a seed; different seeds lead with high probability to different graphs. In SDR, we use the same seed `GRAPH_SEED` for each layer of the SDR graph such that they are all based on the same underlying DRG graph. -The parents of any node can be locally computed without computing the entire graph. We call the parents of a node calculated in this way *base parents*. +The parents of any node can be locally computed without computing the entire graph. We call the parents of a node calculated in this way _base parents_. `BucketSample` extends `BucketSampleInner` to include the node's 'immediate predecessor'. Each node except the first in a DRG generated by `BucketSample` has the node whose index is one less than its own as a parent. This ensures that @@ -210,9 +203,8 @@ visiting nodes whose indexes are sequential will result in a graph traversal in TODO: explain why we link nodes in the current layer {{< /hint >}} - Each node in layers other than the first has `EXPANSION_DEGREE` parents generated via the `ChungExpander` -algorithm. Note that the indexes returned refer to labels from the *previous* layer. TODO: Make this all clearer with explicit notation. +algorithm. Note that the indexes returned refer to labels from the _previous_ layer. TODO: Make this all clearer with explicit notation. {{< hint warning >}} TODO: link to relevant filproofs/algorithms.go @@ -232,7 +224,7 @@ TODO: link to filproofs/feistel # Replication -> The Replication phase turns an *unsealed sector* into a *sealed sector* by first *generating a key*, then using the key to *encode the orignal data*. +> The Replication phase turns an _unsealed sector_ into a _sealed sector_ by first _generating a key_, then using the key to _encode the orignal data_. Before running the `Replicate` algorithm, the prover must ensure that the sector is correctly formatted with a valid "Storage Format" (currently `paddedfr32v1` is the required default). @@ -240,7 +232,7 @@ Before running the `Replicate` algorithm, the prover must ensure that the sector TODO: inputs are missing {{< /hint >}} -The Replication Algorithm proceeds as follows: +The Replication Algorithm proceeds as follows: - Calculate `ReplicaID` using `Hash` (SHA256): @@ -256,11 +248,11 @@ ReplicaID := Hash(ProverID || SectorNumber || RandomSeed || CommD) ``` - Perform `RepHash` on `Data` to yield `CommD` and `TreeD`: + ```text CommD, TreeD = RepHash(data) ``` - ## Layer Labeling {{< hint warning >}} @@ -271,7 +263,6 @@ TODO: Define `Graph`. We need to decide if this is an object we'll explicitly de TODO: link to filproofs/algorithms {{< /hint >}} - # Proof Generation Overview: @@ -285,6 +276,7 @@ TODO: write a single algorithm which includes the spec below {{< /hint >}} ## Challenge Generation + {{< hint warning >}} TODO: Link to filproofs/algorithms {{< /hint >}} @@ -294,11 +286,13 @@ Calculate `LAYER_CHALLENGES : [LAYERS]uint`: Number of challenges per layer. (Th Derive challenges for each layer (call `DeriveChallenges()`). ## Witness Generation + {{< hint warning >}} TODO: Link to filproofs/algorithms {{< /hint >}} ## Layer Challenge Counts + {{< hint warning >}} TODO: we should just list current parameters and show this as a calculation for correctness, this should not mandatory to implement. {{< /hint >}} diff --git a/content/algorithms/porep-old/stacked_drg_circuit.md b/content/algorithms/porep-old/stacked_drg_circuit.md index b0da06ddc..71552b29c 100644 --- a/content/algorithms/porep-old/stacked_drg_circuit.md +++ b/content/algorithms/porep-old/stacked_drg_circuit.md @@ -31,7 +31,7 @@ This circuit proves that given a Merkle root `CommD`, `CommRLast`, and `commRSta ### Public Parameters -*Parameters that are embeded in the circuits or used to generate the circuit* +_Parameters that are embeded in the circuits or used to generate the circuit_ - `LAYERS : UInt`: Number of DRG layers. - `LAYER_CHALLENGES : [LAYERS]UInt`: Number of challenges per layer. @@ -42,23 +42,23 @@ This circuit proves that given a Merkle root `CommD`, `CommRLast`, and `commRSta ### Public Inputs -*Inputs that the prover uses to generate a SNARK proof and that the verifier uses to verify it* +_Inputs that the prover uses to generate a SNARK proof and that the verifier uses to verify it_ - `ReplicaId : Fr`: A unique identifier for the replica. - `CommD : Fr`: the Merkle tree root hash of the original data (input to the first layer). - `CommR : Fr`: The Merkle tree root hash of the final replica (output of the last layer). - `InclusionPath : [LAYERS][]Fr`: Inclusion path for the challenged data and replica leaf. -- `ParentInclusionPath : [LAYERS][][PARENT_COUNT]Fr`: Inclusion path for the parents of the corresponding `InclusionPath[l][c]`. +- `ParentInclusionPath : [LAYERS][][PARENT_COUNT]Fr`: Inclusion path for the parents of the corresponding `InclusionPath[l][c]`. Design notes: - `CommRLast` is a private input used during during Proof-of-Spacetime. - To enable this, the prover must store `CommC` and use it to prove that `CommRLast` is included in `CommR` (TODO: define 'included' language.) + To enable this, the prover must store `CommC` and use it to prove that `CommRLast` is included in `CommR` (TODO: define 'included' language.) - `InclusionPath` and `ParentInclusionPath`: Each layer `l` has `LAYER_CHALLENGES[l]` inclusion paths. ### Private Inputs -*Inputs that the prover uses to generate a SNARK proof, these are not needed by the verifier to verify the proof* +_Inputs that the prover uses to generate a SNARK proof, these are not needed by the verifier to verify the proof_ - `CommR : [LAYERS-1]Fr`: Commitment of the encoded data at each layer. @@ -153,7 +153,6 @@ for l in range LAYERS { } ``` - ## Verification of Offline PoRep Proof - SNARK proof check: **Check** that given the SNARK proof and the public inputs, the SNARK verification outputs true diff --git a/content/algorithms/pos/_index.md b/content/algorithms/pos/_index.md index 868e402fd..3986ad654 100644 --- a/content/algorithms/pos/_index.md +++ b/content/algorithms/pos/_index.md @@ -22,6 +22,6 @@ be the bottleneck in systems such as Filecoin, where storage providers are requi the blockchain network. To address this question, we introduce a new proof, called _Proof-of-Spacetime_, where a verifier can check if a prover -has indeed stored the outsourced data they committed to over (storage) Space and over a period of Time (hence, the name Spacetime). +has indeed stored the outsourced data they committed to over (storage) Space and over a period of Time (hence, the name Spacetime). Recall that in the Filecoin network, miners are storing data in fixed-size [sectors](filecoin_mining#sector). Sectors are filled with client data agreed through regular deals in the [Storage Market](filecoin_markets#storage_market), through [verified deals](algorithms#verified_clients), or with random client data in case of [Committed Capacity sectors](filecoin_mining#sector). diff --git a/content/algorithms/pos/porep.md b/content/algorithms/pos/porep.md index 49beeeae0..eaf4b0dec 100644 --- a/content/algorithms/pos/porep.md +++ b/content/algorithms/pos/porep.md @@ -7,7 +7,6 @@ dashboardAudit: wip dashboardTests: 0 --- - # Proof-of-Replication (PoRep) In order to register a sector with the Filecoin network, the sector has to be sealed. Sealing is a computation-heavy process that produces a unique representation of the data in the form of a proof, called **_Proof-of-Replication_** or PoRep. diff --git a/content/algorithms/pos/post.md b/content/algorithms/pos/post.md index 55888414b..612fea3d6 100644 --- a/content/algorithms/pos/post.md +++ b/content/algorithms/pos/post.md @@ -9,7 +9,7 @@ dashboardTests: 0 # Proof-of-Spacetime (PoSt) -From this point onwards, miners have to prove that they continuously store the data they pledged to store. Proof-of-Spacetime (PoSt) is a procedure during which miners are given cryptographic challenges that can only be correctly answered if the miner is actually storing a copy of the sealed data. +From this point onwards, miners have to prove that they continuously store the data they pledged to store. Proof-of-Spacetime (PoSt) is a procedure during which miners are given cryptographic challenges that can only be correctly answered if the miner is actually storing a copy of the sealed data. There are two types of challenges (and their corresponding mechanisms) that are realised as part of the PoSt process, namely, _WinningPoSt_ and _WindowPoSt_, each of which serve a different purpose. @@ -64,27 +64,24 @@ There are currently three types of Faults, the _Declared Fault_, the _Detected F Summarising: -- A miner maintains its sectors *active* by generating Proofs-of-Spacetime (PoSt) and submit `miner.SubmitWindowedPoSt` for their sectors in a timely manner. +- A miner maintains its sectors _active_ by generating Proofs-of-Spacetime (PoSt) and submit `miner.SubmitWindowedPoSt` for their sectors in a timely manner. - A WindowPoSt proves that sectors are persistently stored through time. -- Each miner proves all of its sectors once per *proving period*; each sector must be proven by a particular time called _deadline_. -- A *proving period* is a period of `WPoStProvingPeriod` epochs in which a `Miner` actor is scheduled to prove its storage. -- A *proving period* is evenly divided in `WPoStPeriodDeadlines` *deadlines*. +- Each miner proves all of its sectors once per _proving period_; each sector must be proven by a particular time called _deadline_. +- A _proving period_ is a period of `WPoStProvingPeriod` epochs in which a `Miner` actor is scheduled to prove its storage. +- A _proving period_ is evenly divided in `WPoStPeriodDeadlines` _deadlines_. - Each miner has a different start of proving period `ProvingPeriodStart` that is assigned at `Power.CreateMiner`. -- A *deadline* is a period of `WPoStChallengeWindow` epochs that divides a proving period. +- A _deadline_ is a period of `WPoStChallengeWindow` epochs that divides a proving period. - Sectors are assigned to a deadline on `miner.ProveCommitSector` and will remain assigned to it throughout their lifetime. - In order to prove that they continuously store a sector, a miner must submit a `miner.SubmitWindowedPoSt` for each deadline. - Sectors are assigned to partitions. A partition is a set of sectors that is not larger than the Seal Proof allowed number of sectors `sp.WindowPoStPartitionSectors`. - Sectors are assigned to a partition at `miner.ProveCommitSector` and they can be re-arranged via `CompactPartitions`. - Partitions are a by-product of our current proof mechanism. There is a limit in the number of sectors (`sp.WindowPoStPartitionSectors`) that can be proven in a single SNARK proof. If more than this amount is required to be proven, more than one SNARK proof is required, given that each SNARK proof represents a partition. - There are four relevant epochs associated to a deadline, shown in the table below: | Name | Distance from `Open` | Description | -|---------------|---------------------------|-------------------------------------------------------------------------------------------------------------------------------| +| ------------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | | `Open` | `0` | Epoch from which a PoSt Proof for this deadline can be submitted. | | `Close` | `WPoStChallengeWindow` | Epoch after which a PoSt Proof for this deadline will be rejected. | | `FaultCutoff` | `-FaultDeclarationCutoff` | Epoch after which a `miner.DeclareFault` and `miner.DeclareFaultRecovered` for sectors in the upcoming deadline are rejected. | | `Challenge` | `-WPoStChallengeLookback` | Epoch at which the randomness for the challenges is available. | - - diff --git a/content/algorithms/sdr/_index.md b/content/algorithms/sdr/_index.md index ae7513675..ec257f229 100644 --- a/content/algorithms/sdr/_index.md +++ b/content/algorithms/sdr/_index.md @@ -1,6 +1,6 @@ --- -title: "Stacked DRG PoRep" -description: "Stacked DRG Proof of Replication Specification" +title: 'Stacked DRG PoRep' +description: 'Stacked DRG Proof of Replication Specification' bookCollapseSection: true weight: 3 @@ -15,6 +15,7 @@ dashboardTests: 0 {{< plain hidden >}} + $$ \gdef\createporepbatch{\textsf{create\_porep\_batch}} \gdef\GrothProof{\textsf{Groth16Proof}} @@ -373,6 +374,7 @@ $$ \gdef\ColumnDigest{\textsf{ColumnDigest}} \gdef\encode{\textsf{encode}} $$ + {{< /plain >}} @@ -382,8 +384,9 @@ $$ ## Merkle Proofs **Implementation:** -* [`storage_proofs::merkle::MerkleTreeWrapper::gen_proof()`](https://github.com/filecoin-project/rust-fil-proofs/blob/e4e3375158ed5d3be4635c47a826812bc9e1a459/storage-proofs/core/src/merkle/tree.rs#L92) -* [`merkle_light::merkle::MerkleTree::gen_proof()`](https://github.com/filecoin-project/merkle_light/blob/64a468807c594d306d12d943dd90cc5f88d0d6b0/src/merkle.rs#L918) + +- [`storage_proofs::merkle::MerkleTreeWrapper::gen_proof()`](https://github.com/filecoin-project/rust-fil-proofs/blob/e4e3375158ed5d3be4635c47a826812bc9e1a459/storage-proofs/core/src/merkle/tree.rs#L92) +- [`merkle_light::merkle::MerkleTree::gen_proof()`](https://github.com/filecoin-project/merkle_light/blob/64a468807c594d306d12d943dd90cc5f88d0d6b0/src/merkle.rs#L918) **Additional Notation:** @@ -413,7 +416,8 @@ $\line{9}{\bi}{\return \BinTreeProof_c \thin \{\ \leaf, \thin \root, \thin \path ``` **Code Comments:** -* **Line 5:** Calculates the node index in layer `$l$` of the node that the verifier calculated using the previous lath element (or the `$\BinTreeProof_c\dot\leaf$` if `$l = 0$`). Note that `$c \gg l \equiv \lfloor c / 2^l \rfloor \thin$`. + +- **Line 5:** Calculates the node index in layer `$l$` of the node that the verifier calculated using the previous lath element (or the `$\BinTreeProof_c\dot\leaf$` if `$l = 0$`). Note that `$c \gg l \equiv \lfloor c / 2^l \rfloor \thin$`. ### OctTreeProofs @@ -448,9 +452,10 @@ $\line{11}{}{\return \OctTreeProof_c \thin \{\ \leaf, \thin \root, \thin \path\ ``` **Code Comments:** -* **Line 5:** Calculates the node index in layer `$l$` of the node that the verifier calculated themselves using the previous path element (or `$\OctTreeProof_c\dot\leaf$` if `$l = 0$`). Note that `$c \gg (3 * l) \equiv \lfloor c / 8^l \rfloor \thin$`. -* **Line 7-8:** Calculates the indexes in tree layer `$l$` of the first and last (both inclusive) Merkle hash inputs for layer `$l$`'s path element. -* **Line 9:** Copies the 7 Merkle hash inputs that will be in layer `$l$`'s path element `$\OctTreeProof\dot\path[l]\dot\siblings \thin$`. + +- **Line 5:** Calculates the node index in layer `$l$` of the node that the verifier calculated themselves using the previous path element (or `$\OctTreeProof_c\dot\leaf$` if `$l = 0$`). Note that `$c \gg (3 * l) \equiv \lfloor c / 8^l \rfloor \thin$`. +- **Line 7-8:** Calculates the indexes in tree layer `$l$` of the first and last (both inclusive) Merkle hash inputs for layer `$l$`'s path element. +- **Line 9:** Copies the 7 Merkle hash inputs that will be in layer `$l$`'s path element `$\OctTreeProof\dot\path[l]\dot\siblings \thin$`. ### Proof Root Validation @@ -459,8 +464,9 @@ The functions `$\bintreeproofisvalid$` and `$\octtreeproofisvalid$` are used to Note that these functions do not verify that a `$\BinTreeProof\dot\path$` or an `$\OctTreeProof\dot\path$` correspond to the expected Merkle challenge `$c$`. To verify that a proof path is consistent with `$c$`, see the psuedocode functions `$\calculatebintreechallenge$` and `$\calculateocttreechallenge$`. **Implementation:** -* [`storage_proofs::core::merkle::proof::SingleProof::verify()`](https://github.com/filecoin-project/rust-fil-proofs/blob/f75804c503d9b97a2b02ef3ea2e5d44e8e2c6470/storage-proofs/core/src/merkle/proof.rs#L540) -* [`storage_proofs::core::merkle::proof::InclusionPath::root()`](https://github.com/filecoin-project/rust-fil-proofs/blob/f75804c503d9b97a2b02ef3ea2e5d44e8e2c6470/storage-proofs/core/src/merkle/proof.rs#L189) + +- [`storage_proofs::core::merkle::proof::SingleProof::verify()`](https://github.com/filecoin-project/rust-fil-proofs/blob/f75804c503d9b97a2b02ef3ea2e5d44e8e2c6470/storage-proofs/core/src/merkle/proof.rs#L540) +- [`storage_proofs::core::merkle::proof::InclusionPath::root()`](https://github.com/filecoin-project/rust-fil-proofs/blob/f75804c503d9b97a2b02ef3ea2e5d44e8e2c6470/storage-proofs/core/src/merkle/proof.rs#L189) ```text $\overline{\underline{\Function \bintreeproofisvalid(\proof: \BinTreeProof) \rightarrow \Bool}\thin}$ @@ -507,7 +513,6 @@ $\line{5}{\bi}{\mathrlap{\llcorner c \lrcorner_{8, \Le}: [8]^{[\hspace{1pt} \log $\line{6}{\bi}{\mathrlap{\llcorner c \lrcorner_{2, \Le}: \Bit^{[\hspace{1pt} \log_2(N_\nodes) \hspace{1pt}]}}\hphantom{\llcorner c \lrcorner_{2, \Le}: \Bit^{[\hspace{1pt} \log_2(N_\nodes) \hspace{1pt}]}} = \big\|_{\pathelem \hspace{1pt} \in \hspace{1pt} \OctTreeProof_c\dot\path} \thin \llcorner \pathelem\dot\missing \lrcorner_{2, \Le}}$ ``` - **Implementation:** [`storage_proofs::merkle::MerkleProofTrait::path_index()`](https://github.com/filecoin-project/rust-fil-proofs/blob/f75804c503d9b97a2b02ef3ea2e5d44e8e2c6470/storage-proofs/core/src/merkle/proof.rs#L89) **Additional Notation:** @@ -535,7 +540,7 @@ $\line{1}{\bi}{\return \sum_{l \in [\OctTreeDepth]}{\path[l]\dot\missing * 8^l}} ## Stacked Depth Robust Graphs -Filecoin utilizes the topological properties of depth robust graphs (DRG's) to build a sequential and regeneration resistant encoding scheme. We stack `$N_\layers$` of DRG's, each containing `$N_\nodes$` nodes, on top of one another and connect each adjacent pair of DRG layers via the edges of bipartite expander. The source layer of each expander is the DRG at layer `$l$` and the sink layer is the DRG at layer `$l + 1$`. The resulting graph is termed a Stacked-DRG. +Filecoin utilizes the topological properties of depth robust graphs (DRG's) to build a sequential and regeneration resistant encoding scheme. We stack `$N_\layers$` of DRG's, each containing `$N_\nodes$` nodes, on top of one another and connect each adjacent pair of DRG layers via the edges of bipartite expander. The source layer of each expander is the DRG at layer `$l$` and the sink layer is the DRG at layer `$l + 1$`. The resulting graph is termed a Stacked-DRG. For every node `$v \in [N_\nodes]$` in the DRG at layer `$l \in [N_\layers]$`, we generate `$d_\drg$` number of DRG parent for `$v$` in layer `$l$`. DRG parents are generated using the [Bucket Sampling algorithm](https://eprint.iacr.org/2017/443.pdf). For every node `$v$` in layers `$l > 0$` we generate `$d_\exp$` number of expander parents for `$v$` where the parents are in layer `$l - 1$`. Expander parents are generated using a psuedorandom permutation (PRP) `$\pi: [N_\nodes] \rightarrow [N_\nodes]$` which maps a node in the DRG layer `$l$` to a node in the the DRG layer `$l - 1$`. The psudeorandom permutation is generated using a `$N_\feistelrounds$`-round Feistel network whose round function is the keyed hash function `$\Blake$`, where round keys are specified by the constant `$\FeistelKeys$`. @@ -680,9 +685,10 @@ $\line{15}{}{\quad \input: \u{64}_{(34)} = \output}$ ``` **Code Comments:** -* **Line 1:** Loops forever until the `$\textsf{return}$` statement is reached (loops until `$\output$` is a valid `$\ExpEdgeIndex$`). -* **Lines 13-14:** Checks if `$\output$` is a valid `$\ExpEdgeIndex$` (true iff. the most-significant bit, the 34th bit, is 0), otherwise the Feistel network is rerun. -* **Line 15:** Signifies that the next Feistel network's input has it's most-significant, its 34th bit, set to 1 (as opposed to the argument `$\input: \ExpEdgeIndex \equiv \u{64}_{(33)}$`, which does not have its 34th bit set). + +- **Line 1:** Loops forever until the `$\textsf{return}$` statement is reached (loops until `$\output$` is a valid `$\ExpEdgeIndex$`). +- **Lines 13-14:** Checks if `$\output$` is a valid `$\ExpEdgeIndex$` (true iff. the most-significant bit, the 34th bit, is 0), otherwise the Feistel network is rerun. +- **Line 15:** Signifies that the next Feistel network's input has it's most-significant, its 34th bit, set to 1 (as opposed to the argument `$\input: \ExpEdgeIndex \equiv \u{64}_{(33)}$`, which does not have its 34th bit set). ### All Parents @@ -769,8 +775,9 @@ $\line{7}{\bi}{\return \Labels}$ ``` **Code Comments:** -* **Lines 2-3:** Label the first Stacked-DRG layer. -* **Lines 4-6:** Label the remaining Stacked-DRG layers. + +- **Lines 2-3:** Label the first Stacked-DRG layer. +- **Lines 4-6:** Label the remaining Stacked-DRG layers. The function `$\labelwithdrgparents$` is used to label a node `$v$` in the first Stacked-DRG layer `$l_0 = 0 \thin$`. @@ -838,7 +845,7 @@ $\line{6}{\bi}{\return \OctTree\cc\new(\leaves)}$ ## Encoding -Encoding is the process by which a sector `$D: \Safe^{[N_\nodes]}$` is transformed into its encoding `$R: \Fqsafe^{[N_\nodes]}$`. The encoding function is *node-wise* prime field addition `$\oplus$`, where "node-wise" means that every distinct `$\Safe$` slice `$D_i \in D$` is discretely encoded. +Encoding is the process by which a sector `$D: \Safe^{[N_\nodes]}$` is transformed into its encoding `$R: \Fqsafe^{[N_\nodes]}$`. The encoding function is _node-wise_ prime field addition `$\oplus$`, where "node-wise" means that every distinct `$\Safe$` slice `$D_i \in D$` is discretely encoded. `$D$` is viewed as an array of `$N_\nodes$` distinct byte arrays `$D_i: \Safe$`. Sector preprocessing ensures that each `$D_i$` is a valid `$\Safe$` (represents a valid 254-bit or less field element `$\Fqsafe)$`. @@ -854,7 +861,7 @@ $\bi K: \Label^{[N_\nodes]} = \Labels[N_\layers - 1][:]$ $\bi K_i: \Label_{i, l_\last} = \Labels[N_\layers - 1][i]$ ``` -`$D$` is encoded into `$R$` via *node-wise* field addition. Each `$D_i \in D$` is interpreted as a field element and encoded into `$R_i$` by adding `$K_i$` to `$D_i$`. The resulting array of field elements produced via field addition is the encoding `$R$` of `$D$`. +`$D$` is encoded into `$R$` via _node-wise_ field addition. Each `$D_i \in D$` is interpreted as a field element and encoded into `$R_i$` by adding `$K_i$` to `$D_i$`. The resulting array of field elements produced via field addition is the encoding `$R$` of `$D$`. ```text $\bi R: \Fq^{[N_\nodes]} = [R_0, \ldots, R_{N_\nodes - 1}]$ @@ -980,11 +987,11 @@ A PoRep prover generates a `$\PorepPartitionProof_k$` for each partition `$k$` i A single partition proof generated by a PoRep prover shows that: -* The prover knows a valid Merkle path for `$c$` in `$\TreeD$` that is consistent with the public `$\CommD$`. -* The prover knows valid Merkle paths for `$c$` in trees `$\TreeC$` and `$\TreeR$` which are consistent with the committed to `$\CommCR$`. -* The prover knows `$c$`'s labeling in each Stacked-DRG layer `$\Column_c = \ColumnProof_c\dot\column \thin$` by hashing `$\Column_c$` into a leaf in `$\TreeC$` that is consistent with `$\CommCR$`. -* For each layer `$l$` in the Stacked-DRG, the prover knows `$c$`'s labeling preimage `$\ParentLabels$` (taken from the columns in `$\ParentColumnProofs$`), such that the parent labels are consistent with `$\CommCR$`. -* The prover knows the key `$K_c$` used to encode `$D_c$` into `$R_c$` (where `$D_c$`, `$K_c$`, and `$R_c$` were already shown to be consistent with the commitments `$\CommD$` and `$\CommCR$`). +- The prover knows a valid Merkle path for `$c$` in `$\TreeD$` that is consistent with the public `$\CommD$`. +- The prover knows valid Merkle paths for `$c$` in trees `$\TreeC$` and `$\TreeR$` which are consistent with the committed to `$\CommCR$`. +- The prover knows `$c$`'s labeling in each Stacked-DRG layer `$\Column_c = \ColumnProof_c\dot\column \thin$` by hashing `$\Column_c$` into a leaf in `$\TreeC$` that is consistent with `$\CommCR$`. +- For each layer `$l$` in the Stacked-DRG, the prover knows `$c$`'s labeling preimage `$\ParentLabels$` (taken from the columns in `$\ParentColumnProofs$`), such that the parent labels are consistent with `$\CommCR$`. +- The prover knows the key `$K_c$` used to encode `$D_c$` into `$R_c$` (where `$D_c$`, `$K_c$`, and `$R_c$` were already shown to be consistent with the commitments `$\CommD$` and `$\CommCR$`). ```text $\overline{\mathbf{Function:}\ \createvanillaporepproof(\ }$ @@ -1031,8 +1038,9 @@ $\line{14}{}{\return \PorepPartitionProof_{R, k}}$ ### Verification **Implementation:** -* [`storage_proofs::porep::stacked::vanilla::proof_scheme::StackedDrg::verify_all_partitions()`](https://github.com/filecoin-project/rust-fil-proofs/blob/c58918b9b2f749a5db40a7952d29a6501c765e13/storage-proofs/porep/src/stacked/vanilla/proof_scheme.rs#L82) -* [`storage_proofs::porep::stacked::vanilla::params::Proof::verify()`](https://github.com/filecoin-project/rust-fil-proofs/blob/447a8ba76da224b8b5f9b7b8dd624ba9a6a107a6/storage-proofs/porep/src/stacked/vanilla/params.rs#L191) + +- [`storage_proofs::porep::stacked::vanilla::proof_scheme::StackedDrg::verify_all_partitions()`](https://github.com/filecoin-project/rust-fil-proofs/blob/c58918b9b2f749a5db40a7952d29a6501c765e13/storage-proofs/porep/src/stacked/vanilla/proof_scheme.rs#L82) +- [`storage_proofs::porep::stacked::vanilla::params::Proof::verify()`](https://github.com/filecoin-project/rust-fil-proofs/blob/447a8ba76da224b8b5f9b7b8dd624ba9a6a107a6/storage-proofs/porep/src/stacked/vanilla/params.rs#L191) ```text $\overline{\Function\ \verifyvanillaporepproof(}$ @@ -1115,7 +1123,6 @@ The node index of a DRG or expander graph parent for `$c$`. `$\parentlabels': \Label^{[N_\parentlabels]}$`\ The set of parent labels repeated until its length is `$N_\parentlabels$`. - ```text $\overline{\Function: \createlabel_\V( \qquad\ }$ $\quad \ReplicaID,$ @@ -1148,8 +1155,9 @@ $\line{11}{}{\return \Sha{254}(\preimage) \as \Fq}$ ## PoRep Circuit **Implementation:** -* [`storage_proofs::porep::stacked::circuit::proof::StackedCircuit::synthesize()`](https://github.com/filecoin-project/rust-fil-proofs/blob/master/storage-proofs/porep/src/stacked/circuit/proof.rs#L77) -* [`storage_proofs::porep::stacked::circuit::params::Proof::synthesize()`](https://github.com/filecoin-project/rust-fil-proofs/blob/master/storage-proofs/porep/src/stacked/circuit/params.rs#L77) + +- [`storage_proofs::porep::stacked::circuit::proof::StackedCircuit::synthesize()`](https://github.com/filecoin-project/rust-fil-proofs/blob/master/storage-proofs/porep/src/stacked/circuit/proof.rs#L77) +- [`storage_proofs::porep::stacked::circuit::params::Proof::synthesize()`](https://github.com/filecoin-project/rust-fil-proofs/blob/master/storage-proofs/porep/src/stacked/circuit/params.rs#L77) **Additional Notation:** @@ -1280,14 +1288,15 @@ $\line{52}{}{\return \cs}$ ``` **Code Comments:** -* **Lines 9-10:** Computes `$\CommCR^\dagger$` within the circuit from the witnessed commitments and assert that `$\CommCR^\dagger$` is equal to the public input `$\CommCR$`. -* **Lines 15-16:** Adds the packed challenge `$c$` as a public input, used when calculating each challenge `$c$`'s column within the circuit. -* **Lines 17-19:** Verifies `$c$`'s `$\TreeDProof_c$` by computing `$\CommD_c^\dagger$` within the circuit and asserting that it is equal to the public input `$\CommD$`. -* **Lines 20-26:** Allocates each of `$c$`'s parent's `$u \in \mathbf{u}_\total$` label and checks that `$u$`'s `$\ColumnProof_u$` is consistent with the previously verified `$\CommC^\dagger \mapsto \CommC \thin$`. -* **Lines 27-44:** Calculates challenge `$c$`'s label in each Stacked-DRG layer `$l$` within the circuit using each parent's allocated column. -* **Lines 45-46:** Verifies that `$c$`'s `$\TreeRProof_c$` is consistent with the previously verified `$\CommR^\dagger \mapsto \CommR$`. -* **Lines 47-48:** Checks that the calculated encoding key `$K_c^\dagger$` for `$c$` encodes the previously verified sector and replica tree leaves `$D_c^\dagger \mapsto D_c$` into `$R_c^\dagger \mapsto R_c$`. -* **Lines 49-51:** Verifies `$c$`'s `$\ColumnProof_c$` against the previously verified `$\CommC$`. + +- **Lines 9-10:** Computes `$\CommCR^\dagger$` within the circuit from the witnessed commitments and assert that `$\CommCR^\dagger$` is equal to the public input `$\CommCR$`. +- **Lines 15-16:** Adds the packed challenge `$c$` as a public input, used when calculating each challenge `$c$`'s column within the circuit. +- **Lines 17-19:** Verifies `$c$`'s `$\TreeDProof_c$` by computing `$\CommD_c^\dagger$` within the circuit and asserting that it is equal to the public input `$\CommD$`. +- **Lines 20-26:** Allocates each of `$c$`'s parent's `$u \in \mathbf{u}_\total$` label and checks that `$u$`'s `$\ColumnProof_u$` is consistent with the previously verified `$\CommC^\dagger \mapsto \CommC \thin$`. +- **Lines 27-44:** Calculates challenge `$c$`'s label in each Stacked-DRG layer `$l$` within the circuit using each parent's allocated column. +- **Lines 45-46:** Verifies that `$c$`'s `$\TreeRProof_c$` is consistent with the previously verified `$\CommR^\dagger \mapsto \CommR$`. +- **Lines 47-48:** Checks that the calculated encoding key `$K_c^\dagger$` for `$c$` encodes the previously verified sector and replica tree leaves `$D_c^\dagger \mapsto D_c$` into `$R_c^\dagger \mapsto R_c$`. +- **Lines 49-51:** Verifies `$c$`'s `$\ColumnProof_c$` against the previously verified `$\CommC$`. ## PoSt Challenges @@ -1323,14 +1332,16 @@ $\line{4}{\bi}{\return \digestint \MOD N_\nodes}$ ``` **Code Comments:** -* **Line 4:** modding by `$N_\nodes$` takes the 64-bit `$\digestint$` to a 32-bit node index `$\NodeIndex$`. + +- **Line 4:** modding by `$N_\nodes$` takes the 64-bit `$\digestint$` to a 32-bit node index `$\NodeIndex$`. ## Vanilla PoSt ### Proving **Implementation:** -* [`storage_proofs::post::fallback::vanilla::FallbackPoSt::prove()`](https://github.com/filecoin-project/rust-fil-proofs/blob/8e8306c942c22571bc784f7536f1704058c45119/storage-proofs/post/src/fallback/vanilla.rs#L249) + +- [`storage_proofs::post::fallback::vanilla::FallbackPoSt::prove()`](https://github.com/filecoin-project/rust-fil-proofs/blob/8e8306c942c22571bc784f7536f1704058c45119/storage-proofs/post/src/fallback/vanilla.rs#L249) **Additional Notation:** @@ -1382,7 +1393,8 @@ $\line{16}{}{\return \PartitionProof_{k \thin \aww}}$ ``` **Code Comments:** -* **Lines 13-15:** If the prover does not have enough replicas to fill an entire PoSt partition proof, pad the partition proof with copies of the last distinct replica's `$\PostReplicaProof_R \thin$`. + +- **Lines 13-15:** If the prover does not have enough replicas to fill an entire PoSt partition proof, pad the partition proof with copies of the last distinct replica's `$\PostReplicaProof_R \thin$`. ### Verification @@ -1437,7 +1449,8 @@ $\line{15}{}{\quad\quad \assert(\octtreeproofisvalid(\TreeRProof^\dagger))}$ ``` **Code Comments:** -* **Line 13:** The dagger is removed from `$\CommR^\dagger$` (producing `$\CommR$`) because `$\CommR^\dagger$` was verified to be consistent with the committed to `$\CommCR$` (Line 8). + +- **Line 13:** The dagger is removed from `$\CommR^\dagger$` (producing `$\CommR$`) because `$\CommR^\dagger$` was verified to be consistent with the committed to `$\CommCR$` (Line 8). ## PoSt Circuit @@ -1527,8 +1540,9 @@ $\line{10}{}{\return \curr_\auxb}$ ``` **Code Comments:** -* **Line 9:** A public input is added to `$\cs$` for the Merkle challenge `$c$` corresponding to the Merkle path which was used to calculate the returned root. -* **Line 10:** The final value for `$\curr_\auxb$` is the Merkle root calculated from `$\leaf_\auxb$` and `$\path$`. + +- **Line 9:** A public input is added to `$\cs$` for the Merkle challenge `$c$` corresponding to the Merkle path which was used to calculate the returned root. +- **Line 10:** The final value for `$\curr_\auxb$` is the Merkle root calculated from `$\leaf_\auxb$` and `$\path$`. ### OctTree Root Gadget @@ -1571,13 +1585,14 @@ $\line{17}{}{\return \curr_\auxb}$ ``` **Code Comments:** -* **Line 1:** Not a reallocation of `$\leaf_\auxb$` within `$\cs$`, but is an in-memory copy. -* **Lines 4-8:** Witnesses the 3-bit missing index for each path element. The first iteration `$i = 0$` corresponds to the least significant bit in `$\missing$`. -* **Lines 9-12:** Witnesses each path element's 7 Merkle hash inputs (the exlucded 8-th Merkle hash input is the calculated hash input `$\curr_\auxb$` for this tree depth). -* **Line 13:** Creates the Merkle hash inputs array by inserting `$\curr$` into `$\siblings$` at index `$\missing$`. -* **Line 14:** Hashes the 8 Merkle hash inputs. -* **Line 16:** Adds the challenge `$c$` as a public input. -* **Line 17:** Returns the calculated root. + +- **Line 1:** Not a reallocation of `$\leaf_\auxb$` within `$\cs$`, but is an in-memory copy. +- **Lines 4-8:** Witnesses the 3-bit missing index for each path element. The first iteration `$i = 0$` corresponds to the least significant bit in `$\missing$`. +- **Lines 9-12:** Witnesses each path element's 7 Merkle hash inputs (the exlucded 8-th Merkle hash input is the calculated hash input `$\curr_\auxb$` for this tree depth). +- **Line 13:** Creates the Merkle hash inputs array by inserting `$\curr$` into `$\siblings$` at index `$\missing$`. +- **Line 14:** Hashes the 8 Merkle hash inputs. +- **Line 16:** Adds the challenge `$c$` as a public input. +- **Line 17:** Returns the calculated root. ### Encoding Gadget @@ -1653,11 +1668,12 @@ $\line{11}{}{\return \label_\auxb}$ ``` **Code Comments:** -* **Line 3:** The constant `$9984 = (2 + N_\parentlabels) * \ell_\block^\bit = (2 + 37) * 256 \thin$`. The constant `$160 = \ell_\block^\bit - \len(\layerindex) - \len(\nodeindex) = 256 - 32 - 64 \thin$`. -* **Lines 4-5:** The constant `$256 = \ell_\block^\bit \thin$`. -* **Lines 5-6:** These are not reallocations. -* **Lines 6-7:** The labeling function is `$\Sha{254}$` not `$\Sha{256}$`. -* **Lines 6,9:** The constant `$254 = \ell_{\Fq, \safe}^\bit \thin$`. + +- **Line 3:** The constant `$9984 = (2 + N_\parentlabels) * \ell_\block^\bit = (2 + 37) * 256 \thin$`. The constant `$160 = \ell_\block^\bit - \len(\layerindex) - \len(\nodeindex) = 256 - 32 - 64 \thin$`. +- **Lines 4-5:** The constant `$256 = \ell_\block^\bit \thin$`. +- **Lines 5-6:** These are not reallocations. +- **Lines 6-7:** The labeling function is `$\Sha{254}$` not `$\Sha{256}$`. +- **Lines 6,9:** The constant `$254 = \ell_{\Fq, \safe}^\bit \thin$`. ### Little-Endian Bits Gadget @@ -1688,7 +1704,8 @@ $\line{9}{\bi}{\return \bits_{[\auxb, \Le]}}$ ``` **Code Comments:** -* **Line 2:** This will pad `$n - \lceil \log_2(\value\dot\int) \rceil$` zero bits onto the most significant end of `$\llcorner \int \lrcorner_{2, \Le} \thin$`. + +- **Line 2:** This will pad `$n - \lceil \log_2(\value\dot\int) \rceil$` zero bits onto the most significant end of `$\llcorner \int \lrcorner_{2, \Le} \thin$`. ### Pack Bits as Input Gadget @@ -1720,9 +1737,9 @@ $\bi (\y - \x) * (\bit) = (\y - \pick)$ This table shows that for `$\bit \in \Bit$` and `$\x, \y \in \Fq$` that the constraint is satisfied for the outputted values of `$\pick$`. | `$\bit$` | `$\pick$` | `$(\y - \x) * (\bit) = (\y - \pick)$` | -|--------|---------|------------------------------------| -| `$1$` | `$\x$` | `$(\y-\x) * (1) = (\y-\x)$` | -| `$0$` | `$\y$` | `$(\y-\x) * (0) = (\y-\y)$` | +| -------- | --------- | ------------------------------------- | +| `$1$` | `$\x$` | `$(\y-\x) * (1) = (\y-\x)$` | +| `$0$` | `$\y$` | `$(\y-\x) * (0) = (\y-\y)$` | ```text $\overline{\Function \pickgadget( \qquad\quad\bi}$ @@ -1786,6 +1803,7 @@ $\pick_{\auxb, i(b_0)}$ $\pick_{\auxb, i(b_0, b_1)}$ $\pick_{\auxb, i(b_0, b_1, b_2)}$ ``` + The pick for the `$i^{th}$` element of the inserted array based upon the value of the first bit (least-significant), first and second bits, and the first, second and third bits respectively. ```text @@ -1796,6 +1814,7 @@ $\and_{(b_0, b_1)} \equiv \and_{\auxb \thin (b_0, b_1)}$ $\arr[i] \equiv \arr_{[\aap]}[i]$ $\arr'[i] \equiv \arr'_{[\auxb]}[i]$ ``` + For ease of notation the subscripts `$_\auxb$` and `$_\aap$` are left off everywhere except in the function signature and when allocating of a value within the circuit. ```text @@ -1863,12 +1882,12 @@ $\bi (\x) * (\y) = (\and)$ This table shows the satisfiablilty of the constraint for all values of `$\x, \y \in \Bit$` and corresponding outputted values of `$\and \in \Bit$`. -| `$\x$` | `$\y$` | `$\and$` | `$(\x) * (\y) = (\and)$` | -|--------|---------|--------|-----------------------| -| `$0$` | `$0$` | `$0$` | `$(0) * (0) = (0)$` | -| `$1$` | `$0$` | `$0$` | `$(1) * (0) = (0)$` | -| `$0$` | `$1$` | `$0$` | `$(0) * (1) = (0)$` | -| `$1$` | `$1$` | `$1$` | `$(1) * (1) = (1)$` | +| `$\x$` | `$\y$` | `$\and$` | `$(\x) * (\y) = (\and)$` | +| ------ | ------ | -------- | ------------------------ | +| `$0$` | `$0$` | `$0$` | `$(0) * (0) = (0)$` | +| `$1$` | `$0$` | `$0$` | `$(1) * (0) = (0)$` | +| `$0$` | `$1$` | `$0$` | `$(0) * (1) = (0)$` | +| `$1$` | `$1$` | `$1$` | `$(1) * (1) = (1)$` | ```text $\overline{\Function \andgadget(\qquad}$ @@ -1896,12 +1915,12 @@ $\bi (1 - \x) * (1 - \y) = (\nor)$ The following table shows the satisfiablilty of the constraint for all values of `$\x, \y \in \Bit$` and corresponding outputted values for `$\nor \in \Bit$`. -| `$\x$` | `$\y$` | `$\nor$` | `$(1 - \x) * (1 - \y) = (\nor)$` | -|--------|---------|--------|-----------------------| -| `$0$` | `$0$` | `$1$` | `$(1) * (1) = (1)$` | -| `$1$` | `$0$` | `$0$` | `$(0) * (1) = (0)$` | -| `$0$` | `$1$` | `$0$` | `$(1) * (0) = (0)$` | -| `$1$` | `$1$` | `$0$` | `$(0) * (0) = (0)$` | +| `$\x$` | `$\y$` | `$\nor$` | `$(1 - \x) * (1 - \y) = (\nor)$` | +| ------ | ------ | -------- | -------------------------------- | +| `$0$` | `$0$` | `$1$` | `$(1) * (1) = (1)$` | +| `$1$` | `$0$` | `$0$` | `$(0) * (1) = (0)$` | +| `$0$` | `$1$` | `$0$` | `$(1) * (0) = (0)$` | +| `$1$` | `$1$` | `$0$` | `$(0) * (0) = (0)$` | ```text $\overline{\Function \norgadget(\qquad}$ diff --git a/content/algorithms/sdr/notation.md b/content/algorithms/sdr/notation.md index 625ad41f8..1ac302dbd 100644 --- a/content/algorithms/sdr/notation.md +++ b/content/algorithms/sdr/notation.md @@ -1,8 +1,8 @@ --- -title: "Notation, Constants, and Types" +title: 'Notation, Constants, and Types' weight: 1 math-mode: true -description: "Notation, Constants, and Types for Stacked DRG PoRep" +description: 'Notation, Constants, and Types for Stacked DRG PoRep' dashboardWeight: 2 dashboardState: stable @@ -13,6 +13,7 @@ dashboardTests: 0 {{< plain hidden >}} + $$ \gdef\createporepbatch{\textsf{create\_porep\_batch}} \gdef\GrothProof{\textsf{Groth16Proof}} @@ -369,6 +370,7 @@ $$ \gdef\encode{\textsf{encode}} \gdef\sector{\textsf{sector}} $$ + {{< /plain >}} @@ -408,6 +410,7 @@ A 64-bit unsigned integer where only the least significant 17 bits are utilized `$\Fq \equiv [q]$`\ An element of the scalar field that arises from BLS12-381's subgroup of prime order `$q$`. Where `$q$`, given in hex and decimal, is: + ```text $\quad q = \text{73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001}_{16}$ $\quad q = 52435875175126190479447740508185965837690552500527637822603658699938581184513_{10}$ @@ -484,7 +487,7 @@ Iterates over each element of `$\textsf{Iterator}$` and ignores each element's v `$\big[ \tern{x = 0}{a}{b \big}]$`\ A ternary expression. Returns `$a$` if `$x = 0$`, otherwise returns `$b$`. -`$\mathbb{T}^{[m]}\dot\extend(\mathbb{T}^{[n]}) \rightarrow \mathbb{T}^{[m + n]}$`\ +`$\mathbb{T}^{[m]}\dot\extend(\mathbb{T}^{[n]}) \rightarrow \mathbb{T}^{[m + n]}$`\ Appends each value of `$\mathbb{T}^{[n]}$` onto the end of the array `$\mathbb{T}^{[m]}$`. Equivalent to writing: ```text @@ -502,7 +505,6 @@ $\line{2}{\bi}{\for i \in [n - m]:}$ $\line{3}{\bi}{\quad x\dot\push(x[i \MOD m])}$ ``` - ## Protocol Constants **Implementation:** [`filecoin_proofs::constants`](https://github.com/filecoin-project/rust-fil-proofs/blob/d088e7b997c13a59e66c062a9ceb110f991c9849/filecoin-proofs/src/constants.rs) @@ -537,7 +539,7 @@ The degree of each expander. The number of expander parents generated for each n `$d_\total = d_\drg + d_\exp = 14$`\ The total number of parents generated for all nodes not in the first Stacked-DRG layer. -`$N_\expedges = d_\exp * N_\nodes = 2^{33} \Edges$`\ +`$N_\expedges = d_\exp * N_\nodes = 2^{33} \Edges$`\ The number of edges per expander graph. `$\ell_\expedge^\bit = \log_2(N_\expedges) = 33 \Bits$`\ @@ -587,8 +589,8 @@ The number of Merkle challenges per challenged replica `$R$` in a PoSt partition `$\GrothVerificationKey_{\langle \textsf{circ} \rangle} = \text{}$`\ The Groth16 keypair used to generate and verify SNARKs for a circuit definition `$\textsf{circ}$` (PoRep, Winning PoSt, Window PoSt each for a given protocol sector size). -`$\DrgStringID: {\Byte_\utf}^{[*]} = ``\text{Filecoin\_DRSample}"$`\ -`$\FeistelStringID: {\Byte_\utf}^{[*]} = ``\text{Filecoin\_Feistel}"$`\ +` $\DrgStringID: {\Byte_\utf}^{[*]} = ``\text{Filecoin\_DRSample}"$ `\ +` $\FeistelStringID: {\Byte_\utf}^{[*]} = ``\text{Filecoin\_Feistel}"$ `\ The ID strings associated with the DRG and Feistel network. `$\DrgSeedPrefix_\PorepID: \Byte^{[28]} = \Sha{256}(\DrgStringID \concat \PorepID)[\ldotdot 28]$`\ @@ -612,6 +614,7 @@ $\quad \ledecode(\FeistelKeysBytes_\PorepID[8 \ldotdot 16]) \thin,$ $\quad \ledecode(\FeistelKeysBytes_\PorepID[16 \ldotdot 24]) \thin,$ $]$ ``` + The Feistel round keys used by every PoRep having version `$\PorepID$`.\ **Implementation:** [`storage_proofs::porep::stacked::vanilla::graph::StackedGraph::new()`](https://github.com/filecoin-project/rust-fil-proofs/blob/b6bf96faff1bf7611982ca1318c20d256fd464d4/storage-proofs/porep/src/stacked/vanilla/graph.rs#L189-#L194) @@ -626,7 +629,6 @@ An array of 32 bytes that utilizes only the first `$\ell_\Fqsafe^\bit = 254$` bi `$\Fqsafe = \Fq \AND 1^{[254]}$`\ A field element that utilizes only its first `$\ell_\Fqsafe^\bit = 254$` bits. `$\Fqsafe$`'s are created from casting a `$\Safe$` into an `$\Fq$` (the casting produces an `$\Fqsafe \thin$`). - `$\leencode(\Fq) \rightarrow \Byte^{[32]}$`\ `$\leencode(\Fqsafe) \rightarrow \Safe$`\ The produced byte array is least significant byte first. @@ -679,9 +681,10 @@ Filecoin utilizes two Merkle tree types, a binary tree type `$\BinTree$` and oct `$\BinTree{\bf \sf s}$` use the `$\Sha{254}_2$` hash function (two inputs per hash function call, tree nodes have type `$\Safe$`). `$\OctTree{\bf \sf s}$` use the hash function `$\Poseidon_8$` (eight inputs per hash function call, tree nodes have type `$\Fq$`). **Implementation:** -* [`merkle_light::merkle::MerkleTree`](https://github.com/filecoin-project/merkle_light/blob/64a468807c594d306d12d943dd90cc5f88d0d6b0/src/merkle.rs#L138) -* [`storage_proofs::core::merkle::MerkleTreeWrapper`](https://github.com/filecoin-project/rust-fil-proofs/blob/f75804c503d9b97a2b02ef3ea2e5d44e8e2c6470/storage-proofs/core/src/merkle/tree.rs#L53) -* [`storage_proofs::core::merkle::MerkleTreeTrait`](https://github.com/filecoin-project/rust-fil-proofs/blob/f75804c503d9b97a2b02ef3ea2e5d44e8e2c6470/storage-proofs/core/src/merkle/tree.rs#L19) + +- [`merkle_light::merkle::MerkleTree`](https://github.com/filecoin-project/merkle_light/blob/64a468807c594d306d12d943dd90cc5f88d0d6b0/src/merkle.rs#L138) +- [`storage_proofs::core::merkle::MerkleTreeWrapper`](https://github.com/filecoin-project/rust-fil-proofs/blob/f75804c503d9b97a2b02ef3ea2e5d44e8e2c6470/storage-proofs/core/src/merkle/tree.rs#L53) +- [`storage_proofs::core::merkle::MerkleTreeTrait`](https://github.com/filecoin-project/rust-fil-proofs/blob/f75804c503d9b97a2b02ef3ea2e5d44e8e2c6470/storage-proofs/core/src/merkle/tree.rs#L19) ```text $\struct \BinTree\ \{$ @@ -693,6 +696,7 @@ $\quad \layer_{\BinTreeDepth - 1}: \Safe^{[2]},$ $\quad \root \sim \layer_\BinTreeDepth: \Safe,$ $\}$ ``` + A binary Merkle tree with hash function arity-2 (each Merkle hash operates on two input values). The hash function for `$\BinTree$`'s is `$\Sha{254}_2$` (`$\Sha{256}$` that operates on two 32-byte inputs where the last two bits of the last byte of their `$\Sha{256}$` digest have been zeroed to produce a `$\Safe$` digest). The fields `$\layer_0, \ldots, \layer_\BinTreeDepth$` are arrays containing each tree layer's node labels. The fields `$\leaves$` and `$\root$` are aliases for the fields `$\layer_0$` and `$\layer_\BinTreeDepth$` respectively. ```text @@ -705,13 +709,15 @@ $\quad \layer_{\OctTreeDepth - 1}: \Fq^{[8]},$ $\quad \root \sim \layer_\OctTreeDepth: \Fq,$ $\}$ ``` + An octal Merkle tree with hash function arity-8 (each Merkle hash operates on 8 input values). The hash function for `$\OctTree$`'s is `$\Poseidon_8$`. The fields `$\layer_0, \ldots, \layer_{10}$` are arrays containing each tree layer's node labels. The fields `$\leaves$` and `$\root$` are aliases for the fields `$\layer_0$` and `$\layer_{10}$` respectively. ### Merkle Proofs **Implementation:** -* `$\BinTreeProof$`, `$\OctTreeProof$`: [`storage_proofs::merkle::SingleProof`](https://github.com/filecoin-project/rust-fil-proofs/blob/f75804c503d9b97a2b02ef3ea2e5d44e8e2c6470/storage-proofs/core/src/merkle/proof.rs#L344) -* `$\BinPathElement$`, `$\OctPathElement$`: [`storage_proofs::merkle::PathElement`](https://github.com/filecoin-project/rust-fil-proofs/blob/f75804c503d9b97a2b02ef3ea2e5d44e8e2c6470/storage-proofs/core/src/merkle/proof.rs#L223) + +- `$\BinTreeProof$`, `$\OctTreeProof$`: [`storage_proofs::merkle::SingleProof`](https://github.com/filecoin-project/rust-fil-proofs/blob/f75804c503d9b97a2b02ef3ea2e5d44e8e2c6470/storage-proofs/core/src/merkle/proof.rs#L344) +- `$\BinPathElement$`, `$\OctPathElement$`: [`storage_proofs::merkle::PathElement`](https://github.com/filecoin-project/rust-fil-proofs/blob/f75804c503d9b97a2b02ef3ea2e5d44e8e2c6470/storage-proofs/core/src/merkle/proof.rs#L223) ```text $\struct \BinTreeProof\ \{$ @@ -720,6 +726,7 @@ $\quad \root: \Safe \thin,$ $\quad \path: \BinPathElement^{[\BinTreeDepth]} \thin,$ $\}$ ``` + A `$\BinTree$` Merkle proof generated for a challenge. The notation `$\BinTreeProof_c$` denotes a proof generated for a challenge `$c$`. The field `$\path$` contains one element for tree layers `$0, \ldots, \BinTreeDepth - 1$` (there is no `$\path$` element for the root layer). The path element `$\BinTreeProof\dot\path[l]$` for tree layers `$l \in 0, \ldots, \BinTreeDepth - 1$` contains one node label in layer `$l$` that the Merkle proof verifier will use to calculate the label for a node in layer `$l + 1 \thin$`. Each path element is for a distinct tree layer. ```text @@ -728,7 +735,8 @@ $\quad \sibling: \Safe \thin,$ $\quad \missing: \Bit \thin,$ $\}$ ``` -A single element in `$\BinTreeProof\dot\path$` associated with a single `$\BinTree$` tree layer `$l$`. Contains the information necessary for a Merkle proof verifier to calculate the label for a node in tree layer `$l + 1$`. The field `$\sibling$` contains the label that the Merkle proof verifier did not calculate for layer `$l$`. The Merkle verifier applies the hash function `$\Sha{254}_2$` to an array of two elements in layer `$l$` to produce the label for a node in layer `$l + 1$`. The order of the elements in each Merkle hash function's 2-element array is given by the field `$\missing$`. If `$\missing = 0$`, then `$\sibling$` is at index `$1$` in the Merkle hash inputs array. Conversely, if `$\missing = 1$` the field `$\sibling$` is at index `$0$` in the 2-element Merkle hash inputs array. + +A single element in `$\BinTreeProof\dot\path$` associated with a single `$\BinTree$` tree layer `$l$`. Contains the information necessary for a Merkle proof verifier to calculate the label for a node in tree layer `$l + 1$`. The field `$\sibling$` contains the label that the Merkle proof verifier did not calculate for layer `$l$`. The Merkle verifier applies the hash function `$\Sha{254}_2$` to an array of two elements in layer `$l$` to produce the label for a node in layer `$l + 1$`. The order of the elements in each Merkle hash function's 2-element array is given by the field `$\missing$`. If `$\missing = 0$`, then `$\sibling$` is at index `$1$` in the Merkle hash inputs array. Conversely, if `$\missing = 1$` the field `$\sibling$` is at index `$0$` in the 2-element Merkle hash inputs array. ```text $\struct \OctTreeProof\ \{$ @@ -737,6 +745,7 @@ $\quad \root: \Fq \thin,$ $\quad \path: \OctPathElement^{[\OctTreeDepth]} \thin,$ $\}$ ``` + A `$\OctTree$` Merkle proof generated for a challenge. The notation `$\OctTreeProof_c$` denotes a proof generated for a challenge `$c$`. The field `$\path$` contains one element for tree layers `$0, \ldots, \OctTreeDepth - 1$` (there is no `$\path$` element for the root layer). The path element `$\OctTreeProof\dot\path[l]$` for tree layers `$l \in 0, \ldots, \OctTreeDepth - 1$` contains one node label in layer `$l$` that the Merkle proof verifier will use to calculate the label for a node in layer `$l + 1 \thin$`. Each path element is for a distinct tree layer. If `$l = 0$` the verifier inserts `$\BinTreeProof\dot\leaf$` into the first path element's Merkle hash inputs array at index `$\BinTreeProof\dot\path[0]\dot\missing \thin$`. ```text @@ -745,7 +754,8 @@ $\quad \siblings: \Fq^{[7]} \thin,$ $\quad \missing: [8] \thin,$ $\}$ ``` -A single element in `$\OctTreeProof\dot\path$` associated with a single `$\OctTree$` tree layer `$l$`. Contains the information necessary for a Merkle proof verifier to calculate the label for a node in tree layer `$l + 1$`. The field `$\sibling$` contains the label that the Merkle proof verifier did not calculate for layer `$l$`. The Merkle verifier applies the hash function `$\Poseidon_8$` to an array of eight elements in layer `$l$` to produce the label for a node in layer `$l + 1$`. The order of the elements in each Merkle hash function's 8-element array is given by the field `$\missing$`. `$\missing$` is an index in an array of 8 elements telling the verifier at which index in the Merkle hash inputs array that the node label calculated by the verifier for this path element's layer is to be inserted into `$\siblings$`. Given an `$\OctPathElement = \OctTreeProof\dot\path[l]$` in tree layer `$l$`, the node label to be inserted into the hash inputs array at index `$\missing$` was calculated using the path element `$\OctTreeProof\dot\path[l]$` (or `$\OctTreeProof\dot\leaf$` if `$l = 0$`). + +A single element in `$\OctTreeProof\dot\path$` associated with a single `$\OctTree$` tree layer `$l$`. Contains the information necessary for a Merkle proof verifier to calculate the label for a node in tree layer `$l + 1$`. The field `$\sibling$` contains the label that the Merkle proof verifier did not calculate for layer `$l$`. The Merkle verifier applies the hash function `$\Poseidon_8$` to an array of eight elements in layer `$l$` to produce the label for a node in layer `$l + 1$`. The order of the elements in each Merkle hash function's 8-element array is given by the field `$\missing$`. `$\missing$` is an index in an array of 8 elements telling the verifier at which index in the Merkle hash inputs array that the node label calculated by the verifier for this path element's layer is to be inserted into `$\siblings$`. Given an `$\OctPathElement = \OctTreeProof\dot\path[l]$` in tree layer `$l$`, the node label to be inserted into the hash inputs array at index `$\missing$` was calculated using the path element `$\OctTreeProof\dot\path[l]$` (or `$\OctTreeProof\dot\leaf$` if `$l = 0$`). ### Graphs @@ -760,6 +770,7 @@ $\quad \leencode(\PorepVersion) \as \Byte^{[8]}$ $\quad \|\ \leencode(\Nonce_\PorepVersion) \as \Byte^{[8]}$ $\quad \|\ 0^{[16]}$ ``` + A unique 32-byte ID assigned to each PoRep version. Each PoRep version is defined by a distinct triple of parameters: (PoRep proof system, sector-size, version number). All PoRep's having the same PoRep version triple have the same `$\PorepID$`. The notation `$0^{[16]}$` denotes 16 zero bytes (not bits).\ **Implementation:** [`filecoin_proofs_api::registry::RegisteredSealProof::porep_id()`](https://github.com/filecoin-project/rust-filecoin-proofs-api/blob/2a01ecc2ff2b9a70fa483d7e36ab79d4f035cf60/src/registry.rs#L110) @@ -804,6 +815,7 @@ $\quad \path: \OctPathElement^{[\OctTreeDepth]},$ $\quad \column: \Column,$ $\}$ ``` + A `$\ColumnProof_c$` is an `$\OctTreeProof_c$` adjoined with an additional field `$\column_c$`, the Merkle challenge `$c$`'s label in each Stacked-DRG layer. A valid `$\ColumnProof$` has `$\ColumnProof\dot\leaf: \ColumnDigest = \Poseidon_{11}(\ColumnProof\dot\column) \thin$`. `$\ParentLabels_{\mathbf{u}_\drg}: \Label^{[d_\drg]} = [\Label_{u_\drg, l_0} \mid \forall u_\drg \in \mathbf{u}_\drg]$`\ @@ -828,6 +840,7 @@ $\quad \TreeRProof_c,$ $\quad \ParentColumnProofs_{\mathbf{u}_\total}: {\ColumnProof_u}^{[d_\total]},$ $\}$ ``` + The proof generated for each Merkle challenge `$c$` in a PoRep partition proof. The field `$\ParentColumnProofs_{\mathbf{u}_\total}$` stores the `$\ColumnProof_u$` for each parent `$u \in \mathbf{u}_\total$` of the challenge `$c$`. `$\PorepPartitionProof_{R, k}: \PorepChallengeProof^{[N_{\porepchallenges / k}]}$`\ @@ -846,6 +859,7 @@ $\quad \CommCR,$ $\quad \SectorID,$ $\}$ ``` + A replica `$R$` that a PoSt prover has access to. All fields are associated with `$R$` at the time of PoSt proof batch generation. `$\PostReplicas_{\P, \batch \thin \aww}: {\PostReplica}^{[*]}$`\ @@ -856,6 +870,7 @@ $\PostReplicas_{\P, k \thin \aww}: {\PostReplica_{\P, \batch}}^{[0 < \ell \leq N $\PostReplicas_{\P, k \thin \aww} =$ $\quad \PostReplicas_{\P, \batch \thin \aww}[k * N_{\postreplicas / k \thin \aww} \ldotdot (k + 1) * N_{\postreplicas / k \thin \aww}]$ ``` + The `$k^{th}$` distinct slice of a PoSt prover `$\P$`'s total replica set `$\PostReplicas_{\P, \batch}$` used to generate prover's partition-`$k$` Winning or Window PoSt proof in a batch. This set contains all replicas that are challenged in PoSt partition `$k$`. `$\PostReplicas_{\P, k}$` does not contain padded replica proofs. The length of a PoSt prover's total replica set may not be divisible by the number of challenged replica's `$N_{\postreplicas / k \thin \aww}$`, thus the length of `$\PostReplicas_{\P, k}$` is in the range `$[1, N_{\postreplicas / k \thin \aww}] \thin$`. `$\PostPartitionProof_{k \thin \aww}: {\PostReplicaProof_{R \thin \aww}}^{[N_{\postreplicas/k \thin \aww}]}$`\ @@ -867,6 +882,7 @@ $\quad \TreeRProofs: \TreeRProof^{[N_{\challenges/R \thin \aww}]} \thin,$ $\quad \CommC,$ $\}$ ``` + The proof for single replica `$R$` challenged in a PoSt partition proof. All fields are associated with `$R$`. ```text @@ -875,6 +891,7 @@ $\quad \SectorID,$ $\quad \CommCR,$ $\}$ ``` + The public information known to a PoSt verifier `$\V$` for each challenged replica `$R$` (distinct up to the PoSt batch) of the PoSt prover. `$\SectorID$` and `$\CommCR$` are associated with the replica `$R$`. `$\PostReplicas_{\V, k \thin \aww}: {\PostReplica_\V}^{[0 < \ell \leq N_{\postreplicas / k \thin \aww}]}$`\ @@ -934,6 +951,7 @@ $\RCS\dot\publicinput(\Fq)$ $\RCS\dot\privateinput(\Fq)$ $\RCS\dot\assert(\LinearCombination * \LinearCombination = \LinearCombination)$ ``` + The `$\RCS$` types has three methods, one for adding primary assignments, one for adding auxiliary assignments, and one for adding constraints. ```text @@ -942,6 +960,7 @@ $\quad \index_{\langle \text{primary|auxiliary} \rangle}: \mathbb{N},$ $\quad \int: \Fq,$ $\}$ ``` + An instance of `$\CircuitVal$` is a reference to a single field element, of value `$\int$`, allocated within a constraint system. The field `$\int$` is a copy of the allocated value, arithmetic on `$\int$` is not circuit arithmetic. The field `$\index$` refers to the index of an allocated integer (a copy of `$\int$`) in either the primary or auxiliary assignments vectors of an `$\RCS$` instance. Every unique wire in a circuit has a unique (up to the instance of `$\RCS$`) `$\CircuitVal\dot\index$` in either the primary or auxiliary assignments vectors. ```text @@ -950,6 +969,7 @@ $\quad \index_{\langle \text{primary|auxiliary} \rangle}: \mathbb{N},$ $\quad \int: \Fq \in \Bit,$ $\}$ ``` + A reference to an allocated bit, a boolean constrained value, within an `$\RCS$` instance. `$\CircuitBitOrConst \equiv \{ \CircuitBit,\thin \Bit \}$`\ @@ -998,9 +1018,9 @@ When linear combinations are instantiated they are not evaluated to a single int Values are chosen for Protocol constants such that the following are true: -* The sector size `$\ell_\sector^\byte$` is divisible by the node size `$\ell_\node^\byte$`. -* The number of nodes always fits an unsigned 32-bit integer `$N_\nodes \leq 2^{32}$`, and thus node indexes are representable using 32-bit unsigned integers. -* Every distinct contiguous 32-byte slice of a sector `$D_i \in D$` represents a valid prime field element `$\Fq$` (every `$D_i$` is a valid `$\Safe$`, this is accomplished via sector preprocessing). +- The sector size `$\ell_\sector^\byte$` is divisible by the node size `$\ell_\node^\byte$`. +- The number of nodes always fits an unsigned 32-bit integer `$N_\nodes \leq 2^{32}$`, and thus node indexes are representable using 32-bit unsigned integers. +- Every distinct contiguous 32-byte slice of a sector `$D_i \in D$` represents a valid prime field element `$\Fq$` (every `$D_i$` is a valid `$\Safe$`, this is accomplished via sector preprocessing). ## Hash Functions @@ -1045,7 +1065,7 @@ A bit array whose least significant bit is at index `$0$` and where each subsequ $\bi \bits_\Le: \Bit^{[n]} = \lebinrep{x}$ ``` -An unsigned integer `$\int$`'s little-endian `$n$`-bit binary representation `$\lebinrep{\int}$`, where `$n = \lceil \log_2(\int) \rceil$`, is defined as: +An unsigned integer `$\int$`'s little-endian `$n$`-bit binary representation `$\lebinrep{\int}$`, where `$n = \lceil \log_2(\int) \rceil$`, is defined as: ```text $\bi \lebinrep{\int}: \Bit^{[n]} = [(\int \gg i) \AND 1 \mid \forall i \in [n]]$ @@ -1057,7 +1077,7 @@ An unsigned integer `$\int$`'s little-endian binary representation can be padded $\bi \lebinrep{\int}: \Bit^{[n']} = \lebinrep{\int} \concat 0^{[n' - n]}$ ``` -An unsigned integer `$\int$`'s little-endian bit string is the reverse of its big-endian bit string: +An unsigned integer `$\int$`'s little-endian bit string is the reverse of its big-endian bit string: ```text $\bi \lebinrep{\int} = \reverse(\bebinrep{\int})$ diff --git a/content/algorithms/verified_clients/_index.md b/content/algorithms/verified_clients/_index.md index ab59f53bd..473ff7dbf 100644 --- a/content/algorithms/verified_clients/_index.md +++ b/content/algorithms/verified_clients/_index.md @@ -9,8 +9,8 @@ dashboardTests: 0 # Verified Clients -As described earlier, verified clients as a construction make the Filecoin Economy more robust and valuable. While a storage miner may choose to forgo deal payments and self-deal to fill their storage and earn block rewards, this is not as valuable to the economy and should not be heavily subsidized. However, in practice, it is impossible to tell useful data apart from encrypted zeros. Introducing verified clients pragmatically solves this problem through social trust and validation. There will be a simple and open verification process to become a verified client; this process should attract clients who will bring real storage demand to the Filecoin Economy. +As described earlier, verified clients as a construction make the Filecoin Economy more robust and valuable. While a storage miner may choose to forgo deal payments and self-deal to fill their storage and earn block rewards, this is not as valuable to the economy and should not be heavily subsidized. However, in practice, it is impossible to tell useful data apart from encrypted zeros. Introducing verified clients pragmatically solves this problem through social trust and validation. There will be a simple and open verification process to become a verified client; this process should attract clients who will bring real storage demand to the Filecoin Economy. -Verifiers should eventually form a decentralized, globally distributed network of entities that confirms the useful storage demand of verified clients. If a verifier evaluates and affirms a client’s demand to have real data stored, that client will be able to add up to a certain amount of data to the network as verified client deals; this limit is called a DataCap allocation. Verified clients can request an increased DataCap once they have used their full allocation and Verifiers should perform some due diligence to ensure that the clients are not maliciously exploiting verification. The verification process will evolve over time to become more efficient, decentralized, and robust. +Verifiers should eventually form a decentralized, globally distributed network of entities that confirms the useful storage demand of verified clients. If a verifier evaluates and affirms a client’s demand to have real data stored, that client will be able to add up to a certain amount of data to the network as verified client deals; this limit is called a DataCap allocation. Verified clients can request an increased DataCap once they have used their full allocation and Verifiers should perform some due diligence to ensure that the clients are not maliciously exploiting verification. The verification process will evolve over time to become more efficient, decentralized, and robust. -Storage demand on the network will shape the storage offering provided by miners. With the ability to deploy data with a greater sector quality multiplier, verified clients play an even more important role in shaping the quality of service, geographic distribution, degree of decentralization, and consensus security of the network. Verifiers and verified clients must be cognizant of the value and responsibility that come with their role. Additionally, it is conceivable for miners to have a business development team to source valuable and useful datasets in the world, growing demand for the storage they provide. Teams would be incentivized to help their clients through the verification process and start storing data on the Filecoin Network, in addition to providing their clients with strong SLAs. +Storage demand on the network will shape the storage offering provided by miners. With the ability to deploy data with a greater sector quality multiplier, verified clients play an even more important role in shaping the quality of service, geographic distribution, degree of decentralization, and consensus security of the network. Verifiers and verified clients must be cognizant of the value and responsibility that come with their role. Additionally, it is conceivable for miners to have a business development team to source valuable and useful datasets in the world, growing demand for the storage they provide. Teams would be incentivized to help their clients through the verification process and start storing data on the Filecoin Network, in addition to providing their clients with strong SLAs. diff --git a/content/appendix/_index.md b/content/appendix/_index.md index d651abd86..84c76955f 100644 --- a/content/appendix/_index.md +++ b/content/appendix/_index.md @@ -6,4 +6,4 @@ dashboardState: wip dashboardAudit: n/a --- -# Appendix \ No newline at end of file +# Appendix diff --git a/content/appendix/address.md b/content/appendix/address.md index 696cf7dfc..6c9650cc0 100644 --- a/content/appendix/address.md +++ b/content/appendix/address.md @@ -1,5 +1,5 @@ --- -title: "Address" +title: 'Address' weight: 2 dashboardWeight: 0.2 dashboardState: reliable @@ -35,8 +35,9 @@ There are 2 ways a filecoin address can be represented. An address appearing on When represented as bytes a filecoin address contains the following: -* A **protocol indicator** byte that identifies the type and version of this address. -* The **payload** used to uniquely identify the actor according to the protocol. +- A **protocol indicator** byte that identifies the type and version of this address. +- The **payload** used to uniquely identify the actor according to the protocol. + ```text |----------|---------| | protocol | payload | @@ -48,10 +49,10 @@ When represented as bytes a filecoin address contains the following: When encoded to a string a filecoin address contains the following: -* A **network prefix** character that identifies the network the address belongs to. -* A **protocol indicator** byte that identifies the type and version of this address. -* A **payload** used to uniquely identify the actor according to the protocol. -* A **checksum** used to validate the address. +- A **network prefix** character that identifies the network the address belongs to. +- A **protocol indicator** byte that identifies the type and version of this address. +- A **payload** used to uniquely identify the actor according to the protocol. +- A **checksum** used to validate the address. ```text |------------|----------|---------|----------| @@ -60,8 +61,6 @@ When encoded to a string a filecoin address contains the following: | 'f' or 't' | 1 byte | n bytes | 4 bytes | ``` - - ### Network Prefix The **network prefix** is prepended to an address when encoding to a string. The network prefix indicates which network an address belongs to. The network prefix may either be `f` for filecoin mainnet or `t` for filecoin testnet. It is worth noting that a network prefix will never appear on chain and is only used when encoding an address to a human readable format. @@ -189,7 +188,6 @@ The payload represents the data specified by the protocol. All payloads except t Filecoin checksums are calculated over the address protocol and payload using blake2b-4. Checksums are base32 encoded and only added to an address when encoding to a string. Addresses following the ID Protocol do not have a checksum. - ### Expected Methods All implementations in Filecoin must have methods for creating, encoding, and decoding addresses in addition to checksum creation and validation. The follwing is a golang version of the Address Interface: @@ -204,6 +202,7 @@ type Address interface { ValidateChecksum(a Address) bool } ``` + #### New() `New()` returns an Address for the specified protocol encapsulating corresponding payload. New fails for unknown protocols. @@ -251,9 +250,10 @@ func Encode(network string, a Address) string { #### Decode() Software decoding a Filecoin address must: -* verify the network is a known network. -* verify the protocol is a number of a known protocol. -* verify the checksum is valid + +- verify the network is a known network. +- verify the protocol is a number of a known protocol. +- verify the checksum is valid Decode an Address from a string by removing the network prefix, validating the address is of a known protocol, decoding the payload and checksum, and validating the checksum. @@ -352,7 +352,6 @@ f018446744073709551615 00ffffffffffffffffff01 ``` - ### Secp256k1 Type Addresses ```text diff --git a/content/appendix/audit_reports.md b/content/appendix/audit_reports.md index 56f8860d1..e69f0db5a 100644 --- a/content/appendix/audit_reports.md +++ b/content/appendix/audit_reports.md @@ -18,6 +18,7 @@ Security is a critical component in ensuring Filecoin can fulfill its mission to - Audit conducted by: **Sigma Prime** The scope of this audit covered: + - The Lotus Daemon: Core component responsible for handling the Blockchain node logic by handling peer- to-peer networking, chain syncing, block validation, data retrieval and transfer, etc. - The Lotus Storage Miner: Mining component used to manage a single storage miner by contributing to the network through Sector commitments and Proofs-of-Spacetime data proving it is storing the sectors it has committed to. This component communicates with the Lotus daemon via JSON-RPC API calls. @@ -28,7 +29,7 @@ The scope of this audit covered: - Report: [**Filecoin Actors Audit**](https://diligence.consensys.net/audits/2020/09/filecoin-actors/) - Audit conducted by: **Consensys Diligence** -This audit covers the implementation of Filecoin's builtin Actors, focusing on the role of Actors as a core component in the business logic of the Filecoin storage network. The audit process involved a manual review of the Actors code and conducting ongoing reviews of changes to the code during the course of the engagement. Issues uncovered through this process are all tracked in the GitHub repository. All Priority 1 issues have been resolved. Most Priority 2 issues have been resolved - ones that are still open have been determined to not be a risk for the Filecoin network or miner experience. Further details on these and all other issues raised available in the report. +This audit covers the implementation of Filecoin's builtin Actors, focusing on the role of Actors as a core component in the business logic of the Filecoin storage network. The audit process involved a manual review of the Actors code and conducting ongoing reviews of changes to the code during the course of the engagement. Issues uncovered through this process are all tracked in the GitHub repository. All Priority 1 issues have been resolved. Most Priority 2 issues have been resolved - ones that are still open have been determined to not be a risk for the Filecoin network or miner experience. Further details on these and all other issues raised available in the report. ## Proofs diff --git a/content/appendix/data_structures.md b/content/appendix/data_structures.md index 3330b3aa5..adf271bc8 100644 --- a/content/appendix/data_structures.md +++ b/content/appendix/data_structures.md @@ -69,4 +69,4 @@ See the draft [IPLD hash map spec](https://github.com/ipld/specs/blob/master/dat ## Other Considerations -- The maximum size of an Object should be 1MB (2^20 bytes). Objects larger than this are invalid. \ No newline at end of file +- The maximum size of an Object should be 1MB (2^20 bytes). Objects larger than this are invalid. diff --git a/content/appendix/network_params.md b/content/appendix/network_params.md index ee18c9ae6..fc95ab662 100644 --- a/content/appendix/network_params.md +++ b/content/appendix/network_params.md @@ -16,11 +16,10 @@ Most are generated/finalized using the [orient framework](https://github.com/fil ⚠️ **WARNING:** Filecoin is not yet launched, and we are finishing protocol spec and implementations. Parameters are set here as placeholders and highly likely to change to fit product and security requirements. {{}} - ## Orient parameters | LAMBDA | SPACEGAP | BLOCK-SIZE-KIB | SECTOR-SIZE-GIB | -|--------|----------|----------------|-----------------| +| ------ | -------- | -------------- | --------------- | | 10 | 0.03 | 2.6084006 | 1024 | | 10 | 0.03 | 2.9687543 | 1024 | | 10 | 0.03 | 4.60544 | 256 | diff --git a/content/glossary/_index.md b/content/glossary/_index.md index 3f9fc4211..073e9c90f 100644 --- a/content/glossary/_index.md +++ b/content/glossary/_index.md @@ -1,12 +1,12 @@ --- -title: "Glossary" +title: 'Glossary' weight: 6 dashboardWeight: 0.2 dashboardState: reliable dashboardAudit: n/a --- -# Glossary +# Glossary ## Account Actor @@ -138,7 +138,6 @@ The term _Filecoin_ is used generically to refer to the Filecoin project, protoc The term `fr32` is derived from the name of a struct that Filecoin uses to represent the elements of the arithmetic field of a pairing-friendly curve, specifically Bls12-381—which justifies use of 32 bytes. `F` stands for "Field", while `r` is simply a mathematic letter-as-variable substitution used to denote the modulus of this particular field. - ## Gas, Gas Fees _Gas_ is a property of a [message](glossary#message), corresponding to the resources involved in including that message in a given [block](glossary#block). For each message included in a block, the block's creator (i.e., miner) charges a fee to the message's sender. @@ -182,7 +181,7 @@ A miner is an actor in the Filecoin system performing a service in the network f There are three types of miners in Filecoin: - [Storage miners](glossary#storage-miner-actor), who store files on behalf of clients. -- [Retrieval miners](glossary#retrieval-miner), who deliver stored files to clients. +- [Retrieval miners](glossary#retrieval-miner), who deliver stored files to clients. - Repair miners, who replicate files to keep them available in the network, when a storage miner presents a fault. ## Multisig Actor @@ -203,7 +202,7 @@ A [_payment channel_](filecoin_token#payment_channels) is set up between actors ## Piece -The [Piece](piece) is the main unit of account and negotiation for the data that a user wants to store on Filecoin. In the Lotus implementation, a piece is a [CAR file](https://github.com/ipld/specs/blob/master/block-layer/content-addressable-archives.md#summary) produced by an IPLD DAG with its own *payload CID* and *piece CID*. However, a piece can be produced in different ways as long as the outcome matches the _piece CID_. A piece is not a unit of storage and therefore, it can be of any size up to the size of a [sector](glossary#sector). If a piece is larger than a sector (currently set to 32GB or 64GB and chosen by the miner), then it has to be split in two (or more) pieces. For more details on the exact sizing of a Filecoin Piece as well as how it can be produced, see the [Piece section](piece). +The [Piece](piece) is the main unit of account and negotiation for the data that a user wants to store on Filecoin. In the Lotus implementation, a piece is a [CAR file](https://github.com/ipld/specs/blob/master/block-layer/content-addressable-archives.md#summary) produced by an IPLD DAG with its own _payload CID_ and _piece CID_. However, a piece can be produced in different ways as long as the outcome matches the _piece CID_. A piece is not a unit of storage and therefore, it can be of any size up to the size of a [sector](glossary#sector). If a piece is larger than a sector (currently set to 32GB or 64GB and chosen by the miner), then it has to be split in two (or more) pieces. For more details on the exact sizing of a Filecoin Piece as well as how it can be produced, see the [Piece section](piece). ## Pledged Storage @@ -215,7 +214,7 @@ See [Power Fraction](glossary#power-fraction). ## Power Fraction -A storage miner's `Power Fraction` or `Power` is the ratio of their committed storage, as of their last PoSt submission, over Filecoin's total committed storage as of the current block. It is used in [Leader Election](glossary#leader-election). It is the proportion of power that a storage miner has in the system as a fraction of the overall power of all miners. +A storage miner's `Power Fraction` or `Power` is the ratio of their committed storage, as of their last PoSt submission, over Filecoin's total committed storage as of the current block. It is used in [Leader Election](glossary#leader-election). It is the proportion of power that a storage miner has in the system as a fraction of the overall power of all miners. ## Power Table @@ -259,7 +258,7 @@ This measurement is the size of a sector in bytes. ## Retrieval miner -A [_retrieval miner_](retrieval_market#retrieval_provider) is a Filecoin participant that enters in retrieval [deals](glossary#deal) with clients, agreeing to supply a client with a particular file in exchange for [FIL](glossary#fil). Note that unlike [storage miners](glossary#storage-miner-actor), retrieval miners are not additionally rewarded with the ability to add blocks to (i.e., extend) the Filecoin blockchain; their only reward is the fee they extract from the client. +A [_retrieval miner_](retrieval_market#retrieval_provider) is a Filecoin participant that enters in retrieval [deals](glossary#deal) with clients, agreeing to supply a client with a particular file in exchange for [FIL](glossary#fil). Note that unlike [storage miners](glossary#storage-miner-actor), retrieval miners are not additionally rewarded with the ability to add blocks to (i.e., extend) the Filecoin blockchain; their only reward is the fee they extract from the client. ## Repair @@ -319,12 +318,12 @@ The [_Storage Power Actor_](sysactors) is responsible for keeping track of the s ## Storage Fault Slashing -Storage Fault Slashing is a term that is used to encompass a broader set of penalties, including (but not limited to) Fault Fees, Sector Penalties, and Termination Fees. These penalties are to be paid by miners if they fail to provide sector reliability or decide to voluntarily exit the network. +Storage Fault Slashing is a term that is used to encompass a broader set of penalties, including (but not limited to) Fault Fees, Sector Penalties, and Termination Fees. These penalties are to be paid by miners if they fail to provide sector reliability or decide to voluntarily exit the network. -* **Fault Fee (FF):** A penalty that a miner incurs for each day a miner's sector is offline. -* **Sector Penalty (SP):** A penalty that a miner incurs for a faulted sector that was not declared faulted before a WindowPoSt check occurs. - * The sector will pay FF after incurring an SP when the fault is detected. -* **Termination Penalty (TP):** A penalty that a miner incurs when a sector is voluntarily or involuntarily terminated and is removed from the network. +- **Fault Fee (FF):** A penalty that a miner incurs for each day a miner's sector is offline. +- **Sector Penalty (SP):** A penalty that a miner incurs for a faulted sector that was not declared faulted before a WindowPoSt check occurs. + - The sector will pay FF after incurring an SP when the fault is detected. +- **Termination Penalty (TP):** A penalty that a miner incurs when a sector is voluntarily or involuntarily terminated and is removed from the network. ## Ticket or VRF Chain diff --git a/content/intro/_index.md b/content/intro/_index.md index 66b02e539..f80773127 100644 --- a/content/intro/_index.md +++ b/content/intro/_index.md @@ -10,7 +10,7 @@ dashboardAudit: n/a # Introduction Filecoin is a distributed storage network based on a blockchain mechanism. -Filecoin *miners* can elect to provide storage capacity for the network, and thereby +Filecoin _miners_ can elect to provide storage capacity for the network, and thereby earn units of the Filecoin cryptocurrency (FIL) by periodically producing cryptographic proofs that certify that they are providing the capacity specified. In addition, Filecoin enables parties to exchange FIL currency @@ -23,19 +23,18 @@ The Filecoin blockchain not only maintains the ledger for FIL transactions and accounts, but also implements the Filecoin VM, a replicated state machine which executes a variety of cryptographic contracts and market mechanisms among participants on the network. -These contracts include *storage deals*, in which clients pay FIL currency to miners +These contracts include _storage deals_, in which clients pay FIL currency to miners in exchange for storing the specific file data that the clients request. Via the distributed implementation of the Filecoin VM, storage deals and other contract mechanisms recorded on the chain continue to be processed over time, without requiring further interaction from the original parties (such as the clients who requested the data storage). - ## Spec Status -Each section of the spec must be stable and audited before it is considered done. The state of each section is tracked below. +Each section of the spec must be stable and audited before it is considered done. The state of each section is tracked below. -- The **State** column indicates the stability as defined in the legend. +- The **State** column indicates the stability as defined in the legend. - The **Theory Audit** column shows the date of the last theory audit with a link to the report. ### Spec Status Legend @@ -81,7 +80,6 @@ This progress bar shows what percentage of the spec sections are considered stab {{}} - ### Implementations Status Known implementations of the filecoin spec are tracked below, with their current CI build status, their test coverage as reported by [codecov.io](https://codecov.io), and a link to their last security audit report where one exists. diff --git a/content/intro/arch.md b/content/intro/arch.md index 273f270ac..ab9c93d0d 100644 --- a/content/intro/arch.md +++ b/content/intro/arch.md @@ -1,5 +1,5 @@ --- -title: "Architecture Diagrams" +title: 'Architecture Diagrams' audit: 1 dashboardWeight: 0.2 dashboardState: reliable diff --git a/content/intro/concepts.md b/content/intro/concepts.md index 0f8fe0a21..ce2ad6fca 100644 --- a/content/intro/concepts.md +++ b/content/intro/concepts.md @@ -1,5 +1,5 @@ --- -title: "Key Concepts" +title: 'Key Concepts' audit: 2 dashboardWeight: 0.2 dashboardState: reliable diff --git a/content/intro/filecoin_vm.md b/content/intro/filecoin_vm.md index 73e097db2..5b1febdf2 100644 --- a/content/intro/filecoin_vm.md +++ b/content/intro/filecoin_vm.md @@ -1,5 +1,5 @@ --- -title: "Filecoin VM" +title: 'Filecoin VM' weight: 3 dashboardWeight: 0.2 dashboardState: reliable @@ -14,8 +14,8 @@ The `global state` here consists of a set of `actors`, each with their own priva An `actor` is the Filecoin equivalent of Ethereum's smart contracts, it is essentially an 'object' in the filecoin network with state and a set of methods that can be used to interact with it. Every actor has a Filecoin balance attributed to it, a `state` pointer, a `code` CID which tells the system what type of actor it is, and a `nonce` which tracks the number of messages sent by this actor. -There are two routes to calling a method on an `actor`. First, to call a method as an external participant of the system (aka, a normal user with Filecoin) you must send a signed `message` to the network, and pay a fee to the miner that includes your `message`. The signature on the message must match the key associated with an account with sufficient Filecoin to pay for the message's execution. The fee here is equivalent to transaction fees in Bitcoin and Ethereum, where it is proportional to the work that is done to process the message (Bitcoin prices messages per byte, Ethereum uses the concept of 'gas'. We also use 'gas'). +There are two routes to calling a method on an `actor`. First, to call a method as an external participant of the system (aka, a normal user with Filecoin) you must send a signed `message` to the network, and pay a fee to the miner that includes your `message`. The signature on the message must match the key associated with an account with sufficient Filecoin to pay for the message's execution. The fee here is equivalent to transaction fees in Bitcoin and Ethereum, where it is proportional to the work that is done to process the message (Bitcoin prices messages per byte, Ethereum uses the concept of 'gas'. We also use 'gas'). -Second, an `actor` may call a method on another actor during the invocation of one of its methods. However, the only time this may happen is as a result of some actor being invoked by an external users message (note: an actor called by a user may call another actor that then calls another actor, as many layers deep as the execution can afford to run for). +Second, an `actor` may call a method on another actor during the invocation of one of its methods. However, the only time this may happen is as a result of some actor being invoked by an external users message (note: an actor called by a user may call another actor that then calls another actor, as many layers deep as the execution can afford to run for). For full implementation details, see the [VM Subsystem](systems/filecoin_vm). diff --git a/content/intro/systems/_index.md b/content/intro/systems/_index.md index 2ec376660..fd1e9ec7d 100644 --- a/content/intro/systems/_index.md +++ b/content/intro/systems/_index.md @@ -7,4 +7,4 @@ dashboardState: reliable dashboardAudit: n/a --- -# System Decomposition \ No newline at end of file +# System Decomposition diff --git a/content/intro/systems/why_systems.md b/content/intro/systems/why_systems.md index 65b11444f..d6f3cab6f 100644 --- a/content/intro/systems/why_systems.md +++ b/content/intro/systems/why_systems.md @@ -32,7 +32,6 @@ This decoupling is useful for: - **Scalability:** systems, and various use cases, may drive different performance requirements for different opertators. System decoupling makes it easier for operators to scale their deployments along system boundaries. - ## Filecoin Nodes don't need all the systems Filecoin Nodes vary significantly, and do not need all the systems. diff --git a/content/libraries/_index.md b/content/libraries/_index.md index 58aae6ac3..ce1989bbd 100644 --- a/content/libraries/_index.md +++ b/content/libraries/_index.md @@ -1,10 +1,10 @@ --- title: Libraries -weight: 3 +weight: 3 dashboardWeight: 1.5 dashboardState: reliable dashboardAudit: n/a dashboardTests: 0 --- -# Libraries \ No newline at end of file +# Libraries diff --git a/content/libraries/drand/_index.md b/content/libraries/drand/_index.md index a81746c52..37b3ca8ac 100644 --- a/content/libraries/drand/_index.md +++ b/content/libraries/drand/_index.md @@ -36,9 +36,9 @@ By polling the appropriate endpoint (see below for specifics on the drand networ Specifically, we have: -- `Signature` -- the threshold BLS signature on the previous signature value `Previous` and the current round number `round`. -- `PreviousSignature` -- the threshold BLS signature from the previous drand round. -- `Round` -- the index of Randomness in the sequence of all random values produced by this drand network. +- `Signature` -- the threshold BLS signature on the previous signature value `Previous` and the current round number `round`. +- `PreviousSignature` -- the threshold BLS signature from the previous drand round. +- `Round` -- the index of Randomness in the sequence of all random values produced by this drand network. Specifically, the message signed is the concatenation of the round number treated as a uint64 and the previous signature. At the moment, drand uses BLS signatures on the BLS12-381 curve with the latest v7 RFC of hash-to-curve and the signature is made over G1 (for more see the [drand spec](https://github.com/drand/drand/blob/master/docs/SPECS.md#cryptographic-specification)). @@ -48,7 +48,7 @@ Filecoin nodes fetch the drand entry from the distribution network of the selected drand network. drand distributes randomness via multiple distribution channels (HTTP servers, -S3 buckets, gossiping...). Simply put, the drand nodes themselves will not be +S3 buckets, gossiping...). Simply put, the drand nodes themselves will not be directly accessible by consumers, rather, highly-available relays will be set up to serve drand values over these distribution channels. See below section for more on the drand network configuration. @@ -56,10 +56,11 @@ more on the drand network configuration. On initialization, Filecoin initializes a [drand client](https://github.com/drand/drand/tree/master/client) with chain `info` that contains the following information: -- `Period` -- the period of time between each drand randomness generation -- `GenesisTime` -- at which the first round in the drand randomness chain is created -- `PublicKey` -- the public key to verify randomness -- `GenesisSeed` -- the seed that has been used for creating the first randomness + +- `Period` -- the period of time between each drand randomness generation +- `GenesisTime` -- at which the first round in the drand randomness chain is created +- `PublicKey` -- the public key to verify randomness +- `GenesisSeed` -- the seed that has been used for creating the first randomness Note that it is possible to simply store the hash of this chain info and to retrieve the contents from the drand distribution network as well on the `/info` @@ -97,11 +98,11 @@ There is a deterministic mapping between a needed drand round number and a Filec After initializing access to a drand beacon, a Filecoin node should have access to the following values: -- `filEpochDuration` -- the Filecoin network's epoch duration (between any two leader elections) -- `filGenesisTime` -- the Filecoin genesis timestamp -- `filEpoch` -- the current Filecoin epoch -- `drandGenesisTime` -- drand's genesis timestamp -- `drandPeriod` -- drand's epoch duration (between any two randomness creations) +- `filEpochDuration` -- the Filecoin network's epoch duration (between any two leader elections) +- `filGenesisTime` -- the Filecoin genesis timestamp +- `filEpoch` -- the current Filecoin epoch +- `drandGenesisTime` -- drand's genesis timestamp +- `drandPeriod` -- drand's epoch duration (between any two randomness creations) Using the above, a Filecoin node can determine the appropriate drand round value to be used for use in [secret leader election](expected_consensus#secret-leader-election) in an epoch using both networks' reliance on real time as follows: diff --git a/content/libraries/ipfs/_index.md b/content/libraries/ipfs/_index.md index e4f47c1ec..d8b51029f 100644 --- a/content/libraries/ipfs/_index.md +++ b/content/libraries/ipfs/_index.md @@ -14,11 +14,10 @@ Filecoin is built on the same underlying stack as IPFS - including connecting no ## Bitswap -[Bitswap](https://github.com/ipfs/go-bitswap) is a simple peer-to-peer data exchange protocol, used primarily in IPFS, which can also be used independently of the rest of the pieces that make up IPFS. In Filecoin, `Bitswap` is used to request and receive blocks when a node is synchonized ("caught up") but `GossipSub` has failed to deliver some blocks to a node. +[Bitswap](https://github.com/ipfs/go-bitswap) is a simple peer-to-peer data exchange protocol, used primarily in IPFS, which can also be used independently of the rest of the pieces that make up IPFS. In Filecoin, `Bitswap` is used to request and receive blocks when a node is synchonized ("caught up") but `GossipSub` has failed to deliver some blocks to a node. Please refer to the [Bitswap specification](https://github.com/ipfs/specs/blob/master/BITSWAP.md) for more information. - ## UnixFS [UnixFS](https://github.com/ipfs/go-unixfs) is a protocol buffers-based format for describing files, directories, and symlinks in IPFS. `UnixFS` is used in Filecoin as a file formatting guideline for files submitted to the Filecoin network. diff --git a/content/math-mode.md b/content/math-mode.md index e7e9541aa..c646943b6 100644 --- a/content/math-mode.md +++ b/content/math-mode.md @@ -5,7 +5,9 @@ math-mode: true --- + {{}} + # Math mode @@ -369,8 +373,9 @@ $$ ### Merkle Proofs **Implementation:** -* [`storage_proofs::merkle::MerkleTreeWrapper::gen_proof()`]() -* [`merkle_light::merkle::MerkleTree::gen_proof()`](https://github.com/filecoin-project/merkle_light/blob/64a468807c594d306d12d943dd90cc5f88d0d6b0/src/merkle.rs#L918) + +- [`storage_proofs::merkle::MerkleTreeWrapper::gen_proof()`]() +- [`merkle_light::merkle::MerkleTree::gen_proof()`](https://github.com/filecoin-project/merkle_light/blob/64a468807c594d306d12d943dd90cc5f88d0d6b0/src/merkle.rs#L918) **Additional Notation:** `$\index_l: [\lfloor N_\nodes / 2^l \rfloor] \equiv [\len(\BinTree\dot\layer_l)]$` @@ -398,9 +403,9 @@ $\line{8}{\bi}{\quad \path\dot\push(\BinPathElement \thin \{\ \sibling, \thin \m $\line{9}{\bi}{\return \BinTreeProof_c \thin \{\ \leaf, \thin \root, \thin \path\ \}}$ ``` - **Code Comments:** -* **Line 5:** Calculates the node index in layer `$l$` of the node that the verifier calculated using the previous lath element (or the `$\BinTreeProof_c\dot\leaf$ if $l = 0$`). Note that `$c \gg l \equiv \lfloor c / 2^l \rfloor \thin$`. + +- **Line 5:** Calculates the node index in layer `$l$` of the node that the verifier calculated using the previous lath element (or the `$\BinTreeProof_c\dot\leaf$ if $l = 0$`). Note that `$c \gg l \equiv \lfloor c / 2^l \rfloor \thin$`. ### OctTreeProofs @@ -431,4 +436,4 @@ $\quad\quad\quad \|\ \OctTree\dot\layer_l[\index_l + 1 \mathbin{\ldotdot} \texts $\line{10}{}{\quad \path\dot\push(\OctPathElement \thin \{\ \siblings, \thin \missing\ \} \thin )}$ $\line{11}{}{\return \OctTreeProof_c \thin \{\ \leaf, \thin \root, \thin \path\ \}}$ -``` \ No newline at end of file +``` diff --git a/content/systems/_index.md b/content/systems/_index.md index bf96bdd09..c95f7dccc 100644 --- a/content/systems/_index.md +++ b/content/systems/_index.md @@ -16,7 +16,7 @@ In this section we are detailing all the system components one by one in increas - Filecoin Nodes: the different types of nodes that participate in the Filecoin Network, as well as important parts and processes that these nodes run, such as the key store and IPLD store, as well as the network interface to libp2p. - Files & Data: the data units of Filecoin, such as the Sectors and the Pieces. - Virtual Machine: the subcomponents of the Filecoin VM, such as the actors, i.e., the smart contracts that run on the Filecoin Blockchain, and the State Tree. -- Blockchain: the main building blocks of the Filecoin blockchain, such as the structure of the Transaction and Block messages, the message pool, as well as how nodes synchronise the blockchain when they first join the network. +- Blockchain: the main building blocks of the Filecoin blockchain, such as the structure of the Transaction and Block messages, the message pool, as well as how nodes synchronise the blockchain when they first join the network. - Token: the components needed for a wallet. - Storage Mining: the details of storage mining, storage power consensus, and how storage miners prove storage (without going into details of proofs, which are discussed later). - Markets: the storage and retrieval markets, which are primarily processes that take place off-chain, but are very important for the smooth operation of the decentralised storage market. diff --git a/content/systems/filecoin_blockchain/_index.md b/content/systems/filecoin_blockchain/_index.md index a6a8b7ead..5b91cc8de 100644 --- a/content/systems/filecoin_blockchain/_index.md +++ b/content/systems/filecoin_blockchain/_index.md @@ -25,5 +25,5 @@ The blockchain system also includes: - A [Chain Manager](chain_manager), which maintains a given chain's state, providing facilities to other blockchain subsystems which will query state about the latest chain in order to run, and ensuring incoming blocks are semantically validated before inclusion into the chain. - A [Block Producer](block_producer) which is called in the event of a successful leader election in order to produce a new block that will extend the current heaviest chain before forwarding it to the syncer for propagation. -At a high-level, the Filecoin blockchain grows through successive rounds of leader election in which a number of miners are elected to generate a block, whose inclusion in the chain will earn them block rewards. +At a high-level, the Filecoin blockchain grows through successive rounds of leader election in which a number of miners are elected to generate a block, whose inclusion in the chain will earn them block rewards. Filecoin's blockchain runs on storage power. That is, its consensus algorithm by which miners agree on which subchain to mine is predicated on the amount of storage backing that subchain. At a high-level, the [Storage Power Consensus](storage_power_consensus) subsystem maintains a _Power Table_ that tracks the amount of storage that [storage miner actors](storage_mining) have contributed to the network through _Sector commitments_ and _Proofs of Spacetime_. diff --git a/content/systems/filecoin_blockchain/bootstrap/index.md b/content/systems/filecoin_blockchain/bootstrap/index.md index 068b2ff9b..39c6c3c91 100644 --- a/content/systems/filecoin_blockchain/bootstrap/index.md +++ b/content/systems/filecoin_blockchain/bootstrap/index.md @@ -6,12 +6,11 @@ draft: true --- # Genesis - starting the Blockchain + {{< hint warning >}} **WARNING:** This section is not yet complete. {{< /hint >}} -Network Bootstrapping starts with production of the genesis block. +Network Bootstrapping starts with production of the genesis block. -The genesis block will contain: - - An initial participant set, including committed storage from these miners (pre-sealed using special-cased randomness) - - An initial randomness ticket \ No newline at end of file +The genesis block will contain: - An initial participant set, including committed storage from these miners (pre-sealed using special-cased randomness) - An initial randomness ticket diff --git a/content/systems/filecoin_blockchain/chainsync/_index.md b/content/systems/filecoin_blockchain/chainsync/_index.md index 61c1322cb..1bed9f108 100644 --- a/content/systems/filecoin_blockchain/chainsync/_index.md +++ b/content/systems/filecoin_blockchain/chainsync/_index.md @@ -14,7 +14,7 @@ It handles retrieval and propagation of blocks and transactions (messages), and thus is in charge of distributed state replication. As such, this process is security critical -- problems with state replication can have severe consequences to the operation of a blockchain. -When a node first joins the network it discovers peers (through the peer discovery discussed above) and joins the `/fil/blocks` and `/fil/msgs` GossipSub topics. It listens to new blocks being propagated by other nodes. It picks one block as the `BestTargetHead` and starts syncing the blockchain up to this height from the `TrustedCheckpoint`, which by default is the `GenesisBlock` or `GenesisCheckpoint`. In order to pick the `BestTargetHead` the peer is comparing a combination of height and weight - the higher these values the higher the chances of the block being on the main chain. If there are two blocks on the same height, the peer should choose the one with the higher weight. Once the peer chooses the `BestTargetHead` it uses the BlockSync protocol to fetch the blocks and get to the current height. From that point on it is in `CHAIN_FOLLOW` mode, where it uses GossipSub to receive new blocks, or Bitswap if it hears about a block that it has not received through GossipSub. +When a node first joins the network it discovers peers (through the peer discovery discussed above) and joins the `/fil/blocks` and `/fil/msgs` GossipSub topics. It listens to new blocks being propagated by other nodes. It picks one block as the `BestTargetHead` and starts syncing the blockchain up to this height from the `TrustedCheckpoint`, which by default is the `GenesisBlock` or `GenesisCheckpoint`. In order to pick the `BestTargetHead` the peer is comparing a combination of height and weight - the higher these values the higher the chances of the block being on the main chain. If there are two blocks on the same height, the peer should choose the one with the higher weight. Once the peer chooses the `BestTargetHead` it uses the BlockSync protocol to fetch the blocks and get to the current height. From that point on it is in `CHAIN_FOLLOW` mode, where it uses GossipSub to receive new blocks, or Bitswap if it hears about a block that it has not received through GossipSub. ## ChainSync Overview @@ -24,18 +24,20 @@ but is general enough that it can serve other blockchains. `ChainSync` is a group of smaller protocols, which handle different parts of the sync process. Chain synchronisation is generally needed in the following cases: + 1. when a node first joins the network and needs to get to the current state before validating or extending the chain. 2. when a node has fell out of sync, e.g., due to a brief disconnection. 3. during normal operation in order to keep up with the latest messages and blocks. There are three main protocols used to achieve synchronisation for these three cases. + - `GossipSub` is the libp2p pubsub protocol used to propagate messages and blocks. It is mainly used in the third process above when a node needs to stay in sync with new blocks being produced and propagated. - `BlockSync` is used to synchronise specific parts of the chain, that is from and to a specific height. - `hello` protocol, which is used when two peers first "meet" (i.e., first time they connect to each other). According to the protocol, they exchange their chain heads. In addition, `Bitswap` is used to request and receive blocks, when a node is synchonized ("caught up"), but GossipSub has failed to deliver some blocks to a node. Finally, `GraphSync` can be used to fetch parts of the blockchain as a more efficient version of `Bitswap`. -Filecoin nodes are libp2p nodes, and therefore may run a variety of other protocols. As with anything else in Filecoin, nodes MAY opt to use additional protocols to achieve the results. That said, nodes MUST implement the version of `ChainSync` as described in this spec in order to be considered implementations of Filecoin. +Filecoin nodes are libp2p nodes, and therefore may run a variety of other protocols. As with anything else in Filecoin, nodes MAY opt to use additional protocols to achieve the results. That said, nodes MUST implement the version of `ChainSync` as described in this spec in order to be considered implementations of Filecoin. ## Terms and Concepts @@ -62,7 +64,7 @@ At a high level, `ChainSync` does the following: - Step 1. Start with a `TrustedCheckpoint` (defaults to `GenesisCheckpoint`). The `TrustedCheckpoint` SHOULD NOT be verified in software, it SHOULD be verified by operators. - Step 2. Get the block it points to, and that block's parents - Step 3. Fetch the `StateTree` -- **Part 4: Catch up to the chain (`CHAIN_CATCHUP`)** +- **Part 4: Catch up to the chain (`CHAIN_CATCHUP`)** - Step 1. Maintain a set of `TargetHeads` (`BlockCIDs`), and select the `BestTargetHead` from it - Step 2. Synchronize to the latest heads observed, validating blocks towards them (requesting intermediate points) - Step 3. As validation progresses, `TargetHeads` and `BestTargetHead` will likely change, as new blocks at the production fringe will arrive, @@ -73,7 +75,6 @@ At a high level, `ChainSync` does the following: - Step 2. Receive, validate, and propagate received `Blocks` - Step 3. Now with greater certainty of having the best chain, finalize Tipsets, and advance chain state. - `ChainSync` uses the following _conceptual_ state machine. Since this is a _conceptual_ state machine, implementations MAY deviate from implementing precisely these states, or dividing them strictly. Implementations MAY blur the lines between the states. If so, implementations MUST ensure security @@ -81,7 +82,6 @@ of the altered protocol. ![ChainSync State Machine](chainsync_fsm.dot) - ## Peer Discovery Peer discovery is a critical part of the overall architecture. Taking this wrong can have severe consequences for the operation of the protocol. The set of peers a new node initially connects to when joining the network may completely dominate the node's awareness of other peers, and therefore the view of the state of the network that the node has. @@ -117,4 +117,3 @@ With explicit peering agreements, the operators must specify a list of peers whi - **BV6 - Chain ancestry and finality**: Verify block links back to trusted chain, not prior to finality. - **BV7 - Message Signatures**: - **BV8 - State tree**: Parent tipset message execution produces the claimed state tree root and receipts. - diff --git a/content/systems/filecoin_blockchain/message_pool/_index.md b/content/systems/filecoin_blockchain/message_pool/_index.md index b8a188b8e..b13120ae7 100644 --- a/content/systems/filecoin_blockchain/message_pool/_index.md +++ b/content/systems/filecoin_blockchain/message_pool/_index.md @@ -9,13 +9,11 @@ dashboardTests: 0 --- # Message Pool + The Message Pool, or `mpool` or `mempool` is a Pool of _Transaction_ Messages in the Filecoin protocol. It acts as the interface between Filecoin nodes and the peer-to-peer network of other nodes used for off-chain message propagation. The message pool is used by nodes to maintain a set of messages they want to transmit to the Filecoin VM and add to the chain (i.e., add for "on-chain" execution). In order for a transaction message to end up in the blockchain it first has to be in the message pool. In reality, at least in the Lotus implementation of Filecoin, there is no central pool of messages stored somewhere. Instead, the message pool is an abstraction and is realised as a list of messages kept by every node in the network. Therefore, when a node puts a new message in the message pool, this message is propagated to the rest of the network using libp2p's pubsub protocol, GossipSub. Nodes need to subscribe to the corresponding pubsub topic in order to receive messages. -Message propagation using GossipSub does not happen immediately and therefore, there is some lag before message pools at different nodes can be in sync. In practice, and given continuous streams of messages being added to the message pool and the delay to propagate messages, the message pool is never synchronised across all nodes in the network. This is not a deficiency of the system, as the message pool does not _need_ to be synchronized across the network. - +Message propagation using GossipSub does not happen immediately and therefore, there is some lag before message pools at different nodes can be in sync. In practice, and given continuous streams of messages being added to the message pool and the delay to propagate messages, the message pool is never synchronised across all nodes in the network. This is not a deficiency of the system, as the message pool does not _need_ to be synchronized across the network. The message pool should have a maximum size defined to avoid DoS attacks, where nodes are spammed and run out of memory. The recommended size for the message pool is 5000 Transaction messages. - - diff --git a/content/systems/filecoin_blockchain/message_pool/message_syncer.md b/content/systems/filecoin_blockchain/message_pool/message_syncer.md index f394acad2..2a469b333 100644 --- a/content/systems/filecoin_blockchain/message_pool/message_syncer.md +++ b/content/systems/filecoin_blockchain/message_pool/message_syncer.md @@ -12,6 +12,7 @@ dashboardTests: 0 The message pool has to interface with the libp2p pubsub [GossipSub](https://github.com/libp2p/specs/tree/master/pubsub/gossipsub) protocol. This is because transaction messages are propagated over [GossipSub](https://github.com/libp2p/specs/tree/master/pubsub/gossipsub) the corresponding `/fil/msgs/` _topic_. Every [Message](message) is announced in the corresponding `/fil/msgs/` topic by any node participating in the network. There are two main pubsub topics related to transactions and blocks: i) the `/fil/msgs/` topic that carries transactions and, ii) the `/fil/blocks/` topic that carries blocks. The `/fil/msgs/` topic is linked to the `mpool`. The process is as follows: + 1. When a client wants to carry out a transaction in the Filecoin network, they publish a transaction message in the `/fil/msgs/` topic. 2. The message propagates to all other nodes in the network using GossipSub and eventually ends up in the `mpool` of all miners. 3. Depending on cryptoeconomic rules, some miner will eventually pick the transaction message from the `mpool` (together with other transaction messages) and include it in a block. @@ -22,5 +23,6 @@ Nodes must check that incoming transaction messages are valid, that is, that the The updated, hardened version of the GossipSub protocol includes a number of attack mitigation strategies. For instance, when a node receives an invalid message it assigns a negative _score_ to the sender peer. Peer scores are not shared with other nodes, but are rather kept locally by every peer for all other peers it is interacting with. If a peer's score drops below a threshold it is excluded from the scoring peer's mesh. We discuss more details on these settings in the GossipSub section. The full details can be found in the [GossipSub Specification](https://github.com/libp2p/specs/tree/master/pubsub/gossipsub). NOTES: + - _Fund Checking:_ It is important to note that the `mpool` logic is not checking whether there are enough funds in the account of the transaction message issuer. This is checked by the miner before including a transaction message in a block. - _Message Sorting:_ Transaction messages are sorted in the `mpool` of miners as they arrive according to cryptoeconomic rules followed by the miner and in order for the miner to compose the next block. diff --git a/content/systems/filecoin_blockchain/storage_power_consensus/_index.md b/content/systems/filecoin_blockchain/storage_power_consensus/_index.md index 94e00053c..05b81091b 100644 --- a/content/systems/filecoin_blockchain/storage_power_consensus/_index.md +++ b/content/systems/filecoin_blockchain/storage_power_consensus/_index.md @@ -17,11 +17,10 @@ Succinctly, the SPC subsystem offers the following services: - Access to the [Power Table](storage_power_actor#the-power-table) for every subchain, accounting for individual storage miner power and total power on-chain. - Access to [Expected Consensus](expected_consensus) for individual storage miners, enabling: - - Access to verifiable randomness [Tickets](storage_power_consensus#tickets) as provided by [drand](drand) for the rest of the protocol. - - Running [Leader Election](expected_consensus#secret-leader-election) to produce new blocks. - - Running [Chain Selection](expected_consensus#chain-selection) across subchains using EC's weighting function. - - Identification of [the most recently finalized tipset](expected_consensus#finality-in-ec), for use by all protocol participants. - + - Access to verifiable randomness [Tickets](storage_power_consensus#tickets) as provided by [drand](drand) for the rest of the protocol. + - Running [Leader Election](expected_consensus#secret-leader-election) to produce new blocks. + - Running [Chain Selection](expected_consensus#chain-selection) across subchains using EC's weighting function. + - Identification of [the most recently finalized tipset](expected_consensus#finality-in-ec), for use by all protocol participants. ## Distinguishing between storage miners and block miners @@ -38,11 +37,11 @@ However, given Filecoin's "useful Proof-of-Work" is achieved through file storag Quality-adjusted power is assigned to every sector as a static function of its **_Sector Quality_** which includes: i) the **Sector Spacetime**, which is the product of the sector size and the promised storage duration, ii) the **Deal Weight** that converts spacetime occupied by deals into consensus power, iii) the **Deal Quality Multiplier** that depends on the type of deal done over the sector (i.e., CC, Regular Deal or Verified Client Deal), and finally, iv) the **Sector Quality Multiplier**, which is an average of deal quality multipliers weighted by the amount of spacetime each type of deal occupies in the sector. -The **Sector Quality** is a measure that maps size, duration and the type of active deals in a sector during its lifetime to its impact on power and reward distribution. +The **Sector Quality** is a measure that maps size, duration and the type of active deals in a sector during its lifetime to its impact on power and reward distribution. The quality of a sector depends on the deals made over the data inside the sector. There are generally three types of deals: the _Committed Capacity (CC)_, where there is effectively no deal and the miner is storing arbitrary data inside the sector, the _Regular Deals_, where a miner and a client agree on a price in the market and the _Verified Client_ deals, which give more power to the sector. We refer the reader to the [Sector](sector) and [Sector Quality](sector#sector_quality) sections for details on Sector Types and Sector Quality, the [Verified Clients](verified_clients) section for more details on what a verified client is, and the [CryptoEconomics](cryptoecon) section for specific parameter values on the Deal Weights and Quality Multipliers. -**Quality-Adjusted Power** is the number of votes a miner has in the [Secret Leader Election](expected_consensus#secret-leader-election) and has been defined to increase linearly with the useful storage that a miner has committed to the network. +**Quality-Adjusted Power** is the number of votes a miner has in the [Secret Leader Election](expected_consensus#secret-leader-election) and has been defined to increase linearly with the useful storage that a miner has committed to the network. More precisely, we have the following definitions: @@ -63,7 +62,7 @@ This randomness may be drawn from various Filecoin chain epochs by the respectiv It is important to note that a given Filecoin network and a given drand network need not have the same round time, i.e. blocks may be generated faster or slower -by Filecoin than randomness is generated by drand. For instance, if the drand +by Filecoin than randomness is generated by drand. For instance, if the drand beacon is producing randomness twice as fast as Filecoin produces blocks, we might expect two random values to be produced in a Filecoin epoch, conversely if the Filecoin network is twice as fast as drand, we might expect a random value @@ -88,7 +87,6 @@ lowest epoch number block will contain the requested beacon entry. Similarly, if there has been null rounds where the beacon should have been inserted, we need to iterate on the chain to find where the entry is inserted. Specifically, the next non-null block must contain the drand entry requested by definition. - ### Fetch randomness from drand network When mining, a miner can fetch entries from the drand network to include them in @@ -96,13 +94,10 @@ the new block. {{}} - {{}} - {{}} - ### Validating Beacon Entries on block reception A Filecoin chain will contain the entirety of the beacon's output from the Filecoin genesis to the current block. @@ -124,6 +119,7 @@ At a Filecoin epoch `n`, a new ticket is generated using the appropriate beacon The miner runs the beacon entry through a Verifiable Random Function (VRF) to get a new unique ticket. The beacon entry is prepended with the ticket domain separation tag and concatenated with the miner actor address (to ensure miners using the same worker keys get different tickets). To generate a ticket for a given epoch n: + ```text randSeed = GetRandomnessFromBeacon(n) newTicketRandomness = VRF_miner(H(TicketProdDST || index || Serialization(randSeed, minerActorAddress))) @@ -146,5 +142,3 @@ Miners smaller than this cannot mine blocks and earn block rewards in the networ Accordingly, to bootstrap the network, the genesis block must include miners, potentially just CommittedCapacity sectors, to initiate the network. The `MIN_MINER_SIZE_TARG` condition will not be used in a network in which any miner has more than `MIN_MINER_SIZE_STOR` power. It is nonetheless defined to ensure liveness in small networks (e.g. close to genesis or after large power drops). - - diff --git a/content/systems/filecoin_blockchain/storage_power_consensus/storage_power_actor.md b/content/systems/filecoin_blockchain/storage_power_consensus/storage_power_actor.md index a638c9960..2a0701a5b 100644 --- a/content/systems/filecoin_blockchain/storage_power_consensus/storage_power_actor.md +++ b/content/systems/filecoin_blockchain/storage_power_consensus/storage_power_actor.md @@ -18,14 +18,13 @@ dashboardTests: 0 {{}} - ## The Power Table The portion of blocks a given miner generates through leader election in EC (and so the block rewards they earn) is proportional to their `Quality-Adjusted Power Fraction` over time. That is, a miner whose quality adjusted power represents 1% of total quality adjusted power on the network should mine 1% of blocks on expectation. SPC provides a power table abstraction which tracks miner power (i.e. miner storage in relation to network storage) over time. The power table is updated for new sector commitments (incrementing miner power), for failed PoSts (decrementing miner power) or for other storage and consensus faults. -Sector ProveCommit is the first time power is proven to the network and hence power is first added upon successful sector ProveCommit. Power is also added when a sector is declared as recovered. Miners are expected to prove over all their sectors that contribute to their power. +Sector ProveCommit is the first time power is proven to the network and hence power is first added upon successful sector ProveCommit. Power is also added when a sector is declared as recovered. Miners are expected to prove over all their sectors that contribute to their power. Power is decremented when a sector expires, when a sector is declared or detected to be faulty, or when it is terminated through miner invocation. Miners can also extend the lifetime of a sector through `ExtendSectorExpiration`. @@ -33,13 +32,13 @@ The Miner lifecycle in the power table should be roughly as follows: - `MinerRegistration`: A new miner with an associated worker public key and address is registered on the power table by the storage mining subsystem, along with their associated sector size (there is only one per worker). - `UpdatePower`: These power increments and decrements are called by various storage actors (and must thus be verified by every full node on the network). Specifically: - - Power is incremented at `SectorProveCommit` - - Power of a partition is decremented immediately after a missed WindowPoSt (`DetectedFault`). - - A particular sector's power is decremented when it enters into a faulty state either through Declared Faults or Skipped Faults. - - A particular sector's power is added back after recovery is declared and proven by PoSt. - - A particular sector's power is removed when the sector is expired or terminated through miner invovation. + - Power is incremented at `SectorProveCommit` + - Power of a partition is decremented immediately after a missed WindowPoSt (`DetectedFault`). + - A particular sector's power is decremented when it enters into a faulty state either through Declared Faults or Skipped Faults. + - A particular sector's power is added back after recovery is declared and proven by PoSt. + - A particular sector's power is removed when the sector is expired or terminated through miner invovation. -To summarize, only sectors in the Active state will command power. A Sector becomes Active when it is added upon `ProveCommit`. Power is immediately decremented when it enters into the faulty state. Power will be restored when its declared recovery is proven. A sector's power is removed when it is expired or terminated through miner invocation. +To summarize, only sectors in the Active state will command power. A Sector becomes Active when it is added upon `ProveCommit`. Power is immediately decremented when it enters into the faulty state. Power will be restored when its declared recovery is proven. A sector's power is removed when it is expired or terminated through miner invocation. ## Pledge Collateral diff --git a/content/systems/filecoin_blockchain/struct/block/_index.md b/content/systems/filecoin_blockchain/struct/block/_index.md index c467167e0..879b4d44a 100644 --- a/content/systems/filecoin_blockchain/struct/block/_index.md +++ b/content/systems/filecoin_blockchain/struct/block/_index.md @@ -44,19 +44,17 @@ The Lotus implementation of the message has the following structure: {{}} - The message is also validated before it is passed to the [chain synchronization logic](chainsync): {{}} - ## Block syntax validation Syntax validation refers to validation that should be performed on a block and its messages _without_ reference to outside information such as the parent state tree. This type of validation is sometimes called _static validation_. An invalid block must not be transmitted or referenced as a parent. -A syntactically valid block header must decode into fields matching the definitions below, must be a valid CBOR PubSub `BlockMsg` message and must have: +A syntactically valid block header must decode into fields matching the definitions below, must be a valid CBOR PubSub `BlockMsg` message and must have: - between 1 and `5*ec.ExpectedLeaders` `Parents` CIDs if `Epoch` is greater than zero (else empty `Parents`), - a non-negative `ParentWeight`, @@ -68,11 +66,11 @@ A syntactically valid block header must decode into fields matching the definiti - a positive `Timestamp`, - a `Ticket` with non-empty `VRFResult`, - `ElectionPoStOutput` containing: - - a `Candidates` array with between 1 and `EC.ExpectedLeaders` values (inclusive), - - a non-empty `PoStRandomness` field, - - a non-empty `Proof` field, + - a `Candidates` array with between 1 and `EC.ExpectedLeaders` values (inclusive), + - a non-empty `PoStRandomness` field, + - a non-empty `Proof` field, - a non-empty `ForkSignal` field. - + A syntactically valid full block must have: - all referenced messages syntactically valid, @@ -92,17 +90,14 @@ In the Lotus implementation the semantic validation of a block is carried out by {{}} - Messages are retrieved through the `Syncer`. There are the following two steps followed by the `Syncer`: -1) Assemble a `FullTipSet` populated with the single block received earlier. The Block's `ParentWeight` is greater than the one from the (first block of the) heaviest tipset. -2) Retrieve all tipsets from the received Block down to our chain. Validation is expanded to every block inside these tipsets. The validation should ensure that: - - Beacon entires are ordered by their round number. - - The Tipset `Parents` CIDs match the fetched parent tipset through BlockSync. - +1- Assemble a `FullTipSet` populated with the single block received earlier. The Block's `ParentWeight` is greater than the one from the (first block of the) heaviest tipset. +2- Retrieve all tipsets from the received Block down to our chain. Validation is expanded to every block inside these tipsets. The validation should ensure that: - Beacon entires are ordered by their round number. - The Tipset `Parents` CIDs match the fetched parent tipset through BlockSync. A semantically valid block must meet all of the following requirements. **`Parents`-Related** + - `Parents` listed in lexicographic order of their header's `Ticket`. - `ParentStateRoot` CID of the block matches the state CID computed from the parent [Tipset](tipset). - `ParentState` matches the state tree produced by executing the parent tipset's messages (as defined by the VM interpreter) against that tipset's parent state. @@ -110,53 +105,56 @@ A semantically valid block must meet all of the following requirements. - `ParentWeight` matches the weight of the chain up to and including the parent tipset. **Time-Related** -- `Epoch` is greater than that of its `Parents`, and - - not in the future according to the node's local clock reading of the current epoch, - - blocks with future epochs should not be rejected, but should not be evaluated (validated or included in a tipset) until the appropriate epoch - - not farther in the past than the soft finality as defined by SPC [Finality](expected_consensus#finality-in-ec), - - this rule only applies when receiving new gossip blocks (i.e. from the current chain head), not when syncing to the chain for the first time. + +- `Epoch` is greater than that of its `Parents`, and + - not in the future according to the node's local clock reading of the current epoch, + - blocks with future epochs should not be rejected, but should not be evaluated (validated or included in a tipset) until the appropriate epoch + - not farther in the past than the soft finality as defined by SPC [Finality](expected_consensus#finality-in-ec), + - this rule only applies when receiving new gossip blocks (i.e. from the current chain head), not when syncing to the chain for the first time. - The `Timestamp` included is in seconds that: - must not be bigger than current time plus `ΑllowableClockDriftSecs` - must not be smaller than previous block's `Timestamp` plus `BlockDelay` (including null blocks) - is of the precise value implied by the genesis block's timestamp, the network's Βlock time and the Βlock's `Epoch`. **`Miner`-Related** + - The `Miner` is active in the storage power table in the parent tipset state. The Miner's address is registered in the `Claims` HAMT of the Power Actor - The `TipSetState` should be included for each tipset being validated. - - Every Block in the tipset should belong to different a miner. + - Every Block in the tipset should belong to different a miner. - The Actor associated with the message's `From` address exists, is an account actor and its Nonce matches the message Nonce. - Valid proofs that the Miner proved access to sealed versions of the sectors it was challenged for are included. In order to achieve that: - - draw randomness for current epoch with `WinningPoSt` domain separation tag. - - get list of sectors challanged in this epoch for this miner, based on the randomness drawn. + - draw randomness for current epoch with `WinningPoSt` domain separation tag. + - get list of sectors challanged in this epoch for this miner, based on the randomness drawn. - Miner is not slashed in `StoragePowerActor`. - **`Beacon`- & `Ticket`-Related** + - Valid `BeaconEntries` should be included: - - Check that every one of the `BeaconEntries` is a signature of a message: `previousSignature || round` signed using DRAND's public key. - - All entries between `MaxBeaconRoundForEpoch` down to `prevEntry` (from previous tipset) should be included. -- A `Ticket` derived from the minimum ticket from the parent tipset's block headers, - - `Ticket.VRFResult` validly signed by the `Miner` actor's worker account public key, + - Check that every one of the `BeaconEntries` is a signature of a message: `previousSignature || round` signed using DRAND's public key. + - All entries between `MaxBeaconRoundForEpoch` down to `prevEntry` (from previous tipset) should be included. +- A `Ticket` derived from the minimum ticket from the parent tipset's block headers, + - `Ticket.VRFResult` validly signed by the `Miner` actor's worker account public key, - `ElectionProof Ticket` is computed correctly by checking BLS signature using miner's key. The `ElectionProof` ticket should be a winning ticket. **Message- & Signature-Related** + - `secp256k1` messages are correctly signed by their sending actor's (`From`) worker account key, - A `BLSAggregate` signature is included that signs the array of CIDs of all the BLS messages referenced by the block with their sending actor's key. - A valid `Signature` over the block header's fields from the block's `Miner` actor's worker account public key is included. - For each message in `ValidForBlockInclusion()` the following hold: - - Message fields `Version`, `To`, `From`, `Value`, `GasPrice`, and `GasLimit` are correctly defined. - - Message `GasLimit` is under the message minimum gas cost (derived from chain height and message length). + - Message fields `Version`, `To`, `From`, `Value`, `GasPrice`, and `GasLimit` are correctly defined. + - Message `GasLimit` is under the message minimum gas cost (derived from chain height and message length). - For each message in `ApplyMessage` (that is before a message is executed), the following hold: - - Basic gas and value checks in `checkMessage()`: - - The Message `GasLimit` is bigger than zero. - - The Message `GasPrice` and `Value` are set. - - The Message's storage gas cost is under the message's `GasLimit`. - - The Message's `Nonce` matches the nonce in the Actor retrieved from the message's `From` address. - - The Message's maximum gas cost (derived from its `GasLimit`, `GasPrice`, and `Value`) is under the balance of the Actor retrieved from message's `From` address. - - The Message's transfer `Value` is under the balance of the Actor retrieved from message's `From` address. + - Basic gas and value checks in `checkMessage()`: + - The Message `GasLimit` is bigger than zero. + - The Message `GasPrice` and `Value` are set. + - The Message's storage gas cost is under the message's `GasLimit`. + - The Message's `Nonce` matches the nonce in the Actor retrieved from the message's `From` address. + - The Message's maximum gas cost (derived from its `GasLimit`, `GasPrice`, and `Value`) is under the balance of the Actor retrieved from message's `From` address. + - The Message's transfer `Value` is under the balance of the Actor retrieved from message's `From` address. There is no semantic validation of the messages included in a block beyond validation of their signatures. -If all messages included in a block are syntactically valid then they may be executed and produce a receipt. +If all messages included in a block are syntactically valid then they may be executed and produce a receipt. A chain sync system may perform syntactic and semantic validation in stages in order to minimize unnecessary resource expenditure. diff --git a/content/systems/filecoin_blockchain/struct/block_producer/_index.md b/content/systems/filecoin_blockchain/struct/block_producer/_index.md index 45e2af2e1..a567c91a2 100644 --- a/content/systems/filecoin_blockchain/struct/block_producer/_index.md +++ b/content/systems/filecoin_blockchain/struct/block_producer/_index.md @@ -11,7 +11,7 @@ dashboardTests: 0 ## Mining Blocks -A miner registered with the storage power actor may begin generating and checking election tickets if it has proven storage that meets the [Minimum Miner Size](storage_power_consensus#minimum-miner-size) threshold requirement. +A miner registered with the storage power actor may begin generating and checking election tickets if it has proven storage that meets the [Minimum Miner Size](storage_power_consensus#minimum-miner-size) threshold requirement. In order to do so, the miner must be running chain validation, and be keeping track of the most recent blocks received. A miner's new block will be based on parents from the previous epoch. @@ -19,7 +19,7 @@ In order to do so, the miner must be running chain validation, and be keeping tr Producing a block for epoch `H` requires waiting for the beacon entry for that epoch and using it to run `GenerateElectionProof`. If `WinCount` ≥ 1 (i.e., when the miner is elected), the same beacon entry is used to run `WinningPoSt`. Armed by the `ElectionProof` ticket (output of `GenerateElectionProof`) and the `WinningPoSt` proof, the miner can produce an new block. -See [VM Interpreter](interpreter) for details of parent tipset evaluation, and [Block](block) for constraints on valid block header values. +See [VM Interpreter](interpreter) for details of parent tipset evaluation, and [Block](block) for constraints on valid block header values. To create a block, the eligible miner must compute a few fields: @@ -48,12 +48,12 @@ messages which will succeed in execution and pay the most gas. The block reward is not evaluated when producing a block. It is paid when the block is included in a tipset in the following epoch. -The block's signature ensures integrity of the block after propagation, since unlike many PoW blockchains, +The block's signature ensures integrity of the block after propagation, since unlike many PoW blockchains, a winning ticket is found independently of block generation. ### Block Broadcast -An eligible miner propagates the completed block to the network using the [GossipSub](gossip_sub) `/fil/blocks` topic and, assuming everything was done correctly, +An eligible miner propagates the completed block to the network using the [GossipSub](gossip_sub) `/fil/blocks` topic and, assuming everything was done correctly, the network will accept it and other miners will mine on top of it, earning the miner a block reward. Miners should output their valid block as soon as it is produced, otherwise they risk other miners receiving the block after the EPOCH_CUTOFF and not including them in the current epoch. diff --git a/content/systems/filecoin_blockchain/struct/chain_manager/_index.md b/content/systems/filecoin_blockchain/struct/chain_manager/_index.md index 2cee890ed..a140b2034 100644 --- a/content/systems/filecoin_blockchain/struct/chain_manager/_index.md +++ b/content/systems/filecoin_blockchain/struct/chain_manager/_index.md @@ -13,12 +13,12 @@ The _Chain Manager_ is a central component in the blockchain system. It tracks a In so doing, the _chain manager_ is the central subsystem that handles bookkeeping for numerous other systems in a Filecoin node and exposes convenience methods for use by those systems, enabling systems to sample randomness from the chain for instance, or to see which block has been finalized most recently. - ## Chain Extension ### Incoming block reception For every incoming block, even if the incoming block is not added to the current heaviest tipset, the chain manager should add it to the appropriate subchain it is tracking, or keep track of it independently until either: + - it is able to add to the current heaviest subchain, through the reception of another block in that subchain, or - it is able to discard it, as the block was mined before finality. @@ -27,6 +27,7 @@ It is important to note that ahead of finality, a given subchain may be abandone Chain selection is a crucial component of how the Filecoin blockchain works. In brief, every chain has an associated weight accounting for the number of blocks mined on it and so the power (storage) they track. The full details of how selection works are provided in the [Chain Selection](expected_consensus#chain-selection) section. **Notes/Recommendations:** + 1. In order to make certain validation checks simpler, blocks should be indexed by height and by parent set. That way sets of blocks with a given height and common parents may be quickly queried. 2. It may also be useful to compute and cache the resultant aggregate state of blocks in these sets, this saves extra state computation when checking which state root to start a block at when it has multiple parents. 3. It is recommended that blocks are kept in the local datastore regardless of whether they are understood as the best tip at this point - this is to avoid having to refetch the same blocks in the future. diff --git a/content/systems/filecoin_blockchain/struct/tipset/_index.md b/content/systems/filecoin_blockchain/struct/tipset/_index.md index 7ce192d69..55ba3a7d2 100644 --- a/content/systems/filecoin_blockchain/struct/tipset/_index.md +++ b/content/systems/filecoin_blockchain/struct/tipset/_index.md @@ -9,13 +9,13 @@ dashboardTests: 0 # Tipset -Expected Consensus probabilistically elects multiple leaders in each epoch meaning a Filecoin chain may contain zero or multiple blocks at each epoch (one per elected miner). Blocks from the same epoch are assembled into tipsets. The [VM Interpreter](interpreter) modifies the Filecoin state tree by executing all messages in a tipset (after de-duplication of identical messages included in more than one block). +Expected Consensus probabilistically elects multiple leaders in each epoch meaning a Filecoin chain may contain zero or multiple blocks at each epoch (one per elected miner). Blocks from the same epoch are assembled into tipsets. The [VM Interpreter](interpreter) modifies the Filecoin state tree by executing all messages in a tipset (after de-duplication of identical messages included in more than one block). + +Each block references a parent tipset and validates _that tipset's state_, while proposing messages to be included for the current epoch. The state to which a new block's messages apply cannot be known until that block is incorporated into a tipset. It is thus not meaningful to execute the messages from a single block in isolation: a new state tree is only known once all messages in that block's tipset are executed. -Each block references a parent tipset and validates _that tipset's state_, while proposing messages to be included for the current epoch. The state to which a new block's messages apply cannot be known until that block is incorporated into a tipset. It is thus not meaningful to execute the messages from a single block in isolation: a new state tree is only known once all messages in that block's tipset are executed. - A valid tipset contains a non-empty collection of blocks that have distinct miners and all specify identical: -- `Epoch` +- `Epoch` - `Parents` - `ParentWeight` - `StateRoot` @@ -31,9 +31,6 @@ The main Tipset structure in the Lotus implementation includes the following: {{}} - Semantic validation of a Tipset includes the following checks. {{}} - - diff --git a/content/systems/filecoin_files/_index.md b/content/systems/filecoin_files/_index.md index 31823298d..51087f421 100644 --- a/content/systems/filecoin_files/_index.md +++ b/content/systems/filecoin_files/_index.md @@ -13,4 +13,4 @@ dashboardTests: 0 Filecoin's primary aim is to store client's Files and Data. This section details data structures and tooling related to working with files, -chunking, encoding, graph representations, `Pieces`, storage abstractions, and more. \ No newline at end of file +chunking, encoding, graph representations, `Pieces`, storage abstractions, and more. diff --git a/content/systems/filecoin_files/data_transfer/_index.md b/content/systems/filecoin_files/data_transfer/_index.md index f91c50308..eeb62857d 100644 --- a/content/systems/filecoin_files/data_transfer/_index.md +++ b/content/systems/filecoin_files/data_transfer/_index.md @@ -31,7 +31,7 @@ but their code belongs in the Markets system. - **Responder**: The party that receives the data transfer request - normally the storage provider. - **Data Transfer Voucher or Token**: A wrapper around storage- or retrieval-related data that can identify and validate the transfer request to the other party. - **Request Validator**: The data transfer module only initiates a transfer when the responder can validate that the request is tied directly to either an existing storage or retrieval deal. Validation is not performed by the data transfer module itself. Instead, a request validator inspects the data transfer voucher to determine whether to respond to the request or disregard the request. -- **Transporter**: Once a request is negotiated and validated, the actual transfer is managed by a transporter on both sides. The transporter is part of the data transfer module but is isolated from the negotiation process. It has access to an underlying verifiable transport protocol and uses it to send data and track progress. +- **Transporter**: Once a request is negotiated and validated, the actual transfer is managed by a transporter on both sides. The transporter is part of the data transfer module but is isolated from the negotiation process. It has access to an underlying verifiable transport protocol and uses it to send data and track progress. - **Subscriber**: An external component that monitors progress of a data transfer by subscribing to data transfer events, such as progress or completion. - **GraphSync**: The default underlying transport protocol used by the Transporter. The full graphsync specification can be found [here](https://github.com/ipld/specs/blob/master/block-layer/graphsync/graphsync.md) @@ -43,7 +43,7 @@ There are two basic phases to any data transfer: 2. Transfer: once the negotiation phase is complete, the data is actually transferred. The default protocol used to do the transfer is Graphsync. Note that the Negotiation and Transfer stages can occur in separate round trips, -or potentially the same round trip, where the requesting party implicitly agrees by sending the request, and the responding party can agree and immediately send or receive data. Whether the process is taking place in a single or multiple round-trips depends in part on whether the request is a push request (storage deal) or a pull request (retrieval deal), and on whether the data transfer negotiation process is able to piggy back on the underlying transport mechanism. +or potentially the same round trip, where the requesting party implicitly agrees by sending the request, and the responding party can agree and immediately send or receive data. Whether the process is taking place in a single or multiple round-trips depends in part on whether the request is a push request (storage deal) or a pull request (retrieval deal), and on whether the data transfer negotiation process is able to piggy back on the underlying transport mechanism. In the case of GraphSync as transport mechanism, data transfer requests can piggy back as an extension to the GraphSync protocol using [GraphSync's built-in extensibility](https://github.com/ipld/specs/blob/master/block-layer/graphsync/graphsync.md#extensions). So, only a single round trip is required for Pull Requests. However, because Graphsync is a request/response protocol with no direct support for `push` type requests, in the Push case, negotiation happens in a seperate request over data transfer's own libp2p protocol `/fil/datatransfer/1.0.0`. Other future transport mechinisms might handle both Push and Pull, either, or neither as a single round trip. Upon receiving a data transfer request, the data transfer module does the decoding the voucher and delivers it to the request validators. In storage deals, the request validator checks if the deal included is one that the recipient has agreed to before. For retrieval deals the request includes the proposal for the retrieval deal itself. As long as request validator accepts the deal proposal, everything is done at once as a single round-trip. @@ -66,7 +66,6 @@ It is worth noting that in the case of retrieval the provider can accept the dea The push flow is ideal for storage deals, where the client initiates the data transfer straightaway once the provider indicates their intent to accept and publish the client's deal proposal. - ## Pull Flow - Single Round Trip ![Data Transfer - Single Round Trip Pull Flow](alternate-pull-flow.mmd) @@ -88,11 +87,8 @@ transport mechanism (including offline mechanisms) is acceptable. ## Data Structures - {{}} - {{}} - {{}} diff --git a/content/systems/filecoin_files/error_correction/_index.md b/content/systems/filecoin_files/error_correction/_index.md index d74fb3ba9..8746d1154 100644 --- a/content/systems/filecoin_files/error_correction/_index.md +++ b/content/systems/filecoin_files/error_correction/_index.md @@ -11,7 +11,6 @@ In order to reduce likelihood of problems,`Clients` prepare their data by applying erasure or error correcting codes. This section describes how that will happen. - {{< hint warning >}} TODO {{< /hint >}} diff --git a/content/systems/filecoin_files/file/_index.md b/content/systems/filecoin_files/file/_index.md index 104df22dc..4e1526950 100644 --- a/content/systems/filecoin_files/file/_index.md +++ b/content/systems/filecoin_files/file/_index.md @@ -6,9 +6,8 @@ dashboardWeight: 1 dashboardState: reliable dashboardAudit: n/a dashboardTests: 0 - --- # File - + {{< embed src="file.id" lang="go" >}} diff --git a/content/systems/filecoin_files/piece/_index.md b/content/systems/filecoin_files/piece/_index.md index fda15e992..80e2a1e6c 100644 --- a/content/systems/filecoin_files/piece/_index.md +++ b/content/systems/filecoin_files/piece/_index.md @@ -13,7 +13,7 @@ dashboardTests: 0 The _Filecoin Piece_ is the main _unit of negotiation_ for data that users store on the Filecoin network. The Filecoin Piece is _not a unit of storage_, it is not of a specific size, but is upper-bounded by the size of the _Sector_. A Filecoin Piece can be of any size, but if a Piece is larger than the size of a Sector that the miner supports it has to be split into more Pieces so that each Piece fits into a Sector. A `Piece` is an object that represents a whole or part of a `File`, -and is used by `Storage Clients` and `Storage Miners` in `Deals`. `Storage Clients` hire `Storage Miners` to store `Pieces`. +and is used by `Storage Clients` and `Storage Miners` in `Deals`. `Storage Clients` hire `Storage Miners` to store `Pieces`. The Piece data structure is designed for proving storage of arbitrary IPLD graphs and client data. This diagram shows the detailed composition @@ -32,11 +32,10 @@ The first three steps take place on the client side. 1. When a client wants to store a file in the Filecoin network, they start by producing the IPLD DAG of the file. The hash that represents the root node of the DAG is an IPFS-style CID, called _Payload CID_. -2. In order to make a _Filecoin Piece_, the IPLD DAG is serialised into a ["Content-Addressable aRchive" (.car)](https://github.com/ipld/specs/blob/master/block-layer/content-addressable-archives.md#summary) file, which is in raw bytes format. A CAR file is an opaque blob of data that packs together and transfers IPLD nodes. The _Payload CID_ is common between the CAR'ed and un-CAR'ed constructions. This helps later during data retrieval, when data is transferred between the storage client and the storage provider as we discuss later. +2. In order to make a _Filecoin Piece_, the IPLD DAG is serialised into a ["Content-Addressable aRchive" (.car)](https://github.com/ipld/specs/blob/master/block-layer/content-addressable-archives.md#summary) file, which is in raw bytes format. A CAR file is an opaque blob of data that packs together and transfers IPLD nodes. The _Payload CID_ is common between the CAR'ed and un-CAR'ed constructions. This helps later during data retrieval, when data is transferred between the storage client and the storage provider as we discuss later. 3. The resulting .car file is _padded_ with extra zero bits in order for the file to make a binary Merkle tree. To achieve a clean binary Merkle Tree the .car file size has to be in some power of two (^2) size. A padding process, called `Fr32 padding`, which adds two (2) zero bits to every 254 out of every 256 bits is applied to the input file. At the next step, the padding process takes the output of the `Fr32 padding` process and finds the size above it that makes for a power of two size. This gap between the result of the `Fr32 padding` and the next power of two size is padded with zeros. - In order to justify the reasoning behind these steps, it is important to understand the overall negotiation process between the `StorageClient` and a `StorageProvider`. The piece CID or CommP is what is included in the deal that the client negotiates and agrees with the storage provider. When the deal is agreed, the client sends the file to the provider (using GraphSync). The provider has to construct the CAR file out of the file received and derive the Piece CID on their side. In order to avoid the client sending a different file to the one agreed, the Piece CID that the provider generates has to be the same as the one included in the deal negotiated earlier. The following steps take place on the `StorageProvider` side (apart from step 4 that can also take place at the client side). @@ -51,10 +50,10 @@ The following steps take place on the `StorageProvider` side (apart from step 4 8. Finally, _CommR_ (or _Commitment of Replication_) is the hash of CommC || CommRLast. - **IMPORTANT NOTES:** + - `Fr32` is a 32-bit representation of a field element (which, in our case, is the arithmetic field of BLS12-381). To be well-formed, a value of type `Fr32` must _actually_ fit within that field, but this is not enforced by the type system. It is an invariant which must be perserved by correct usage. -In the case of so-called `Fr32 padding`, two zero bits are inserted 'after' a number requiring at most 254 bits to represent. This guarantees that the result will be `Fr32`, regardless of the value of the initial 254 bits. This is a 'conservative' technique, since for some initial values, only one bit of zero-padding would actually be required. + In the case of so-called `Fr32 padding`, two zero bits are inserted 'after' a number requiring at most 254 bits to represent. This guarantees that the result will be `Fr32`, regardless of the value of the initial 254 bits. This is a 'conservative' technique, since for some initial values, only one bit of zero-padding would actually be required. - Steps 2 and 3 above are specific to the Lotus implementation. The same outcome can be achieved in different ways, e.g., without using `Fr32` bit-padding. However, any implementation has to make sure that the initial IPLD DAG is serialised and padded so that it gives a clean binary tree, and therefore, calculating the Merkle root out of the resulting blob of data gives the same **Piece CID**. As long as this is the case, implementations can deviate from the first three steps above. - Finally, it is important to add a note related to the _Payload CID_ (discussed in the first two steps above) and the data retrieval process. The retrieval deal is negotiated on the basis of the _Payload CID_. When the retrieval deal is agreed, the retrieval miner starts sending the unsealed and "un-CAR'ed" file to the client. The transfer starts from the root node of the IPLD Merkle Tree and in this way the client can validate the _Payload CID_ from the beginning of the transfer and verify that the file they are receiving is the file they negotiated in the deal and not random bits. diff --git a/content/systems/filecoin_files/serialization/_index.md b/content/systems/filecoin_files/serialization/_index.md index e8ec4f204..7e265d9f5 100644 --- a/content/systems/filecoin_files/serialization/_index.md +++ b/content/systems/filecoin_files/serialization/_index.md @@ -1,5 +1,5 @@ --- -title: Formats and Serialization +title: Formats and Serialization weight: 4 dashboardWeight: 1 dashboardState: reliable @@ -10,7 +10,7 @@ dashboardTests: 0 # Data Formats and Serialization Filecoin seeks to make use of as few data formats as needed, with well-specced serialization rules to -better protocol security through simplicity and enable interoperability amongst implementations of the +better protocol security through simplicity and enable interoperability amongst implementations of the Filecoin protocol. Read more on design considerations [here for CBOR-usage](https://github.com/filecoin-project/specs/issues/621) and [here for int types in Filecoin](https://github.com/filecoin-project/specs/issues/615). @@ -37,4 +37,4 @@ You can find the encoding structure for major data types in CBOR [here](https:// For illustration, an in-memory map would be represented as a CBOR-array of the keys and values listed in some pre-determined order. A near-term update to the serialization format will involve tagging fields appropriately -to ensure appropriate serialization/deserialization as the protocol evolves. \ No newline at end of file +to ensure appropriate serialization/deserialization as the protocol evolves. diff --git a/content/systems/filecoin_markets/_index.md b/content/systems/filecoin_markets/_index.md index 40abbcafb..00475d5d7 100644 --- a/content/systems/filecoin_markets/_index.md +++ b/content/systems/filecoin_markets/_index.md @@ -1,6 +1,6 @@ --- -title: "Market" -description: "Markets in Filecoin" +title: 'Market' +description: 'Markets in Filecoin' bookCollapseSection: true weight: 7 dashboardWeight: 2 @@ -11,6 +11,6 @@ dashboardTests: 0 # Markets -Filecoin is a consensus protocol, a data-storage platform, and a marketplace for storing and retrieving data. There are two major components to Filecoin markets, the storage market and the retrieval market. While storage and retrieval negotiations for both the storage and the retrieval markets are taking place primarily *off the blockchain* (at least in the current version of Filecoin), storage deals made in the storage market will be published on-chain and will be enforced by the protocol. Storage deal negotiation and order matching are expected to happen off-chain in the first version of Filecoin. Retrieval deals are also negotiated off-chain and executed with micropayments between transacting parties in payment channels. +Filecoin is a consensus protocol, a data-storage platform, and a marketplace for storing and retrieving data. There are two major components to Filecoin markets, the storage market and the retrieval market. While storage and retrieval negotiations for both the storage and the retrieval markets are taking place primarily _off the blockchain_ (at least in the current version of Filecoin), storage deals made in the storage market will be published on-chain and will be enforced by the protocol. Storage deal negotiation and order matching are expected to happen off-chain in the first version of Filecoin. Retrieval deals are also negotiated off-chain and executed with micropayments between transacting parties in payment channels. Even though most of the market actions happen off the blockchain, there are on-chain invariants that create economic structure for network success and allow for positive emergent behavior. You can read more about the relationship between on-chain deals and storage power in [Storage Power Consensus](storage_power_consensus). diff --git a/content/systems/filecoin_markets/onchain_storage_market/faults.md b/content/systems/filecoin_markets/onchain_storage_market/faults.md index 9fd9ad02e..9697f81b5 100644 --- a/content/systems/filecoin_markets/onchain_storage_market/faults.md +++ b/content/systems/filecoin_markets/onchain_storage_market/faults.md @@ -1,5 +1,5 @@ --- -title: "Faults" +title: 'Faults' weight: 4 dashboardWeight: 2 dashboardState: reliable diff --git a/content/systems/filecoin_markets/onchain_storage_market/storage_deal_flow.md b/content/systems/filecoin_markets/onchain_storage_market/storage_deal_flow.md index 6c87fa4e9..55cf50dab 100644 --- a/content/systems/filecoin_markets/onchain_storage_market/storage_deal_flow.md +++ b/content/systems/filecoin_markets/onchain_storage_market/storage_deal_flow.md @@ -16,12 +16,8 @@ dashboardTests: 0 1. `StorageClient` and `StorageProvider` call `StorageMarketActor.AddBalance` to deposit funds into Storage Market. - `StorageClient` and `StorageProvider` can call `WithdrawBalance` before any deal is made. 2. `StorageClient` and `StorageProvider` negotiate a deal off chain. `StorageClient` sends a `StorageDealProposal` to a `StorageProvider`. - - `StorageProvider` verifies the `StorageDeal` by checking: - - the address and signature of the `StorageClient`, - - the proposal's `StartEpoch` is after the current Epoch, - - (tentative) the `StorageClient` did not call withdraw in the last *X* epochs (`WithdrawBalance` should take at least *X* epochs) - *X* is currently set to 0, but the setting will be re-considered in the near future. - - both `StorageProvider` and `StorageClient` have sufficient available balances in `StorageMarketActor`. -3. `StorageProvider` signs the `StorageDealProposal` by constructing an on-chain message. + - `StorageProvider` verifies the `StorageDeal` by checking: - the address and signature of the `StorageClient`, - the proposal's `StartEpoch` is after the current Epoch, - (tentative) the `StorageClient` did not call withdraw in the last _X_ epochs (`WithdrawBalance` should take at least _X_ epochs) - _X_ is currently set to 0, but the setting will be re-considered in the near future. - both `StorageProvider` and `StorageClient` have sufficient available balances in `StorageMarketActor`. +3. `StorageProvider` signs the `StorageDealProposal` by constructing an on-chain message. - `StorageProvider` calls `PublishStorageDeals` in `StorageMarketActor` to publish this on-chain message which will generate a `DealID` for each `StorageDeal` and store a mapping from `DealID` to `StorageDeal`. However, the deals are not active at this point. - As a backup, `StorageClient` may call `PublishStorageDeals` with the `StorageDeal`, to activate the deal if they can obtain the signed on-chain message from `StorageProvider`. - It is possible for either `StorageProvider` or `StorageClient` to try to enter into two deals simultaneously with funds available only for one. Only the first deal to commit to the chain will clear, the second will fail with error `errorcode.InsufficientFunds`. @@ -42,19 +38,21 @@ dashboardTests: 0 8. A sector pays a Sector Fault Fee for every proving period during which it is marked as faulty. ## Skipped Faults + 9. After a WindowPoSt deadline opens, a miner can mark one of their sectors as faulty and exempted by WindowPoSt checks, hence Skipped Faults. This could avoid paying a Sector Fault Detection Fee on the whole partition. ## Detected Faults -10. If a partition misses a WindowPoSt submission deadline, all previously non-faulty sectors in the partition are detected as faulty and a Fault Detection Fee is charged. +10. If a partition misses a WindowPoSt submission deadline, all previously non-faulty sectors in the partition are detected as faulty and a Fault Detection Fee is charged. ## Sector Expiration 11. Sector expires when its expiration epoch is reached and sector expiration epoch must be greater than the expiration epoch of all its deals. ## Sector Termination + 12. Termination of a sector can be triggered in two ways. One when sector remains faulty for 14 consecutive days and the other when a miner initiates a termination by calling `TerminateSectors`. In both cases, a `TerminationFee` is penalized, which is in principle equivalent to how much the sector has earned so far. Miners are also penalized for the `DealCollateral` that the sector contains and remaining `DealPayement` will be returned to clients. ## Deal Payment and slashing -13. Deal payment and slashing are evaluated lazily through `updatePendingDealState` called at `CronTick`. +13. Deal payment and slashing are evaluated lazily through `updatePendingDealState` called at `CronTick`. diff --git a/content/systems/filecoin_markets/onchain_storage_market/storage_deal_states.md b/content/systems/filecoin_markets/onchain_storage_market/storage_deal_states.md index 558f38529..c23afb8d3 100644 --- a/content/systems/filecoin_markets/onchain_storage_market/storage_deal_states.md +++ b/content/systems/filecoin_markets/onchain_storage_market/storage_deal_states.md @@ -30,7 +30,7 @@ The following describes how a deal transitions between its different states. The - The sector containing the deal has expired. This is triggered by `StorageMinerActorCode._submitPowerReport` which is called whenver a PoSt is submitted. Power associated with the deals in the sector will be lost, collaterals returned, and all remaining storage fees unlocked. - The sector containing the active deal has been terminated. This is triggered by `StorageMinerActor._submitFaultReport` for `TerminatedFaults`. No storage deal collateral will be slashed on fault declaration or detection, only on termination. A terminated fault is triggered when a sector is in the `Failing` state for `MAX_CONSECUTIVE_FAULTS` consecutive proving periods. -Given the **onchain deal states and their transitions** discussed above, below is a description of the relationships between **onchain deal states** and other economic states and activities in the protocol. +Given the **onchain deal states and their transitions** discussed above, below is a description of the relationships between **onchain deal states** and other economic states and activities in the protocol. - `Power`: only payload data in an Active storage deal counts towards power. - `Deal Payment`: happens on `_onSuccessfulPoSt` and at deal/sector expiration through `_submitPowerReport`, paying out `StoragePricePerEpoch` for each epoch since the last PoSt. diff --git a/content/systems/filecoin_markets/onchain_storage_market/storage_market_actor.md b/content/systems/filecoin_markets/onchain_storage_market/storage_market_actor.md index 8bab6ff95..ad1fe9c4b 100644 --- a/content/systems/filecoin_markets/onchain_storage_market/storage_market_actor.md +++ b/content/systems/filecoin_markets/onchain_storage_market/storage_market_actor.md @@ -12,7 +12,7 @@ math-mode: true # Storage Market Actor -The `StorageMarketActor` is responsible for processing and managing on-chain deals. This is also the entry point of all storage deals and data into the system. It maintains a mapping of `StorageDealID` to `StorageDeal` and keeps track of locked balances of `StorageClient` and `StorageProvider`. When a deal is posted on chain through the `StorageMarketActor`, it will first check if both transacting parties have sufficient balances locked up and include the deal on chain. +The `StorageMarketActor` is responsible for processing and managing on-chain deals. This is also the entry point of all storage deals and data into the system. It maintains a mapping of `StorageDealID` to `StorageDeal` and keeps track of locked balances of `StorageClient` and `StorageProvider`. When a deal is posted on chain through the `StorageMarketActor`, it will first check if both transacting parties have sufficient balances locked up and include the deal on chain. ## `StorageMarketActor` implementation @@ -24,9 +24,8 @@ The `StorageMarketActor` is responsible for processing and managing on-chain dea {{}} **Storage Market Actor Balance states and mutations** - -{{}} +{{}} ## Storage Deal Collateral diff --git a/content/systems/filecoin_markets/retrieval_market/_index.md b/content/systems/filecoin_markets/retrieval_market/_index.md index f622ebe24..50c907d3b 100644 --- a/content/systems/filecoin_markets/retrieval_market/_index.md +++ b/content/systems/filecoin_markets/retrieval_market/_index.md @@ -23,11 +23,10 @@ The main components are as follows: - A client module to query retrieval miners and initiate deals for retrieval - A provider module to respond to queries and deal proposals -The retrieval market operate by piggybacking on the Data Transfer system and Graphsync to handle transfer and verification, to support arbitrary selectors, and to reduce round trips. The retrieval market can support sending arbitrary payload CIDs & selectors within a piece. +The retrieval market operate by piggybacking on the Data Transfer system and Graphsync to handle transfer and verification, to support arbitrary selectors, and to reduce round trips. The retrieval market can support sending arbitrary payload CIDs & selectors within a piece. The Data Transfer System is augmented accordingly to support pausing/resuming and sending intermediate vouchers to facilitate this. - ## Deal Flow in the Retrieval Market ![Retrieval Flow](retrieval_flow_v1.mmd) @@ -56,7 +55,7 @@ Some extra notes worth making with regard to the above process are as follows: - The payment channel is created when the provider accepts the deal, unless an open payment channel already exists between the given client and provider. - The vouchers are also created by the client and (a reference/identifier to these vouchers is) sent to the provider. - The payment indicated in the voucher is not taken out of the payment channel funds upon creation and exchange of vouchers between the client and the provider. -- In order for money to be transferred to the provider's payment channel side, the provider has to *redeem* the voucher +- In order for money to be transferred to the provider's payment channel side, the provider has to _redeem_ the voucher - In order for money to be taken out of the payment channel, the provider has to submit the voucher on-chain and `Collect` the funds. - Both redeeming and collecting vouchers/funds can be done at any time during the data transfer, but redeeming vouchers and collecting funds involves the blockchain, which further means that it incurs gas cost. - Once the data transfer is complete, the client or provider may Settle the channel. There is then a 12hr period within which the provider has to submit the redeemed vouchers on-chain in order to collect the funds. Once the 12hr period is complete, the client may collect any unclaimed funds from the channel, and the provider loses the funds for vouchers they did not submit. @@ -64,21 +63,22 @@ Some extra notes worth making with regard to the above process are as follows: ## Bootstrapping Trust -Neither the client nor the provider have any specific reason to trust each other. Therefore, trust is established indirectly by payments for a retrieval deal done *incrementally*. This is achieved by sending vouchers as the data transfer progresses. +Neither the client nor the provider have any specific reason to trust each other. Therefore, trust is established indirectly by payments for a retrieval deal done _incrementally_. This is achieved by sending vouchers as the data transfer progresses. Trust establishment proceeds as follows: + - When the deal is created, client & provider agree to a "payment interval" in bytes, which is the _minimum_ amount of data the provider will send before each required increment. - They also agree to a "payment interval increment". This means that the interval will increase by this value after each successful transfer and payment, as trust develops between client and provider. - Example: - - If my "payment interval" is 1000, and my "payment interval increase" is 300, then: - - The provider must send at least 1000 bytes before they require any payment (they may end up sending slightly more because block boundaries are uneven). - - The client must pay (i.e., issue a voucher) for all bytes sent when the provider requests payment, provided that the provider has sent at least 1000 bytes. - - The provider now must send at least 1300 bytes before they request payment again. - - The client must pay (i.e., issue subsequent vouchers) for all bytes it has not yet paid for when the provider requests payment, assuming it has received at least 1300 bytes since last payment. - - The process continues until the end of the retrieval, when the last payment will simply be for the remainder of bytes. + - If my "payment interval" is 1000, and my "payment interval increase" is 300, then: + - The provider must send at least 1000 bytes before they require any payment (they may end up sending slightly more because block boundaries are uneven). + - The client must pay (i.e., issue a voucher) for all bytes sent when the provider requests payment, provided that the provider has sent at least 1000 bytes. + - The provider now must send at least 1300 bytes before they request payment again. + - The client must pay (i.e., issue subsequent vouchers) for all bytes it has not yet paid for when the provider requests payment, assuming it has received at least 1300 bytes since last payment. + - The process continues until the end of the retrieval, when the last payment will simply be for the remainder of bytes. ## Data Representation in the Retrieval Market -The retrieval market works based on the Payload CID. The PayloadCID is the hash that represents the root of the IPLD DAG of the UnixFS version of the file. At this stage the file is a raw system file with IPFS-style representation. In order for a client to request for some data under the retrieval market, they have to know the PayloadCID. It is important to highlight that PayloadCIDs are not stored or registered on-chain. +The retrieval market works based on the Payload CID. The PayloadCID is the hash that represents the root of the IPLD DAG of the UnixFS version of the file. At this stage the file is a raw system file with IPFS-style representation. In order for a client to request for some data under the retrieval market, they have to know the PayloadCID. It is important to highlight that PayloadCIDs are not stored or registered on-chain. {{}} diff --git a/content/systems/filecoin_markets/retrieval_market/deal_status.md b/content/systems/filecoin_markets/retrieval_market/deal_status.md index 89754a604..9ad51b008 100644 --- a/content/systems/filecoin_markets/retrieval_market/deal_status.md +++ b/content/systems/filecoin_markets/retrieval_market/deal_status.md @@ -1,5 +1,5 @@ --- -title: "Retrieval Deal Status" +title: 'Retrieval Deal Status' weight: 5 dashboardWeight: 2 dashboardState: stable diff --git a/content/systems/filecoin_markets/retrieval_market/retrieval_client.md b/content/systems/filecoin_markets/retrieval_market/retrieval_client.md index 8c8ab5ca0..caebde4fa 100644 --- a/content/systems/filecoin_markets/retrieval_market/retrieval_client.md +++ b/content/systems/filecoin_markets/retrieval_market/retrieval_client.md @@ -1,5 +1,5 @@ --- -title: "Retrieval Client" +title: 'Retrieval Client' weight: 3 dashboardWeight: 2 dashboardState: stable @@ -18,4 +18,4 @@ The Retrieval Client Depends On The Following Dependencies - **BlockStore**: Same as one used by data transfer module - **Data Transfer**: Module used for transferring payload. Writes to the blockstore. -{{}} \ No newline at end of file +{{}} diff --git a/content/systems/filecoin_markets/retrieval_market/retrieval_peer_resolver.md b/content/systems/filecoin_markets/retrieval_market/retrieval_peer_resolver.md index a432dd57f..25038ab30 100644 --- a/content/systems/filecoin_markets/retrieval_market/retrieval_peer_resolver.md +++ b/content/systems/filecoin_markets/retrieval_market/retrieval_peer_resolver.md @@ -1,5 +1,5 @@ --- -title: "Retrieval Peer Resolver" +title: 'Retrieval Peer Resolver' weight: 1 dashboardWeight: 2 dashboardState: stable @@ -18,4 +18,4 @@ It can be backed by both a local store of previous storage deals or by querying type PeerResolver interface { GetPeers(payloadCID cid.Cid) ([]RetrievalPeer, error) // TODO: channel } -``` \ No newline at end of file +``` diff --git a/content/systems/filecoin_markets/retrieval_market/retrieval_protocols.md b/content/systems/filecoin_markets/retrieval_market/retrieval_protocols.md index f967ff83f..2532ede53 100644 --- a/content/systems/filecoin_markets/retrieval_market/retrieval_protocols.md +++ b/content/systems/filecoin_markets/retrieval_market/retrieval_protocols.md @@ -1,5 +1,5 @@ --- -title: "Retrieval Protocols" +title: 'Retrieval Protocols' weight: 2 dashboardWeight: 2 dashboardState: stable @@ -18,4 +18,3 @@ The `retrieval market` is implemented using the following `libp2p` service. Request: CBOR Encoded RetrievalQuery Data Structure Response: CBOR Encoded RetrievalQueryResponse Data Structure - diff --git a/content/systems/filecoin_markets/retrieval_market/retrieval_provider.md b/content/systems/filecoin_markets/retrieval_market/retrieval_provider.md index 5dfdd20c1..4c98cc06b 100644 --- a/content/systems/filecoin_markets/retrieval_market/retrieval_provider.md +++ b/content/systems/filecoin_markets/retrieval_market/retrieval_provider.md @@ -1,5 +1,5 @@ --- -title: "Retrieval Provider (Miner)" +title: 'Retrieval Provider (Miner)' weight: 4 dashboardWeight: 2 dashboardState: stable diff --git a/content/systems/filecoin_markets/storage_market/_index.md b/content/systems/filecoin_markets/storage_market/_index.md index a7a9c2cb0..6dfc31344 100644 --- a/content/systems/filecoin_markets/storage_market/_index.md +++ b/content/systems/filecoin_markets/storage_market/_index.md @@ -27,9 +27,9 @@ The lifecycle for a deal within the storage market contains distinct phases: 1. **Discovery** - The client identifies miners and determines their current asks. 2. **Negotiation** (out of band) - Both parties come to an agreement about the terms of the deal, each party commits funds to the deal and data is transferred from the client to the provider. -4. **Publishing** - The deal is published on chain, making the storage provider publicly accountable for the deal. -5. **Handoff** - Once the deal is published, it is handed off and handled by the Storage Mining Subsystem. The Storage Mining Subsystem will add the data corresponding to the deal to a sector, -seal the sector, and tell the Storage Market Actor that the deal is in a sector, thereby marking the deal as active. +3. **Publishing** - The deal is published on chain, making the storage provider publicly accountable for the deal. +4. **Handoff** - Once the deal is published, it is handed off and handled by the Storage Mining Subsystem. The Storage Mining Subsystem will add the data corresponding to the deal to a sector, + seal the sector, and tell the Storage Market Actor that the deal is in a sector, thereby marking the deal as active. From that point on, the deal is handled by the Storage Mining Subsystem, which communicates with the Storage Market Actor in order to process deal payments. See [Storage Mining Subsystem](storage_mining) for more details. @@ -42,6 +42,7 @@ The following diagram outlines the phases of deal flow within the storage market Discovery is the client process of identifying storage providers (i.e. a miner) who (subject to agreement on the deal's terms) are offering to store the client's data. There are many ways which a client can use to identify a provider to store their data. The list below outlines the minimum discovery services a filecoin implementation MUST provide. As the network evolves, third parties may build systems that supplement or enhance these services. Discovery involves identifying providers and determining their current `StorageAsk`. The steps are as follows: + 1. A client queries the chain to retrieve a list of Storage Miner Actors who have registerd as miners with the StoragePowerActor. 2. A client may perform additional queries to each Storage Miner Actor to determine their properties. Among others, these properties can include worker address, sector size, libp2p Multiaddress etc. 3. Once the client identifies potentially suitable providers, it sends a direct libp2p message using the `Storage Query Protocol` to get each potential provider's current `StorageAsk`. @@ -53,7 +54,7 @@ A `StorageAsk` contains all the properties that a client will need to determine Negotiation is the out-of-band process during which a storage client and a storage provider come to an agreement about a storage deal and reach the point where a deal is published on chain. -Negotiation begins once a client has discovered a miner whose `StorageAsk` meets their desired criteria. The *recommended* order of operations for negotiating and publishing a deal is as follows: +Negotiation begins once a client has discovered a miner whose `StorageAsk` meets their desired criteria. The _recommended_ order of operations for negotiating and publishing a deal is as follows: 1. In order to propose a storage deal, the `StorageClient` calculates the piece commitment (`CommP`) for the data it intends to store. This is neccesary so that the `StorageProvider` can verify that the data the `StorageClient` sends to be stored matches the `CommP` in the `StorageDealProposal`. For more detail about the relationship between payloads, pieces, and `CommP` see [Piece](piece). 2. Before sending a proposal to the provider, the `StorageClient` adds funds for a deal, as necessary, to the `StorageMarketActor` (by calling `AddBalance`). @@ -62,7 +63,7 @@ Negotiation begins once a client has discovered a miner whose `StorageAsk` meets From this point onwards, execution moves to the `StorageProvider`. 4. The `StorageProvider` inspects the deal to verify that the deal's parameters match its own internal criteria (such as price, piece size, deal duration, etc). The `StorageProvider` rejects the proposal if the parameters don't match its own criteria by sending a rejection to the client over the -`Storage Deal Protocol`. + `Storage Deal Protocol`. 5. The `StorageProvider` queries the `StorageMarketActor` to verify the `StorageClient` has deposited enough funds to make the deal (i.e. the client's balance is greater than the total storage price) and rejects the proposal if it hasn't. 6. If all criteria are met, the `StorageProvider` responds using the `Storage Deal Protocol` to indicate an intent to accept the deal. @@ -70,7 +71,7 @@ From this point onwards execution moves back to the `StorageClient`. 7. The `StorageClient` opens a push request for the payload data using the `Data Transfer Module`, and sends the request to the provider along with a voucher containing the CID for the `StorageDealProposal`. 8. The `StorageProvider` checks the voucher and verifies that the CID matches the storage deal proposal it has received and verified but not put on chain already. If so, it accepts the data transfer request from the `StorageClient`. -9. The `Data Transfer Module` now transfers the payload data to be stored from the `StorageClient` to the `StorageProvider` using `GraphSync`. +9. The `Data Transfer Module` now transfers the payload data to be stored from the `StorageClient` to the `StorageProvider` using `GraphSync`. 10. Once complete, the `Data Transfer Module` notifies the `StorageProvider`. 11. The `StorageProvider` recalculates the piece commitment (`CommP`) from the data transfer that just completed and verifies it matches the piece commitment in the `StorageDealProposal`. @@ -91,8 +92,8 @@ Finally, the `StorageClient` verifies the deal. Now that a deal is published, it needs to be stored, sealed, and proven in order for the provider to be paid. See Storage Deal for more information about how deal payments are made. These later stages of a deal are handled by the [Storage Mining Subsystem](storage_mining). So the final task for the Storage Market is to handoff to the Storage Mining Subsystem. -1. The `StorageProvider` writes the serialized, padded piece to a shared [Filestore](filestore). -2. The `StorageProvider` calls `HandleStorageDeal` on the `StorageMiner` with the published `StorageDeal` and filestore path (in Go this is the `io.Reader`). +1. The `StorageProvider` writes the serialized, padded piece to a shared [Filestore](filestore). +2. The `StorageProvider` calls `HandleStorageDeal` on the `StorageMiner` with the published `StorageDeal` and filestore path (in Go this is the `io.Reader`). A note regarding the order of operations: the only requirement to publish a storage deal with the `StorageMarketActor` is that the `StorageDealProposal` is signed by the `StorageClient`, the publish message is signed by the `StorageProvider`, and both parties have deposited adequate funds/collateral in the `StorageMarketActor`. As such, it's not required that the steps listed above happen in this exact order. However, the above order is _recommended_ because it generally minimizes the ability of either party to act maliciously. @@ -111,10 +112,10 @@ The following data types are unique to the Storage Market: {{}} - Details about `StorageDealProposal` and `StorageDeal` (which are used in the Storage Market and elsewhere) specifically can be found in Storage Deal. ## Protocols + {{}} **Name**: Storage Query Protocol **Protocol ID**: `/fil//storage/ask/1.0.1` diff --git a/content/systems/filecoin_mining/_index.md b/content/systems/filecoin_mining/_index.md index 5780f88c0..ec8dd1916 100644 --- a/content/systems/filecoin_mining/_index.md +++ b/content/systems/filecoin_mining/_index.md @@ -23,6 +23,6 @@ primarily describes in detail what MUST and SHOULD happen here, and leaves ample various optimizations for implementers, miners, and users to make. In some parts, we describe algorithms that could be replaced by other, more optimized versions, but in those cases it is important that the **protocol constraints** are satisfied. The **protocol constraints** are -spelled out in clear detail. It is up +spelled out in clear detail. It is up to implementers who deviate from the algorithms presented here to ensure their modifications satisfy those constraints, especially those relating to protocol security. diff --git a/content/systems/filecoin_mining/miner_collaterals.md b/content/systems/filecoin_mining/miner_collaterals.md index f3635d538..4f55fab47 100644 --- a/content/systems/filecoin_mining/miner_collaterals.md +++ b/content/systems/filecoin_mining/miner_collaterals.md @@ -54,6 +54,6 @@ However, the protocol should provide liquidity for miners to support their minin In general, fault fees are slashed first from the soonest-to-vest unvested block rewards followed by the minerʼs account balance. When a minerʼs balance is insufficient to cover their minimum requirements, their ability to participate in consensus, win block rewards, and grow storage power will be restricted until their balance is restored. Overall, this reduces the initial pledge requirement and creates a sufficient economic deterrent for faults without slashing the miner's balance for every penalty. -## Storage Deal Collateral +## Storage Deal Collateral The third form of collateral is provided by the storage provider to collateralize deals. See the [Storage Market Actor](storage_market_actor) for further details on the Storage Deal Collateral. diff --git a/content/systems/filecoin_mining/sector/_index.md b/content/systems/filecoin_mining/sector/_index.md index 641b0b36a..9fb8bd364 100644 --- a/content/systems/filecoin_mining/sector/_index.md +++ b/content/systems/filecoin_mining/sector/_index.md @@ -12,7 +12,7 @@ dashboardTests: 0 Sectors are the basic units of storage on Filecoin. They have standard sizes, as well as well-defined time-increments for commitments. The size of a sector balances security concerns against usability. A sectorʼs lifetime is determined in the storage market, and sets the promised duration of the sector. -In the first iteration of the protocol, 32GiB and 64GiB sectors are supported. Maximum sector lifetime is determined by the proof algorithm. Maximum sector lifetime is initially 18 months. A sector naturally expires when it reaches the end of its lifetime. Additionally, the miner can extend the lifetime of their sectors. Rewards are earned and collaterals recovered when the miner fulfils their commitment. +In the first iteration of the protocol, 32GiB and 64GiB sectors are supported. Maximum sector lifetime is determined by the proof algorithm. Maximum sector lifetime is initially 18 months. A sector naturally expires when it reaches the end of its lifetime. Additionally, the miner can extend the lifetime of their sectors. Rewards are earned and collaterals recovered when the miner fulfils their commitment. Individual deals are formed when a storage miner and client are matched on Filecoinʼs storage market. The protocol does not distinguish miners matching with real clients from miners generating self-deals. However, **committed capacity** is a construction that is introduced to make self-dealing unnecessary and economically irrational. In earlier designs of the network, only sectors filled with deals increased the minerʼs likelihood of winning the block reward. This led to the expectation that miners would attack and exploit the network by playing the role of both storage provider and client, creating a malicious self-deal. @@ -28,11 +28,8 @@ This diagram shows the composition of an unsealed sector and a sealed sector. ![Unsealed Sectors and Sealed Sectors](sectors.png) - **Sector Storage & Window PoSt** The Lotus implementation of the Window PoSt scheduler can be found [here](https://github.com/filecoin-project/lotus/blob/master/storage/wdpost_sched.go) and the actual execution of Window PoSt on a sector can be found [here](https://github.com/filecoin-project/lotus/blob/master/storage/wdpost_run.go). The Lotus block store implementation for sectors can be found [here](https://github.com/filecoin-project/lotus/blob/master/storage/sectorblocks/blocks.go). - - diff --git a/content/systems/filecoin_mining/sector/adding_storage.md b/content/systems/filecoin_mining/sector/adding_storage.md index 24898a6b8..238c65585 100644 --- a/content/systems/filecoin_mining/sector/adding_storage.md +++ b/content/systems/filecoin_mining/sector/adding_storage.md @@ -10,12 +10,14 @@ dashboardTests: 0 # Adding Storage A Miner adds more storage in the form of Sectors. Adding more storage is a two-step process: + 1. **PreCommitting a Sector**: A Miner publishes a Sector's SealedCID and makes a deposit. The Sector is now registered to the Miner, and the Miner must ProveCommit the Sector or lose their deposit. 2. **ProveCommitting a Sector**: The Miner provides a Proof of Replication (PoRep) for the Sector. This proof must be submitted AFTER a delay (the InteractiveEpoch), and BEFORE PreCommit expiration. This two-step process provides assurance that the Miner's PoRep _actually proves_ that the Miner has replicated the Sector data and is generating proofs from it: -* ProveCommitments must happen AFTER the InteractiveEpoch (150 blocks after Sector PreCommit), as the randomness included at that epoch is used in the PoRep. -* ProveCommitments must happen BEFORE the PreCommit expiration, which is a boundary established to make sure Miners don't have enough time to "fake" PoRep generation. + +- ProveCommitments must happen AFTER the InteractiveEpoch (150 blocks after Sector PreCommit), as the randomness included at that epoch is used in the PoRep. +- ProveCommitments must happen BEFORE the PreCommit expiration, which is a boundary established to make sure Miners don't have enough time to "fake" PoRep generation. For each Sector successfully ProveCommitted, the Miner becomes responsible for continuously proving the existence of their Sectors' data. In return, the Miner is awarded storage power. @@ -26,8 +28,9 @@ Miners are granted storage power in exchange for the storage space they dedicate In order for a Miner to maximize storage power (and profit), they should take advantage of all available storage space immediately, _even before they find enough Clients to use this space_. To facilitate this, there are _two types_ of Sectors that may be sealed and ProveCommitted: -* **Regular Sector**: A Sector that contains Client data -* **Committed Capacity (CC) Sector**: A Sector with no data (all zeroes) + +- **Regular Sector**: A Sector that contains Client data +- **Committed Capacity (CC) Sector**: A Sector with no data (all zeroes) Miners are free to coose which types of Sectors to store. CC sectors, in particular, allow Miners to immediately make use of existing disk space: earning storage power and a higher chance at producing a block. Miners can decide if they should upgrade their CC sectors to take client deals or continue proving CC sectors. Currently, CC sectors store randomness by default in client implementation, but this does not preclude miners from storing any type of useful data that increase their private utility in CC sectors (as long as it is legal). The protocol expects that new use-cases and diversity will emerge out of such behaviour. @@ -36,7 +39,3 @@ To incentivize Miners to hoard storage space and dedicate it to Filecoin, CC Sec Miners upgrade their ProveCommitted CC Sectors by PreCommitting a Regular Sector, and specifying that it should replace an existing CC Sector. Once the Regular Sector is successfully ProveCommitted, it will replace the existing CC Sector. If the newly ProveCommitted Regular sector contains a Verified Client deal, i.e., a deal with higher Sector Quality, then the miner's storage power will increase accordingly. Upgrading capacity currently involves resealing, that is, creating a unique representation of the new data included in the Sector through a computationally intensive process. Looking ahead, committed capacity upgrades should eventually be possible without a reseal. A succinct and publicly verifiable proof that the committed capacity has been correctly replaced with replicated data should achieve this goal. However, this mechanism must be fully specified to preserve the security and incentives of the network before it can be implemented and is, therefore, left as a future improvement. - - - - diff --git a/content/systems/filecoin_mining/sector/lifecycle.md b/content/systems/filecoin_mining/sector/lifecycle.md index d1cc3dcd8..07d2ba915 100644 --- a/content/systems/filecoin_mining/sector/lifecycle.md +++ b/content/systems/filecoin_mining/sector/lifecycle.md @@ -26,7 +26,7 @@ Miners can extend the lifetime of a sector at any time, though the sector will b A sector can be in one of the following states. | State | Description | -|----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------| +| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | `Precommitted` | Miner seals sector and submits `miner.PreCommitSector` | | `Committed` | Miner generates a Seal proof and submits `miner.ProveCommitSector` | | `Active` | Miner generate valid PoSt proofs and timely submits `miner.SubmitWindowedPoSt` | diff --git a/content/systems/filecoin_mining/sector/sealing.md b/content/systems/filecoin_mining/sector/sealing.md index 27b286f00..108499f1b 100644 --- a/content/systems/filecoin_mining/sector/sealing.md +++ b/content/systems/filecoin_mining/sector/sealing.md @@ -11,10 +11,10 @@ dashboardTests: 0 Before a Sector can be used, the Miner must _seal_ the Sector: encode the data in the Sector to prepare it for the proving process. -* **Unsealed Sector**: A Sector of raw data. - * **UnsealedCID (CommD)**: The root hash of the Unsealed Sector's merkle tree. Also called CommD, or "data commitment." -* **Sealed Sector**: A Sector that has been encoded to prepare it for the proving process. - * **SealedCID (CommR)**: The root hash of the Sealed Sector's merkle tree. Also called CommR, or "replica commitment." +- **Unsealed Sector**: A Sector of raw data. + - **UnsealedCID (CommD)**: The root hash of the Unsealed Sector's merkle tree. Also called CommD, or "data commitment." +- **Sealed Sector**: A Sector that has been encoded to prepare it for the proving process. + - **SealedCID (CommR)**: The root hash of the Sealed Sector's merkle tree. Also called CommR, or "replica commitment." Sealing a sector through Proof-of-Replication (PoRep) is a computation-intensive process that results in a unique encoding of the sector. Once data is sealed, storage miners: generate a proof; run a SNARK on the proof to compress it; and finally, submit the result of the compression to the blockchain as a certification of the storage commitment. Depending on the PoRep algorithm and protocol security parameters, cost profiles and performance characteristics vary and tradeoffs have to be made among sealing cost, security, onchain footprint, retrieval latency and so on. However, sectors can be sealed with commercial hardware and sealing cost is expected to decrease over time. The Filecoin Protocol will launch with Stacked Depth Robust (SDR) PoRep with a planned upgrade to Narrow Stacked Expander (NSE) PoRep with improvement in both cost and retrieval latency. @@ -23,17 +23,18 @@ The Lotus-specific set of functions applied to the sealing of a sector can be fo ## Randomness Randomness is an important attribute that helps the network verify the integrity of Miners' stored data. Filecoin's block creation process includes two types of randomness: -* [DRAND](drand): Values pulled from a distributed random beacon -* VRF: The output of a _Verifiable Random Function_ (VRF), which takes the previous block's VRF value and produces the current block's VRF value. + +- [DRAND](drand): Values pulled from a distributed random beacon +- VRF: The output of a _Verifiable Random Function_ (VRF), which takes the previous block's VRF value and produces the current block's VRF value. Each block produced in Filecoin includes values pulled from these two sources of randomness. When Miners submit proofs about their stored data, the proofs incorporate references to randomness added at specific epochs. Assuming these values were not able to be predicted ahead of time, this helps ensure that Miners generated proofs at a specific point in time. There are two proof types. Each uses one of the two sources of randomness: -* Windowed PoSt: Uses Drand values -* Proof of Replication (PoRep): Uses VRF values +- Windowed PoSt: Uses Drand values +- Proof of Replication (PoRep): Uses VRF values ## Drawing randomness for sector commitments @@ -49,8 +50,8 @@ We present precisely how ticket selection and verification should work. In the b - `X`-- round in which SEALing starts - `Z`-- round in which the SEAL appears (in a block) - `Y`-- round announced in the SEAL `commitSector` (should be X, but a miner could use any Y <= X), denoted by the ticket selection - - `T`-- estimated time for SEAL, dependent on sector size - - `G = T + variance`-- necessary flexibility to account for network delay and SEAL-time variance. +- `T`-- estimated time for SEAL, dependent on sector size +- `G = T + variance`-- necessary flexibility to account for network delay and SEAL-time variance. We expect Filecoin will be able to produce estimates for sector commitment time based on sector sizes, e.g.: `(estimate, variance) <--- SEALTime(sectors)` @@ -60,7 +61,6 @@ G and T will be selected using these. **Verifying a Seal's ticket:** When verifying a SEAL in round Z, a verifier should ensure that the ticket used to generate the SEAL is found in the range of rounds `[Z-T-F-G, Z-T-F+G]`. - ```text Prover ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ diff --git a/content/systems/filecoin_mining/sector/sector-faults.md b/content/systems/filecoin_mining/sector/sector-faults.md index 1897c8f7c..8b765ac73 100644 --- a/content/systems/filecoin_mining/sector/sector-faults.md +++ b/content/systems/filecoin_mining/sector/sector-faults.md @@ -11,6 +11,6 @@ dashboardTests: 0 It is very important for storage providers to have a strong incentive to both report the failure to the chain and attempt recovery from the fault in order to uphold the storage guarantee for the networkʼs clients. Without this incentive, it is impossible to distinguish an honest minerʼs hardware failure from malicious behavior, which is necessary to treat miners fairly. The size of the fault fees depend on the severity of the failure and the rewards that the miner is expected to earn from the sector to make sure incentives are aligned. The three types of sector storage fault fees are: -* **Sector fault fee:** This fee is paid per sector per day while the sector is in a faulty state. The size of the fee is slightly more than the amount the sector is expected to earn per day in block rewards. If a sector remains faulty for more than 2 consecutive weeks, the sector will pay a termination fee and be _removed from the chain state_. As storage miner reliability increases above a reasonable threshold, the risk posed by these fees decreases rapidly. -* **Sector fault detection fee:** This is a one-time fee paid in the event of a failure if the miner does not report it honestly and instead the unreported failure is caught by the chain. Given the probabilistic nature of our PoSt checks, this is set to a few days worth of block reward that would be expected to be earned by a particular sector. -* **Sector termination fee:** A sector can be terminated before its expiration through automatic faults or miner decisions. A termination fee is charged that is, in principle, equivalent to how much a sector has earned so far, up to a limit in order to avoid discouraging long sector lifetimes. In an active termination, the miner decides to stop mining and they pay a fee to leave. In a fault termination, a sector is in a faulty state for too long, and the chain terminates the deal, returns unpaid deal fees to the client and penalizes the miner. Termination fee is currently capped at 90 days worth of block reward that a sector will earn. Miners are responsible for deciding to comply with local regulations, and may sometimes need to accept a termination fee for complying with content laws. Many of the concepts and parameters above make use of the notion of “how much a sector would have earned in a day” in order to understand and align incentives for participants. This concept is robustly tracked and extrapolated on chain. \ No newline at end of file +- **Sector fault fee:** This fee is paid per sector per day while the sector is in a faulty state. The size of the fee is slightly more than the amount the sector is expected to earn per day in block rewards. If a sector remains faulty for more than 2 consecutive weeks, the sector will pay a termination fee and be _removed from the chain state_. As storage miner reliability increases above a reasonable threshold, the risk posed by these fees decreases rapidly. +- **Sector fault detection fee:** This is a one-time fee paid in the event of a failure if the miner does not report it honestly and instead the unreported failure is caught by the chain. Given the probabilistic nature of our PoSt checks, this is set to a few days worth of block reward that would be expected to be earned by a particular sector. +- **Sector termination fee:** A sector can be terminated before its expiration through automatic faults or miner decisions. A termination fee is charged that is, in principle, equivalent to how much a sector has earned so far, up to a limit in order to avoid discouraging long sector lifetimes. In an active termination, the miner decides to stop mining and they pay a fee to leave. In a fault termination, a sector is in a faulty state for too long, and the chain terminates the deal, returns unpaid deal fees to the client and penalizes the miner. Termination fee is currently capped at 90 days worth of block reward that a sector will earn. Miners are responsible for deciding to comply with local regulations, and may sometimes need to accept a termination fee for complying with content laws. Many of the concepts and parameters above make use of the notion of “how much a sector would have earned in a day” in order to understand and align incentives for participants. This concept is robustly tracked and extrapolated on chain. diff --git a/content/systems/filecoin_mining/sector/sector-quality/_index.md b/content/systems/filecoin_mining/sector/sector-quality/_index.md index f6edcf4dc..28b7dbe44 100644 --- a/content/systems/filecoin_mining/sector/sector-quality/_index.md +++ b/content/systems/filecoin_mining/sector/sector-quality/_index.md @@ -15,7 +15,7 @@ Given different sector contents, not all sectors have the same usefulness to the - **Sector Spacetime:** This measurement is the sector size multiplied by its promised duration in byte-epochs. - **Deal Weight:** This weight converts spacetime occupied by deals into consensus power. Deal weight of verified client deals in a sector is called Verified Deal Weight and will be greater than the regular deal weight. - **Deal Quality Multiplier:** This factor is assigned to different deal types (committed -capacity, regular deals, and verified client deals) to reward different content. + capacity, regular deals, and verified client deals) to reward different content. - **Sector Quality Multiplier:** Sector quality is assigned on Activation (the epoch when the miner starts proving theyʼre storing the file). The sector quality multiplier is computed as an average of deal quality multipliers (committed capacity, regular deals, and verified client deals), weighted by the amount of spacetime each type of deal occupies in the sector. {{}} @@ -31,24 +31,22 @@ The high quality multiplier and easy verification process for verified client de ![Sector Quality](sector-quality.jpg) - **Sector Quality Adjusted Power** is a weighted average of the quality of its space and it is based on the size, duration and quality of its deals. -| Name | Description | -|------------------------------|-------------------------------------------------------| -| QualityBaseMultiplier (QBM) | Multiplier for power for storage without deals. | -| DealWeightMultiplier (DWM) | Multiplier for power for storage with deals. | +| Name | Description | +| ----------------------------------- | ----------------------------------------------------- | +| QualityBaseMultiplier (QBM) | Multiplier for power for storage without deals. | +| DealWeightMultiplier (DWM) | Multiplier for power for storage with deals. | | VerifiedDealWeightMultiplier (VDWM) | Multiplier for power for storage with verified deals. | - The formula for calculating Sector Quality Adjusted Power (or QAp, often referred to as power) makes use of the following factors: + - `dealSpaceTime`: sum of the `duration*size` of each deal - `verifiedSpaceTime`: sum of the `duration*size` of each verified deal - `baseSpaceTime` (spacetime without deals): `sectorSize*sectorDuration - dealSpaceTime - verifiedSpaceTime` Based on these the average quality of a sector is: - {{}} $avgQuality = \frac{baseSpaceTime*QBM + dealSpaceTime*DWM + verifiedSpaceTime*VDWM}{sectorSize*sectorDuration*QBM}$ {{}} diff --git a/content/systems/filecoin_mining/sector/sector-recovery.md b/content/systems/filecoin_mining/sector/sector-recovery.md index 490af9ce5..be16d952f 100644 --- a/content/systems/filecoin_mining/sector/sector-recovery.md +++ b/content/systems/filecoin_mining/sector/sector-recovery.md @@ -13,4 +13,4 @@ Miners should try to recover faulty sectors in order to avoid paying the penalty Note that if a sector is in a faulty state for 14 consecutive days it will be terminated and the miner will receive a penalty. The miner can terminate the sector themselves by calling `TerminationDeclaration`, if they know that they cannot recover it, in which case they will receive a smaller penalty fee. -Both the `RecoveryDeclaration` and the `TerminationDeclaration` can be found in the [miner actor implementation](https://github.com/filecoin-project/specs-actors/blob/master/actors/builtin/miner/miner_actor.go). \ No newline at end of file +Both the `RecoveryDeclaration` and the `TerminationDeclaration` can be found in the [miner actor implementation](https://github.com/filecoin-project/specs-actors/blob/master/actors/builtin/miner/miner_actor.go). diff --git a/content/systems/filecoin_mining/storage_mining/_index.md b/content/systems/filecoin_mining/storage_mining/_index.md index 0c4d2b28a..c4d69c41d 100644 --- a/content/systems/filecoin_mining/storage_mining/_index.md +++ b/content/systems/filecoin_mining/storage_mining/_index.md @@ -23,7 +23,6 @@ The above involves a number of steps to putting on and maintaining online storag - Continuously proving storage (see [WinningPoSt](expected_consensus#winning-a-block) and Window PoSt) - Declaring [storage faults](sector#sector-faults) and recovering from them. - ## Filecoin Proofs ### Proof of Replication @@ -39,20 +38,23 @@ When Miners commit to storing data, they must first produce a valid Proof of Rep ### Proof of Spacetime A [Proof of Spacetime (aka PoSt)](post) is a long-term assurance of a Miner's continuous storage of their Sectors' data. _This is not a single proof,_ but a collection of proofs the Miner has submitted over time. Periodically, a Miner must add to these proofs by submitting a **WindowPoSt**: -* Fundamentally, a WindowPoSt is a collection of merkle proofs over the underlying data in a Miner's Sectors. -* WindowPoSts bundle proofs of various leaves across groups of Sectors (called **Partitions**). -* These proofs are submitted as a single SNARK. + +- Fundamentally, a WindowPoSt is a collection of merkle proofs over the underlying data in a Miner's Sectors. +- WindowPoSts bundle proofs of various leaves across groups of Sectors (called **Partitions**). +- These proofs are submitted as a single SNARK. The historical and ongoing submission of WindowPoSts creates assurance that the Miner has been storing, and continues to store the Sectors they agreed to store in the storage deal. Once a Miner successfully adds and ProveCommits a Sector, the Sector is assigned to a Deadline: a specific window of time during which PoSts must be submitted. The day is broken up into 48 individual Deadlines of 30 minutes each, and ProveCommitted Sectors are assigned to one of these 48 Deadlines. -* PoSts may only be submitted for the currently-active Deadline. Deadlines are open for 30 minutes, starting from the Deadline's "Open" epoch and ending at its "Close" epoch. -* PoSts must incorporate randomness pulled from a random beacon. This randomness becomes publicly available at the Deadline's "Challenge" epoch, which is 20 epochs prior to its "Open" epoch. -* Deadlines also have a `FaultCutoff` epoch, 70 epochs prior to its "Open" epoch. After this epoch, Faults can no longer be declared for the Deadline's Sectors. + +- PoSts may only be submitted for the currently-active Deadline. Deadlines are open for 30 minutes, starting from the Deadline's "Open" epoch and ending at its "Close" epoch. +- PoSts must incorporate randomness pulled from a random beacon. This randomness becomes publicly available at the Deadline's "Challenge" epoch, which is 20 epochs prior to its "Open" epoch. +- Deadlines also have a `FaultCutoff` epoch, 70 epochs prior to its "Open" epoch. After this epoch, Faults can no longer be declared for the Deadline's Sectors. ## Miner Accounting A Miner's financial gain or loss is affected by the following three actions: + 1. Miners deposit tokens to act as collateral for their `PreCommitted` and `ProveCommitted` Sectors 2. Miners earn tokens from block rewards, when they are elected to mine a new block and extend the blockchain. 3. Miners lose tokens if they fail to prove storage of a sector and are given penalties as a result. @@ -60,24 +62,26 @@ A Miner's financial gain or loss is affected by the following three actions: ### Balance Requirements A Miner's token balance MUST cover ALL of the following: -* **PreCommit Deposits**: When a Miner PreCommits a Sector, they must supply a "precommit deposit" for the Sector, which acts as collateral. If the Sector is not ProveCommitted on time, this deposit is removed and burned. -* **Initial Pledge**: When a Miner ProveCommits a Sector, they must supply an "initial pledge" for the Sector, which acts as collateral. If the Sector is terminated, this deposit is removed and burned along with rewards earned by this sector up to a limit. -* **Locked Funds**: When a Miner receives tokens from block rewards, the tokens are locked and added to the Miner's vesting table to be unlocked linearly over some future epochs. + +- **PreCommit Deposits**: When a Miner PreCommits a Sector, they must supply a "precommit deposit" for the Sector, which acts as collateral. If the Sector is not ProveCommitted on time, this deposit is removed and burned. +- **Initial Pledge**: When a Miner ProveCommits a Sector, they must supply an "initial pledge" for the Sector, which acts as collateral. If the Sector is terminated, this deposit is removed and burned along with rewards earned by this sector up to a limit. +- **Locked Funds**: When a Miner receives tokens from block rewards, the tokens are locked and added to the Miner's vesting table to be unlocked linearly over some future epochs. ### Faults, Penalties and Fee Debt **Faults** A Sector's PoSts must be submitted on time, or that Sector is marked "faulty." There are three types of faults: -* **Declared Fault**: When the Miner explicitly declares a Sector "faulty" _before_ its Deadline's FaultCutoff. Recall that `WindowPoSt` proofs are submitted per partition for a specific `ChallengeWindow`. A miner has to declare the sector as faulty before the `ChallengeWindow` for the particular partition opens. Until the sectors are recovered they will be masked from proofs in subsequent proving periods. -* **Detected Fault**: Partitions of sectors without PoSt proof verification records, which have not been declared faulty before the `FaultCutoff` epoch's deadline are marked as detected faults. -* **Skipped Fault**: If a sector is currently in active or recovering state and has not been declared faulty before, but the miner's PoSt submission does not include a proof for this sector, then this is a "skipped fault" sector (also referred to as "skipped undeclared fault"). In other words, when a miner submits PoSt proofs for a partition but does not include proofs for some sectors in the partition, then these sectors are in "skipped fault" state. This is in contrast to the "detected fault" state, where the miner does not submit a PoSt proof for any section in the partition at all. The skipped fault is helpful in case a sector becomes faulty after the `FaultCutoff` epoch. + +- **Declared Fault**: When the Miner explicitly declares a Sector "faulty" _before_ its Deadline's FaultCutoff. Recall that `WindowPoSt` proofs are submitted per partition for a specific `ChallengeWindow`. A miner has to declare the sector as faulty before the `ChallengeWindow` for the particular partition opens. Until the sectors are recovered they will be masked from proofs in subsequent proving periods. +- **Detected Fault**: Partitions of sectors without PoSt proof verification records, which have not been declared faulty before the `FaultCutoff` epoch's deadline are marked as detected faults. +- **Skipped Fault**: If a sector is currently in active or recovering state and has not been declared faulty before, but the miner's PoSt submission does not include a proof for this sector, then this is a "skipped fault" sector (also referred to as "skipped undeclared fault"). In other words, when a miner submits PoSt proofs for a partition but does not include proofs for some sectors in the partition, then these sectors are in "skipped fault" state. This is in contrast to the "detected fault" state, where the miner does not submit a PoSt proof for any section in the partition at all. The skipped fault is helpful in case a sector becomes faulty after the `FaultCutoff` epoch. Note that the "skipped fault" allows for sector-wise fault penalties, as compared to partition-wide faults and penalties, as is the case with "detected faults". **Deadlines** -A *deadline* is a period of `WPoStChallengeWindow` epochs that divides a proving period. +A _deadline_ is a period of `WPoStChallengeWindow` epochs that divides a proving period. Sectors are assigned to a deadline on `miner.ProveCommitSector` and will remain assigned to it throughout their lifetime. Recall that Sectors are also assigned to a partition. A miner must submit a `miner.SubmitWindowedPoSt` for each deadline. @@ -85,13 +89,12 @@ A miner must submit a `miner.SubmitWindowedPoSt` for each deadline. There are four relevant epochs associated to a deadline: | Name | Distance from `Open` | Description | -|---------------|---------------------------|-------------------------------------------------------------------------------------------------------------------------------| +| ------------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | | `Open` | `0` | Epoch from which a PoSt Proof for this deadline can be submitted. | | `Close` | `WPoStChallengeWindow` | Epoch after which a PoSt Proof for this deadline will be rejected. | | `FaultCutoff` | `-FaultDeclarationCutoff` | Epoch after which a `miner.DeclareFault` and `miner.DeclareFaultRecovered` for sectors in the upcoming deadline are rejected. | | `Challenge` | `-WPoStChallengeLookback` | Epoch at which the randomness for the challenges is available. | - **Fault Recovery** Regardless of how a fault first becomes known (declared, skipped, detected), the sector stays faulty and is excluded from future proofs until the miner explicitly declares it recovered. The declaration of recovery restores the sector to the proving set at the start of the subsequent proving period. When a PoSt for a just-recovered sector is received, power for that sector is restored. @@ -99,16 +102,18 @@ Regardless of how a fault first becomes known (declared, skipped, detected), the **Penalties** A Miner may accrue penalties for many reasons: -* **PreCommit Expiry Penalty**: Occurs if a Miner fails to `ProveCommit` a PreCommitted Sector in time. This happens the first time that a miner declares that it proves a sector and falls into the PoRep consensus. -* **Undeclared Fault Penalty**: Occurs if a Miner fails to submit a PoSt for a Sector on time. Depending on whether the "Skipped Fault" option is implemented, this penalty applies to either a sector or a whole partition. -* **Declared Fault Penalty**: Occurs if a Miner fails to submit a PoSt for a Sector on time, but they declare the Sector faulty before the system finds out (in which case the fault falls in the "Undeclared Fault Penalty" above). **This penalty fee should be lower than the undeclared fault penalty**, in order to incentivize Miners to declare faults early. -* **Ongoing Fault Penalty**: Occurs every Proving Period a Miner fails to submit a PoSt for a Sector. -* **Termination Penalty**: Occurs if a Sector is terminated before its expiration. -* **Consensus Fault Penalty**: Occurs if a Miner commits a consensus fault and is reported. + +- **PreCommit Expiry Penalty**: Occurs if a Miner fails to `ProveCommit` a PreCommitted Sector in time. This happens the first time that a miner declares that it proves a sector and falls into the PoRep consensus. +- **Undeclared Fault Penalty**: Occurs if a Miner fails to submit a PoSt for a Sector on time. Depending on whether the "Skipped Fault" option is implemented, this penalty applies to either a sector or a whole partition. +- **Declared Fault Penalty**: Occurs if a Miner fails to submit a PoSt for a Sector on time, but they declare the Sector faulty before the system finds out (in which case the fault falls in the "Undeclared Fault Penalty" above). **This penalty fee should be lower than the undeclared fault penalty**, in order to incentivize Miners to declare faults early. +- **Ongoing Fault Penalty**: Occurs every Proving Period a Miner fails to submit a PoSt for a Sector. +- **Termination Penalty**: Occurs if a Sector is terminated before its expiration. +- **Consensus Fault Penalty**: Occurs if a Miner commits a consensus fault and is reported. When a Miner accrues penalties, the amount penalized is tracked as "Fee Debt." If a Miner has Fee Debt, they are restricted from certain actions until the amount owed is paid off. Miners with Fee Debt may not: -* PreCommit new Sectors -* Declare faulty Sectors "recovered" -* Withdraw balance + +- PreCommit new Sectors +- Declare faulty Sectors "recovered" +- Withdraw balance Faults are implied to be "temporary" - that is, a Miner that temporarily loses internet connection may choose to declare some Sectors for their upcoming proving period as faulty, because the Miner knows they will eventually regain the ability to submit proofs for those Sectors. This declaration allows the Miner to still submit a valid proof for their Deadline (minus the faulty Sectors). This is very important for Miners, as missing a Deadline's PoSt entirely incurs a high penalty. diff --git a/content/systems/filecoin_mining/storage_mining/mining_cycle/_index.md b/content/systems/filecoin_mining/storage_mining/mining_cycle/_index.md index a32307bf7..4683e620a 100644 --- a/content/systems/filecoin_mining/storage_mining/mining_cycle/_index.md +++ b/content/systems/filecoin_mining/storage_mining/mining_cycle/_index.md @@ -27,13 +27,13 @@ After the chain has caught up to the current head using [ChainSync](chainsync), - The node receives and transmits messages using the [Message Syncer](message_syncer) - At the same time the node receives blocks through [BlockSync](block_sync). - - Each block has an associated timestamp and epoch (quantized time window in which it was crafted) - - Blocks are validated as they come in [block validation](block) -- After an epoch's "cutoff", the miner should take all the valid blocks received for this epoch and assemble them into tipsets according to [Tipset validation rules](tipset) + - Each block has an associated timestamp and epoch (quantized time window in which it was crafted) + - Blocks are validated as they come in [block validation](block) +- After an epoch's "cutoff", the miner should take all the valid blocks received for this epoch and assemble them into tipsets according to [Tipset validation rules](tipset) - The miner then attempts to mine atop the heaviest tipset (as calculated with [EC's weight function](expected_consensus#chain-selection)) using its smallest ticket to run leader election - - The miner runs [Leader Election](expected_consensus#secret-leader-election) using the most recent [random](storage_power_consensus#beacon-entries) output by a [drand](drand) beacon. - - if this yields a valid `ElectionProof`, the miner generates a new [ticket](storage_power_consensus#tickets) and winning PoSt for inclusion in the block. - - the miner then assembles a new block (see "block creation" below) and waits until this epoch's quantized timestamp to broadcast it + - The miner runs [Leader Election](expected_consensus#secret-leader-election) using the most recent [random](storage_power_consensus#beacon-entries) output by a [drand](drand) beacon. + - if this yields a valid `ElectionProof`, the miner generates a new [ticket](storage_power_consensus#tickets) and winning PoSt for inclusion in the block. + - the miner then assembles a new block (see "block creation" below) and waits until this epoch's quantized timestamp to broadcast it This process is repeated until either the [Leader Election](expected_consensus#secret-leader-election) process yields a winning ticket (in EC) and the miner publishes a block or a new valid block comes in from the network. @@ -51,24 +51,24 @@ Anytime a miner receives new valid blocks, it should evaluate what is the heavie The timing diagram above describes the sequence of block creation "mining", propagation and reception. -This sequence of events applies only when the node is in the `CHAIN_FOLLOW` syncing mode. Nodes in other syncing modes do not mine blocks. +This sequence of events applies only when the node is in the `CHAIN_FOLLOW` syncing mode. Nodes in other syncing modes do not mine blocks. The upper row represents the conceptual consumption channel consisting of successive receiving periods `Rx` during which nodes validate incoming blocks. -The lower row is the conceptual production channel made up of a period of mining `M` followed by a period of transmission `Tx` (which lasts long enough for blocks to propagate throughout the network). The lengths of the periods are not to scale. +The lower row is the conceptual production channel made up of a period of mining `M` followed by a period of transmission `Tx` (which lasts long enough for blocks to propagate throughout the network). The lengths of the periods are not to scale. The above diagram represents the important events within an epoch: - **Epoch boundary**: change of current epoch. New blocks mined are mined in new epoch, and timestamped accordingly. - **Epoch cutoff**: blocks from the prior epoch propagated on the network are no longer accepted. Miners can form a new tipset to mine on. -In an epoch, blocks are received and validated during `Rx` up to the prior epoch's cutoff. At the cutoff, the miner computes the heaviest tipset from the blocks received during `Rx`, and uses it as the head to build on during the next mining period `M`. If mining is successful, the miner sets the block's timestamp to the epoch boundary and waits until the boundary to release the block. While some blocks could be submitted a bit later, blocks are all transmitted during `Tx`, the transmission period. +In an epoch, blocks are received and validated during `Rx` up to the prior epoch's cutoff. At the cutoff, the miner computes the heaviest tipset from the blocks received during `Rx`, and uses it as the head to build on during the next mining period `M`. If mining is successful, the miner sets the block's timestamp to the epoch boundary and waits until the boundary to release the block. While some blocks could be submitted a bit later, blocks are all transmitted during `Tx`, the transmission period. The timing validation rules are as follows: - Blocks whose timestamps are not exactly on the epoch boundary are rejected. - Blocks received with a timestamp in the future are rejected. - Blocks received after the cutoff are rejected. - - Note that those blocks are not invalid, just not considered for the miner's own tipset building. Tipsets received with such a block as a parent should be accepted. + - Note that those blocks are not invalid, just not considered for the miner's own tipset building. Tipsets received with such a block as a parent should be accepted. In a fully synchronized network most of period `Rx` does not see any network traffic, only its beginning should. While there may be variance in operator mining time, most miners are expected to finish mining by the epoch boundary. diff --git a/content/systems/filecoin_mining/storage_mining/storage_miner_actor.md b/content/systems/filecoin_mining/storage_mining/storage_miner_actor.md index e66e94c6c..45b47682a 100644 --- a/content/systems/filecoin_mining/storage_mining/storage_miner_actor.md +++ b/content/systems/filecoin_mining/storage_mining/storage_miner_actor.md @@ -11,7 +11,6 @@ dashboardTests: 0 # Storage Miner Actor - {{}} {{}} diff --git a/content/systems/filecoin_mining/storage_proving/poster/_index.md b/content/systems/filecoin_mining/storage_proving/poster/_index.md index 3c0c4ea4b..8310834f4 100644 --- a/content/systems/filecoin_mining/storage_proving/poster/_index.md +++ b/content/systems/filecoin_mining/storage_proving/poster/_index.md @@ -9,7 +9,7 @@ dashboardTests: 0 # Sector Poster ## PoSt Generator object + {{}} {{}} - diff --git a/content/systems/filecoin_nodes/clock/_index.md b/content/systems/filecoin_nodes/clock/_index.md index 52ccb3ca0..3ba084fd5 100644 --- a/content/systems/filecoin_nodes/clock/_index.md +++ b/content/systems/filecoin_nodes/clock/_index.md @@ -11,14 +11,13 @@ dashboardTests: 0 Filecoin assumes weak clock synchrony amongst participants in the system. That is, the system relies on participants having access to a globally synchronized clock (tolerating some bounded offset). -Filecoin relies on this system clock in order to secure consensus. Specifically, the clock is necessary to support validation rules that prevent block producers from mining blocks with a future timestamp and running leader elections more frequently than the protocol allows. - +Filecoin relies on this system clock in order to secure consensus. Specifically, the clock is necessary to support validation rules that prevent block producers from mining blocks with a future timestamp and running leader elections more frequently than the protocol allows. ## Clock uses The Filecoin system clock is used: -- by syncing nodes to validate that incoming blocks were mined in the appropriate epoch given their timestamp (see [Block Validation](block#block-syntax-validation)). This is possible because the system clock maps all times to a unique epoch number totally determined by the start time in the genesis block. +- by syncing nodes to validate that incoming blocks were mined in the appropriate epoch given their timestamp (see [Block Validation](block#block-syntax-validation)). This is possible because the system clock maps all times to a unique epoch number totally determined by the start time in the genesis block. - by syncing nodes to drop blocks coming from a future epoch - by mining nodes to maintain protocol liveness by allowing participants to try leader election in the next round if no one has produced a block in the current round (see [Storage Power Consensus](storage_power_consensus)). @@ -29,7 +28,6 @@ In order to allow miners to do the above, the system clock must: It is expected that other subsystems will register to a `NewRound()` event from the clock subsystem. - ## Clock Requirements Clocks used as part of the Filecoin protocol should be kept in sync, with offset less than 1 second so as to enable appropriate validation. @@ -44,4 +42,4 @@ Computer-grade crystals can be expected to deviate by [1ppm](https://www.hindawi - `time.nist.gov` ([details](https://tf.nist.gov/tf-cgi/servers.cgi)) - Larger mining operations MAY consider using local NTP/PTP servers with GPS references and/or frequency-stable external clocks for improved timekeeping. -Mining operations have a strong incentive to prevent their clock skewing ahead more than one epoch to keep their block submissions from being rejected. Likewise they have an incentive to prevent their clocks skewing behind more than one epoch to avoid partitioning themselves off from the synchronized nodes in the network. +Mining operations have a strong incentive to prevent their clock skewing ahead more than one epoch to keep their block submissions from being rejected. Likewise they have an incentive to prevent their clocks skewing behind more than one epoch to avoid partitioning themselves off from the synchronized nodes in the network. diff --git a/content/systems/filecoin_nodes/node_types/_index.md b/content/systems/filecoin_nodes/node_types/_index.md index 70563b04a..e7e13e510 100644 --- a/content/systems/filecoin_nodes/node_types/_index.md +++ b/content/systems/filecoin_nodes/node_types/_index.md @@ -11,6 +11,7 @@ dashboardTests: 0 # Node Types Nodes in the Filecoin network are primarily identified in terms of the services they provide. The type of node, therefore, depends on which services a node provides. A basic set of services in the Filecoin network include: + - chain verification - storage market client - storage market provider @@ -42,6 +43,7 @@ type ChainVerifierNode interface { systems.Blockchain } ``` + The Lotus implementation of the Chain Verifier Node can be found [here](https://github.com/filecoin-project/lotus/blob/master/node/impl/full.go). ## Client Node @@ -56,6 +58,7 @@ type ClientNode struct { markets.DataTransfers } ``` + The Lotus implementation of the Client Node can be found [here](https://github.com/filecoin-project/lotus/blob/master/node/impl/client/client.go). ## Storage Miner Node @@ -70,6 +73,7 @@ type StorageMinerNode interface { markets.DataTransfers } ``` + The Lotus implementation of the Storage Miner Node can be found [here](https://github.com/filecoin-project/lotus/blob/master/node/impl/storminer.go). ## Retrieval Miner Node @@ -96,4 +100,4 @@ type RelayerNode interface { ## Node Configuration -The Lotus implementation of Filecoin Node configuration values can be found [here](https://github.com/filecoin-project/lotus/blob/master/node/config/def.go). \ No newline at end of file +The Lotus implementation of Filecoin Node configuration values can be found [here](https://github.com/filecoin-project/lotus/blob/master/node/config/def.go). diff --git a/content/systems/filecoin_nodes/repository/_index.md b/content/systems/filecoin_nodes/repository/_index.md index d1499030c..ee0164023 100644 --- a/content/systems/filecoin_nodes/repository/_index.md +++ b/content/systems/filecoin_nodes/repository/_index.md @@ -16,4 +16,4 @@ The repository is accessible to the node's systems and subsystems and can be com The repository stores the node's keys, the IPLD data structures of stateful objects as well as the node configuration settings. -The Lotus implementation of the FileStore Repository can be found [here](https://github.com/filecoin-project/lotus/blob/master/node/repo/fsrepo.go). \ No newline at end of file +The Lotus implementation of the FileStore Repository can be found [here](https://github.com/filecoin-project/lotus/blob/master/node/repo/fsrepo.go). diff --git a/content/systems/filecoin_nodes/repository/ipldstore/_index.md b/content/systems/filecoin_nodes/repository/ipldstore/_index.md index 0c3e1dd8b..baf0bdfee 100644 --- a/content/systems/filecoin_nodes/repository/ipldstore/_index.md +++ b/content/systems/filecoin_nodes/repository/ipldstore/_index.md @@ -21,10 +21,9 @@ IPLD is fundamentally comprised of three layers: Further details about IPLD can be found in its [specification](https://github.com/ipld/specs). - ## The Data Model -At its core, IPLD defines a [Data Model](https://github.com/ipld/specs/blob/master/data-model-layer/data-model.md) for representing data. The Data Model is designed for practical implementation across a wide variety of programming languages, while maintaining usability for content-addressed data and a broad range of generalized tools that interact with that data. +At its core, IPLD defines a [Data Model](https://github.com/ipld/specs/blob/master/data-model-layer/data-model.md) for representing data. The Data Model is designed for practical implementation across a wide variety of programming languages, while maintaining usability for content-addressed data and a broad range of generalized tools that interact with that data. The Data Model includes a range of standard primitive types (or "kinds"), such as booleans, integers, strings, nulls and byte arrays, as well as two recursive types: lists and maps. Because IPLD is designed for content-addressed data, it also includes a "link" primitive in its Data Model. In practice, links use the [CID](https://github.com/multiformats/cid) specification. IPLD data is organized into "blocks", where a block is represented by the raw, encoded data and its content-address, or CID. Every content-addressable chunk of data can be represented as a block, and together, blocks can form a coherent graph, or [Merkle DAG](https://docs.ipfs.io/guides/concepts/merkle-dag/). @@ -33,6 +32,7 @@ Applications interact with IPLD via the Data Model, and IPLD handles marshalling ## IPLD in Filecoin IPLD is used in two ways in the Filecoin network: + - All system datastructures are stored using DAG-CBOR (an IPLD codec). DAG-CBOR is a more strict subset of CBOR with a predefined tagging scheme, designed for storage, retrieval and traversal of hash-linked data DAGs. As compared to CBOR, DAG-CBOR can guarantee determinism. - Files and data stored on the Filecoin network are also stored using various IPLD codecs (not necessarily DAG-CBOR). diff --git a/content/systems/filecoin_nodes/repository/key_store/_index.md b/content/systems/filecoin_nodes/repository/key_store/_index.md index 87abfb5ec..236f5b3bd 100644 --- a/content/systems/filecoin_nodes/repository/key_store/_index.md +++ b/content/systems/filecoin_nodes/repository/key_store/_index.md @@ -13,7 +13,6 @@ The `Key Store` is a fundamental abstraction in any full Filecoin node used to s Node security depends in large part on keeping these keys secure. To that end we strongly recommend: 1) keeping keys separate from all subsystems, 2) using a separate key store to sign requests as required by other subsystems, and 3) keeping those keys that are not used as part of mining in cold storage. - Filecoin storage miners rely on three main components: - **The storage miner _actor_ address** is uniquely assigned to a given storage miner actor upon calling `registerMiner()` in the Storage Power Consensus Subsystem. In effect, the storage miner does not have an address itself, but is rather identified by the address of the actor it is tied to. This is a unique identifier for a given storage miner to which its power and other keys will be associated. The `actor value` specifies the address of an already created miner actor. @@ -22,7 +21,7 @@ Filecoin storage miners rely on three main components: Multiple storage miner actors can share one owner public key or likewise a worker public key. -The process for changing the worker keypairs on-chain (i.e. the worker Key associated with a storage miner actor) is specified in [Storage Miner Actor](storage_miner_actor). Note that this is a two-step process. First, a miner stages a change by sending a message to the chain. When received, the key change is staged to occur in twice the randomness lookback parameter number of epochs, to prevent adaptive key selection attacks. +The process for changing the worker keypairs on-chain (i.e. the worker Key associated with a storage miner actor) is specified in [Storage Miner Actor](storage_miner_actor). Note that this is a two-step process. First, a miner stages a change by sending a message to the chain. When received, the key change is staged to occur in twice the randomness lookback parameter number of epochs, to prevent adaptive key selection attacks. Every time a worker key is queried, a pending change is lazily checked and state is potentially updated as needed. Key security is of utmost importance in Filecoin, as is also the case with keys in every blockchain. **Failure to securely store and use keys or exposure of private keys to adversaries can result in the adversary having access to the miner's funds.** diff --git a/content/systems/filecoin_token/multisig.md b/content/systems/filecoin_token/multisig.md index f96a9a6dd..b17cf12b2 100644 --- a/content/systems/filecoin_token/multisig.md +++ b/content/systems/filecoin_token/multisig.md @@ -12,10 +12,8 @@ dashboardTests: 0 # Multisig Wallet & Actor - -The Multisig actor is a single actor representing a group of Signers. Signers may be external users, other Multisigs, or even the Multisig itself. There should be a maximum of 256 signers in a multisig wallet. In case more signers are needed, then the multisigs should be combined into a tree. +The Multisig actor is a single actor representing a group of Signers. Signers may be external users, other Multisigs, or even the Multisig itself. There should be a maximum of 256 signers in a multisig wallet. In case more signers are needed, then the multisigs should be combined into a tree. The implementation of the Multisig Actor can be found [here](https://github.com/filecoin-project/specs-actors/blob/master/actors/builtin/multisig/multisig_actor.go). The Multisig Actor statuses can be found [here](https://github.com/filecoin-project/specs-actors/blob/master/actors/builtin/multisig/multisig_state.go). - diff --git a/content/systems/filecoin_token/payment_channels.md b/content/systems/filecoin_token/payment_channels.md index c329bbf9c..816d5c211 100644 --- a/content/systems/filecoin_token/payment_channels.md +++ b/content/systems/filecoin_token/payment_channels.md @@ -14,7 +14,7 @@ Payment channels are generally used as a mechanism to increase the scalability o The goal of the Payment Channel Actor specified here is to enable a series of off-chain microtransactions for applications built on top of Filecoin to be reconciled on-chain at a later time with fewer messages that involve the blockchain. Payment channels are already used in the Retrieval Market of the Filecoin Network, but their applicability is not constrained within this use-case only. Hence, here, we provide a detailed description of Payment Channels in the Filecoin network and then describe how Payment Channels are used in the specific case of the Filecoin Retrieval Market. -The payment channel actor can be used to open long-lived, flexible payment channels between users. Filecoin payment channels are _uni-directional_ and can be funded by adding to their balance. Given the context of _uni-directional_ payment channels, we define the **payment channel sender** as the party that receives some service, creates the channel, deposits funds and _sends_ payments (hence the term _payment channel sender_). The *payment channel recipient*, on the other hand is defined as the party that provides services and *receives payment* for the services delivered (hence the term *payment channel recipient*). The fact that payment channels are uni-directional means that only the payment channel sender can add funds and the recipient can receive funds. Payment channels are identified by a unique address, as is the case with all Filecoin actors. +The payment channel actor can be used to open long-lived, flexible payment channels between users. Filecoin payment channels are _uni-directional_ and can be funded by adding to their balance. Given the context of _uni-directional_ payment channels, we define the **payment channel sender** as the party that receives some service, creates the channel, deposits funds and _sends_ payments (hence the term _payment channel sender_). The _payment channel recipient_, on the other hand is defined as the party that provides services and _receives payment_ for the services delivered (hence the term _payment channel recipient_). The fact that payment channels are uni-directional means that only the payment channel sender can add funds and the recipient can receive funds. Payment channels are identified by a unique address, as is the case with all Filecoin actors. The payment channel state structure looks like this: @@ -49,7 +49,6 @@ Before continuing with the details of the Payment Channel and its components and - `Settle`: this process starts closing the channel. It can be called by either the channel creator (sender) or the channel recipient. - `Collect`: with this process funds are eventually transferred from the payment channel sender to the payment channel recipient. This process incurs transaction/gas costs. - ## Vouchers Traditionally, in order to transact through a Payment Channel, the payment channel parties send to each other signed messages that update the balance of the channel. In Filecoin, these signed messages are called _vouchers_. @@ -101,14 +100,13 @@ Vouchers are signed by the party that creates them and are authenticated using a Once their transactions have completed, either party can choose to `Settle` (i.e., close) the channel. There is a 12hr period after `Settle` during which either party can submit any outstanding vouchers. Once the vouchers are submitted, either party can then call `Collect`. This will send the payment channel recipient the `ToPay` amount from the channel, and the channel sender (`From` address) will be refunded the remaining balance in the channel (if any). - ## Lanes In addition, payment channels in Filecoin can be split into `lane`s created as part of updating the channel state with a payment `voucher`. Each lane has an associated `nonce` and amount of tokens it can be `redeemed` for. Lanes can be thought of as transactions for several different services provided by the channel recipient to the channel sender. The `nonce` plays the role of a sequence number of vouchers within a given lane, where a voucher with a higher nonce replaces a voucher with a lower nonce. Payment channel lanes allow for a lot of accounting between parties to be done off-chain and reconciled via single updates to the payment channel. The multiple lanes enable two parties to use a single payment channel to adjudicate multiple independent sets of payments. -One example of such accounting is *merging of lanes*. When a pair of channel sender-recipient nodes have a payment channel established between them with many lanes, the channel recipient will have to pay gas cost for each one of the lanes in order to `Collect` funds. Merging of lanes allow the channel recipient to send a "merge" request to the channel sender to request merging of (some of the) lanes and consolidate the funds. This way, the recipient can reduce the overall gas cost. As an incentive for the channel sender to accept the merge lane request, the channel recipient can ask for a lower total value to balance out the gas cost. For instance, if the recipient has collected vouchers worth of 10 FIL from two lanes, say 5 from each, and the gas cost of submitting the vouchers for these funds is 2, then it can ask for 9 from the creator if the latter accepts to merge the two lanes. This way, the channel sender pays less overall for the services it received and the channel recipient pays less gas cost to submit the voucher for the services they provided. +One example of such accounting is _merging of lanes_. When a pair of channel sender-recipient nodes have a payment channel established between them with many lanes, the channel recipient will have to pay gas cost for each one of the lanes in order to `Collect` funds. Merging of lanes allow the channel recipient to send a "merge" request to the channel sender to request merging of (some of the) lanes and consolidate the funds. This way, the recipient can reduce the overall gas cost. As an incentive for the channel sender to accept the merge lane request, the channel recipient can ask for a lower total value to balance out the gas cost. For instance, if the recipient has collected vouchers worth of 10 FIL from two lanes, say 5 from each, and the gas cost of submitting the vouchers for these funds is 2, then it can ask for 9 from the creator if the latter accepts to merge the two lanes. This way, the channel sender pays less overall for the services it received and the channel recipient pays less gas cost to submit the voucher for the services they provided. ## Lifecycle of a Payment Channel @@ -126,17 +124,14 @@ Summarising, we have the following sequence: 9. Either the channel sender or the channel recipient calls `Collect`. 10. Funds are transferred to the channel recipient's account and any unclaimed balance goes back to channel sender. - ## Payment Channels as part of the Filecoin Retrieval - + Payment Channels are used in the Filecoin [Retrieval Market](retrieval_market) to enable efficient off-chain payments and accounting between parties for what is expected to be a series of microtransactions, as these occur during data retrieval. In particular, given that there is no proving method provided for the act of sending data from a provider (miner) to a client, there is no trust anchor between the two. Therefore, in order to avoid mis-behaviour, Filecoin is making use of payment channels in order to realise a step-wise "data transfer <-> payment" relationship between the data provider and the client (data receiver). Clients issue requests for data that miners are responding to. The miner is entitled to ask for interim payments, the volume-oriented interval for which is agreed in the Deal phase. In order to facilitate this process, the Filecoin client is creating a payment channel once the provider has agreed on the proposed deal. The client should also lock monetary value in the payment channel equal to the one needed for retrieval of the entire block of data requested. Every time a provider is completing transfer of the pre-specified amount of data, they can request a payment. The client is responding to this payment with a voucher which the provider can redeem (immediately or later), as per the process described earlier. - {{}} {{}} {{}} - diff --git a/content/systems/filecoin_token/token_allocation/_index.md b/content/systems/filecoin_token/token_allocation/_index.md index 462a8a809..221f996f5 100644 --- a/content/systems/filecoin_token/token_allocation/_index.md +++ b/content/systems/filecoin_token/token_allocation/_index.md @@ -22,19 +22,18 @@ Filecoinʼs token distribution is broken down as follows. A maximum of 2,000,000 **Total Burnt Funds.** Some filecoin are burned to fund on-chain computations and bandwidth as network transaction fees, in addition to those burned in penalties for storage faults and consensus faults, creating long-term deflationary pressure on the token. Accompanying the network transaction fees is the priority fee that is not burned, but goes to the block-producing miners for including a transaction. - -| **Parameter** | **Value** | **Description** | -| :------------- | :----------: | :-----------: | -||| | -| `FIL_BASE` | 2,000,000,000 FIL | The maximum amount of FIL that will ever be created. | -| `FIL_MiningReserveAlloc` | 300,000,000 FIL | Tokens reserved for funding mining to support growth of the Filecoin Economy, whose future usage will be decided by the Filecoin community | -| `FIL_StorageMiningAlloc` | 1,100,000,000 FIL | The amount of FIL allocated to storage miners through block rewards, network initialization | -| `FIL_Vested` | Sum of genesis `MultisigActors.`
`AmountUnlocked` | Total amount of FIL that is vested from genesis allocation. | -| `FIL_StorageMined` | `RewardActor.`
`TotalStoragePowerReward` | The amount of FIL that has been mined by storage miners | -| `FIL_Locked` | `TotalPledgeCollateral` + `TotalProviderDealCollateral` + `TotalClientDealCollateral` + `TotalPendingDealPayment` + `OtherLockedFunds` | The amount of FIL locked as part of mining, deals, and other mechanisms. | -| `FIL_CirculatingSupply` | `FIL_Vested` + `FIL_Mined` - `TotalBurntFunds` - `FIL_Locked` | The amount of FIL circulating and tradeable in the economy. The basis for Market Cap calculations. | -| `TotalBurntFunds` | `BurntFundsActor.`
`Balance` | Total FIL burned as part of penalties and on-chain computations. | -| `TotalPledgeCollateral` | `StoragePowerActor.`
`TotalPledgeCollateral` | Total FIL locked as pledge collateral in all miners. | -| `TotalProviderDealCollateral` | `StorageMarketActor.`
`TotalProviderDealCollateral` | Total FIL locked as provider deal collateral | -| `TotalClientDealCollateral` | `StorageMarketActor.`
`TotalClientDealColateral` | Total FIL locked as client deal collateral | -| `TotalPendingDealPayment` | `StorageMarketActor.`
`TotalPendingDealPayment` | Total FIL locked as pending client deal payment | +| **Parameter** | **Value** | **Description** | +| :---------------------------- | :------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------: | +| | | | +| `FIL_BASE` | 2,000,000,000 FIL | The maximum amount of FIL that will ever be created. | +| `FIL_MiningReserveAlloc` | 300,000,000 FIL | Tokens reserved for funding mining to support growth of the Filecoin Economy, whose future usage will be decided by the Filecoin community | +| `FIL_StorageMiningAlloc` | 1,100,000,000 FIL | The amount of FIL allocated to storage miners through block rewards, network initialization | +| `FIL_Vested` | Sum of genesis `MultisigActors.`
`AmountUnlocked` | Total amount of FIL that is vested from genesis allocation. | +| `FIL_StorageMined` | `RewardActor.`
`TotalStoragePowerReward` | The amount of FIL that has been mined by storage miners | +| `FIL_Locked` | `TotalPledgeCollateral` + `TotalProviderDealCollateral` + `TotalClientDealCollateral` + `TotalPendingDealPayment` + `OtherLockedFunds` | The amount of FIL locked as part of mining, deals, and other mechanisms. | +| `FIL_CirculatingSupply` | `FIL_Vested` + `FIL_Mined` - `TotalBurntFunds` - `FIL_Locked` | The amount of FIL circulating and tradeable in the economy. The basis for Market Cap calculations. | +| `TotalBurntFunds` | `BurntFundsActor.`
`Balance` | Total FIL burned as part of penalties and on-chain computations. | +| `TotalPledgeCollateral` | `StoragePowerActor.`
`TotalPledgeCollateral` | Total FIL locked as pledge collateral in all miners. | +| `TotalProviderDealCollateral` | `StorageMarketActor.`
`TotalProviderDealCollateral` | Total FIL locked as provider deal collateral | +| `TotalClientDealCollateral` | `StorageMarketActor.`
`TotalClientDealColateral` | Total FIL locked as client deal collateral | +| `TotalPendingDealPayment` | `StorageMarketActor.`
`TotalPendingDealPayment` | Total FIL locked as pending client deal payment | diff --git a/content/systems/filecoin_vm/actor/_index.md b/content/systems/filecoin_vm/actor/_index.md index 9dbc52933..61f39b84a 100644 --- a/content/systems/filecoin_vm/actor/_index.md +++ b/content/systems/filecoin_vm/actor/_index.md @@ -19,4 +19,4 @@ The _actor address_ is a stable address generated by hashing the sender's public {{}} -The `ActorState` structure is composed of the actor's balance, in terms of tokens held by this actor, as well as a group of state methods used to query, inspect and interact with chain state. \ No newline at end of file +The `ActorState` structure is composed of the actor's balance, in terms of tokens held by this actor, as well as a group of state methods used to query, inspect and interact with chain state. diff --git a/content/systems/filecoin_vm/gas_fee.md b/content/systems/filecoin_vm/gas_fee.md index 95bda27ef..7cc5ad836 100644 --- a/content/systems/filecoin_vm/gas_fee.md +++ b/content/systems/filecoin_vm/gas_fee.md @@ -15,7 +15,7 @@ As is traditionally the case with many blockchains, Gas is a unit of measure of Traditionally, `GasUsed * GasFeeCap` goes to the block producing miner as a reward. The result of this product is treated as the priority fee for message inclusion, that is, messages are ordered in decreasing sequence and those with the highest `GasUsed * GasFeeCap` are prioritised, given that they return more profit to the miner. -However, it has been observed that this tactic (of paying `GasUsed * GasFee`) is problematic for block producing miners for a few reasons. Firstly, a block producing miner may include a very expensive message (in terms of chain resources required) for free in which case the chain itself needs to bear the cost. Secondly, message senders can set arbitrarily high prices but for low-cost messages (again, in terms of chain resources), leading to a DoS vulnerability. +However, it has been observed that this tactic (of paying `GasUsed * GasFee`) is problematic for block producing miners for a few reasons. Firstly, a block producing miner may include a very expensive message (in terms of chain resources required) for free in which case the chain itself needs to bear the cost. Secondly, message senders can set arbitrarily high prices but for low-cost messages (again, in terms of chain resources), leading to a DoS vulnerability. In order to overcome this situation, the Filecoin blockchain defines a `BaseFee`, which is burnt for every message. The rationale is that given that Gas is a measure of on-chain resource consumption, it makes sense for it to be burned, as compared to be rewarded to miners. This way, fee manipulation from miners is avoided. The `BaseFee` is dynamic, adjusted automatically according to network congestion. This fact, makes the network resilient against spam attacks. Given that the network load increases during SPAM attacks, maintaining full blocks of SPAM transactions for an extended period of time is impossible for an attacker due to the increasing `BaseFee`. @@ -25,16 +25,15 @@ Finally, `GasPremium` is the priority fee included by senders to incentivize min - `GasUsed` is a measure of the amount of resources (or units of gas) consumed, in order to execute a message. Each unit of gas is measured in attoFIL and therefore, `GasUsed` is a number that represents the units of energy consumed. `GasUsed` is independent of whether a message was executed correctly or failed. - `BaseFee` is the set price per unit of gas (measured in attoFIL/gas unit) to be burned (sent to an unrecoverable address) for every message execution. The value of the `BaseFee` is dynamic and adjusts according to current network congestion parameters. For example, when the network exceeds 5B gas limit usage, the `BaseFee` increases and the opposite happens when gas limit usage falls below 5B. The `BaseFee` applied to each block should be included in the block itself. It should be possible to get the value of the current `BaseFee` from the head of the chain. The `BaseFee` applies per unit of `GasUsed` and therefore, the total amount of gas burned for a message is `BaseFee * GasUsed`. Note that the `BaseFee` is incurred for every message, but its value is the same for all messages in the same block. -- `GasLimit` is measured in units of gas and set by the message sender. It imposes a hard limit on the amount of gas (i.e., number of units of gas) that a message’s execution should be allowed to consume on chain. A message consumes gas for every fundamental operation it triggers, and a message that runs out of gas fails. When a message fails, every modification to the state that happened as a result of this message's execution is reverted back to its previous state. Independently of whether a message execution was successful or not, the miner will receive a reward for the resources they consumed to execute the message (see `GasPremium` below). +- `GasLimit` is measured in units of gas and set by the message sender. It imposes a hard limit on the amount of gas (i.e., number of units of gas) that a message’s execution should be allowed to consume on chain. A message consumes gas for every fundamental operation it triggers, and a message that runs out of gas fails. When a message fails, every modification to the state that happened as a result of this message's execution is reverted back to its previous state. Independently of whether a message execution was successful or not, the miner will receive a reward for the resources they consumed to execute the message (see `GasPremium` below). - `GasFeeCap` is the maximum price that the message sender is willing to pay per unit of gas (measured in attoFIL/gas unit). Together with the `GasLimit`, the `GasFeeCap` is setting the maximum amount of FIL that a sender will pay for a message: a sender is guaranteed that a message will never cost them more than `GasLimit * GasFeeCap` attoFIL (not including any Premium that the message includes for its recipient). - `GasPremium` is the price per unit of gas (measured in attoFIL/gas) that the message sender is willing to pay (on top of the `BaseFee`) to "tip" the miner that will include this message in a block. A message typically earns its miner `GasLimit * GasPremium` attoFIL, where effectively `GasPremium = GasFeeCap - BaseFee`. Note that `GasPremium` is applied on `GasLimit`, as opposed to `GasUsed`, in order to make message selection for miners more straightforward. - {{}} {{}} -## Notes & Implications +## Notes & Implications - The `GasFeeCap` should always be higher than the network's `BaseFee`. If a message’s `GasFeeCap` is lower than the `BaseFee`, then the remainder comes from the miner (as a penalty). This penalty is applied to the miner because they have selected a message that pays less than the network `BaseFee` (i.e., does not cover the network costs). However, a miner might want to choose a message whose `GasFeeCap` is smaller than the `BaseFee` if the same sender has another message in the message pool whose `GasFeeCap` is much bigger than the `BaseFee`. Recall, that a miner should pick all the messages of a sender from the message pool, if more than one exists. The justification is that the increased fee of the second message will pay off the loss from the first. diff --git a/content/systems/filecoin_vm/interpreter/_index.md b/content/systems/filecoin_vm/interpreter/_index.md index 321a0b02b..7194b232d 100644 --- a/content/systems/filecoin_vm/interpreter/_index.md +++ b/content/systems/filecoin_vm/interpreter/_index.md @@ -11,7 +11,7 @@ dashboardTests: 0 The VM interpreter orchestrates the execution of messages from a tipset on that tipset's parent state, producing a new state and a sequence of message receipts. The CIDs of this new state and of the receipt -collection are included in blocks from the subsequent epoch, which must agree about those CIDs +collection are included in blocks from the subsequent epoch, which must agree about those CIDs in order to form a new tipset. Every state change is driven by the execution of a message. @@ -28,7 +28,7 @@ by the interpreter at evaluation time. For each block in a tipset, an implicit message: - invokes the block producer's miner actor to process the (already-validated) election PoSt submission, -as the first message in the block; + as the first message in the block; - invokes the reward actor to pay the block reward to the miner's owner account, as the final message in the block; For each tipset, an implicit message: @@ -39,7 +39,7 @@ All implicit messages are constructed with a `From` address being the distinguis They specify a gas price of zero, but must be included in the computation. They must succeed (have an exit code of zero) in order for the new state to be computed. Receipts for implicit messages are not included in the receipt list; only explicit messages have an -explicit receipt. +explicit receipt. ## Gas payments @@ -47,16 +47,16 @@ In most cases, the sender of a message pays the miner which produced the block i a gas fee for its execution. The gas payments for each message execution are paid to the miner owner account immediately after -that message is executed. There are no encumbrances to either the block reward or gas fees earned: -both may be spent immediately. +that message is executed. There are no encumbrances to either the block reward or gas fees earned: +both may be spent immediately. ## Duplicate messages -Since different miners produce blocks in the same epoch, multiple blocks in a single tipset may -include the same message (identified by the same CID). +Since different miners produce blocks in the same epoch, multiple blocks in a single tipset may +include the same message (identified by the same CID). When this happens, the message is processed only the first time it is encountered in the tipset's -canonical order. Subsequent instances of the message are ignored and do not result in any -state mutation, produce a receipt, or pay gas to the block producer. +canonical order. Subsequent instances of the message are ignored and do not result in any +state mutation, produce a receipt, or pay gas to the block producer. The sequence of executions for a tipset is thus summarised: @@ -67,31 +67,32 @@ The sequence of executions for a tipset is thus summarised: - process election post for second block - messages for second block (BLS before SECP, skipping any already encountered) - `[... subsequent blocks ...]` -- cron tick +- cron tick ## Message validity and failure + Every message in a valid block can be processed and produce a receipt (note that block validity implies all messages are syntactically valid -- see [Message Syntax](message#message-syntax-validation) -- and correctly signed). However, execution may or may not succeed, depending on the state to which the message is applied. If the execution -of a message fails, the corresponding receipt will carry a non-zero exit code. +of a message fails, the corresponding receipt will carry a non-zero exit code. If a message fails due to a reason that can reasonably be attributed to the miner including a message that could never have succeeded in the parent state, or because the sender lacks funds -to cover the maximum message cost, then the miner pays a penalty by burning the gas fee +to cover the maximum message cost, then the miner pays a penalty by burning the gas fee (rather than the sender paying fees to the block miner). The only state changes resulting from a message failure are either: - incrementing of the sending actor's `CallSeqNum`, and payment of gas fees from the sender to the owner of the miner of the block including the message; or - a penalty equivalent to the gas fee for the failed message, burnt by the miner (sender's `CallSeqNum` unchanged). - + A message execution will fail if, in the immediately preceding state: - the `From` actor does not exist in the state (miner penalized), - the `From` actor is not an account actor (miner penalized), - the `CallSeqNum` of the message does not match the `CallSeqNum` of the `From` actor (miner penalized), - the `From` actor does not have sufficient balance to cover the sum of the message `Value` plus the -maximum gas cost, `GasLimit * GasPrice` (miner penalized), + maximum gas cost, `GasLimit * GasPrice` (miner penalized), - the `To` actor does not exist in state and the `To` address is not a pubkey-style address, - the `To` actor exists (or is implicitly created as an account) but does not have a method corresponding to the non-zero `MethodNum`, - deserialized `Params` is not an array of length matching the arity of the `To` actor's `MethodNum` method, @@ -100,5 +101,5 @@ maximum gas cost, `GasLimit * GasPrice` (miner penalized), - the invoked method exits with a non-zero code (via `Runtime.Abort()`), or - any inner message sent by the receiver fails for any of the above reasons. -Note that if the `To` actor does not exist in state and the address is a valid `H(pubkey)` address, +Note that if the `To` actor does not exist in state and the address is a valid `H(pubkey)` address, it will be created as an account actor. diff --git a/content/systems/filecoin_vm/interpreter/vm_interpreter_old.md b/content/systems/filecoin_vm/interpreter/vm_interpreter_old.md index 4a069ac08..69d35866b 100644 --- a/content/systems/filecoin_vm/interpreter/vm_interpreter_old.md +++ b/content/systems/filecoin_vm/interpreter/vm_interpreter_old.md @@ -5,7 +5,6 @@ bookhidden: true # VM Interpreter - ## Sending Funds As all messages carry a method ID, the method ID '0' is reserved for simple @@ -64,7 +63,6 @@ The current state of a given actor can be accessed first by calling `Head` to re To store data, `Put` is used. Any number of objects may be `Put`, but only the object whose CID is committed, or objects that are linked to in some way by the committed object will be kept. All other objects are dropped after the method invocation returns. Objects stored via `Put` are first marshaled to CBOR-IPLD, and then stored, the returned CID is a 32 byte sha2-256 CBOR-IPLD content identifier. - ## Pledge Collateral Filecoin includes a concept of "Pledge Collateral", which is FIL collateral that storage miners must lock up when participating as miners. @@ -75,7 +73,6 @@ Pledge collateral serves several functions in Filecoin. It: - ensures that miners have skin in the game (for the Filecoin network as a whole) - increases the cost of launching a 51% attack - ### Computing Pledge Collateral The total pledge collateral across all miners is a fixed proportion of available FIL. @@ -107,6 +104,7 @@ minerPerCapitaCollateral := totalPerCapitaCollateral / numMiners ``` Putting all these variables together, we have each miner's individual collateral requirement: + ```go minerPlegeCollateral := availableFil * ( POWER_COLLATERAL_PROPORTION * minerPower / totalNetworkPower PER_CAPITA_COLLATERAL_PROPORTION / numMiners) ``` diff --git a/content/systems/filecoin_vm/message/_index.md b/content/systems/filecoin_vm/message/_index.md index 82e0eab16..83544519b 100644 --- a/content/systems/filecoin_vm/message/_index.md +++ b/content/systems/filecoin_vm/message/_index.md @@ -15,11 +15,11 @@ in state. A message combines: - a token amount to be transferred from the sender to the receiver, and - a method with parameters to be invoked on the receiver (optional/where applicable). -Actor code may send additional messages to other actors while processing a received message. +Actor code may send additional messages to other actors while processing a received message. Messages are processed synchronously, that is, an actor waits for a sent message to complete before resuming control. -The processing of a message consumes units of computation and storage, both of which are denominated in gas. -A message's _gas limit_ provides an upper bound on the computation required to process it. The sender of a message pays +The processing of a message consumes units of computation and storage, both of which are denominated in gas. +A message's _gas limit_ provides an upper bound on the computation required to process it. The sender of a message pays for the gas units consumed by a message's execution (including all nested messages) at a gas price they determine. A block producer chooses which messages to include in a block and is rewarded according to each message's gas price and consumption, forming a market. @@ -42,13 +42,12 @@ type SignedMessage struct { A syntactically valid `UnsignedMessage`: - has a well-formed, non-empty `To` address, -- has a well-formed, non-empty `From` address, +- has a well-formed, non-empty `From` address, - has `Value` no less than zero and no greater than the total token supply (`2e9 * 1e18`), and - has non-negative `GasPrice`, - has `GasLimit` that is at least equal to the gas consumption associated with the message's serialized bytes, - has `GasLimit` that is no greater than the block gas limit network parameter. - ```go type Message struct { // Version of this message (has to be non-negative) @@ -80,24 +79,23 @@ There should be several functions able to extract information from the `Message Given that Transaction Messages should eventually be included in a Block and added to the blockchain, the validity of the message should be checked with regard to the sender and the receiver of the message, the value (which should be non-negative and always smaller than the circulating supply), the gas price (which again should be non-negative) and the `BlockGasLimit` which should not be greater than the block's gas limit. - ## Message semantic validation Semantic validation refers to validation requiring information outside of the message itself. A semantically valid `SignedMessage` must carry a signature that verifies the payload as having -been signed with the public key of the account actor identified by the `From` address. +been signed with the public key of the account actor identified by the `From` address. Note that when the `From` address is an ID-address, the public key must be looked up in the state of the sending account actor in the parent state identified by the block. Note: the sending actor must exist _in the parent state identified by the block_ that includes the message. -This means that it is not valid for a single block to include a message that creates a new account -actor and a message from that same actor. +This means that it is not valid for a single block to include a message that creates a new account +actor and a message from that same actor. The first message from that actor must wait until a subsequent epoch. Message pools may exclude messages from an actor that is not yet present in the chain state. -There is no further semantic validation of a message that can cause a block including the message -to be invalid. Every syntactically valid and correctly signed message can be included in a block and +There is no further semantic validation of a message that can cause a block including the message +to be invalid. Every syntactically valid and correctly signed message can be included in a block and will produce a receipt from execution. The `MessageReceipt sturct` includes the following: ```go @@ -108,13 +106,11 @@ type MessageReceipt struct { } ``` - -However, a message may fail to execute to completion, in which case it will not trigger the desired state change. +However, a message may fail to execute to completion, in which case it will not trigger the desired state change. The reason for this "no message semantic validation" policy is that the state that a message will be applied to cannot be known before the message is executed _as part of a tipset_. A block producer does not know whether another block will precede it in the tipset, thus altering the state to which the block's messages will apply from the declared parent state. - {{}} diff --git a/content/systems/filecoin_vm/runtime/_index.md b/content/systems/filecoin_vm/runtime/_index.md index 5677ed6e2..7b3dae31e 100644 --- a/content/systems/filecoin_vm/runtime/_index.md +++ b/content/systems/filecoin_vm/runtime/_index.md @@ -12,7 +12,7 @@ dashboardTests: 0 ## Receipts -A `MessageReceipt` contains the result of a top-level message execution. Every syntactically valid and correctly signed message can be included in a block and will produce a receipt from execution. +A `MessageReceipt` contains the result of a top-level message execution. Every syntactically valid and correctly signed message can be included in a block and will produce a receipt from execution. A syntactically valid receipt has: diff --git a/content/systems/filecoin_vm/sysactors/_index.md b/content/systems/filecoin_vm/sysactors/_index.md index ebc6eb785..335cae9dd 100644 --- a/content/systems/filecoin_vm/sysactors/_index.md +++ b/content/systems/filecoin_vm/sysactors/_index.md @@ -15,12 +15,12 @@ dashboardTests: 0 There are eleven (11) builtin System Actors in total, but not all of them interact with the VM. Each actor is identified by a _Code ID_ (or CID). There are two system actors required for VM processing: - - the [InitActor](sysactors#initactor), which initializes new actors and records the network name, and - - the [CronActor](sysactors#cronactor), a scheduler actor that runs critical functions at every epoch. -There are another two actors that interact with the VM: - - the [AccountActor](sysactors#accountactor) responsible for user accounts (non-singleton), and - - the [RewardActor](sysactors#rewardactor) for block reward and token vesting (singleton). +- the [InitActor](sysactors#initactor), which initializes new actors and records the network name, and +- the [CronActor](sysactors#cronactor), a scheduler actor that runs critical functions at every epoch. + There are another two actors that interact with the VM: +- the [AccountActor](sysactors#accountactor) responsible for user accounts (non-singleton), and +- the [RewardActor](sysactors#rewardactor) for block reward and token vesting (singleton). The remaining seven (7) builtin System Actors that do not interact directly with the VM are the following: @@ -28,7 +28,7 @@ The remaining seven (7) builtin System Actors that do not interact directly with - `StorageMinerActor`: actor responsible to deal with storage mining operations and collect proofs [[Storage Miner Actor Repo](https://github.com/filecoin-project/specs-actors/blob/master/actors/builtin/miner/miner_actor.go)] - `MultisigActor` (or Multi-Signature Wallet Actor): responsible for dealing with operations involving the Filecoin wallet [[Multisig Actor Repo](https://github.com/filecoin-project/specs-actors/blob/master/actors/builtin/multisig/multisig_actor.go)] - `PaymentChannelActor`: responsible for setting up and settling funds related to payment channels [[Paych Actor Repo](https://github.com/filecoin-project/specs-actors/blob/master/actors/builtin/paych/paych_actor.go)] -- `StoragePowerActor`: responsible for keeping track of the storage power allocated at each storage miner [[Storage Power Actor](https://github.com/filecoin-project/specs-actors/blob/master/actors/builtin/power/power_actor.go)] +- `StoragePowerActor`: responsible for keeping track of the storage power allocated at each storage miner [[Storage Power Actor](https://github.com/filecoin-project/specs-actors/blob/master/actors/builtin/power/power_actor.go)] - `VerifiedRegistryActor`: responsible for managing verified clients [[Verifreg Actor Repo](https://github.com/filecoin-project/specs-actors/blob/master/actors/builtin/verifreg/verified_registry_actor.go)] - `SystemActor`: general system actor [[System Actor Repo](https://github.com/filecoin-project/specs-actors/blob/master/actors/builtin/system/system_actor.go)] diff --git a/content/test-embed.js b/content/test-embed.js index 8398577f2..8d9a11824 100644 --- a/content/test-embed.js +++ b/content/test-embed.js @@ -1,3 +1,3 @@ -function () { - return 1 +function name() { + return 1 } diff --git a/content/test.md b/content/test.md index 144eef22f..b44a9eeac 100644 --- a/content/test.md +++ b/content/test.md @@ -11,7 +11,7 @@ dashboardAudit: n/a # Introduction Filecoin is a distributed storage network based on a blockchain mechanism. -Filecoin *miners* can elect to provide storage capacity for the network, and thereby +Filecoin _miners_ can elect to provide storage capacity for the network, and thereby earn units of the Filecoin cryptocurrency (FIL) by periodically producing cryptographic proofs that certify that they are providing the capacity specified. In addition, Filecoin enables parties to exchange FIL currency @@ -24,14 +24,13 @@ The Filecoin blockchain not only maintains the ledger for FIL transactions and accounts, but also implements the Filecoin VM, a replicated state machine which executes a variety of cryptographic contracts and market mechanisms among participants on the network. -These contracts include *storage deals*, in which clients pay FIL currency to miners +These contracts include _storage deals_, in which clients pay FIL currency to miners in exchange for storing the specific file data that the clients request. Via the distributed implementation of the Filecoin VM, storage deals and other contract mechanisms recorded on the chain continue to be processed over time, without requiring further interaction from the original parties (such as the clients who requested the data storage). - ## Embeds ### Github master @@ -43,29 +42,32 @@ over time, without requiring further interaction from the original parties {{}} ### Github code comments + {{}} ### Github code small #### Lorem ipsum + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sed congue eros, sit amet efficitur turpis. Ut arcu dui, tempor non ex consequat, dictum egestas est. Morbi et pulvinar magna. Nam scelerisque fermentum felis ut vehicula. Fusce malesuada tortor sed arcu pretium laoreet. Praesent ac nibh eu leo euismod condimentum. Suspendisse vitae fringilla nulla. Donec in fermentum odio. Nullam ut congue leo. Fusce urna lorem, tincidunt ac porta nec, dapibus ac ipsum. Nulla posuere vulputate nisi. Integer eget elementum diam. Aenean tincidunt lectus eu quam tincidunt aliquam. Curabitur eget lacinia diam. {{}} -### Local file +### Local file + {{}} +## Diagrams -## Diagrams ![Protocol Overview Diagram](/intro/diagrams/orient/filecoin.dot) ## Blockquotes + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sed congue eros, sit amet efficitur turpis. Ut arcu dui, tempor non ex consequat, dictum egestas est. > **Note** > Morbi et pulvinar magna. Nam scelerisque fermentum felis ut vehicula. Fusce malesuada tortor sed arcu pretium laoreet. Praesent ac nibh eu leo euismod condimentum. Suspendisse vitae fringilla nulla. Donec in fermentum odio. Nullam ut congue leo. Fusce urna lorem, tincidunt ac porta nec, dapibus ac ipsum. Nulla posuere vulputate nisi. Integer eget elementum diam. Aenean tincidunt lectus eu quam tincidunt aliquam. Curabitur eget lacinia diam. - ## Katex shortcode Filecoin Miners must commit resources in order to participate in the economy; the protocol can use the minersʼ stake in the network to ensure that rational behavior benefits the network, rewarding the creation of value and penalizing malicious behavior via slashing. The pledge size is meant to adequately incentivize the fulfillment of a sectorʼs promised lifetime and provide sufficient consensus security. @@ -78,8 +80,7 @@ $SectorInitialPledge = SectorInitialStoragePledge + SectorInitialConsensusPledge {{
}} -Morbi et pulvinar magna. Nam scelerisque fermentum felis ut vehicula. Fusce malesuada tortor sed arcu pretium laoreet. Praesent ac nibh eu leo euismod condimentum. Suspendisse vitae fringilla nulla. Donec in fermentum odio. Nullam ut congue leo. Fusce urna lorem, tincidunt ac porta nec, dapibus ac ipsum. Nulla posuere vulputate nisi. Integer eget elementum diam. Aenean tincidunt lectus eu quam tincidunt aliquam. Curabitur eget lacinia diam. - +Morbi et pulvinar magna. Nam scelerisque fermentum felis ut vehicula. Fusce malesuada tortor sed arcu pretium laoreet. Praesent ac nibh eu leo euismod condimentum. Suspendisse vitae fringilla nulla. Donec in fermentum odio. Nullam ut congue leo. Fusce urna lorem, tincidunt ac porta nec, dapibus ac ipsum. Nulla posuere vulputate nisi. Integer eget elementum diam. Aenean tincidunt lectus eu quam tincidunt aliquam. Curabitur eget lacinia diam. {{}} @@ -87,7 +88,7 @@ $SectorInitialConsensusPledge = 30\% \times FILCirculatingSupply \times \frac{S {{}} -Morbi et pulvinar magna. Nam scelerisque fermentum felis ut vehicula. Fusce malesuada tortor sed arcu pretium laoreet. Praesent ac nibh eu leo euismod condimentum. Suspendisse vitae fringilla nulla. Donec in fermentum odio. Nullam ut congue leo. Fusce urna lorem, tincidunt ac porta nec, dapibus ac ipsum. Nulla posuere vulputate nisi. Integer eget elementum diam. Aenean tincidunt lectus eu quam tincidunt aliquam. Curabitur eget lacinia diam. +Morbi et pulvinar magna. Nam scelerisque fermentum felis ut vehicula. Fusce malesuada tortor sed arcu pretium laoreet. Praesent ac nibh eu leo euismod condimentum. Suspendisse vitae fringilla nulla. Donec in fermentum odio. Nullam ut congue leo. Fusce urna lorem, tincidunt ac porta nec, dapibus ac ipsum. Nulla posuere vulputate nisi. Integer eget elementum diam. Aenean tincidunt lectus eu quam tincidunt aliquam. Curabitur eget lacinia diam. {{}} $SectorQualityMultiplier = \frac{\sum\nolimits_{deals} DealWeight * DealQualityMultiplier}{SectorSpaceTime}$ diff --git a/data/iconmoon-project.json b/data/iconmoon-project.json index d26f2fb0c..21d398a95 100644 --- a/data/iconmoon-project.json +++ b/data/iconmoon-project.json @@ -1176,9 +1176,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "activity" - ], + "tags": ["activity"], "grid": 24 }, { @@ -1189,9 +1187,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "airplay" - ], + "tags": ["airplay"], "grid": 24 }, { @@ -1202,9 +1198,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "alert-circle" - ], + "tags": ["alert-circle"], "grid": 24 }, { @@ -1215,9 +1209,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "alert-octagon" - ], + "tags": ["alert-octagon"], "grid": 24 }, { @@ -1228,9 +1220,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "alert-triangle" - ], + "tags": ["alert-triangle"], "grid": 24 }, { @@ -1241,9 +1231,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "align-center" - ], + "tags": ["align-center"], "grid": 24 }, { @@ -1254,9 +1242,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "align-justify" - ], + "tags": ["align-justify"], "grid": 24 }, { @@ -1267,9 +1253,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "align-left" - ], + "tags": ["align-left"], "grid": 24 }, { @@ -1280,9 +1264,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "align-right" - ], + "tags": ["align-right"], "grid": 24 }, { @@ -1293,9 +1275,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "anchor" - ], + "tags": ["anchor"], "grid": 24 }, { @@ -1306,9 +1286,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "aperture" - ], + "tags": ["aperture"], "grid": 24 }, { @@ -1319,9 +1297,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "archive" - ], + "tags": ["archive"], "grid": 24 }, { @@ -1332,9 +1308,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "arrow-down" - ], + "tags": ["arrow-down"], "grid": 24 }, { @@ -1345,9 +1319,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "arrow-down-circle" - ], + "tags": ["arrow-down-circle"], "grid": 24 }, { @@ -1358,9 +1330,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "arrow-down-left" - ], + "tags": ["arrow-down-left"], "grid": 24 }, { @@ -1371,9 +1341,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "arrow-down-right" - ], + "tags": ["arrow-down-right"], "grid": 24 }, { @@ -1384,9 +1352,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "arrow-left" - ], + "tags": ["arrow-left"], "grid": 24 }, { @@ -1397,9 +1363,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "arrow-left-circle" - ], + "tags": ["arrow-left-circle"], "grid": 24 }, { @@ -1410,9 +1374,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "arrow-right" - ], + "tags": ["arrow-right"], "grid": 24 }, { @@ -1423,9 +1385,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "arrow-right-circle" - ], + "tags": ["arrow-right-circle"], "grid": 24 }, { @@ -1436,9 +1396,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "arrow-up" - ], + "tags": ["arrow-up"], "grid": 24 }, { @@ -1449,9 +1407,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "arrow-up-circle" - ], + "tags": ["arrow-up-circle"], "grid": 24 }, { @@ -1462,9 +1418,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "arrow-up-left" - ], + "tags": ["arrow-up-left"], "grid": 24 }, { @@ -1475,9 +1429,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "arrow-up-right" - ], + "tags": ["arrow-up-right"], "grid": 24 }, { @@ -1488,9 +1440,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "at-sign" - ], + "tags": ["at-sign"], "grid": 24 }, { @@ -1501,9 +1451,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "award" - ], + "tags": ["award"], "grid": 24 }, { @@ -1514,9 +1462,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "bar-chart" - ], + "tags": ["bar-chart"], "grid": 24 }, { @@ -1527,9 +1473,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "bar-chart-2" - ], + "tags": ["bar-chart-2"], "grid": 24 }, { @@ -1540,9 +1484,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "battery" - ], + "tags": ["battery"], "grid": 24 }, { @@ -1553,9 +1495,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "battery-charging" - ], + "tags": ["battery-charging"], "grid": 24 }, { @@ -1566,9 +1506,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "bell" - ], + "tags": ["bell"], "grid": 24 }, { @@ -1579,9 +1517,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "bell-off" - ], + "tags": ["bell-off"], "grid": 24 }, { @@ -1592,9 +1528,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "bluetooth" - ], + "tags": ["bluetooth"], "grid": 24 }, { @@ -1605,9 +1539,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "bold" - ], + "tags": ["bold"], "grid": 24 }, { @@ -1618,9 +1550,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "book" - ], + "tags": ["book"], "grid": 24 }, { @@ -1631,9 +1561,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "book-open" - ], + "tags": ["book-open"], "grid": 24 }, { @@ -1644,9 +1572,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "bookmark" - ], + "tags": ["bookmark"], "grid": 24 }, { @@ -1657,9 +1583,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "box" - ], + "tags": ["box"], "grid": 24 }, { @@ -1670,9 +1594,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "briefcase" - ], + "tags": ["briefcase"], "grid": 24 }, { @@ -1683,9 +1605,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "calendar" - ], + "tags": ["calendar"], "grid": 24 }, { @@ -1696,9 +1616,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "camera" - ], + "tags": ["camera"], "grid": 24 }, { @@ -1709,9 +1627,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "camera-off" - ], + "tags": ["camera-off"], "grid": 24 }, { @@ -1722,9 +1638,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "cast" - ], + "tags": ["cast"], "grid": 24 }, { @@ -1735,9 +1649,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "check" - ], + "tags": ["check"], "grid": 24 }, { @@ -1748,9 +1660,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "check-circle" - ], + "tags": ["check-circle"], "grid": 24 }, { @@ -1761,9 +1671,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "check-square" - ], + "tags": ["check-square"], "grid": 24 }, { @@ -1774,9 +1682,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "chevron-down" - ], + "tags": ["chevron-down"], "grid": 24 }, { @@ -1787,9 +1693,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "chevron-left" - ], + "tags": ["chevron-left"], "grid": 24 }, { @@ -1800,9 +1704,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "chevron-right" - ], + "tags": ["chevron-right"], "grid": 24 }, { @@ -1813,9 +1715,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "chevron-up" - ], + "tags": ["chevron-up"], "grid": 24 }, { @@ -1826,9 +1726,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "chevrons-down" - ], + "tags": ["chevrons-down"], "grid": 24 }, { @@ -1839,9 +1737,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "chevrons-left" - ], + "tags": ["chevrons-left"], "grid": 24 }, { @@ -1852,9 +1748,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "chevrons-right" - ], + "tags": ["chevrons-right"], "grid": 24 }, { @@ -1865,9 +1759,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "chevrons-up" - ], + "tags": ["chevrons-up"], "grid": 24 }, { @@ -1878,9 +1770,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "chrome" - ], + "tags": ["chrome"], "grid": 24 }, { @@ -1891,9 +1781,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "circle" - ], + "tags": ["circle"], "grid": 24 }, { @@ -1904,9 +1792,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "clipboard" - ], + "tags": ["clipboard"], "grid": 24 }, { @@ -1917,9 +1803,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "clock" - ], + "tags": ["clock"], "grid": 24 }, { @@ -1930,9 +1814,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "cloud" - ], + "tags": ["cloud"], "grid": 24 }, { @@ -1943,9 +1825,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "cloud-drizzle" - ], + "tags": ["cloud-drizzle"], "grid": 24 }, { @@ -1956,9 +1836,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "cloud-lightning" - ], + "tags": ["cloud-lightning"], "grid": 24 }, { @@ -1969,9 +1847,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "cloud-off" - ], + "tags": ["cloud-off"], "grid": 24 }, { @@ -1982,9 +1858,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "cloud-rain" - ], + "tags": ["cloud-rain"], "grid": 24 }, { @@ -1995,9 +1869,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "cloud-snow" - ], + "tags": ["cloud-snow"], "grid": 24 }, { @@ -2008,9 +1880,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "code" - ], + "tags": ["code"], "grid": 24 }, { @@ -2021,9 +1891,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "codepen" - ], + "tags": ["codepen"], "grid": 24 }, { @@ -2034,9 +1902,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "codesandbox" - ], + "tags": ["codesandbox"], "grid": 24 }, { @@ -2047,9 +1913,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "coffee" - ], + "tags": ["coffee"], "grid": 24 }, { @@ -2060,9 +1924,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "columns" - ], + "tags": ["columns"], "grid": 24 }, { @@ -2073,9 +1935,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "command" - ], + "tags": ["command"], "grid": 24 }, { @@ -2086,9 +1946,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "compass" - ], + "tags": ["compass"], "grid": 24 }, { @@ -2099,9 +1957,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "copy" - ], + "tags": ["copy"], "grid": 24 }, { @@ -2112,9 +1968,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "corner-down-left" - ], + "tags": ["corner-down-left"], "grid": 24 }, { @@ -2125,9 +1979,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "corner-down-right" - ], + "tags": ["corner-down-right"], "grid": 24 }, { @@ -2138,9 +1990,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "corner-left-down" - ], + "tags": ["corner-left-down"], "grid": 24 }, { @@ -2151,9 +2001,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "corner-left-up" - ], + "tags": ["corner-left-up"], "grid": 24 }, { @@ -2164,9 +2012,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "corner-right-down" - ], + "tags": ["corner-right-down"], "grid": 24 }, { @@ -2177,9 +2023,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "corner-right-up" - ], + "tags": ["corner-right-up"], "grid": 24 }, { @@ -2190,9 +2034,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "corner-up-left" - ], + "tags": ["corner-up-left"], "grid": 24 }, { @@ -2203,9 +2045,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "corner-up-right" - ], + "tags": ["corner-up-right"], "grid": 24 }, { @@ -2216,9 +2056,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "cpu" - ], + "tags": ["cpu"], "grid": 24 }, { @@ -2229,9 +2067,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "credit-card" - ], + "tags": ["credit-card"], "grid": 24 }, { @@ -2242,9 +2078,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "crop" - ], + "tags": ["crop"], "grid": 24 }, { @@ -2255,9 +2089,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "crosshair" - ], + "tags": ["crosshair"], "grid": 24 }, { @@ -2268,9 +2100,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "database" - ], + "tags": ["database"], "grid": 24 }, { @@ -2281,9 +2111,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "delete" - ], + "tags": ["delete"], "grid": 24 }, { @@ -2294,9 +2122,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "disc" - ], + "tags": ["disc"], "grid": 24 }, { @@ -2307,9 +2133,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "dollar-sign" - ], + "tags": ["dollar-sign"], "grid": 24 }, { @@ -2320,9 +2144,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "download" - ], + "tags": ["download"], "grid": 24 }, { @@ -2333,9 +2155,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "download-cloud" - ], + "tags": ["download-cloud"], "grid": 24 }, { @@ -2346,9 +2166,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "droplet" - ], + "tags": ["droplet"], "grid": 24 }, { @@ -2359,9 +2177,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "edit" - ], + "tags": ["edit"], "grid": 24 }, { @@ -2372,9 +2188,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "edit-2" - ], + "tags": ["edit-2"], "grid": 24 }, { @@ -2385,9 +2199,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "edit-3" - ], + "tags": ["edit-3"], "grid": 24 }, { @@ -2398,9 +2210,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "external-link" - ], + "tags": ["external-link"], "grid": 24 }, { @@ -2411,9 +2221,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "eye" - ], + "tags": ["eye"], "grid": 24 }, { @@ -2424,9 +2232,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "eye-off" - ], + "tags": ["eye-off"], "grid": 24 }, { @@ -2437,9 +2243,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "facebook" - ], + "tags": ["facebook"], "grid": 24 }, { @@ -2450,9 +2254,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "fast-forward" - ], + "tags": ["fast-forward"], "grid": 24 }, { @@ -2463,9 +2265,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "feather" - ], + "tags": ["feather"], "grid": 24 }, { @@ -2476,9 +2276,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "figma" - ], + "tags": ["figma"], "grid": 24 }, { @@ -2489,9 +2287,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "file" - ], + "tags": ["file"], "grid": 24 }, { @@ -2502,9 +2298,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "file-minus" - ], + "tags": ["file-minus"], "grid": 24 }, { @@ -2515,9 +2309,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "file-plus" - ], + "tags": ["file-plus"], "grid": 24 }, { @@ -2528,9 +2320,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "file-text" - ], + "tags": ["file-text"], "grid": 24 }, { @@ -2541,9 +2331,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "film" - ], + "tags": ["film"], "grid": 24 }, { @@ -2554,9 +2342,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "filter" - ], + "tags": ["filter"], "grid": 24 }, { @@ -2567,9 +2353,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "flag" - ], + "tags": ["flag"], "grid": 24 }, { @@ -2580,9 +2364,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "folder" - ], + "tags": ["folder"], "grid": 24 }, { @@ -2593,9 +2375,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "folder-minus" - ], + "tags": ["folder-minus"], "grid": 24 }, { @@ -2606,9 +2386,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "folder-plus" - ], + "tags": ["folder-plus"], "grid": 24 }, { @@ -2619,9 +2397,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "framer" - ], + "tags": ["framer"], "grid": 24 }, { @@ -2632,9 +2408,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "frown" - ], + "tags": ["frown"], "grid": 24 }, { @@ -2645,9 +2419,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "gift" - ], + "tags": ["gift"], "grid": 24 }, { @@ -2658,9 +2430,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "git-branch" - ], + "tags": ["git-branch"], "grid": 24 }, { @@ -2671,9 +2441,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "git-commit" - ], + "tags": ["git-commit"], "grid": 24 }, { @@ -2684,9 +2452,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "git-merge" - ], + "tags": ["git-merge"], "grid": 24 }, { @@ -2697,9 +2463,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "git-pull-request" - ], + "tags": ["git-pull-request"], "grid": 24 }, { @@ -2710,9 +2474,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "github" - ], + "tags": ["github"], "grid": 24 }, { @@ -2723,9 +2485,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "gitlab" - ], + "tags": ["gitlab"], "grid": 24 }, { @@ -2736,9 +2496,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "globe" - ], + "tags": ["globe"], "grid": 24 }, { @@ -2749,9 +2507,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "grid" - ], + "tags": ["grid"], "grid": 24 }, { @@ -2762,9 +2518,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "hard-drive" - ], + "tags": ["hard-drive"], "grid": 24 }, { @@ -2775,9 +2529,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "hash" - ], + "tags": ["hash"], "grid": 24 }, { @@ -2788,9 +2540,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "headphones" - ], + "tags": ["headphones"], "grid": 24 }, { @@ -2801,9 +2551,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "heart" - ], + "tags": ["heart"], "grid": 24 }, { @@ -2814,9 +2562,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "help-circle" - ], + "tags": ["help-circle"], "grid": 24 }, { @@ -2827,9 +2573,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "hexagon" - ], + "tags": ["hexagon"], "grid": 24 }, { @@ -2840,9 +2584,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "home" - ], + "tags": ["home"], "grid": 24 }, { @@ -2853,9 +2595,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "image" - ], + "tags": ["image"], "grid": 24 }, { @@ -2866,9 +2606,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "inbox" - ], + "tags": ["inbox"], "grid": 24 }, { @@ -2879,9 +2617,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "info" - ], + "tags": ["info"], "grid": 24 }, { @@ -2892,9 +2628,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "instagram" - ], + "tags": ["instagram"], "grid": 24 }, { @@ -2905,9 +2639,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "italic" - ], + "tags": ["italic"], "grid": 24 }, { @@ -2918,9 +2650,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "key" - ], + "tags": ["key"], "grid": 24 }, { @@ -2931,9 +2661,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "layers" - ], + "tags": ["layers"], "grid": 24 }, { @@ -2944,9 +2672,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "layout" - ], + "tags": ["layout"], "grid": 24 }, { @@ -2957,9 +2683,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "life-buoy" - ], + "tags": ["life-buoy"], "grid": 24 }, { @@ -2970,9 +2694,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "link" - ], + "tags": ["link"], "grid": 24 }, { @@ -2983,9 +2705,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "link-2" - ], + "tags": ["link-2"], "grid": 24 }, { @@ -2996,9 +2716,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "linkedin" - ], + "tags": ["linkedin"], "grid": 24 }, { @@ -3009,9 +2727,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "list" - ], + "tags": ["list"], "grid": 24 }, { @@ -3022,9 +2738,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "loader" - ], + "tags": ["loader"], "grid": 24 }, { @@ -3035,9 +2749,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "lock" - ], + "tags": ["lock"], "grid": 24 }, { @@ -3048,9 +2760,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "log-in" - ], + "tags": ["log-in"], "grid": 24 }, { @@ -3061,9 +2771,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "log-out" - ], + "tags": ["log-out"], "grid": 24 }, { @@ -3074,9 +2782,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "mail" - ], + "tags": ["mail"], "grid": 24 }, { @@ -3087,9 +2793,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "map" - ], + "tags": ["map"], "grid": 24 }, { @@ -3100,9 +2804,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "map-pin" - ], + "tags": ["map-pin"], "grid": 24 }, { @@ -3113,9 +2815,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "maximize" - ], + "tags": ["maximize"], "grid": 24 }, { @@ -3126,9 +2826,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "maximize-2" - ], + "tags": ["maximize-2"], "grid": 24 }, { @@ -3139,9 +2837,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "meh" - ], + "tags": ["meh"], "grid": 24 }, { @@ -3152,9 +2848,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "menu" - ], + "tags": ["menu"], "grid": 24 }, { @@ -3165,9 +2859,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "message-circle" - ], + "tags": ["message-circle"], "grid": 24 }, { @@ -3178,9 +2870,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "message-square" - ], + "tags": ["message-square"], "grid": 24 }, { @@ -3191,9 +2881,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "mic" - ], + "tags": ["mic"], "grid": 24 }, { @@ -3204,9 +2892,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "mic-off" - ], + "tags": ["mic-off"], "grid": 24 }, { @@ -3217,9 +2903,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "minimize" - ], + "tags": ["minimize"], "grid": 24 }, { @@ -3230,9 +2914,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "minimize-2" - ], + "tags": ["minimize-2"], "grid": 24 }, { @@ -3243,9 +2925,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "minus" - ], + "tags": ["minus"], "grid": 24 }, { @@ -3256,9 +2936,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "minus-circle" - ], + "tags": ["minus-circle"], "grid": 24 }, { @@ -3269,9 +2947,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "minus-square" - ], + "tags": ["minus-square"], "grid": 24 }, { @@ -3282,9 +2958,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "monitor" - ], + "tags": ["monitor"], "grid": 24 }, { @@ -3295,9 +2969,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "moon" - ], + "tags": ["moon"], "grid": 24 }, { @@ -3308,9 +2980,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "more-horizontal" - ], + "tags": ["more-horizontal"], "grid": 24 }, { @@ -3321,9 +2991,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "more-vertical" - ], + "tags": ["more-vertical"], "grid": 24 }, { @@ -3334,9 +3002,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "mouse-pointer" - ], + "tags": ["mouse-pointer"], "grid": 24 }, { @@ -3347,9 +3013,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "move" - ], + "tags": ["move"], "grid": 24 }, { @@ -3360,9 +3024,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "music" - ], + "tags": ["music"], "grid": 24 }, { @@ -3373,9 +3035,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "navigation" - ], + "tags": ["navigation"], "grid": 24 }, { @@ -3386,9 +3046,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "navigation-2" - ], + "tags": ["navigation-2"], "grid": 24 }, { @@ -3399,9 +3057,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "octagon" - ], + "tags": ["octagon"], "grid": 24 }, { @@ -3412,9 +3068,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "package" - ], + "tags": ["package"], "grid": 24 }, { @@ -3425,9 +3079,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "paperclip" - ], + "tags": ["paperclip"], "grid": 24 }, { @@ -3438,9 +3090,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "pause" - ], + "tags": ["pause"], "grid": 24 }, { @@ -3451,9 +3101,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "pause-circle" - ], + "tags": ["pause-circle"], "grid": 24 }, { @@ -3464,9 +3112,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "pen-tool" - ], + "tags": ["pen-tool"], "grid": 24 }, { @@ -3477,9 +3123,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "percent" - ], + "tags": ["percent"], "grid": 24 }, { @@ -3490,9 +3134,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "phone" - ], + "tags": ["phone"], "grid": 24 }, { @@ -3503,9 +3145,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "phone-call" - ], + "tags": ["phone-call"], "grid": 24 }, { @@ -3516,9 +3156,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "phone-forwarded" - ], + "tags": ["phone-forwarded"], "grid": 24 }, { @@ -3529,9 +3167,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "phone-incoming" - ], + "tags": ["phone-incoming"], "grid": 24 }, { @@ -3542,9 +3178,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "phone-missed" - ], + "tags": ["phone-missed"], "grid": 24 }, { @@ -3555,9 +3189,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "phone-off" - ], + "tags": ["phone-off"], "grid": 24 }, { @@ -3568,9 +3200,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "phone-outgoing" - ], + "tags": ["phone-outgoing"], "grid": 24 }, { @@ -3581,9 +3211,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "pie-chart" - ], + "tags": ["pie-chart"], "grid": 24 }, { @@ -3594,9 +3222,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "play" - ], + "tags": ["play"], "grid": 24 }, { @@ -3607,9 +3233,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "play-circle" - ], + "tags": ["play-circle"], "grid": 24 }, { @@ -3620,9 +3244,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "plus" - ], + "tags": ["plus"], "grid": 24 }, { @@ -3633,9 +3255,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "plus-circle" - ], + "tags": ["plus-circle"], "grid": 24 }, { @@ -3646,9 +3266,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "plus-square" - ], + "tags": ["plus-square"], "grid": 24 }, { @@ -3659,9 +3277,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "pocket" - ], + "tags": ["pocket"], "grid": 24 }, { @@ -3672,9 +3288,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "power" - ], + "tags": ["power"], "grid": 24 }, { @@ -3685,9 +3299,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "printer" - ], + "tags": ["printer"], "grid": 24 }, { @@ -3698,9 +3310,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "radio" - ], + "tags": ["radio"], "grid": 24 }, { @@ -3711,9 +3321,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "refresh-ccw" - ], + "tags": ["refresh-ccw"], "grid": 24 }, { @@ -3724,9 +3332,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "refresh-cw" - ], + "tags": ["refresh-cw"], "grid": 24 }, { @@ -3737,9 +3343,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "repeat" - ], + "tags": ["repeat"], "grid": 24 }, { @@ -3750,9 +3354,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "rewind" - ], + "tags": ["rewind"], "grid": 24 }, { @@ -3763,9 +3365,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "rotate-ccw" - ], + "tags": ["rotate-ccw"], "grid": 24 }, { @@ -3776,9 +3376,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "rotate-cw" - ], + "tags": ["rotate-cw"], "grid": 24 }, { @@ -3789,9 +3387,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "rss" - ], + "tags": ["rss"], "grid": 24 }, { @@ -3802,9 +3398,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "save" - ], + "tags": ["save"], "grid": 24 }, { @@ -3815,9 +3409,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "scissors" - ], + "tags": ["scissors"], "grid": 24 }, { @@ -3828,9 +3420,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "search" - ], + "tags": ["search"], "grid": 24 }, { @@ -3841,9 +3431,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "send" - ], + "tags": ["send"], "grid": 24 }, { @@ -3854,9 +3442,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "server" - ], + "tags": ["server"], "grid": 24 }, { @@ -3867,9 +3453,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "settings" - ], + "tags": ["settings"], "grid": 24 }, { @@ -3880,9 +3464,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "share" - ], + "tags": ["share"], "grid": 24 }, { @@ -3893,9 +3475,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "share-2" - ], + "tags": ["share-2"], "grid": 24 }, { @@ -3906,9 +3486,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "shield" - ], + "tags": ["shield"], "grid": 24 }, { @@ -3919,9 +3497,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "shield-off" - ], + "tags": ["shield-off"], "grid": 24 }, { @@ -3932,9 +3508,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "shopping-bag" - ], + "tags": ["shopping-bag"], "grid": 24 }, { @@ -3945,9 +3519,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "shopping-cart" - ], + "tags": ["shopping-cart"], "grid": 24 }, { @@ -3958,9 +3530,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "shuffle" - ], + "tags": ["shuffle"], "grid": 24 }, { @@ -3971,9 +3541,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "sidebar" - ], + "tags": ["sidebar"], "grid": 24 }, { @@ -3984,9 +3552,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "skip-back" - ], + "tags": ["skip-back"], "grid": 24 }, { @@ -3997,9 +3563,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "skip-forward" - ], + "tags": ["skip-forward"], "grid": 24 }, { @@ -4010,9 +3574,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "slack" - ], + "tags": ["slack"], "grid": 24 }, { @@ -4023,9 +3585,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "slash" - ], + "tags": ["slash"], "grid": 24 }, { @@ -4036,9 +3596,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "sliders" - ], + "tags": ["sliders"], "grid": 24 }, { @@ -4049,9 +3607,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "smartphone" - ], + "tags": ["smartphone"], "grid": 24 }, { @@ -4062,9 +3618,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "smile" - ], + "tags": ["smile"], "grid": 24 }, { @@ -4075,9 +3629,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "speaker" - ], + "tags": ["speaker"], "grid": 24 }, { @@ -4088,9 +3640,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "square" - ], + "tags": ["square"], "grid": 24 }, { @@ -4101,9 +3651,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "star" - ], + "tags": ["star"], "grid": 24 }, { @@ -4114,9 +3662,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "stop-circle" - ], + "tags": ["stop-circle"], "grid": 24 }, { @@ -4127,9 +3673,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "sun" - ], + "tags": ["sun"], "grid": 24 }, { @@ -4140,9 +3684,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "sunrise" - ], + "tags": ["sunrise"], "grid": 24 }, { @@ -4153,9 +3695,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "sunset" - ], + "tags": ["sunset"], "grid": 24 }, { @@ -4166,9 +3706,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "tablet" - ], + "tags": ["tablet"], "grid": 24 }, { @@ -4179,9 +3717,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "tag" - ], + "tags": ["tag"], "grid": 24 }, { @@ -4192,9 +3728,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "target" - ], + "tags": ["target"], "grid": 24 }, { @@ -4205,9 +3739,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "terminal" - ], + "tags": ["terminal"], "grid": 24 }, { @@ -4218,9 +3750,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "thermometer" - ], + "tags": ["thermometer"], "grid": 24 }, { @@ -4231,9 +3761,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "thumbs-down" - ], + "tags": ["thumbs-down"], "grid": 24 }, { @@ -4244,9 +3772,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "thumbs-up" - ], + "tags": ["thumbs-up"], "grid": 24 }, { @@ -4257,9 +3783,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "toggle-left" - ], + "tags": ["toggle-left"], "grid": 24 }, { @@ -4270,9 +3794,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "toggle-right" - ], + "tags": ["toggle-right"], "grid": 24 }, { @@ -4283,9 +3805,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "tool" - ], + "tags": ["tool"], "grid": 24 }, { @@ -4296,9 +3816,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "trash" - ], + "tags": ["trash"], "grid": 24 }, { @@ -4309,9 +3827,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "trash-2" - ], + "tags": ["trash-2"], "grid": 24 }, { @@ -4322,9 +3838,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "trello" - ], + "tags": ["trello"], "grid": 24 }, { @@ -4335,9 +3849,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "trending-down" - ], + "tags": ["trending-down"], "grid": 24 }, { @@ -4348,9 +3860,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "trending-up" - ], + "tags": ["trending-up"], "grid": 24 }, { @@ -4361,9 +3871,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "triangle" - ], + "tags": ["triangle"], "grid": 24 }, { @@ -4374,9 +3882,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "truck" - ], + "tags": ["truck"], "grid": 24 }, { @@ -4387,9 +3893,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "tv" - ], + "tags": ["tv"], "grid": 24 }, { @@ -4400,9 +3904,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "twitch" - ], + "tags": ["twitch"], "grid": 24 }, { @@ -4413,9 +3915,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "twitter" - ], + "tags": ["twitter"], "grid": 24 }, { @@ -4426,9 +3926,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "type" - ], + "tags": ["type"], "grid": 24 }, { @@ -4439,9 +3937,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "umbrella" - ], + "tags": ["umbrella"], "grid": 24 }, { @@ -4452,9 +3948,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "underline" - ], + "tags": ["underline"], "grid": 24 }, { @@ -4465,9 +3959,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "unlock" - ], + "tags": ["unlock"], "grid": 24 }, { @@ -4478,9 +3970,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "upload" - ], + "tags": ["upload"], "grid": 24 }, { @@ -4491,9 +3981,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "upload-cloud" - ], + "tags": ["upload-cloud"], "grid": 24 }, { @@ -4504,9 +3992,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "user" - ], + "tags": ["user"], "grid": 24 }, { @@ -4517,9 +4003,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "user-check" - ], + "tags": ["user-check"], "grid": 24 }, { @@ -4530,9 +4014,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "user-minus" - ], + "tags": ["user-minus"], "grid": 24 }, { @@ -4543,9 +4025,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "user-plus" - ], + "tags": ["user-plus"], "grid": 24 }, { @@ -4556,9 +4036,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "user-x" - ], + "tags": ["user-x"], "grid": 24 }, { @@ -4569,9 +4047,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "users" - ], + "tags": ["users"], "grid": 24 }, { @@ -4582,9 +4058,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "video" - ], + "tags": ["video"], "grid": 24 }, { @@ -4595,9 +4069,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "video-off" - ], + "tags": ["video-off"], "grid": 24 }, { @@ -4608,9 +4080,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "voicemail" - ], + "tags": ["voicemail"], "grid": 24 }, { @@ -4621,9 +4091,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "volume" - ], + "tags": ["volume"], "grid": 24 }, { @@ -4634,9 +4102,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "volume-1" - ], + "tags": ["volume-1"], "grid": 24 }, { @@ -4647,9 +4113,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "volume-2" - ], + "tags": ["volume-2"], "grid": 24 }, { @@ -4660,9 +4124,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "volume-x" - ], + "tags": ["volume-x"], "grid": 24 }, { @@ -4673,9 +4135,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "watch" - ], + "tags": ["watch"], "grid": 24 }, { @@ -4686,9 +4146,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "wifi" - ], + "tags": ["wifi"], "grid": 24 }, { @@ -4699,9 +4157,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "wifi-off" - ], + "tags": ["wifi-off"], "grid": 24 }, { @@ -4712,9 +4168,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "wind" - ], + "tags": ["wind"], "grid": 24 }, { @@ -4725,9 +4179,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "x" - ], + "tags": ["x"], "grid": 24 }, { @@ -4738,9 +4190,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "x-circle" - ], + "tags": ["x-circle"], "grid": 24 }, { @@ -4751,9 +4201,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "x-octagon" - ], + "tags": ["x-octagon"], "grid": 24 }, { @@ -4764,9 +4212,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "x-square" - ], + "tags": ["x-square"], "grid": 24 }, { @@ -4777,9 +4223,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "youtube" - ], + "tags": ["youtube"], "grid": 24 }, { @@ -4790,9 +4234,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "zap" - ], + "tags": ["zap"], "grid": 24 }, { @@ -4803,9 +4245,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "zap-off" - ], + "tags": ["zap-off"], "grid": 24 }, { @@ -4816,9 +4256,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "zoom-in" - ], + "tags": ["zoom-in"], "grid": 24 }, { @@ -4829,9 +4267,7 @@ "attrs": [], "isMulticolor": false, "isMulticolor2": false, - "tags": [ - "zoom-out" - ], + "tags": ["zoom-out"], "grid": 24 } ] @@ -4867,4 +4303,4 @@ }, "historySize": 50 } -} \ No newline at end of file +} diff --git a/layouts/shortcodes/mermaid.html b/layouts/shortcodes/mermaid.html index 417f87ecb..45bb4c2fd 100644 --- a/layouts/shortcodes/mermaid.html +++ b/layouts/shortcodes/mermaid.html @@ -1,6 +1,6 @@ {{ if not (.Page.Scratch.Get "mermaid") }} - + diff --git a/package.json b/package.json index 6daab4a6e..13293d327 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "The Filecoin protocol specification", "main": "index.js", "scripts": { - "test": "remark content --frail --quiet", + "test": "remark content --frail --quiet && prettier --check .", "start": "concurrently npm:watch-*", "serve": "npm start", "watch-hugo": "npm run build-diagrams && hugo server --bind=0.0.0.0 --disableFastRender --renderToDisk", @@ -33,20 +33,36 @@ "execa": "^4.0.3", "globby": "^11.0.1", "graphviz-cli": "^1.0.0", - "hugo-extended": "^0.76.4", + "hugo-extended": "^0.76.5", + "husky": ">=4", "jsdom": "^16.4.0", + "lint-staged": ">=10", "np": "^6.5.0", "premove": "^3.0.1", - "remark-cli": "^8.0.1", - "remark-frontmatter": "^2.0.0", + "prettier": "2.1.2", + "remark-cli": "^9.0.0", + "remark-frontmatter": "^3.0.0", "remark-lint-fenced-code-flag": "^2.0.1", "remark-lint-first-heading-level": "^2.0.1", "remark-lint-heading-increment": "^2.0.1", "remark-lint-no-hr-after-heading": "^1.0.0", "remark-lint-no-multiple-toplevel-headings": "^2.0.1", - "remark-math": "^3.0.1", - "remark-preset-lint-recommended": "^4.0.1", + "remark-math": "^4.0.0", + "remark-preset-lint-recommended": "^5.0.0", "remark-toc": "^7.0.0", "to-vfile": "^6.1.0" + }, + "prettier": { + "tabWidth": 2, + "semi": false, + "singleQuote": true + }, + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } + }, + "lint-staged": { + "**/*": "prettier --write --ignore-unknown" } } diff --git a/static/sw.js b/static/sw.js index 03c3e4f0b..1fdcf32d5 100644 --- a/static/sw.js +++ b/static/sw.js @@ -1,21 +1,23 @@ //This is the service worker with the Advanced caching -importScripts('https://storage.googleapis.com/workbox-cdn/releases/5.0.0/workbox-sw.js'); +importScripts( + 'https://storage.googleapis.com/workbox-cdn/releases/5.0.0/workbox-sw.js' +) -const HTML_CACHE = "html"; -const JS_CACHE = "javascript"; -const STYLE_CACHE = "stylesheets"; -const IMAGE_CACHE = "images"; -const FONT_CACHE = "fonts"; +const HTML_CACHE = 'html' +const JS_CACHE = 'javascript' +const STYLE_CACHE = 'stylesheets' +const IMAGE_CACHE = 'images' +const FONT_CACHE = 'fonts' -self.addEventListener("message", (event) => { - if (event.data && event.data.type === "SKIP_WAITING") { - self.skipWaiting(); +self.addEventListener('message', (event) => { + if (event.data && event.data.type === 'SKIP_WAITING') { + self.skipWaiting() } -}); +}) workbox.routing.registerRoute( - ({event}) => event.request.destination === 'document', + ({ event }) => event.request.destination === 'document', new workbox.strategies.NetworkFirst({ cacheName: HTML_CACHE, plugins: [ @@ -24,10 +26,10 @@ workbox.routing.registerRoute( }), ], }) -); +) workbox.routing.registerRoute( - ({event}) => event.request.destination === 'script', + ({ event }) => event.request.destination === 'script', new workbox.strategies.StaleWhileRevalidate({ cacheName: JS_CACHE, plugins: [ @@ -36,10 +38,10 @@ workbox.routing.registerRoute( }), ], }) -); +) workbox.routing.registerRoute( - ({event}) => event.request.destination === 'style', + ({ event }) => event.request.destination === 'style', new workbox.strategies.StaleWhileRevalidate({ cacheName: STYLE_CACHE, plugins: [ @@ -48,10 +50,10 @@ workbox.routing.registerRoute( }), ], }) -); +) workbox.routing.registerRoute( - ({event}) => event.request.destination === 'image', + ({ event }) => event.request.destination === 'image', new workbox.strategies.StaleWhileRevalidate({ cacheName: IMAGE_CACHE, plugins: [ @@ -60,10 +62,10 @@ workbox.routing.registerRoute( }), ], }) -); +) workbox.routing.registerRoute( - ({event}) => event.request.destination === 'font', + ({ event }) => event.request.destination === 'font', new workbox.strategies.StaleWhileRevalidate({ cacheName: FONT_CACHE, plugins: [ @@ -72,7 +74,4 @@ workbox.routing.registerRoute( }), ], }) -); - - - +) diff --git a/tools/diagrams.js b/tools/diagrams.js index 77bdea53f..a2d5abead 100755 --- a/tools/diagrams.js +++ b/tools/diagrams.js @@ -1,69 +1,62 @@ #!/usr/bin/env node -const globby = require('globby'); +const globby = require('globby') const execa = require('execa') const path = require('path') const fs = require('fs') const runMmd = (p) => { - const outDir = path.dirname(p).replace('content/', 'static/_gen/diagrams/') - const outFile = path.basename(p).replace('.mmd', '.svg') + const outDir = path.dirname(p).replace('content/', 'static/_gen/diagrams/') + const outFile = path.basename(p).replace('.mmd', '.svg') - fs.mkdirSync(outDir, { recursive: true }) - const config = process.env.CI ? ['-p', 'tools/pptr.config'] :[] - return execa('mmdc', [ - ...config, - '-i', p, - '-o', path.join(outDir, outFile) - ], { preferLocal: true }); + fs.mkdirSync(outDir, { recursive: true }) + const config = process.env.CI ? ['-p', 'tools/pptr.config'] : [] + return execa('mmdc', [...config, '-i', p, '-o', path.join(outDir, outFile)], { + preferLocal: true, + }) } const runMmdAll = async () => { - const paths = await globby(['content/**/*.mmd']); - await Promise.all(paths.map(runMmd)) + const paths = await globby(['content/**/*.mmd']) + await Promise.all(paths.map(runMmd)) } const runDot = (p) => { - const outDir =path.dirname(p).replace('content/', 'static/_gen/diagrams/') - const outFile = path.basename(p).replace('.dot', '.svg') - fs.mkdirSync(outDir, { recursive: true }) - return execa('graphviz', [ - '-Tsvg', - `-o${path.join(outDir, outFile)}`, - p - ], { preferLocal: true }); + const outDir = path.dirname(p).replace('content/', 'static/_gen/diagrams/') + const outFile = path.basename(p).replace('.dot', '.svg') + fs.mkdirSync(outDir, { recursive: true }) + return execa('graphviz', ['-Tsvg', `-o${path.join(outDir, outFile)}`, p], { + preferLocal: true, + }) } const runDotAll = async () => { - const paths = await globby(['content/**/*.dot']); - await Promise.all(paths.map(runDot)) + const paths = await globby(['content/**/*.dot']) + await Promise.all(paths.map(runDot)) } const run = async () => { - const args = process.argv.slice(2); - console.log('Processing *.{mmd,dot}'); - console.time('Processed *.{mmd,dot}') - await Promise.all([ - runDotAll(), - runMmdAll() - ]) - console.timeEnd('Processed *.{mmd,dot}') + const args = process.argv.slice(2) + console.log('Processing *.{mmd,dot}') + console.time('Processed *.{mmd,dot}') + await Promise.all([runDotAll(), runMmdAll()]) + console.timeEnd('Processed *.{mmd,dot}') } module.exports.configureWatcher = (watcher) => { watcher.on('all', async (_, p) => { const ext = path.extname(p) switch (ext) { - case ".dot": - await runDot(p) - console.log('done ', p) - break; - case ".mmd": - await runMmd(p) - console.log('done ', p) - break - default: - break; + case '.dot': + await runDot(p) + console.log('done ', p) + break + case '.mmd': + await runMmd(p) + console.log('done ', p) + break + default: + break } }) watcher.add('content/**/*.{mmd,dot}') @@ -71,5 +64,5 @@ module.exports.configureWatcher = (watcher) => { // run as script, so do the thing if (require.main === module) { - run() + run() } diff --git a/tools/readme.js b/tools/readme.js index cc5b21590..063f6d057 100755 --- a/tools/readme.js +++ b/tools/readme.js @@ -8,7 +8,7 @@ const readme = vfile.readSync('README.md') // inject toc into readme remark() .use(toc, { tight: true }) - .process(readme, function(err) { + .process(readme, function (err) { if (err) throw err vfile.writeSync(readme) console.log('Updated README.md') diff --git a/tools/toc.js b/tools/toc.js index 76025ec55..10ec5c605 100755 --- a/tools/toc.js +++ b/tools/toc.js @@ -13,14 +13,14 @@ if (require.main === module) { run(src, dest) } -async function run (src, dest) { +async function run(src, dest) { console.log('Building toc.json') console.time('Built toc.json') await buildToc(src, dest) console.timeEnd('Built toc.json') } -async function buildToc (src, dest) { +async function buildToc(src, dest) { if (!fs.existsSync(path.dirname(dest))) { fs.mkdirSync(path.dirname(dest)) } diff --git a/tools/toc/build-model.js b/tools/toc/build-model.js index 2221a1c27..9bf502b1c 100644 --- a/tools/toc/build-model.js +++ b/tools/toc/build-model.js @@ -9,83 +9,78 @@ // ] function buildTocModel(root) { - const model = []; - const headingList = root.querySelectorAll("h1,h2,h3,h4,h5,h6"); - let parents = [{ tagName: "H0", children: model }]; - let prevSibling = null; - let sectionNumber = [0]; + const model = [] + const headingList = root.querySelectorAll('h1,h2,h3,h4,h5,h6') + let parents = [{ tagName: 'H0', children: model }] + let prevSibling = null + let sectionNumber = [0] - function addSibling(node) { - sectionNumber[sectionNumber.length - 1] = - sectionNumber[sectionNumber.length - 1] + 1; - node.number = sectionNumber.join("."); - parents[parents.length - 1].children.push(node); - prevSibling = node; - } + function addSibling(node) { + sectionNumber[sectionNumber.length - 1] = + sectionNumber[sectionNumber.length - 1] + 1 + node.number = sectionNumber.join('.') + parents[parents.length - 1].children.push(node) + prevSibling = node + } - function addChild(node) { - sectionNumber.push(1); - node.number = sectionNumber.join("."); - parents.push(prevSibling); - prevSibling.children.push(node); - prevSibling = node; - } + function addChild(node) { + sectionNumber.push(1) + node.number = sectionNumber.join('.') + parents.push(prevSibling) + prevSibling.children.push(node) + prevSibling = node + } - for (let el of headingList) { - let node = { - id: el.id, - number: "", - tagName: el.tagName, - text: cleanHeadingText(el), - page: Boolean(el.dataset.page), - dashboardWeight: el.dataset.dashboardWeight, - dashboardAudit: el.dataset.dashboardAudit, - dashboardAuditURL: el.dataset.dashboardAuditUrl, - dashboardAuditDate: el.dataset.dashboardAuditDate, - dashboardState: el.dataset.dashboardState, - children: [], - }; + for (let el of headingList) { + let node = { + id: el.id, + number: '', + tagName: el.tagName, + text: cleanHeadingText(el), + page: Boolean(el.dataset.page), + dashboardWeight: el.dataset.dashboardWeight, + dashboardAudit: el.dataset.dashboardAudit, + dashboardAuditURL: el.dataset.dashboardAuditUrl, + dashboardAuditDate: el.dataset.dashboardAuditDate, + dashboardState: el.dataset.dashboardState, + children: [], + } - if (!prevSibling || headingNum(node) === headingNum(prevSibling)) { - // sibling: h2 == h2 - addSibling(node); - } else if (headingNum(node) > headingNum(prevSibling)) { - // child: h3 > h2 - addChild(node); - } else { - // h2 or h1 after an h3... gotta find out how far to unwind. Parents may not be contiguous, so walk till we find a parent - let target = headingNum(node); - let rmCount = 0; - while ( - target <= headingNum(parents[parents.length - (rmCount + 1)]) - ) { - rmCount++; - } - parents = parents.slice(0, parents.length - rmCount); - sectionNumber = sectionNumber.slice( - 0, - sectionNumber.length - rmCount - ); + if (!prevSibling || headingNum(node) === headingNum(prevSibling)) { + // sibling: h2 == h2 + addSibling(node) + } else if (headingNum(node) > headingNum(prevSibling)) { + // child: h3 > h2 + addChild(node) + } else { + // h2 or h1 after an h3... gotta find out how far to unwind. Parents may not be contiguous, so walk till we find a parent + let target = headingNum(node) + let rmCount = 0 + while (target <= headingNum(parents[parents.length - (rmCount + 1)])) { + rmCount++ + } + parents = parents.slice(0, parents.length - rmCount) + sectionNumber = sectionNumber.slice(0, sectionNumber.length - rmCount) - addSibling(node); - } + addSibling(node) } + } - return model; + return model } function cleanHeadingText(el) { - const anchorText = el.querySelector("a"); - if (anchorText) { - return anchorText.textContent.trim(); - } - return el.textContent.trim(); + const anchorText = el.querySelector('a') + if (anchorText) { + return anchorText.textContent.trim() + } + return el.textContent.trim() } function headingNum(el) { - return Number(el.tagName[1]); + return Number(el.tagName[1]) } module.exports = { - buildTocModel, -}; + buildTocModel, +} diff --git a/tools/watch.js b/tools/watch.js index 8476faf42..f0c34ea0c 100755 --- a/tools/watch.js +++ b/tools/watch.js @@ -5,19 +5,19 @@ const toc = require('./toc') watch() -function watch () { +function watch() { const watcher = chokidar.watch([], { awaitWriteFinish: { stabilityThreshold: 1000, - pollInterval: 100 + pollInterval: 100, }, - ignoreInitial: true + ignoreInitial: true, }) watcher - .on('error', err => console.error('error watching: ', err)) + .on('error', (err) => console.error('error watching: ', err)) .on('all', (event, p) => console.log(event, p)) toc.configureWatcher(watcher) diagrams.configureWatcher(watcher) -} \ No newline at end of file +}