upload blog posts tools.html and labrador.html
jump to
@@ -88,7 +88,7 @@ real money (not part of <em>reality</em>), while few hackers already saw them as a valid
form of currency.</p> <p>Another example was the <a href="https://youtu.be/KCAGb7oSwDs?t=72">usage of “AFK”</a> by -the ThePirateBay founders, to refer to the world offline (instead of “IRL”):</p> +the ThePirateBay founders, to refer to the offline world (instead of “IRL”):</p> <blockquote> <p><em>“We don’t use the expression IRL, we say ‘Away From Keyboard”, AFK; we think@@ -296,7 +296,7 @@ if (hElem.parentNode.className=="row postThumb") {
continue; } hElem.id = hElem.innerHTML.toLowerCase().replace(" ", "-"); - hElem.innerHTML = "<a style='text-decoration:none;color:black;' href='#"+hElem.id+"'>"+hElem.innerHTML+"</a>"; + hElem.innerHTML = "<a style='color:black!important;' href='#"+hElem.id+"'>"+hElem.innerHTML+"</a>"; } } tagLinks("h2");@@ -304,8 +304,77 @@ tagLinks("h3");
tagLinks("h4"); tagLinks("h5"); </script> - <script src="js/mermaid.min.js"></script> + <script> +function generateTOC(tocContainerId = "toc") { + const tocContainer = document.getElementById(tocContainerId); + if (!tocContainer) return; + + const headings = document.querySelectorAll("h2, h3, h4, h5, h6"); + if (!headings.length) return; + + // Generate IDs if missing + headings.forEach((heading, index) => { + if (!heading.id) { + heading.id = + heading.textContent + .trim() + .toLowerCase() + .replace(/[^\w\s-]/g, "") + .replace(/\s+/g, "-") + + "-" + + index; + } + }); + + const toc = document.createElement("ul"); + let currentLevel = 1; + let currentList = toc; + const stack = [toc]; + + headings.forEach((heading) => { + const level = parseInt(heading.tagName.substring(1), 10); + + const li = document.createElement("li"); + const a = document.createElement("a"); + + a.href = "#" + heading.id; + a.textContent = heading.textContent; + + li.appendChild(a); + + if (level > currentLevel) { + for (let i = currentLevel; i < level; i++) { + const nestedList = document.createElement("ul"); + const lastLi = + currentList.lastElementChild || + currentList.appendChild(document.createElement("ul")); + + lastLi.appendChild(nestedList); + + stack.push(nestedList); + currentList = nestedList; + } + } else if (level < currentLevel) { + for (let i = currentLevel; i > level; i--) { + stack.pop(); + } + currentList = stack[stack.length - 1]; + } + + currentList.appendChild(li); + currentLevel = level; + }); + + tocContainer.innerHTML = ""; + tocContainer.appendChild(toc); +} + +document.addEventListener("DOMContentLoaded", () => { + generateTOC(); +}); + </script> + <script src="js/mermaid.min.js"></script> </body> </html>
@@ -283,7 +283,7 @@ if (hElem.parentNode.className=="row postThumb") {
continue; } hElem.id = hElem.innerHTML.toLowerCase().replace(" ", "-"); - hElem.innerHTML = "<a style='text-decoration:none;color:black;' href='#"+hElem.id+"'>"+hElem.innerHTML+"</a>"; + hElem.innerHTML = "<a style='color:black!important;' href='#"+hElem.id+"'>"+hElem.innerHTML+"</a>"; } } tagLinks("h2");@@ -291,8 +291,77 @@ tagLinks("h3");
tagLinks("h4"); tagLinks("h5"); </script> - <script src="js/mermaid.min.js"></script> + <script> +function generateTOC(tocContainerId = "toc") { + const tocContainer = document.getElementById(tocContainerId); + if (!tocContainer) return; + + const headings = document.querySelectorAll("h2, h3, h4, h5, h6"); + if (!headings.length) return; + + // Generate IDs if missing + headings.forEach((heading, index) => { + if (!heading.id) { + heading.id = + heading.textContent + .trim() + .toLowerCase() + .replace(/[^\w\s-]/g, "") + .replace(/\s+/g, "-") + + "-" + + index; + } + }); + + const toc = document.createElement("ul"); + let currentLevel = 1; + let currentList = toc; + const stack = [toc]; + + headings.forEach((heading) => { + const level = parseInt(heading.tagName.substring(1), 10); + + const li = document.createElement("li"); + const a = document.createElement("a"); + + a.href = "#" + heading.id; + a.textContent = heading.textContent; + + li.appendChild(a); + + if (level > currentLevel) { + for (let i = currentLevel; i < level; i++) { + const nestedList = document.createElement("ul"); + const lastLi = + currentList.lastElementChild || + currentList.appendChild(document.createElement("ul")); + + lastLi.appendChild(nestedList); + + stack.push(nestedList); + currentList = nestedList; + } + } else if (level < currentLevel) { + for (let i = currentLevel; i > level; i--) { + stack.pop(); + } + currentList = stack[stack.length - 1]; + } + + currentList.appendChild(li); + currentLevel = level; + }); + + tocContainer.innerHTML = ""; + tocContainer.appendChild(toc); +} + +document.addEventListener("DOMContentLoaded", () => { + generateTOC(); +}); + </script> + <script src="js/mermaid.min.js"></script> </body> </html>
@@ -501,7 +501,7 @@ if (hElem.parentNode.className=="row postThumb") {
continue; } hElem.id = hElem.innerHTML.toLowerCase().replace(" ", "-"); - hElem.innerHTML = "<a style='text-decoration:none;color:black;' href='#"+hElem.id+"'>"+hElem.innerHTML+"</a>"; + hElem.innerHTML = "<a style='color:black!important;' href='#"+hElem.id+"'>"+hElem.innerHTML+"</a>"; } } tagLinks("h2");@@ -509,8 +509,77 @@ tagLinks("h3");
tagLinks("h4"); tagLinks("h5"); </script> - <script src="js/mermaid.min.js"></script> + <script> +function generateTOC(tocContainerId = "toc") { + const tocContainer = document.getElementById(tocContainerId); + if (!tocContainer) return; + + const headings = document.querySelectorAll("h2, h3, h4, h5, h6"); + if (!headings.length) return; + + // Generate IDs if missing + headings.forEach((heading, index) => { + if (!heading.id) { + heading.id = + heading.textContent + .trim() + .toLowerCase() + .replace(/[^\w\s-]/g, "") + .replace(/\s+/g, "-") + + "-" + + index; + } + }); + + const toc = document.createElement("ul"); + let currentLevel = 1; + let currentList = toc; + const stack = [toc]; + + headings.forEach((heading) => { + const level = parseInt(heading.tagName.substring(1), 10); + + const li = document.createElement("li"); + const a = document.createElement("a"); + + a.href = "#" + heading.id; + a.textContent = heading.textContent; + + li.appendChild(a); + + if (level > currentLevel) { + for (let i = currentLevel; i < level; i++) { + const nestedList = document.createElement("ul"); + const lastLi = + currentList.lastElementChild || + currentList.appendChild(document.createElement("ul")); + + lastLi.appendChild(nestedList); + + stack.push(nestedList); + currentList = nestedList; + } + } else if (level < currentLevel) { + for (let i = currentLevel; i > level; i--) { + stack.pop(); + } + currentList = stack[stack.length - 1]; + } + + currentList.appendChild(li); + currentLevel = level; + }); + + tocContainer.innerHTML = ""; + tocContainer.appendChild(toc); +} + +document.addEventListener("DOMContentLoaded", () => { + generateTOC(); +}); + </script> + <script src="js/mermaid.min.js"></script> </body> </html>
@@ -640,7 +640,7 @@ if (hElem.parentNode.className=="row postThumb") {
continue; } hElem.id = hElem.innerHTML.toLowerCase().replace(" ", "-"); - hElem.innerHTML = "<a style='text-decoration:none;color:black;' href='#"+hElem.id+"'>"+hElem.innerHTML+"</a>"; + hElem.innerHTML = "<a style='color:black!important;' href='#"+hElem.id+"'>"+hElem.innerHTML+"</a>"; } } tagLinks("h2");@@ -648,8 +648,77 @@ tagLinks("h3");
tagLinks("h4"); tagLinks("h5"); </script> - <script src="js/mermaid.min.js"></script> + <script> +function generateTOC(tocContainerId = "toc") { + const tocContainer = document.getElementById(tocContainerId); + if (!tocContainer) return; + + const headings = document.querySelectorAll("h2, h3, h4, h5, h6"); + if (!headings.length) return; + + // Generate IDs if missing + headings.forEach((heading, index) => { + if (!heading.id) { + heading.id = + heading.textContent + .trim() + .toLowerCase() + .replace(/[^\w\s-]/g, "") + .replace(/\s+/g, "-") + + "-" + + index; + } + }); + + const toc = document.createElement("ul"); + let currentLevel = 1; + let currentList = toc; + const stack = [toc]; + + headings.forEach((heading) => { + const level = parseInt(heading.tagName.substring(1), 10); + + const li = document.createElement("li"); + const a = document.createElement("a"); + + a.href = "#" + heading.id; + a.textContent = heading.textContent; + + li.appendChild(a); + + if (level > currentLevel) { + for (let i = currentLevel; i < level; i++) { + const nestedList = document.createElement("ul"); + const lastLi = + currentList.lastElementChild || + currentList.appendChild(document.createElement("ul")); + + lastLi.appendChild(nestedList); + + stack.push(nestedList); + currentList = nestedList; + } + } else if (level < currentLevel) { + for (let i = currentLevel; i > level; i--) { + stack.pop(); + } + currentList = stack[stack.length - 1]; + } + + currentList.appendChild(li); + currentLevel = level; + }); + + tocContainer.innerHTML = ""; + tocContainer.appendChild(toc); +} + +document.addEventListener("DOMContentLoaded", () => { + generateTOC(); +}); + </script> + <script src="js/mermaid.min.js"></script> </body> </html>
@@ -78,7 +78,7 @@
.container[style*='max-width:800px'], #markdown.container { margin-top: 30px !important; - max-width: 760px !important; + max-width: 800px !important; padding: 0 18px; }@@ -96,16 +96,19 @@ padding-top: 0;
line-height: 1.35; } -h1 { font-size: 1.95rem; } -h2 { font-size: 1.5rem; } -h3 { font-size: 1.24rem; } +h1 { font-size: 1.95rem!important; } +h2 { font-size: 1.5rem!important; } +h3 { font-size: 1.24rem!important; } +h4 { font-size: 1.0rem!important; } +h5 { font-size: 0.84rem!important; } +h6 { font-size: 0.60rem!important; } h1 a, h2 a, h3 a, h4 a, h5 a { - color: inherit !important; + color: black !important; text-decoration: none !important; }@@ -141,7 +144,7 @@ }
blockquote { background: transparent; - border-left: 2px solid var(--border); + border-left: 6px solid var(--border); margin: 1.3em 0; padding: 0.3em 0 0.3em 0.9em; color: var(--muted);
@@ -1,270 +0,0 @@
-@font-face { - font-family: 'Roboto'; - src: url('Roboto-Regular.ttf') format('truetype'); - font-display: swap; -} - -:root { - --bg: #ffffff; - --text: #111111; - --muted: #666666; - --border: #e6e6e6; - --accent: #4d9aa1; - --hover-bg: #f3f3f3; -} - -body { - margin: 0; - background: var(--bg); - color: var(--text); - font-family: 'Avenir Next', 'Montserrat', 'Helvetica Neue', 'Segoe UI', Arial, sans-serif; - font-size: 16px; - line-height: 1.7; - -webkit-font-smoothing: antialiased; - text-rendering: optimizeLegibility; -} - -.dark-theme { - --bg: #0f0f10; - --text: #e9e9ea; - --muted: #a9a9ad; - --border: #2a2a2d; - --accent: #4d9aa1; - --hover-bg: #1b1b1e; -} - -.navbar.navbar-default { - background: var(--bg); - border: 0; - border-bottom: 1px solid var(--border); -} - -#mainNav .container { - min-height: 50px; - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; -} - -#mainNav .container > div { - display: flex; - align-items: center; - gap: 10px; -} - -#mainNav a, -#mainNav a[style], -#mainNav a[style*='color:#000'] { - color: var(--text) !important; -} - -#mainNav a:hover { - color: var(--accent) !important; -} - -#mainNav .container > div:last-child > a { - font-size: 0.9em; - display: inline-flex; - align-items: center; - height: 24px; - line-height: 24px; -} - -#mainNav img[src*='gradient-line'] { - display: none; -} - -.container[style*='max-width:800px'], -#markdown.container { - margin-top: 30px !important; - max-width: 760px !important; - padding: 0 18px; -} - -h1, -h2, -h3, -h4, -h5 { - color: var(--text); - border-bottom: 1px solid var(--border); - margin-top: 1.9rem; - margin-bottom: 0.8rem; - padding-bottom: 0.35rem; - padding-top: 0; - line-height: 1.35; -} - -h1 { font-size: 1.95rem; } -h2 { font-size: 1.5rem; } -h3 { font-size: 1.24rem; } - -h1 a, -h2 a, -h3 a, -h4 a, -h5 a { - color: inherit !important; - text-decoration: none !important; -} - -p, -li { - color: var(--text); -} - -a, -a[style], -a[style*='color:black'], -a[style*='color:gray'] { - color: var(--accent) !important; - text-decoration: none !important; -} - -a:hover { - text-decoration: none !important; -} - -pre { - background: transparent; - border: 1px solid var(--border); - border-radius: 0; - padding: 12px 14px; - overflow-x: auto; -} - -.dark-theme .hljs { - background: #1b1b1e !important; - color: #e9e9ea !important; -} - -blockquote { - background: transparent; - border-left: 2px solid var(--border); - margin: 1.3em 0; - padding: 0.3em 0 0.3em 0.9em; - /* color: var(--muted); */ - color: gray!important; -} - -.postThumb { - display: block; - color: var(--text) !important; - border: 0; - border-radius: 0; - background: var(--bg); - padding: 7px 10px; - margin: 0; - transition: background-color 0.15s ease; -} - -.postThumb a { - color: inherit !important; -} - -.postThumb h2, -.postThumb h3, -.postThumb h4, -.postThumb h5 { - margin-top: 0.45rem; - margin-bottom: 0.3rem; - padding-bottom: 0.2rem; - border-bottom: 0; - line-height: 1.25; -} - -.postThumb p, -.postThumb li { - margin: 0.25rem 0; - line-height: 1.45; -} - -.postThumb:hover { - background: var(--hover-bg); -} - -.container[style*='max-width:800px'] h1 a, -.container[style*='max-width:800px'] h2 a, -.container[style*='max-width:800px'] h3 a, -.container[style*='max-width:800px'] h4 a, -.container[style*='max-width:800px'] h5 a, -#markdown h1 a, -#markdown h2 a, -#markdown h3 a, -#markdown h4 a, -#markdown h5 a { - color: var(--text) !important; -} - -footer, -footer .container { - color: var(--muted); -} - -.onoffswitch { - display: inline-flex !important; - align-items: center; - justify-content: center; - width: 24px; - height: 24px; - user-select: none; - margin: 0 !important; - vertical-align: middle; - align-self: center; - position: static; -} - -.onoffswitch-checkbox { - display: none; -} - -.onoffswitch-label { - display: inline-flex; - align-items: center; - justify-content: center; - cursor: pointer; - width: 24px; - height: 24px; - border: 1px solid var(--border); - border-radius: 50%; - background-color: transparent; - margin-bottom: 0; - transition: background-color 0.2s ease, border-color 0.2s ease; -} - -.onoffswitch-label:before { - content: '☀'; - display: block; - font-size: 12px; - line-height: 1; - color: var(--text); - transform: none; -} - -.onoffswitch-checkbox:checked + .onoffswitch-label:before { - content: '☾'; -} - -.onoffswitch-label:hover { - background: var(--hover-bg); -} - -@media (max-width: 840px) { - #mainNav .container { - display: block; - padding-top: 4px; - } - - #mainNav .container > div { - float: none !important; - display: block; - text-align: center; - } - - .container[style*='max-width:800px'], - #markdown.container { - margin-top: 96px !important; - padding: 0 14px; - } -}
@@ -426,7 +426,7 @@ if (hElem.parentNode.className=="row postThumb") {
continue; } hElem.id = hElem.innerHTML.toLowerCase().replace(" ", "-"); - hElem.innerHTML = "<a style='text-decoration:none;color:black;' href='#"+hElem.id+"'>"+hElem.innerHTML+"</a>"; + hElem.innerHTML = "<a style='color:black!important;' href='#"+hElem.id+"'>"+hElem.innerHTML+"</a>"; } } tagLinks("h2");@@ -434,8 +434,77 @@ tagLinks("h3");
tagLinks("h4"); tagLinks("h5"); </script> - <script src="js/mermaid.min.js"></script> + <script> +function generateTOC(tocContainerId = "toc") { + const tocContainer = document.getElementById(tocContainerId); + if (!tocContainer) return; + + const headings = document.querySelectorAll("h2, h3, h4, h5, h6"); + if (!headings.length) return; + + // Generate IDs if missing + headings.forEach((heading, index) => { + if (!heading.id) { + heading.id = + heading.textContent + .trim() + .toLowerCase() + .replace(/[^\w\s-]/g, "") + .replace(/\s+/g, "-") + + "-" + + index; + } + }); + + const toc = document.createElement("ul"); + let currentLevel = 1; + let currentList = toc; + const stack = [toc]; + + headings.forEach((heading) => { + const level = parseInt(heading.tagName.substring(1), 10); + + const li = document.createElement("li"); + const a = document.createElement("a"); + + a.href = "#" + heading.id; + a.textContent = heading.textContent; + + li.appendChild(a); + + if (level > currentLevel) { + for (let i = currentLevel; i < level; i++) { + const nestedList = document.createElement("ul"); + const lastLi = + currentList.lastElementChild || + currentList.appendChild(document.createElement("ul")); + + lastLi.appendChild(nestedList); + + stack.push(nestedList); + currentList = nestedList; + } + } else if (level < currentLevel) { + for (let i = currentLevel; i > level; i--) { + stack.pop(); + } + currentList = stack[stack.length - 1]; + } + + currentList.appendChild(li); + currentLevel = level; + }); + + tocContainer.innerHTML = ""; + tocContainer.appendChild(toc); +} + +document.addEventListener("DOMContentLoaded", () => { + generateTOC(); +}); + </script> + <script src="js/mermaid.min.js"></script> </body> </html>
@@ -62,7 +62,25 @@
<div class="container" style="margin-top:0px;max-width:800px;"> - <a href='/blog/ai-connections.html'><div class="row postThumb"> + <a href='/blog/labrador.html'><div class="row postThumb"> + <h3>Notes on Labrador, a lattice-based SNARK scheme</h3> + +<p>Overview of how the (post-quantum) lattice-based SNARK scheme “Labrador” works.</p> + +<p><em>2026-06-11</em></p> + +</div> + +</a><a href='/blog/tools.html'><div class="row postThumb"> + <h3>Tools</h3> + +<p>A non-exhaustive overview of tools I use daily.</p> + +<p><em>2026-06-08</em></p> + +</div> + +</a><a href='/blog/ai-connections.html'><div class="row postThumb"> <h3>Human connections in the early days of AI</h3> <p>A short note on aiccelerationism, hyperreality, and the recovery of human connections.</p>@@ -285,7 +303,7 @@ if (hElem.parentNode.className=="row postThumb") {
continue; } hElem.id = hElem.innerHTML.toLowerCase().replace(" ", "-"); - hElem.innerHTML = "<a style='text-decoration:none;color:black;' href='#"+hElem.id+"'>"+hElem.innerHTML+"</a>"; + hElem.innerHTML = "<a style='color:black!important;' href='#"+hElem.id+"'>"+hElem.innerHTML+"</a>"; } } tagLinks("h2");@@ -293,8 +311,77 @@ tagLinks("h3");
tagLinks("h4"); tagLinks("h5"); </script> - <script src="js/mermaid.min.js"></script> + <script> +function generateTOC(tocContainerId = "toc") { + const tocContainer = document.getElementById(tocContainerId); + if (!tocContainer) return; + + const headings = document.querySelectorAll("h2, h3, h4, h5, h6"); + if (!headings.length) return; + + // Generate IDs if missing + headings.forEach((heading, index) => { + if (!heading.id) { + heading.id = + heading.textContent + .trim() + .toLowerCase() + .replace(/[^\w\s-]/g, "") + .replace(/\s+/g, "-") + + "-" + + index; + } + }); + + const toc = document.createElement("ul"); + let currentLevel = 1; + let currentList = toc; + const stack = [toc]; + + headings.forEach((heading) => { + const level = parseInt(heading.tagName.substring(1), 10); + + const li = document.createElement("li"); + const a = document.createElement("a"); + + a.href = "#" + heading.id; + a.textContent = heading.textContent; + + li.appendChild(a); + + if (level > currentLevel) { + for (let i = currentLevel; i < level; i++) { + const nestedList = document.createElement("ul"); + const lastLi = + currentList.lastElementChild || + currentList.appendChild(document.createElement("ul")); + + lastLi.appendChild(nestedList); + + stack.push(nestedList); + currentList = nestedList; + } + } else if (level < currentLevel) { + for (let i = currentLevel; i > level; i--) { + stack.pop(); + } + currentList = stack[stack.length - 1]; + } + + currentList.appendChild(li); + currentLevel = level; + }); + + tocContainer.innerHTML = ""; + tocContainer.appendChild(toc); +} + +document.addEventListener("DOMContentLoaded", () => { + generateTOC(); +}); + </script> + <script src="js/mermaid.min.js"></script> </body> </html>
@@ -689,7 +689,7 @@ if (hElem.parentNode.className=="row postThumb") {
continue; } hElem.id = hElem.innerHTML.toLowerCase().replace(" ", "-"); - hElem.innerHTML = "<a style='text-decoration:none;color:black;' href='#"+hElem.id+"'>"+hElem.innerHTML+"</a>"; + hElem.innerHTML = "<a style='color:black!important;' href='#"+hElem.id+"'>"+hElem.innerHTML+"</a>"; } } tagLinks("h2");@@ -697,8 +697,77 @@ tagLinks("h3");
tagLinks("h4"); tagLinks("h5"); </script> - <script src="js/mermaid.min.js"></script> + <script> +function generateTOC(tocContainerId = "toc") { + const tocContainer = document.getElementById(tocContainerId); + if (!tocContainer) return; + + const headings = document.querySelectorAll("h2, h3, h4, h5, h6"); + if (!headings.length) return; + + // Generate IDs if missing + headings.forEach((heading, index) => { + if (!heading.id) { + heading.id = + heading.textContent + .trim() + .toLowerCase() + .replace(/[^\w\s-]/g, "") + .replace(/\s+/g, "-") + + "-" + + index; + } + }); + + const toc = document.createElement("ul"); + let currentLevel = 1; + let currentList = toc; + const stack = [toc]; + + headings.forEach((heading) => { + const level = parseInt(heading.tagName.substring(1), 10); + + const li = document.createElement("li"); + const a = document.createElement("a"); + + a.href = "#" + heading.id; + a.textContent = heading.textContent; + + li.appendChild(a); + + if (level > currentLevel) { + for (let i = currentLevel; i < level; i++) { + const nestedList = document.createElement("ul"); + const lastLi = + currentList.lastElementChild || + currentList.appendChild(document.createElement("ul")); + + lastLi.appendChild(nestedList); + + stack.push(nestedList); + currentList = nestedList; + } + } else if (level < currentLevel) { + for (let i = currentLevel; i > level; i--) { + stack.pop(); + } + currentList = stack[stack.length - 1]; + } + + currentList.appendChild(li); + currentLevel = level; + }); + + tocContainer.innerHTML = ""; + tocContainer.appendChild(toc); +} + +document.addEventListener("DOMContentLoaded", () => { + generateTOC(); +}); + </script> + <script src="js/mermaid.min.js"></script> </body> </html>
@@ -201,7 +201,7 @@ if (hElem.parentNode.className=="row postThumb") {
continue; } hElem.id = hElem.innerHTML.toLowerCase().replace(" ", "-"); - hElem.innerHTML = "<a style='text-decoration:none;color:black;' href='#"+hElem.id+"'>"+hElem.innerHTML+"</a>"; + hElem.innerHTML = "<a style='color:black!important;' href='#"+hElem.id+"'>"+hElem.innerHTML+"</a>"; } } tagLinks("h2");@@ -209,8 +209,77 @@ tagLinks("h3");
tagLinks("h4"); tagLinks("h5"); </script> - <script src="js/mermaid.min.js"></script> + <script> +function generateTOC(tocContainerId = "toc") { + const tocContainer = document.getElementById(tocContainerId); + if (!tocContainer) return; + + const headings = document.querySelectorAll("h2, h3, h4, h5, h6"); + if (!headings.length) return; + + // Generate IDs if missing + headings.forEach((heading, index) => { + if (!heading.id) { + heading.id = + heading.textContent + .trim() + .toLowerCase() + .replace(/[^\w\s-]/g, "") + .replace(/\s+/g, "-") + + "-" + + index; + } + }); + + const toc = document.createElement("ul"); + let currentLevel = 1; + let currentList = toc; + const stack = [toc]; + + headings.forEach((heading) => { + const level = parseInt(heading.tagName.substring(1), 10); + + const li = document.createElement("li"); + const a = document.createElement("a"); + + a.href = "#" + heading.id; + a.textContent = heading.textContent; + + li.appendChild(a); + + if (level > currentLevel) { + for (let i = currentLevel; i < level; i++) { + const nestedList = document.createElement("ul"); + const lastLi = + currentList.lastElementChild || + currentList.appendChild(document.createElement("ul")); + + lastLi.appendChild(nestedList); + + stack.push(nestedList); + currentList = nestedList; + } + } else if (level < currentLevel) { + for (let i = currentLevel; i > level; i--) { + stack.pop(); + } + currentList = stack[stack.length - 1]; + } + + currentList.appendChild(li); + currentLevel = level; + }); + + tocContainer.innerHTML = ""; + tocContainer.appendChild(toc); +} + +document.addEventListener("DOMContentLoaded", () => { + generateTOC(); +}); + </script> + <script src="js/mermaid.min.js"></script> </body> </html>
@@ -228,7 +228,7 @@ if (hElem.parentNode.className=="row postThumb") {
continue; } hElem.id = hElem.innerHTML.toLowerCase().replace(" ", "-"); - hElem.innerHTML = "<a style='text-decoration:none;color:black;' href='#"+hElem.id+"'>"+hElem.innerHTML+"</a>"; + hElem.innerHTML = "<a style='color:black!important;' href='#"+hElem.id+"'>"+hElem.innerHTML+"</a>"; } } tagLinks("h2");@@ -236,8 +236,77 @@ tagLinks("h3");
tagLinks("h4"); tagLinks("h5"); </script> - <script src="js/mermaid.min.js"></script> + <script> +function generateTOC(tocContainerId = "toc") { + const tocContainer = document.getElementById(tocContainerId); + if (!tocContainer) return; + + const headings = document.querySelectorAll("h2, h3, h4, h5, h6"); + if (!headings.length) return; + + // Generate IDs if missing + headings.forEach((heading, index) => { + if (!heading.id) { + heading.id = + heading.textContent + .trim() + .toLowerCase() + .replace(/[^\w\s-]/g, "") + .replace(/\s+/g, "-") + + "-" + + index; + } + }); + + const toc = document.createElement("ul"); + let currentLevel = 1; + let currentList = toc; + const stack = [toc]; + + headings.forEach((heading) => { + const level = parseInt(heading.tagName.substring(1), 10); + + const li = document.createElement("li"); + const a = document.createElement("a"); + + a.href = "#" + heading.id; + a.textContent = heading.textContent; + + li.appendChild(a); + + if (level > currentLevel) { + for (let i = currentLevel; i < level; i++) { + const nestedList = document.createElement("ul"); + const lastLi = + currentList.lastElementChild || + currentList.appendChild(document.createElement("ul")); + + lastLi.appendChild(nestedList); + + stack.push(nestedList); + currentList = nestedList; + } + } else if (level < currentLevel) { + for (let i = currentLevel; i > level; i--) { + stack.pop(); + } + currentList = stack[stack.length - 1]; + } + + currentList.appendChild(li); + currentLevel = level; + }); + + tocContainer.innerHTML = ""; + tocContainer.appendChild(toc); +} + +document.addEventListener("DOMContentLoaded", () => { + generateTOC(); +}); + </script> + <script src="js/mermaid.min.js"></script> </body> </html>
@@ -0,0 +1,743 @@
+<!DOCTYPE html> +<html lang="en"> + + <head> + <meta name="description" content="Overview of how the post-quantum lattice-based SNARK scheme Labrador works." /> + <meta charset="utf-8"> + <title> Notes on Labrador, a lattice-based SNARK scheme - arnaucube - blog</title> + <meta name="title" content=" Notes on Labrador, a lattice-based SNARK scheme - arnaucube - blog"> + <meta name="description" content="Overview of how the post-quantum lattice-based SNARK scheme Labrador works."> + + <meta property="og:title" content=" Notes on Labrador, a lattice-based SNARK scheme - arnaucube - blog" /> + <meta property="og:description" content="Overview of how the post-quantum lattice-based SNARK scheme Labrador works." /> + <meta property="og:url" content="https://arnaucube.com/blog/labrador.html" /> + <meta property="og:type" content="article" /> + <meta property="og:image" content="https://arnaucube.com/blog/" /> + <meta name="twitter:title" content=" Notes on Labrador, a lattice-based SNARK scheme - arnaucube - blog"> + <meta name="twitter:description" content="Overview of how the post-quantum lattice-based SNARK scheme Labrador works."> + <meta name="twitter:image" content="https://arnaucube.com/blog/"> + <meta name="twitter:card" content="summary_large_image"> + <meta name="author" content="arnaucube"> + + <link rel="icon" type="image/png" href="img/logoArnauCubeFavicon.png"> + + <meta name="viewport" content="width=device-width, initial-scale=1"> + + <link href="css/bootstrap.min.css" rel="stylesheet"> + <link rel="stylesheet" href="css/style.css"> + + <!-- highlightjs --> + <!-- <link rel="stylesheet" href="js/highlightjs/atom-one-dark.css"> --> + <link rel="stylesheet" href="js/highlightjs/atom-one-light.css"> + <!-- <link rel="stylesheet" href="js/highlightjs/gruvbox-dark.css"> --> + <script src="js/highlightjs/highlight.pack.js"></script> + + <!-- katex --> + <link rel="stylesheet" href="js/katex/katex.min.css"> + </head> + + <body> + + <!-- o_gradient_background" --> + <nav id="mainNav" class="navbar navbar-default navbar-fixed-top" + style="height:50px;font-size:100%;"> + <div class="container"> + <div style="float:left;"> + <a href="/blog" style="color:#000;display:inline-block;">blog</a> + <span style="margin-right:20px; margin-left:20px;">|</span> + <a href="/blog/notes.html" style="font-size:90%;color:#000;display:inline-block;">notes</a> + </div> + <div style="float:right;"> + <a href="/" style="color:#000;display:inline-block;margin-right:10px;">arnaucube.com</a> + <div class="onoffswitch" style="margin-left:0px;display:inline-block;" title="change theme"> + <input onclick="switchTheme()" type="checkbox" name="onoffswitch" class="onoffswitch-checkbox" + id="themeSwitcher"> + <label class="onoffswitch-label" for="themeSwitcher"></label> + </div> + </div> + </div> + <!-- <img style="height:5px; width:100%; margin-top:8px;" src="img/gradient-line.jpg" /> --> + </nav> + + + + <div class="container" style="margin-top:0px;max-width:800px;"> + <h1>Notes on Labrador, a lattice-based SNARK scheme</h1> + +<p><em>2026-06-11</em></p> + +<p>I was curious on how (post-quantum) lattice-based SNARKs would work, and since I had a bit of time these past days, I’ve been reading the <a href="https://eprint.iacr.org/2022/1341.pdf">LaBRADOR paper</a>, by Ward Beullens and Gregor Seiler. This post tries to explain the intuition and steps not explicitly shown in the paper, which helped me understanding it.</p> + +<p>The structure of this post is the following:</p> + +<div id="toc"></div> + +<h2>Notation</h2> + +<p>The paper uses <span class="math inline">\(\vec{z} \in R^n\)</span> to indicate a vector of length <span class="math inline">\(n\)</span>; for simplicity, in this post I’ll use <span class="math inline">\(z \in R^n\)</span>, since it is already clear that we’re working with a vector since we use <span class="math inline">\(\in R^n\)</span>.</p> + +<p>Also, will use <span class="math inline">\(i \in [n]\)</span> to indicate <span class="math inline">\(i = 1, \ldots, n\)</span>. And <span class="math inline">\((a_{ij})\)</span> denotes the matrix with all the <span class="math inline">\(a_{ij}\)</span> values as entries.</p> + +<h2>A bit of background on lattices</h2> + +<h3>Lattices intro</h3> + +<p>Intuitively, think of lattices as a discrete version of finite-dimensional vector spaces. In the same way that vector spaces span over some basis vectors <span class="math inline">\(\{ b_i ~|~ i \in [n] \}\)</span> in a field, lattices span over the basis in a ring:</p> +<p><span class="math display">\[\mathcal{L} = \mathcal{L}(B) := B \cdot \mathbb{Z}^k = \{ \sum_{i=1}^k z_i \cdot b_i ~|~ z_i \in \mathbb{Z} \}\]</span></p><p>for a basis <span class="math inline">\(B = \{ b_1, \ldots, b_k \}\)</span> (which since they are a basis, they are linearly independent). <span class="math inline">\(k\)</span> is the rank of the basis, and we restrict our attention to <em>full-rank</em> lattices where <span class="math inline">\(k=n\)</span>.</p> + +<p>Since we’re at the lattices realm, we work with the polynomial ring <span class="math inline">\(R_q = \mathbb{Z}_q[X]/(X^d + 1)\)</span>, and particularly in Labrador we pick <span class="math inline">\(q\)</span> and <span class="math inline">\(d\)</span> such that <span class="math inline">\(X^d + 1\)</span> splits in two irreducible factors <span class="math inline">\(\pmod q\)</span>.</p> + +<p><br></p> + +<p>To provide a bit of more intuition, we can relate it to the <em>algebraic coding theory</em> concept of <em>sphere packing problem</em>, where in lattices we use short vectors and in coding theory we’re interested into low weighted codewords. Both in lattices and coding theory we have q-ary {lattices/matrices} respectively and also parity check {lattices/matrices} respectively.</p> + +<div style="text-align:center;"> +<img style="width:70%" +src="img/posts/labrador/lattice-spherepackingproblem.png"></img> +<br> +<i></i> +</div> + +<h3>MSIS problem</h3> + +<p><em>MSIS</em> problem stands for <em>Module Short Integer Solution</em> problem, and it is as follows:</p> + +<p>Given a uniformly random matrix <span class="math inline">\(A \in^R R_q^{n \times m}\)</span>, with columns <span class="math inline">\(a_i \in R_q^n\)</span>, find a non-zero integer vector <span class="math inline">\(z \in R^m\)</span> of norm <span class="math inline">\(||z|| \leq \beta\)</span> such that</p> +<p><span class="math display">\[A \cdot z = \sum_i a_i \cdot z_i = 0 \in R_q^n\]</span></p> +<blockquote> +<p><em>Remarks:</em></p> + +<ul> +<li>without <span class="math inline">\(||z|| \leq \beta\)</span>, it is easy to find a solution via <a href="https://math.libretexts.org/Courses/Palo_Alto_College/College_Algebra/05:_Systems_of_Equations_and_Inequalities/5.04:_Solving_Systems_with_Gaussian_Elimination">Gaussian elimination</a>.</li> +<li><span class="math inline">\(\beta < q\)</span>, because otherwise <span class="math inline">\(z=(q, 0, \ldots, 0) \in R^m\)</span> would always be a valid solution.</li> +<li><span class="math inline">\(\beta\)</span> and <span class="math inline">\(m\)</span> must be large enough so that a solution is guaranteed to exist, usually <span class="math inline">\(\beta \geq \sqrt{\hat{m}}\)</span> and <span class="math inline">\(m \geq \hat{m}\)</span> where <span class="math inline">\(\hat{m}\)</span> is the smallest integer greater than <span class="math inline">\(n \log q\)</span>.</li> +</ul> +</blockquote> + +<p>So overall when doing lattice-based cryptography, we will be interested into keeping the values <em>short</em>, aka. of small norm.</p> + +<p>Specifically, Labrador relies on the hardness of finding short solutions to linear equations over polynomial rings.</p> + +<p><br></p> + +<p>With this in mind, usually a common trick used when doing operations is to decompose the values with small bases, so that we can represent them while keeping the norm low. We will also use this in Labrador.</p> + +<h3>Ajtai commitments</h3> + +<p>We use a variant of the Ajtai commitments, where the messages are ring elements with small norms.</p> + +<p>For a sampled random matrix <span class="math inline">\(A \in^R R_q^{n \times m}\)</span>, and given <span class="math inline">\(x \in R^m\)</span> as input, where <span class="math inline">\(||x|| \leq \beta\)</span>, the Ajtai commitment is defined as <span class="math inline">\(cm := A \cdot x \in R_q^n\)</span>.</p> + +<p>In other words, Ajtai commitments are essentially MSIS instances.</p> + +<p>Notice that this is binding but it is not hiding, but we could make it hiding by appending a small random veector to <span class="math inline">\(x\)</span>.</p> + +<h2>Labrador constraints</h2> + +<p>As usual in SNARK schemes, we have the witness, denoted <span class="math inline">\(s= s_1, \ldots, s_r\)</span>, where each <span class="math inline">\(s_i \in R_q^n\)</span>, ie. the witness is a vector of <span class="math inline">\(r\)</span> vectors of ring elements.</p> + +<p>And since we’re at the lattices world and we’re using the assumptions of SIS and Module-SIS, the witness values are “short”, that is, their norm is bounded by some parameter: <span class="math inline">\(\sum_{i=1}^r || s_i ||_2^2 \leq \beta^2\)</span>.</p> + +<p>While in other SNARK systems we worked with constraints in the shape of R1CS or Plonkish, LaBRADOR defines its constraints as</p> +<p><span class="math display">\[\boxed{ +f(s) = \sum_{i, j}^r a_{ij} \langle s_i, s_j \rangle + \sum_{i=1}^r \langle \phi_i, s_i \rangle + b = 0 +} +\]</span></p><p>where <span class="math inline">\(s_i, \phi_i \in R_q^n\)</span>, and <span class="math inline">\(a_{ij}, b \in R_q\)</span>.</p> + +<p>There are two different families of constraints, which are evaluated differently:</p> + +<ul> +<li><span class="math inline">\(\mathcal{F}\)</span> family, which are those constraints where the entire polynomial must evaluate to zero, ie. <span class="math inline">\(f(s) = 0\)</span></li> +<li><span class="math inline">\(\mathcal{F}~'\)</span> family, which are those constraints where we’re only interested in their constant term being zero, ie. <span class="math inline">\(ct( f'(s) ) = 0\)</span></li> +</ul> + +<p>To avoid future doubts, let’s write down what are some constant values:</p> + +<ul> +<li><span class="math inline">\(r\)</span>: amount of witness vectors <span class="math inline">\(s_1, \ldots, s_r\)</span></li> +<li><span class="math inline">\(K = |\mathcal{F}|\)</span>: amount of original constraints of fully vanishing dot products</li> +<li><span class="math inline">\(L = |\mathcal{F}~'|\)</span>: amount of original constraints where the constant term is zero</li> +</ul> + +<!-- +(HIDDEN) +> *Side derailed observation*: since Labrador works with $\mathbb{Z}_q[X]/(X^d + 1)$, where $q, d$ are such that $X^d + 1$ splits in two irreducible factors $\pmod q$, this is a ring, the polynomial ring $R_q$. +> +> But imagine for a moment (only imagine, since in many lattice-based cryptography this is not the case) that we pick $q, d$ parameters such that $X^d+1$ is irreducible, then we would have $\mathbb{Z}_q[X]/(X^d+1)$ being a finite field, $\mathbb{F}_{q^d}$; and then, the set of solutions satisfying the family $\mathcal{F}$ would be an *algebraic variety*, which is an interesting algebraic object, although not so useful in the context where we're now. But this is not the case, and in our case the $X^d+1$ that splits into two irreducible factors so that it ensures that through the protocol we can find challenges that are both computationally *short* and invertible when subtracted ([iacr:2017-523](https://eprint.iacr.org/2017/523)). +--> + +<h2>Protocol</h2> + +<p>For the rest of this post, I’ll use colours to easily identify the different concepts across the various equations of the protocol.</p> + +<p>We start from the <em>original</em> constraints. Those are both, the fully vanishing constraints of the family <span class="math inline">\(\mathcal{F}\)</span> and the constraints where the constant term is zero of the family <span class="math inline">\(\mathcal{F}~'\)</span>:</p> +<p><span class="math display">\[ +f(s) = \sum_{i, j}^r \textcolor{magenta}{a_{ij}} \langle s_i, s_j \rangle + \sum_{i=1}^r \langle \textcolor{blue}{\phi_i}, s_i \rangle - \textcolor{orange}{b} = 0 +\]</span></p><p><span class="math display">\[ +f'(s) = \sum_{i, j}^r \textcolor{magenta}{a'_{ij}} \langle s_i, s_j \rangle + \sum_{i=1}^r \langle \textcolor{blue}{\phi'_i}, s_i \rangle - \textcolor{orange}{b'} ~~such~that~~ ct(f'(s))=0 +\]</span></p><p>In this blog post we will not cover how do we get from the R1CS constraints into the Labrador constraints shape, it can be found in the section 6 of the paper, but the main idea you need to have is that for different kinds of R1CS constraints (binary and non-binary constraints) we can obtain the <span class="math inline">\(f(s)\)</span> and <span class="math inline">\(f'(s)\)</span> Labrador constraints.</p> + +<p>Once we have the original constraints, the idea is to do some reductions adding some more constraints, and then do multiple aggregation steps in order to end up having fewer constraints.</p> + +<div style="text-align:center;"> +<img style="width:60%" +src="img/posts/labrador/constraint-polynomials-evolution.jpg"></img> +<br> +<i style="color:#374072">$f(s), f'(s)$ are original polynomials, from the mentioned families $\mathcal{F}$ and $\mathcal{F}~'$ respectively,<br> +$f_{proj}(s), f''(s)$, $F(s)$ are polynomials that we will describe in the following sections.</i> +</div> + +<p><br> +Labrador reduces proof size through four transformations unfolded over the next sections:</p> + +<ol> +<li>Commit to the witness</li> +<li>Reduce norm checking with Johnson-Lindenstrauss projections</li> +<li>Aggregate many constraints into one</li> +<li>Amortize openings</li> +<li>Recurse</li> +</ol> + +<h3>1. Committing to the witness</h3> + +<p>We name <em>inner commitments</em> the commitments to the original witness. Those commitments are</p> +<p><span class="math display">\[cm(s_i) = t_i = A \cdot s_i \in R_q^k ~~ \forall i \in [r]\]</span></p><p>But beware, sending all those <span class="math inline">\(r\)</span> commitments would be too costly, so what the protocol does is it commits to them. The issue then is that <span class="math inline">\(t_i\)</span> (the inner commitments) have coefficients that are <span class="math inline">\(\pmod q\)</span>, whose norm would be too big. The trick that the protocol uses, is the one we mentioned at the <a href="#a-bit%20of%20background%20on%20lattices"><em>‘a bit of background on lattices’</em> section</a>, this is, to decompose them into <span class="math inline">\(t_1 \geq 2\)</span> parts with small base <span class="math inline">\(b_1\)</span> before committing, ie.</p> +<p><span class="math display">\[t_i = t_i^{(0)} + t_i^{(1)} b_1 + \ldots + t_i^{(t_1-1)} \cdot b_1^{t_1 -1}\]</span></p><p>with <span class="math inline">\(|| t_i^{(k)} ||_{\infty} \leq b_1/2\)</span>.</p> + +<p>Now, let <span class="math inline">\(t \in R_q^{r \cdot t_1 \cdot k}\)</span> be the concatenation of all decomposition parts of <span class="math inline">\(t_i^{(k)}\)</span>.</p> + +<p>The commitment to the original commitment, is named <em>outer commitment</em>, and it is computed as</p> +<p><span class="math display">\[u_1 = B \cdot t\]</span></p><p>that is</p> +<p><span class="math display">\[u_1 = \sum_{i=1}^r \sum_{k=0}^{t_1-1} B_{ik} \cdot t_i^{(k)}\]</span></p><p>with <span class="math inline">\(||t|| \leq \gamma_1\)</span> for a fixed norm parameter <span class="math inline">\(\gamma_1\)</span>.</p> + +<p>Now, we want to show that <span class="math inline">\(s=(s_1, \ldots, s_r)\)</span> satisfies the norm bound that we mentioned at the <a href="#setup">setup</a> section. But sending <span class="math inline">\(r\)</span> witness to the verifier would be again too costly, so the protocol reduces it through a projection.</p> + +<h3>The σ₋₁ trick</h3> + +<p>Before going forward with the protocol, we will overview a trick that Labrador uses in order to compute inner products within the polynomial ring realm.</p> + +<p><span class="math inline">\(\sigma_{-1}(\pi_i)\)</span> allows us to convert the coefficient-wise vector inner product into a polynomial multiplication relation.</p> + +<p>Within the protocol, we want to compute <span class="math inline">\(p_j = \sum_{i=1}^r \langle \pi_i^{(j)}, s_i \rangle\)</span>, but we want to compute it using ring operations, concretely using polynomial multiplication.</p> + +<p>Notice that in Labrador, vectors are encoded as polynomials in <span class="math inline">\(R_q\)</span>, ie</p> +<p><span class="math display">\[a=(a_1, \ldots, a_{n}) ~\longleftrightarrow~ a(x) = \sum_{k=1}^n a_k X^k \in R_q\]</span></p><p>Let <span class="math inline">\(a(x) = \sum_k a_k X^k,~~ b(x) = \sum_k b_k X^k\)</span>, then when computing <span class="math inline">\(a(x) \cdot b(x)\)</span></p> +<p><span class="math display">\[a(x) b(x) = (\sum_k a_k X^k) \cdot (\sum_k b_k X^k) = \sum_{i,j} a_i b_j X^{i+j}\]</span></p><p>which is different from the dot product that we’re interested in.</p> + +<p>We want to only multiply together the coefficients of the equal-index terms, ie. <span class="math inline">\(a_0 b_0 + a_1 b_1 + \ldots + a_{n-1} b_{n-1}\)</span>. Thus we need some trick to force getting only the values when <span class="math inline">\(i=j\)</span>.</p> + +<p>The trick is with <span class="math inline">\(\sigma_{-1}\)</span> and getting the constant term of the result of multiplying <span class="math inline">\(\sigma_{-1}(a) \cdot b\)</span>.</p> + +<p>Define the automorphism <span class="math inline">\(\sigma_{-1}\)</span> as <span class="math inline">\(\sigma_{-1}(X) = X^{-1}\)</span>, so that <span class="math inline">\(\sigma_{-1}(a(X)) = a(X^{-1}) = \sum_i a_i X^{-i}\)</span>.</p> + +<p>Then, when multiplying</p> +<p><span class="math display">\[\sigma_{-1}(a(X)) \cdot b(X) = \sum_{i,j} a_i b_j X^{j-i}\]</span></p><p>So, when <span class="math inline">\(j=i\)</span>, <span class="math inline">\(X\)</span> will be <span class="math inline">\(0\)</span>; hence the result we’re interested in, the dot product result, will be contained in the constant terms of the result of that multiplication, where the <span class="math inline">\(X^{j-i}=X^0\)</span>.</p> + +<p>In other words, <span class="math inline">\(\langle a, b \rangle = ct(\sigma_{-1}(a(X)) \cdot b(X))\)</span>, where <span class="math inline">\(ct\)</span> denotes getting the constant term. That is, applied to the projection case, <span class="math inline">\(\langle \pi_i^{(j)}, s_i \rangle= ct(\sigma_{-1}(\pi_i^{(j)}) \cdot s_i)\)</span>.</p> + +<h3>2. Johnson-Lindenstrauss projection</h3> + +<p>The main idea here, is that we will use random linear projections that almost preserve the <span class="math inline">\(l_2\)</span>-norm while allowing us to reduce the amount of equations to check, while keeping the bounds in place.</p> + +<p>This relies on the Johnson-Lindenstrauss Lemma, which basically is used as instead of revealing all the witnes values <span class="math inline">\(s_1, \ldots, s_r\)</span>, the verifier samples a random linear map <span class="math inline">\(\Pi: \mathbb{Z}_q^{nd} \longrightarrow \mathbb{Z}_q^{256}\)</span>, and then the prover reveals only <span class="math inline">\(\Pi \cdot s\)</span>, which has many less elements than the original <span class="math inline">\(s\)</span> vector.</p> + +<p>And the Johnson-Lindenstrauss Lemma guarantees that <span class="math inline">\(||\Pi s||\)</span> contains almost the same norm information as <span class="math inline">\(||s||\)</span> with very high probability.</p> + +<p>In practice, verifier samples <span class="math inline">\(\Pi_i = (\pi_i^{(j)})_j \in \chi^{256 \times nd}\)</span>, the prover computes <span class="math inline">\(p_j = \sum_{i=1}^r \langle \pi_i^{(j)}, s_i \rangle\)</span>, and the verifier checks that <span class="math inline">\(||p|| \leq \sqrt{128} \beta\)</span>.</p> + +<p>So at this point, in the family <span class="math inline">\(\mathcal{F}~'\)</span>, we have the original constraints, without any projection related stuff:</p> +<p><span class="math display">\[ +f'(s) = \sum_{i, j}^r \textcolor{magenta}{a'_{ij}} \langle s_i, s_j \rangle + \sum_{i=1}^r \langle \textcolor{blue}{\phi'_i}, s_i \rangle + \textcolor{orange}{b'} = 0 +\]</span></p><p>And the projection constraints, which encode the check for the J-L projection in the Labrador constraints shape:</p> + +<p>Notice that the projection equation, <span class="math inline">\(p_j = \sum_{i=1}^r \langle \sigma_{-1}(\pi_i^{(j)}), s_i \rangle\)</span> is also</p> +<p><span class="math display">\[\sum_{i=1}^r \langle \sigma_{-1}(\pi_i^{(j)}), s_i \rangle - p_j = 0\]</span></p><p>which looks almost like the Labrador canonical constraints, we just need to add the <span class="math inline">\(a_{ij}\)</span> part. We do it by setting <span class="math inline">\(a_{ij}=0\)</span>:</p> +<p><span class="math display">\[ +f_{proj}(s) = \sum_{i, j}^r \textcolor{magenta}{0} \cdot \langle s_i, s_j \rangle + \sum_{i=1}^r \langle \textcolor{blue}{\sigma_{-1}(\pi_i^{(j)})}, s_i \rangle - \textcolor{orange}{p_j} = 0 +\]</span></p><p>this is, we set <span class="math inline">\(\textcolor{magenta}{a_{proj,ij}=0}\)</span>, <span class="math inline">\(\textcolor{blue}{\phi_{proj,~i}=\sigma_{-1}(\pi_i^{(j)})}\)</span> and <span class="math inline">\(\textcolor{orange}{b_{proj} = -p_j}\)</span>. In this way we have the projection constraints encoded as Labrador constraints.</p> + +<p>Recall that both the original <span class="math inline">\(f'(s)\)</span> constraints, and the projection constraints <span class="math inline">\(f_{proj}(s)\)</span> are in the family <span class="math inline">\(\mathcal{F}~'\)</span>, so that we care about their constant term being zero, not the full polynomial vanishing.</p> + +<h3>3. Aggregation steps</h3> + +<p>At this point of the protocol, in the family <span class="math inline">\(\mathcal{F}~'\)</span>, we have <span class="math inline">\(L\)</span> original constraints <span class="math inline">\(f'(s)\)</span> and <span class="math inline">\(256\)</span> projection constraints <span class="math inline">\(f_{proj}(s)\)</span>. The idea now is to linearly combine them into fewer constraints.</p> + +<p>The verifier sends the challenges <span class="math inline">\(\psi^{(k)} \in^R (\mathbb{Z}_q)^L\)</span> and <span class="math inline">\(w^{(k)} \in^R (\mathbb{Z}_q)^{256}\)</span>, which the prover will use to compute the random linear combinations of the constraints we have so far, which preserves the validity while reducing the number of constraints.</p> + +<h4>3.1. 1st aggregation</h4> + +<p>The prover linearly combines</p> + +<ul> +<li>the <span class="math inline">\(L\)</span> original constraints from <span class="math inline">\(f'(s)\)</span></li> +<li>the <span class="math inline">\(256\)</span> constraints that prove the Johnson-Lindenstrauss projection, <span class="math inline">\(f_{proj}(s)\)</span></li> +</ul> + +<p>as follows</p> +<p><span class="math display">\[ +f''^{(k)}(s) = +\sum_{l=1}^L \psi_l^{(k)} \cdot f'^{(l)}(s) ++ +\sum_{j=1}^{256} w_j^{(k)} \cdot f_{proj}(s) += +\\ += \underbrace{ \sum_{l=1}^L \psi_l^{(k)} \cdot f'^{(l)}(s) }_{(i.)~ \text{lin.comb. of the original constraints}} ++ +\underbrace{ \sum_{j=1}^{256} w_j^{(k)} \cdot ( \langle \sigma_{-1}(\pi_i^{(j)}), s_i \rangle -p_j) }_{(ii.)~ \text{lin.comb. of the 256 projection constraints}} +\]</span></p><p>First we will tackle <span class="math inline">\((i.)\)</span>. Recall that <span class="math inline">\(f'(s) = \sum_{i, j}^r a_{ij}' \cdot \langle s_i, s_j \rangle + \sum_{i=1}^r \langle \phi_i', s_i \rangle + b'\)</span>, so by distributing the challenge <span class="math inline">\(\psi_l^{(k)}\)</span>,</p> +<p><span class="math display">\[ +(i.):~ +\sum_l \psi_l^{(k)} \cdot f'^{(l)}(s) = +\sum_l \psi_l^{(k)} \cdot \left( + \sum_{i,j} a_{ij}^{'(l)} \cdot \langle s_i, s_j \rangle + + \sum_i \langle \phi_i^{'(l)}, s_i \rangle - b_0^{'(k)} +\right)\\ += +\underbrace{ +\sum_{i,j} \left( \sum_l \psi_l^{(k)} \cdot a_{ij}^{'(l)} \right) \cdot \langle s_i, s_j \rangle +}_{(i.A)} +\underbrace{ ++ \sum_i \left( \sum_l \psi_l^{(k)} \cdot \phi_i^{'(l)} \right) \cdot s_i +- \sum_l \psi_l^{(k)} \cdot b_0^{'(l)} +}_{(i.B)} +\]</span></p><p>For the <span class="math inline">\((i.A)\)</span> part, we can set</p> +<p><span class="math display">\[\textcolor{magenta}{a_{ij}^{''(k)}} = \sum_{l=1}^L \psi_l^{(k)} \cdot a_{ij}^{'(l)}\]</span></p><p>so that:</p> +<p><span class="math display">\[ +(i.A):~ + \sum_{i,j} \left( \sum_l \psi_l^{(k)} \cdot a_{ij}^{'(l)} \right) \cdot \langle s_i, s_j \rangle ~=~ \sum_{i,j} \textcolor{magenta}{a''^{(k)}_{ij}} \cdot \langle s_i, s_j \rangle +\]</span></p><p>Now tackle <span class="math inline">\((ii.)\)</span>:</p> +<p><span class="math display">\[ +(ii.):~ \sum_{j=1}^{256} w_j^{(k)} \cdot \big( \langle \sigma_{-1}(\pi_i^{(j)}), s_i \rangle - p_j \big) = \sum_i \langle \sum_j w_j^{(k)} \sigma_{-1}(\pi_i^{(j)}),~ s_i \rangle +- \sum_j w_j^{(k)} \cdot p_j +\]</span></p><p>Now let’s put together <span class="math inline">\((i.B)\)</span> and <span class="math inline">\((ii)\)</span>:</p> +<p><span class="math display">\[ +\overbrace{ + \textcolor{green}{ + \sum_i ( \sum_l \psi_l^{(k)} \cdot \phi_i^{'(l)} ) \cdot s_i + } + \textcolor{blue}{ + - \sum_l \psi_l^{(k)} \cdot b_0^{'(l)} + } +}^{(i.B)} ++ +\overbrace{ + \textcolor{orange}{ + \sum_i \langle \sum_j w_j^{(k)} \sigma_{-1}(\pi_i^{(j)}),~ s_i \rangle + } + \textcolor{red}{ + - \sum_j w_j^{(k)} \cdot p_j + } +}^{(ii.)} +=\\~\\ += + \sum_i +\textcolor{green}{ + \big\langle + \sum_l \psi_l^{(k)} \cdot \phi_i^{'(l)} + } + + + \textcolor{orange}{ + \sum_l w_j^{(k)} \cdot \sigma_{-1}(\pi_i^{(j)}), s_i + \big\rangle + } + + + \textcolor{blue}{ + \sum_l \psi_l^{(k)} \cdot b_0^{'(l)} + } + + + \textcolor{red}{ + \sum_j w_j^{(k)} \cdot p_j + } +\]</span></p><p>without colors:</p> +<p><span class="math display">\[ += +\sum_i + \big\langle + \underbrace{ + \sum_l \psi_l^{(k)} \cdot \phi_i^{'(l)} + + + \sum_l w_j^{(k)} \cdot \sigma_{-1}(\pi_i^{(j)}) + }_{\phi_i^{''(k)}}, + s_i + \big\rangle + + + \underbrace{ + \sum_l \psi_l^{(k)} \cdot b_0^{'(l)} + + + \sum_j w_j^{(k)} \cdot p_j + }_{b_0^{''(k)} ~\text{(constant term)}} + \\ + = + \sum_{i=1}^r \langle \textcolor{blue}{\phi_i^{''(k)}}, s_i \rangle - \textcolor{orange}{b_0^{''(k)}} +\]</span></p><p>So by putting it together with the <span class="math inline">\((i.A)\)</span> part, we get the full <span class="math inline">\(f''^{(k)}(s)\)</span> aggregated constraint:</p> +<p><span class="math display">\[ +\boxed{ +f''^{(k)}(s) = +\overbrace{ +\sum_{i,j=1}^r \textcolor{magenta}{a_{ij}^{''(k)}} \cdot \langle s_i, s_j \rangle +}^{(i.A)} ++ +\overbrace{ +\sum_{i=1}^r \langle \textcolor{blue}{\phi_i^{''(k)}}, s_i \rangle - \textcolor{orange}{b_0^{''(k)}} +}^{(i.B) + (ii.)} +} +\]</span></p><p>such that <span class="math inline">\(ct(f''^{(k)}(s)) = 0\)</span>.</p> + +<p><br></p> + +<p>Right now, <span class="math inline">\(f''^{(k)}(s)\)</span> is a constraint where only the constant term evaluates to zero (ie. it is a constraint from the <span class="math inline">\(\mathcal{F}'\)</span> family). However, for the next aggregation step, we need it to be a <em>completely vanishing</em> polynomial, that is, a function from the <span class="math inline">\(\mathcal{F}\)</span> family.</p> + +<p>To do so, the prover takes the integer constant <span class="math inline">\(\textcolor{orange}{b_0^{''(k)}}\)</span> and extends it into a full polynomial <span class="math inline">\(\textcolor{orange}{b''^{(k)}}\)</span> so that the newly formed function <span class="math inline">\(f''^{(k)}(s)\)</span> evaluates exactly to zero. The prover then sends these full <span class="math inline">\(b''^{(k)}\)</span> polynomials to the verifier.</p> + +<p>To ensure that the prover isn’t cheating by just making up a polynomial that matches the equation, the verifier will check that the constant coefficient of the prover’s <span class="math inline">\(b''^{(k)}\)</span> corresponds to the <span class="math inline">\(b_0^{''(k)}\)</span> value that the verifier computes from the public values and challenges, that is <span class="math inline">\(b_0^{''(k)} \stackrel{?}{=} \langle w^{(k)}, p \rangle + \sum_{l=1}^L \psi_l^{(k)} b_0^{'(l)}\)</span>.</p> + +<p><br></p> + +<p>Recall that along these past steps, we’ve defined the new <span class="math inline">\(a_{ij}^{''(k)},~ \phi_i^{''(k)},~ b''\)</span> as</p> +<p><span class="math display">\[\textcolor{magenta}{a_{ij}^{''(k)}} = \sum_{l=1}^L \psi_l^{(k)} \cdot \textcolor{magenta}{a_{ij}^{'(l)}}\]</span></p><p><span class="math display">\[\textcolor{blue}{\phi_i^{''(k)}} = \sum_{l=1}^L \psi_l^{(k)} \textcolor{blue}{\phi_i^{'(l)}} + \sum_{j=1}^{256} w_j^{(k)} \cdot \sigma_{-1}(\textcolor{blue}{\pi_i^{(j)}})\]</span></p><p><span class="math display">\[\textcolor{orange}{b''} = \sum_{i,j=1}^r \textcolor{magenta}{a_{ij}^{''(k)}} \cdot \langle s_i, s_j \rangle + \sum_{i=1}^r \langle \textcolor{blue}{\phi_i^{''(k)}}, s_i \rangle\]</span></p> +<h4>3.2. 2nd aggregation</h4> + +<p>Till here, the 1st aggregation step has taken a random linear combination of the original constant-vanishing constraints and the projection constraints, and linearly combined them into Labrador-shaped constraints.</p> + +<p>Now the idea is to random linearly combine the original fully vanishing constraints <span class="math inline">\(f(s)\)</span>, and separately the aggregated <span class="math inline">\(f''(s)\)</span> constraints that come from the original constant-vanishing constraints (<span class="math inline">\(f'(s)\)</span>) and projection constraints (<span class="math inline">\(f_{proj}(s)\)</span>).</p> + +<p>The verifier sends the challenges <span class="math inline">\(\alpha \in^R R_q^K,~ \beta \in^R R_q^{\lceil 128/\log{q} \rceil}\)</span>.</p> + +<p>Then the prover will compute</p> +<p><span class="math display">\[ +F(s) = \sum_{k=1}^K \alpha_k \cdot f^{(k)}(s) + \sum_{k=1}^{\lceil 128/ \log q \rceil} \beta_k \cdot f''^{(k)}\\ += +\sum_{k=1}^K \alpha_k \cdot \left( \sum_{i, j}^r a_{ij} \langle s_i, s_j \rangle + \sum_{i=1}^r \langle \phi_i, s_i \rangle + b \right) ++ +\sum_{k=1}^{\lceil 128/ \log q \rceil} \beta_k \cdot +\left( + \sum_{i,j}^r a_{ij}^{''(k)} \langle s_i, s_j \rangle + \sum_i^r \langle \phi_i^{''(k)}, s_i \rangle - b_0^{''(k)} +\right) +\]</span></p><p>distribute <span class="math inline">\(\alpha_k\)</span> and <span class="math inline">\(\beta_k\)</span> while keeping the witness values separated</p> +<p><span class="math display">\[ += \sum_{i,j=1}^r +\underbrace{ + \left( \sum_{k=1}^K \alpha_k a_{ij}^{(k)} + + + \sum_{k=1}^{\lceil 128/\log q \rceil} \beta_k a_{ij}''^{(k)} + \right) +}_{a_{ij}} +\langle s_i, s_j \rangle + \sum_{i=1}^r \langle \underbrace{ \left( \sum_{k=1}^K \alpha_k \phi_i^{(k)} + \sum_{k=1}^{\lceil 128/\log q \rceil} \beta_k \phi_i''^{(k)} \right) }_{\phi_i}, s_i \rangle +\\ +- \underbrace{ \left( \sum_{k=1}^K \alpha_k b^{(k)} + \sum_{k=1}^{\lceil 128/\log q \rceil} \beta_k b''^{(k)} \right) }_{b} +\]</span></p><p><span class="math display">\[ +\boxed{ + F(s) = \sum_{i,j} a_{ij} \langle s_i, s_j \rangle + \sum_i \langle \phi_i, s_i \rangle - b +} +\]</span></p><p>again, a Labrador-shaped constraint, which in this case encodes the linear combinations of the original <span class="math inline">\(f^{(k)}(s)\)</span> constraints and the <span class="math inline">\(f''^{(k)}(s)\)</span> aggregated constraints, which for a valid witness it satisfies <span class="math inline">\(F(s)=0\)</span>.</p> + +<h3>4. Amortization</h3> + +<p>Now, at this point we have <span class="math inline">\(r\)</span> witness vectors, which in practice it is too much to deal with. Thus, instead of opening the individual inner commitments <span class="math inline">\(t_i\)</span> by sending all the <span class="math inline">\(s_i\)</span> and then checking the constraint <span class="math inline">\(F(s)\)</span>, the prover opens a random linear combination</p> +<p><span class="math display">\[z = \sum_{i=1}^r c_i \cdot s_i\]</span></p><p>and its respective committed version</p> +<p><span class="math display">\[\overline{z} = \sum_{i=1}^r c_i \cdot t_i\]</span></p><p>with challenge polynomials <span class="math inline">\(c_i \in R_q\)</span>.</p> + +<p>Then the verifier checks</p> +<p><span class="math display">\[A \cdot z = \sum_{i=1}^r c_i \cdot t_i\]</span></p><p>which should match since <span class="math inline">\(t_i\)</span> are the commitments to <span class="math inline">\(s_i\)</span> such that <span class="math inline">\(t_i = A s_i\)</span>, so that</p> +<p><span class="math display">\[A \cdot z = A \cdot \sum_i^r c_i s_i = \sum_i c_i \cdot (A s_i) = \sum_i c_i t_i\]</span></p><p>And also checks that <span class="math inline">\(||z|| \leq \gamma\)</span> which comes from the instantiation parameters, to ensure small norm.</p> + +<p><br></p> + +<p>The following step is verifying that <span class="math inline">\(F(s)=0\)</span>, but recall that <span class="math inline">\(F(s) = \sum_{i,j} a_{ij} \langle s_i, s_j \rangle + \sum_i \langle \phi_i, s_i \rangle - b\)</span>, and the verifier does not know the values of <span class="math inline">\(s_i\)</span> hence can not compute it.</p> + +<p>To do the check, both prover and verifier use what the paper calls <em>garbage polynomials</em>, but intuitively we can think about them as some auxiliary polynomials to do this final check.</p> + +<p>Let the <em>garbage polynomials</em> be</p> +<p><span class="math display">\[g_{ij}= \langle s_i, s_j \rangle,~~~~ h_{ij} = \frac{1}{2} ( \langle \phi_i, s_j \rangle + \langle \phi_j, s_i \rangle )\]</span></p><p>for <span class="math inline">\(i,j \in [r]\)</span>.</p> + +<p>The prover will commit to those polynomials early on, before the challenges <span class="math inline">\(c_i\)</span> are generated, so that the prover is not able to select the garbage polynomials based on knowledge of the challenges, which would allow the prover to cheat.</p> + +<blockquote> +<p>Observation: the paper does the commitment to the “garbage” polynomials <span class="math inline">\(g\)</span> and <span class="math inline">\(h\)</span> differently at the protocol conceptual explanation of page 14 and at the protocol description of Figure 2 at page 17.</p> + +<p>The conceptual explanation describes the second outer commitment <span class="math inline">\(u_2\)</span> as</p> +<p><span class="math display">\[u_2 = C g + D h\]</span></p><p>but later at the protocol description, the commitment to <span class="math inline">\(g\)</span> is moved to the outer commitment <span class="math inline">\(u_1\)</span>, leaving only the commitment to <span class="math inline">\(h\)</span> at <span class="math inline">\(u_2\)</span>. So the outer commitments end up being</p> +<p><span class="math display">\[u_1 = B \cdot t + C \cdot g = \sum_{i=1}^r \sum_{k=0}^{t_1-1} B_{ik} t_i^{(k)} + \sum_{i \leq j} \sum_{k=0}^{t_2-1} C_{ijk} g_{ij}^{(k)}\]</span></p><p><span class="math display">\[u_2 = D \cdot h = \sum_{i \leq j} \sum_{k=0}^{t_1-1} D_{ijk} h_{ij}^{(k)}\]</span></p><p>This is done because the <span class="math inline">\(g_{ij}\)</span> polynomials are independent of all challenges, thus they can be computed at the very beginning of the protocol and included in <span class="math inline">\(u_1\)</span>, allowing a slightly better security proof.</p> +</blockquote> + +<p>Then, the verifier will do the following checks:</p> +<p><span class="math display">\[(i.)~~~ <z, z> = \sum_{i,j=1}^r g_{ij} c_i c_j\]</span></p><p><span class="math display">\[(ii.)~~~ \sum_{i=1}^r \langle \phi_i, z \rangle \cdot c_i = \sum_{i,j=1}^r h_{ij} c_i c_j\]</span></p><p><span class="math display">\[(iii.)~~~ \sum_{i,j=1}^r a_{ij} g_{ij} + \sum_{i=1}^r h_{ii} -b = 0\]</span></p><p>Where <span class="math inline">\((i.)\)</span> and <span class="math inline">\((ii.)\)</span> are to check the correctness of <span class="math inline">\(g_{ij}\)</span> and <span class="math inline">\(h_{ij}\)</span> respectively, and <span class="math inline">\((iii.)\)</span> is checking the <span class="math inline">\(F(s)=0\)</span> equality through the garbage polynomials <span class="math inline">\(g_{ij}, h_{ij}\)</span>.</p> + +<h3>5. Recursion</h3> + +<p>At this point, the prover has sent the <em>proof</em> which consists of <span class="math inline">\((z, t, h, g)\)</span>, and the norm checks get grouped into a single norm check <span class="math inline">\(||z||^2 + ||t||^2 + ||g||^2 + ||h||^2 \leq \gamma^2 + \gamma_1^2 + \gamma_2^2\)</span>.</p> + +<p>The idea now, is to recurse, encoding the protocol checks into new Labrador constraints, and then performing again the protocol.</p> + +<p>First, we simplify the target relation, let <span class="math inline">\(v = t || g || h \in R_q^m\)</span>, so that the norm check becomes</p> +<p><span class="math display">\[||z||^2 + ||v||^2 \leq \gamma^2 + \gamma_1^2 + \gamma_2^2\]</span></p><p>Now we decompose the witness <span class="math inline">\(z = z^{(0)} + b \cdot z^{(1)}\)</span>, with <span class="math inline">\(z^{(0)}, z^{(1)} \in R_q^n\)</span>, to keep the coefficients with small norm.</p> + +<p>Then, the quadratic dot product becomes</p> +<p><span class="math display">\[\langle z, z \rangle = \langle z^{(0)}, z^{(0)} \rangle + 2b \langle z^{(0)}, z^{(1)} \rangle + b^2 \langle z^{(1)}, z^{(1)} \rangle\]</span></p><p>and we can do the norm check as <span class="math inline">\(||z^{(0)}||^2 + ||z^{(1)}||^2 + ||v||^2 \leq \frac{2}{b^2} \gamma^2 + \gamma_1^2 + \gamma_2^2 = (\beta')^2\)</span>, which implies <span class="math inline">\(||z|| = ||z^{(0)} + b z^{(1)} || \leq (1+b)\beta'\)</span>.</p> + +<p>Write the vectors <span class="math inline">\(z^{(0)}, z^{(1)}, v\)</span> as concatenations of vectors:</p> +<p><span class="math display">\[ +z^{(0)} = s_1 || s_2 || \ldots || s_{\nu}\\ +z^{(1)} = s_{\nu+1} || \ldots || s_{2\nu}\\ +v = s_{2\nu+1} || \ldots || s_{2\nu+ \mu} +\]</span></p><p>that is <span class="math inline">\(r' = 2 \nu + \mu\)</span> vectors in total.</p> + +<p>Observe that the final verifications from the previous section, the checks <span class="math inline">\((i.),~ (ii.),~ (iii.)\)</span> can be done as dot product constraints (ie. Labrador constraints):</p> + +<h4>encoding check (i.)</h4> + +<p>from the check <span class="math inline">\((i.)\)</span>, <span class="math inline">\(\langle z, z \rangle = \sum_{i,j=1}^r g_{ij} \cdot c_i \cdot c_j\)</span>, since we’ve decomposed <span class="math inline">\(z = z^{(0)} + b z^{(1)}\)</span> then we have</p> +<p><span class="math display">\[ +\langle z, z \rangle = \langle z^{(0)} + b z^{(1)}, z^{(0)} + b z^{(1)} \rangle\\ += \langle z^{(0)}, z^{(0)} \rangle + 2b \langle z^{(0)}, z^{(1)} \rangle + b^2 \langle z^{(1)}, z^{(1)} \rangle +\]</span></p><p>that is, three separate dot products. Then we place</p> +<p><span class="math display">\[ +z^{(0)} ~\text{into}~ s_1', \ldots, s'_{\nu}\\ +z^{(1)} ~\text{into}~ s_{\nu+1}', \ldots, s'_{2\nu} +\]</span></p><p>so that</p> +<p><span class="math display">\[\langle z^{(0)}, z^{(0)} \rangle = \langle s_1', s_1' \rangle + \langle s_2', s_2' \rangle + \ldots + \langle s_{\nu}', s_{\nu}' \rangle\]</span></p><p>and similarly with <span class="math inline">\(\langle z^{(1)}, z^{(1)} \rangle\)</span></p> +<p><span class="math display">\[\langle z^{(1)}, z^{(1)} \rangle = \langle s_{\nu+1}', s_{\nu+1}' \rangle + \ldots + \langle s_{2\nu}', s_{2\nu}' \rangle\]</span></p><p>and</p> +<p><span class="math display">\[\langle z^{(0)}, z^{(1)} \rangle = \langle s_1', s_{\nu+1}' \rangle + \langle s_2', s_{\nu+2}' \rangle + \ldots + \langle s_{\nu}', s_{2\nu}' \rangle\]</span></p><p>So that now, it is a matter of setting appropriate <span class="math inline">\(a_{ij}\)</span> values to reshape the original check into a Labrador-shaped constraint:</p> + +<ul> +<li>from <span class="math inline">\(1\)</span> to <span class="math inline">\(\nu\)</span>: if <span class="math inline">\(i=j\)</span>, set <span class="math inline">\(a_{ij}=1,~ 0\)</span> otherwise,</li> +<li>from <span class="math inline">\(\nu+1\)</span> to <span class="math inline">\(2\nu\)</span>: if <span class="math inline">\(i=j\)</span>, set <span class="math inline">\(a_{ij}=b^2,~ 0\)</span> otherwise,</li> +<li>from <span class="math inline">\(2\nu\)</span> to <span class="math inline">\(2\nu+\mu\)</span>: if <span class="math inline">\(j=i+\nu\)</span>, set <span class="math inline">\(a_{ij}=b\)</span>. And since the <span class="math inline">\((a_{ij})\)</span> matrix is symmetric, then <span class="math inline">\(a_{ji}=b\)</span>, which implies that when both combined we get <span class="math inline">\(2b\)</span>.</li> +</ul> + +<p>Therefore, we can perform the <span class="math inline">\(\langle z, z \rangle\)</span> with the decompositions <span class="math inline">\(z^{(0)},~ z^{(1)}\)</span>.</p> + +<h4>encoding check (ii.)</h4> + +<p>The next check is <span class="math inline">\((ii.)\)</span>, <span class="math inline">\(\sum_{i=1}^r \langle \phi_i, z \rangle \cdot c_i = \sum_{i, j = 1}^r h_{ij} c_i c_j\)</span>. Reshape it into</p> +<p><span class="math display">\[\sum_{i=1}^r \langle \phi_i \cdot c_i, z \rangle - \sum_{i, j = 1}^r (c_i c_j)\cdot h_{ij} = 0\]</span></p><p>and, recall that we set</p> +<p><span class="math display">\[ +z^{(0)} = s_1 || s_2 || \ldots || s_{\nu}\\ +z^{(1)} = s_{\nu+1} || \ldots || s_{2\nu}\\ +v = s_{2\nu+1} || \ldots || s_{2\nu+ \mu} +\]</span></p><p>also recall that <span class="math inline">\(v=t || g || h\)</span>. Thus, similarly as for the check <span class="math inline">\((i.)\)</span>,</p> + +<ul> +<li>from <span class="math inline">\(1\)</span> to <span class="math inline">\(\nu\)</span>: <span class="math inline">\(\phi_i^{(k)} = \sum \phi_i c_i\)</span>,</li> +<li>from <span class="math inline">\(\nu+1\)</span> to <span class="math inline">\(2\nu\)</span>: <span class="math inline">\(\phi_i^{(k)} = \sum \phi_i c_i \cdot b\)</span>,</li> +<li>from <span class="math inline">\(2\nu\)</span> to <span class="math inline">\(2\nu+\mu\)</span>: <span class="math inline">\(\phi_i^{(k)} = - (c_i c_j)\)</span>.</li> +</ul> + +<h4>encoding check (iii.)</h4> + +<p>For the check <span class="math inline">\((iii.)\)</span>, <span class="math inline">\(\sum a_{ij} g_{ij} + \sum h_{ii} - b =0\)</span>, recall again that <span class="math inline">\(g_{ij}, h_{ij}\)</span> are inside <span class="math inline">\(v\)</span>, so it is just a matter of setting <span class="math inline">\(a_{ij}=a_{ij}\)</span> and <span class="math inline">\(a_{ij}=1\)</span> at their corresponding positions, and <span class="math inline">\(b^{(k)}=b\)</span>.</p> + +<p>With this, we can encode the checks <span class="math inline">\((i.),~ (ii.),~ (iii.)\)</span> into the Labraddor constraint shape</p> +<p><span class="math display">\[g^{(k)}(s') = \sum_{i,j=1}^{r'} a_{ij}^{(k)} \langle s_i', s_j' \rangle + \sum_i^{r'} \langle \phi_i^{(k)}, s_i' \rangle - b^{(k)} = 0\]</span></p><p>with <span class="math inline">\(s' := z^{(0)} || z^{(1)} || v\)</span>, where <span class="math inline">\(v=t ||g||h\)</span>.</p> + +<h4>encoding commitments checks</h4> + +<p>Similarly we encode he commitments checks. We had <span class="math inline">\(Az = \sum c_i t_i\)</span>, which we can do in a dot product constraint as</p> +<p><span class="math display">\[\langle A_{row}, z \rangle - \sum c_i \cdot ~ (t_i)_{row} = 0\]</span></p><p>which in a similar fashion as the checks <span class="math inline">\((i.), (ii.), (iii.)\)</span>, we can reshape it into a Labrador constraint, since the inner commitments <span class="math inline">\(t_i\)</span> are placed inside <span class="math inline">\(v\)</span>.</p> + +<p><br></p> + +<p>And for the outer commitments <span class="math inline">\(u_1,~ u_2\)</span>, as we saw in the section [<a href="#4.-amortization">4. Amortization</a>] we want</p> +<p><span class="math display">\[ +u_1 = B \cdot t + C \cdot g\\ +u_2 = D \cdot h +\]</span></p><p>which we can encode as</p> +<p><span class="math display">\[ +\langle B_{row}, t \rangle + \langle C_{row}, g \rangle = (u_1)_{row}\\ +\langle D_{row}, h \rangle = (u_2)_{row} +\]</span></p><p>which, as before, we can also reshape into the dot product Labrador constraints.</p> + +<p><br></p> + +<p>The paper mentions that <a href="https://giphy.com/gifs/67-six-seven-john-chungus-08uBcURaMq6vA93TGc">6 or 7</a> iterations of the recursive protocol give the best results, where at each recursion the witness shrinks down from <span class="math inline">\(|s^{(i)}|\)</span> into <span class="math inline">\(|s^{(i)}|^{2/3}\)</span>.</p> + +<h2>Conclusions</h2> + +<p>It has been very interesting to see how a lattice-based SNARK works, its machinery and tricks. +Also, Labrador is of special interest because it was a big step forward on lattice-based SNARKs since it has good proof sizes; asymptotically of <span class="math inline">\(\mathcal{O}(\log n)\)</span>, and for <span class="math inline">\(2^{16}\)</span> R1CS constraints, the paper reports proof sizes of 54 kbytes.</p> + +<div style="text-align:center;"> +<a target="_blank" href="img/posts/labrador/proof-sizes-table.png"> + <img style="width:80%" + src="img/posts/labrador/proof-sizes-table.png"></img> +</a> +<br> +<i style="color:#374072">table from the Labrador paper</i> +</div> + +<p><br></p> + +<p>An interesting thing in Labrador is the capacity to mix binary and non-binary R1CS constraints, which get reshaped into the <span class="math inline">\(f(s)\)</span> and <span class="math inline">\(f'(s)\)</span> Labrador constraints. This is nice, because for many zk-circuits we want to prove binary-based operations (such as traditional hashes), while doing arithmetic logic, and in Labrador we can mix both in parallel.</p> + +<p>Other post-quantum safe SNARK schemes are based on hashes (eg. STARKs), widely used in the industry; but what seems interesting of the lattice-based cryptography is that lattices have more algebraic structure, and while a bit less developed in production than hash based proofs, they have interesting potential.</p> + +<p>I’ve enjoyed studying Labrador, and definitely I’ll keep exploring other lattice-based schemes while also going a bit down to the math behind them.</p> + + </div> + + <footer style="text-align:center; margin-top:100px;margin-bottom:50px;"> + <div class="container"> + <br> + <a href="/blog">Go to main</a> + <br><br> + <div class="row"> + <ul class="list-inline"> + <li><a href="https://twitter.com/arnaucube" + style="color:gray;text-decoration:none;" + target="_blank">twitter.com/arnaucube</a> + </li> + <li><a href="https://github.com/arnaucube" + style="color:gray;text-decoration:none;" + target="_blank">github.com/arnaucube</a> + </li> + </ul> + </div> + <div class="row" style="display:inline-block;"> + Blog made with <a href="http://github.com/arnaucube/blogo/" + target="_blank" style="color: gray;text-decoration:none;">Blogo</a> + </div> + </div> + </footer> + + <script> + </script> + <script src="js/external-links.js"></script> + <script>hljs.initHighlightingOnLoad();</script> + <script defer src="js/katex/katex.min.js"></script> + <script defer src="js/katex/auto-render.min.js"></script> + <script> + document.addEventListener("DOMContentLoaded", function() { + renderMathInElement(document.body, { + displayMode: false, + // customised options + // • auto-render specific keys, e.g.: + delimiters: [ + {left: '$$', right: '$$', display: true}, + {left: '$', right: '$', display: false}, + {left: "\\[", right: "\\]", display: true}, + {left: "\\(", right: "\\)", display: false}, + {left: "\\begin{equation}", right: "\\end{equation}", display: true} + ], + // • rendering keys, e.g.: + throwOnError : true + }); + }); + + /// + let theme = localStorage.getItem("theme"); + if ((theme === "light-theme")||(theme==null)) { + theme = "light-theme"; + document.getElementById("themeSwitcher").checked = false; + } else if (theme === "dark-theme") { + theme = "dark-theme"; + document.getElementById("themeSwitcher").checked = true; + } + document.body.className = theme; + localStorage.setItem("theme", theme); + + function switchTheme() { + theme = localStorage.getItem("theme"); + if (theme === "light-theme") { + theme = "dark-theme"; + document.getElementById("themeSwitcher").checked = true; + } else { + theme = "light-theme"; + document.getElementById("themeSwitcher").checked = false; + } + document.body.className = theme; + localStorage.setItem("theme", theme); + + console.log(theme); + } + </script> + <script> + function tagLinks(tagName) { + var tags = document.getElementsByTagName(tagName); + for (var i=0, hElem; hElem = tags[i]; i++) { + if (hElem.parentNode.className=="row postThumb") { + continue; + } + hElem.id = hElem.innerHTML.toLowerCase().replace(" ", "-"); + hElem.innerHTML = "<a style='color:black!important;' href='#"+hElem.id+"'>"+hElem.innerHTML+"</a>"; + } + } + tagLinks("h2"); + tagLinks("h3"); + tagLinks("h4"); + tagLinks("h5"); + </script> + <script> +function generateTOC(tocContainerId = "toc") { + const tocContainer = document.getElementById(tocContainerId); + if (!tocContainer) return; + + const headings = document.querySelectorAll("h2, h3, h4, h5, h6"); + if (!headings.length) return; + + // Generate IDs if missing + headings.forEach((heading, index) => { + if (!heading.id) { + heading.id = + heading.textContent + .trim() + .toLowerCase() + .replace(/[^\w\s-]/g, "") + .replace(/\s+/g, "-") + + "-" + + index; + } + }); + + const toc = document.createElement("ul"); + let currentLevel = 1; + let currentList = toc; + const stack = [toc]; + + headings.forEach((heading) => { + const level = parseInt(heading.tagName.substring(1), 10); + + const li = document.createElement("li"); + const a = document.createElement("a"); + + a.href = "#" + heading.id; + a.textContent = heading.textContent; + + li.appendChild(a); + + if (level > currentLevel) { + for (let i = currentLevel; i < level; i++) { + const nestedList = document.createElement("ul"); + + const lastLi = + currentList.lastElementChild || + currentList.appendChild(document.createElement("ul")); + + lastLi.appendChild(nestedList); + + stack.push(nestedList); + currentList = nestedList; + } + } else if (level < currentLevel) { + for (let i = currentLevel; i > level; i--) { + stack.pop(); + } + currentList = stack[stack.length - 1]; + } + + currentList.appendChild(li); + currentLevel = level; + }); + + tocContainer.innerHTML = ""; + tocContainer.appendChild(toc); +} + +document.addEventListener("DOMContentLoaded", () => { + generateTOC(); +}); + </script> + <script src="js/mermaid.min.js"></script> + + </body> +</html> +
@@ -401,7 +401,7 @@ if (hElem.parentNode.className=="row postThumb") {
continue; } hElem.id = hElem.innerHTML.toLowerCase().replace(" ", "-"); - hElem.innerHTML = "<a style='text-decoration:none;color:black;' href='#"+hElem.id+"'>"+hElem.innerHTML+"</a>"; + hElem.innerHTML = "<a style='color:black!important;' href='#"+hElem.id+"'>"+hElem.innerHTML+"</a>"; } } tagLinks("h2");@@ -409,8 +409,77 @@ tagLinks("h3");
tagLinks("h4"); tagLinks("h5"); </script> - <script src="js/mermaid.min.js"></script> + <script> +function generateTOC(tocContainerId = "toc") { + const tocContainer = document.getElementById(tocContainerId); + if (!tocContainer) return; + + const headings = document.querySelectorAll("h2, h3, h4, h5, h6"); + if (!headings.length) return; + + // Generate IDs if missing + headings.forEach((heading, index) => { + if (!heading.id) { + heading.id = + heading.textContent + .trim() + .toLowerCase() + .replace(/[^\w\s-]/g, "") + .replace(/\s+/g, "-") + + "-" + + index; + } + }); + + const toc = document.createElement("ul"); + let currentLevel = 1; + let currentList = toc; + const stack = [toc]; + + headings.forEach((heading) => { + const level = parseInt(heading.tagName.substring(1), 10); + + const li = document.createElement("li"); + const a = document.createElement("a"); + + a.href = "#" + heading.id; + a.textContent = heading.textContent; + + li.appendChild(a); + + if (level > currentLevel) { + for (let i = currentLevel; i < level; i++) { + const nestedList = document.createElement("ul"); + const lastLi = + currentList.lastElementChild || + currentList.appendChild(document.createElement("ul")); + + lastLi.appendChild(nestedList); + + stack.push(nestedList); + currentList = nestedList; + } + } else if (level < currentLevel) { + for (let i = currentLevel; i > level; i--) { + stack.pop(); + } + currentList = stack[stack.length - 1]; + } + + currentList.appendChild(li); + currentLevel = level; + }); + + tocContainer.innerHTML = ""; + tocContainer.appendChild(toc); +} + +document.addEventListener("DOMContentLoaded", () => { + generateTOC(); +}); + </script> + <script src="js/mermaid.min.js"></script> </body> </html>
@@ -524,7 +524,7 @@ if (hElem.parentNode.className=="row postThumb") {
continue; } hElem.id = hElem.innerHTML.toLowerCase().replace(" ", "-"); - hElem.innerHTML = "<a style='text-decoration:none;color:black;' href='#"+hElem.id+"'>"+hElem.innerHTML+"</a>"; + hElem.innerHTML = "<a style='color:black!important;' href='#"+hElem.id+"'>"+hElem.innerHTML+"</a>"; } } tagLinks("h2");@@ -532,8 +532,77 @@ tagLinks("h3");
tagLinks("h4"); tagLinks("h5"); </script> - <script src="js/mermaid.min.js"></script> + <script> +function generateTOC(tocContainerId = "toc") { + const tocContainer = document.getElementById(tocContainerId); + if (!tocContainer) return; + + const headings = document.querySelectorAll("h2, h3, h4, h5, h6"); + if (!headings.length) return; + + // Generate IDs if missing + headings.forEach((heading, index) => { + if (!heading.id) { + heading.id = + heading.textContent + .trim() + .toLowerCase() + .replace(/[^\w\s-]/g, "") + .replace(/\s+/g, "-") + + "-" + + index; + } + }); + + const toc = document.createElement("ul"); + let currentLevel = 1; + let currentList = toc; + const stack = [toc]; + + headings.forEach((heading) => { + const level = parseInt(heading.tagName.substring(1), 10); + + const li = document.createElement("li"); + const a = document.createElement("a"); + + a.href = "#" + heading.id; + a.textContent = heading.textContent; + + li.appendChild(a); + + if (level > currentLevel) { + for (let i = currentLevel; i < level; i++) { + const nestedList = document.createElement("ul"); + const lastLi = + currentList.lastElementChild || + currentList.appendChild(document.createElement("ul")); + + lastLi.appendChild(nestedList); + + stack.push(nestedList); + currentList = nestedList; + } + } else if (level < currentLevel) { + for (let i = currentLevel; i > level; i--) { + stack.pop(); + } + currentList = stack[stack.length - 1]; + } + + currentList.appendChild(li); + currentLevel = level; + }); + + tocContainer.innerHTML = ""; + tocContainer.appendChild(toc); +} + +document.addEventListener("DOMContentLoaded", () => { + generateTOC(); +}); + </script> + <script src="js/mermaid.min.js"></script> </body> </html>
@@ -328,7 +328,7 @@ if (hElem.parentNode.className=="row postThumb") {
continue; } hElem.id = hElem.innerHTML.toLowerCase().replace(" ", "-"); - hElem.innerHTML = "<a style='text-decoration:none;color:black;' href='#"+hElem.id+"'>"+hElem.innerHTML+"</a>"; + hElem.innerHTML = "<a style='color:black!important;' href='#"+hElem.id+"'>"+hElem.innerHTML+"</a>"; } } tagLinks("h2");@@ -336,8 +336,77 @@ tagLinks("h3");
tagLinks("h4"); tagLinks("h5"); </script> - <script src="js/mermaid.min.js"></script> + <script> +function generateTOC(tocContainerId = "toc") { + const tocContainer = document.getElementById(tocContainerId); + if (!tocContainer) return; + + const headings = document.querySelectorAll("h2, h3, h4, h5, h6"); + if (!headings.length) return; + + // Generate IDs if missing + headings.forEach((heading, index) => { + if (!heading.id) { + heading.id = + heading.textContent + .trim() + .toLowerCase() + .replace(/[^\w\s-]/g, "") + .replace(/\s+/g, "-") + + "-" + + index; + } + }); + + const toc = document.createElement("ul"); + let currentLevel = 1; + let currentList = toc; + const stack = [toc]; + + headings.forEach((heading) => { + const level = parseInt(heading.tagName.substring(1), 10); + + const li = document.createElement("li"); + const a = document.createElement("a"); + + a.href = "#" + heading.id; + a.textContent = heading.textContent; + + li.appendChild(a); + + if (level > currentLevel) { + for (let i = currentLevel; i < level; i++) { + const nestedList = document.createElement("ul"); + const lastLi = + currentList.lastElementChild || + currentList.appendChild(document.createElement("ul")); + + lastLi.appendChild(nestedList); + + stack.push(nestedList); + currentList = nestedList; + } + } else if (level < currentLevel) { + for (let i = currentLevel; i > level; i--) { + stack.pop(); + } + currentList = stack[stack.length - 1]; + } + + currentList.appendChild(li); + currentLevel = level; + }); + + tocContainer.innerHTML = ""; + tocContainer.appendChild(toc); +} + +document.addEventListener("DOMContentLoaded", () => { + generateTOC(); +}); + </script> + <script src="js/mermaid.min.js"></script> </body> </html>
@@ -233,7 +233,7 @@ if (hElem.parentNode.className=="row postThumb") {
continue; } hElem.id = hElem.innerHTML.toLowerCase().replace(" ", "-"); - hElem.innerHTML = "<a style='text-decoration:none;color:black;' href='#"+hElem.id+"'>"+hElem.innerHTML+"</a>"; + hElem.innerHTML = "<a style='color:black!important;' href='#"+hElem.id+"'>"+hElem.innerHTML+"</a>"; } } tagLinks("h2");@@ -241,8 +241,77 @@ tagLinks("h3");
tagLinks("h4"); tagLinks("h5"); </script> - <script src="js/mermaid.min.js"></script> + <script> +function generateTOC(tocContainerId = "toc") { + const tocContainer = document.getElementById(tocContainerId); + if (!tocContainer) return; + + const headings = document.querySelectorAll("h2, h3, h4, h5, h6"); + if (!headings.length) return; + + // Generate IDs if missing + headings.forEach((heading, index) => { + if (!heading.id) { + heading.id = + heading.textContent + .trim() + .toLowerCase() + .replace(/[^\w\s-]/g, "") + .replace(/\s+/g, "-") + + "-" + + index; + } + }); + + const toc = document.createElement("ul"); + let currentLevel = 1; + let currentList = toc; + const stack = [toc]; + + headings.forEach((heading) => { + const level = parseInt(heading.tagName.substring(1), 10); + + const li = document.createElement("li"); + const a = document.createElement("a"); + + a.href = "#" + heading.id; + a.textContent = heading.textContent; + + li.appendChild(a); + + if (level > currentLevel) { + for (let i = currentLevel; i < level; i++) { + const nestedList = document.createElement("ul"); + const lastLi = + currentList.lastElementChild || + currentList.appendChild(document.createElement("ul")); + + lastLi.appendChild(nestedList); + + stack.push(nestedList); + currentList = nestedList; + } + } else if (level < currentLevel) { + for (let i = currentLevel; i > level; i--) { + stack.pop(); + } + currentList = stack[stack.length - 1]; + } + + currentList.appendChild(li); + currentLevel = level; + }); + + tocContainer.innerHTML = ""; + tocContainer.appendChild(toc); +} + +document.addEventListener("DOMContentLoaded", () => { + generateTOC(); +}); + </script> + <script src="js/mermaid.min.js"></script> </body> </html>
@@ -0,0 +1,424 @@
+<!DOCTYPE html> +<html lang="en"> + + <head> + <meta name="description" content="A non-exhaustive overview of tools I use daily." /> + <meta charset="utf-8"> + <title> Tools - arnaucube - blog</title> + <meta name="title" content=" Tools - arnaucube - blog"> + <meta name="description" content="A non-exhaustive overview of tools I use daily."> + + <meta property="og:title" content=" Tools - arnaucube - blog" /> + <meta property="og:description" content="A non-exhaustive overview of tools I use daily." /> + <meta property="og:url" content="https://arnaucube.com/blog/tools.html" /> + <meta property="og:type" content="article" /> + <meta property="og:image" content="https://arnaucube.com/blog/img/posts/tools/tools-preview.png" /> + <meta name="twitter:title" content=" Tools - arnaucube - blog"> + <meta name="twitter:description" content="A non-exhaustive overview of tools I use daily."> + <meta name="twitter:image" content="https://arnaucube.com/blog/img/posts/tools/tools-preview.png"> + <meta name="twitter:card" content="summary_large_image"> + <meta name="author" content="arnaucube"> + + <link rel="icon" type="image/png" href="img/logoArnauCubeFavicon.png"> + + <meta name="viewport" content="width=device-width, initial-scale=1"> + + <link href="css/bootstrap.min.css" rel="stylesheet"> + <link rel="stylesheet" href="css/style.css"> + + <!-- highlightjs --> + <!-- <link rel="stylesheet" href="js/highlightjs/atom-one-dark.css"> --> + <link rel="stylesheet" href="js/highlightjs/atom-one-light.css"> + <!-- <link rel="stylesheet" href="js/highlightjs/gruvbox-dark.css"> --> + <script src="js/highlightjs/highlight.pack.js"></script> + + <!-- katex --> + <link rel="stylesheet" href="js/katex/katex.min.css"> + </head> + + <body> + + <!-- o_gradient_background" --> + <nav id="mainNav" class="navbar navbar-default navbar-fixed-top" + style="height:50px;font-size:100%;"> + <div class="container"> + <div style="float:left;"> + <a href="/blog" style="color:#000;display:inline-block;">blog</a> + <span style="margin-right:20px; margin-left:20px;">|</span> + <a href="/blog/notes.html" style="font-size:90%;color:#000;display:inline-block;">notes</a> + </div> + <div style="float:right;"> + <a href="/" style="color:#000;display:inline-block;margin-right:10px;">arnaucube.com</a> + <div class="onoffswitch" style="margin-left:0px;display:inline-block;" title="change theme"> + <input onclick="switchTheme()" type="checkbox" name="onoffswitch" class="onoffswitch-checkbox" + id="themeSwitcher"> + <label class="onoffswitch-label" for="themeSwitcher"></label> + </div> + </div> + </div> + <!-- <img style="height:5px; width:100%; margin-top:8px;" src="img/gradient-line.jpg" /> --> + </nav> + + + + <div class="container" style="margin-top:0px;max-width:800px;"> + <h1>Tools</h1> + +<p><em>2026-06-08</em></p> + +<p>A non-exhaustive overview of tools I use daily.</p> + +<p><em>I had this post half-written for a couple of years, finally I’ve put the time to ‘complete’ it enough to publish it. See it as a kind of email to a friend sharing my ways of approaching everyday tools.</em></p> + +<h2>in general</h2> + +<ul> +<li>Always be willing to <strong>customize and optimmize</strong> the tools you use</li> +<li>Usually closed source tools come with ‘defaults’ that may ‘suit the majority’ (or that it’s acceptable enough), but does not mean that you have to conform with it. This is another reason (from many) to use freesoftware or if not, opensource.</li> +<li>when willing to use a tool, if it is closed source, always check <strong>free software / opensource</strong> alternatives, usually they exist and have an active community around them</li> +<li>prefer <strong>terminal</strong> tools over GUI tools. In this way, working remotely inside a server feels the same as working locally in your laptop, just the terminal and the tool in there</li> +<li>prefer offline tools than online tools</li> +<li>In physical objects optimize for <strong>portability</strong>; also usually is good to not centralize many functions in a single object</li> +</ul> + +<h2>computer</h2> + +<ul> +<li>in general + +<ul> +<li>maximize keyboard shortcuts, and minimize keyboard surface + +<ul> +<li>I use a <strong>40% keyboard, <a href="https://github.com/foostan/crkbd">corne</a></strong> keyboard</li> +<li>the idea is that instead of moving your fingers to the keys, you want to move the keys under your fingers (eg. for numbers & symbols, F1 keys, etc) +<div style="text-align:center;"> +<a target="_blank" href="img/posts/tools/corne.png"><img style="width:60%" src="img/posts/tools/corne.png"></img></a> +<br> +<i style="font-size:75%">the Corne keyboard I use, self-assembled and soldered</i> +</div></li> +</ul></li> +<li><strong>minimize mouse</strong> usage: + +<ul> +<li>using the mouse means lifting the fingers from the keyboard</li> +<li>mouse limits an entire hand to only click buttons, you have many more fingers to use</li> +<li>so, naturally, <strong><a href="https://www.vim.org">vim</a> & <a href="https://neovim.io">neovim</a></strong> as code and text editors + +<ul> +<li>also this has the benefit that working in my laptop or working in the server feels the same</li> +</ul></li> +<li>window manager: till last year I was using <a href="https://i3wm.org">i3</a>, now I’m using <a href="https://swaywm.org">sway</a>. The main idea is to be able to move quick across windows and panels, while avoiding touching the mouse.</li> +</ul></li> +<li>automate: + +<ul> +<li>this is, bash & python scripts are very useful; for almost every task that has more than 3 iterations of the same steps</li> +<li>OS: of course, Linux. For the past 3 years I’ve been using <a href="https://nixos.org">NixOS</a>, attracted by it’s imperative philosophy: you have a set of config files, format a laptop, load your config, and you have all your software with your configurations ready to use (ie. not manually {apt/pacman}-installing each software package)</li> +</ul></li> +</ul></li> +</ul> + +<p>This is not only for code files; for example, when working on a <span class="math inline">\(LaTeX\)</span> document, edit it in neovim with <a href="https://github.com/lervag/vimtex">vimtex</a>, so that at save it re-renders the pdf and reloads it in <a href="https://pwmt.org/projects/zathura/">zathura</a>. Additionally, this works great while being offline, compared to mainstream options such as overleaf. +Similarly, when working on a markdown document, use <a href="https://github.com/arnaucube/md-live-server">md-live-server</a> locally, splitting the screen between the terminal for editing and <a href="https://qutebrowser.org/">qutebrowser</a>, so that no mouse is needed any time.</p> + +<div style="text-align:center;"> +<a target="_blank" href="img/posts/tools/desk-setup.jpg"><img style="width:75%" src="img/posts/tools/desk-setup.jpg"></img></a> +<br> +<i style="font-size:75%">a bit old (~2023) desk setup, with my 'old' Thinkpad Carbon X1</i> +</div> + +<h4>devices</h4> + +<!-- TODO picture corne + screens --> + +<ul> +<li>At home: + +<ul> +<li>I build a ~850 euros server (year 2024), with AMD Ryzen 9 5900XT 3.3 GHz 16-Core, 64 GB of RAM.</li> +<li>to work, I usually <a href="https://mosh.org/">mosh</a> (ssh) into it, then open a <a href="https://github.com/tmux/tmux">tmux</a> and <a href="https://neovim.io/">neovim</a>, and work there, so that I can use any (lightweight) laptop while remotely running everything in the powerful-enough server. +Another point for using vim/neovim as default code editor, is that when working remotely on a server, the setup remains the same. Another perspective is that you treat your laptop as a server for those kinds of tasks, so that when you work remotely on a server there is almost no difference. +<div style="text-align:center;"> +<a target="_blank" href="img/posts/tools/server.jpg"><img style="width:60%" src="img/posts/tools/server.jpg"></img></a> +</div></li> +</ul></li> +<li>Outside home: + +<ul> +<li>for many years I used a Thinkpad x270 of 12.5” screen, then a Thinkpad Carbon X1 of 13”, but then few years ago I started using smaller laptops of 10”</li> +<li>10” laptop: I was using a 2nd hand <a href="https://support.microsoft.com/en-us/surface/surface-go-2-specs-and-features-0fc6a657-2851-484f-6f82-bd3c589ed92c">Surface Go 2</a> that I bought for 200 euros, but recently I’ve replaced it with a <a href="https://www.chuwi.com/product/items/chuwi-minibook-x-n150.html">Chuwi Minibook X</a> which costs 300 euros (brand new). Both laptops have 10”.</li> +<li>8” tablet + notebook; or if in the sun, instead of the tablet, a eink reader (if so, a <a href="https://koreader.rocks">hacked one</a>)</li> +</ul></li> +</ul> + +<div style="text-align:center;"> +<a target="_blank" href="img/posts/tools/big-slingbag-contents.jpg"><img style="width:60%;" src="img/posts/tools/big-slingbag-contents.jpg"></img></a> +<br> +<i style="font-size:75%">8" tablet and 10" laptop</i> +</div> + +<p>A benefit of using lightweight laptops combined with a remote <em>powerful</em> server, is that the battery lasts longer than doing everything in the laptop, since you’re not doing many computations locally in the laptop!</p> + +<h2>services</h2> + +<p>Useful to have some raspberrypi/minipc at home with some services to make your life more comfortable (for example, with: <a href="https://github.com/arnaucube/configs/blob/master/server-dockers/docker-compose.yml">docker compose configuration file</a>).</p> + +<p>For about 2 years I’ve been running multiple media services in a raspberry pi 5, recently replaced it with a Beelink S13 NUC, where I can support many users of non-technical friends. +<div style="text-align:center;"> +<a target="_blank" href="img/posts/tools/media-setup.png"><img style="width:80%;" src="img/posts/tools/media-setup.png"></img></a> +</div></p> + +<p>Useful services:</p> + +<ul> +<li><a href="https://github.com/qbittorrent/qBittorrent">qbittorrent-nox</a>: the server version of qbittorrent, managed through a web UI</li> +<li><a href="https://jellyfin.org/">Jellyfin</a>: stream all the downloaded content (shows, movies, music) to your devices (tv, tablet, phone, laptop, etc)</li> +<li><a href="https://github.com/slskd/slskd/">slskd</a>: server client for the <a href="https://en.wikipedia.org/wiki/Soulseek">Soulseek network</a>, in order to download individual songs or albums that are not found through torrent</li> +</ul> + +<p>Highly recommended: with the previous services, you have all the minimal setup to enjoy media at home; but there is more dimension to it; the various projects within the *arr family, eg: +<a href="https://sonarr.tv/">sonarr</a>, <a href="https://radarr.video/">radarr</a>, <a href="https://www.bazarr.media/">bazarr</a>, <a href="https://prowlarr.com/">prawlarr</a>, etc.</p> + +<p>Then you can tie all those services through a main page with <a href="https://gethomepage.dev">homepage</a>, so that all links are accessible with a single click.</p> + +<p>Also, using browser <a href="https://github.com/gorhill/uBlock">adblockers</a> has become a must over the years, additionally having a <a href="https://pi-hole.net">pi-hole</a> connected to your router filters out adds in general on your home network (on all devices!), while also filtering out a big portion of trackers.</p> + +<h2>AFK / on the go</h2> + +<p><em>(AFK: away from keyboard, when I go outside of home)</em></p> + +<ul> +<li>sometimes I bring a slim-foldable keyboard</li> +<li>during the day (if I don’t require a laptop): slingbag with + +<ul> +<li>notebook and fountain pens</li> +<li>eink reader (ie. hacked kindle) or a 8” tablet</li> +</ul></li> +</ul> + +<div style="text-align:center;"> +<a target="_blank" href="img/posts/tools/small-slingbag-contents.jpg"><img style="width:60%;" src="img/posts/tools/small-slingbag-contents.jpg"></img></a> +</div> + +<p><br> +<div style="text-align:center;"> +<a target="_blank" href="img/posts/tools/notebook-kindle-outside.jpg"><img style="width:60%" src="img/posts/tools/notebook-kindle-outside.jpg"></img></a> + <br> + <i style="font-size:75%">can just randomly pop out of home and just sit anywhere to keep studying</i> +</div></p> + +<ul> +<li>travel: <a href="https://www.patagonia.com/product/refugio-daypack-30-liters/47928.html">30L backpack</a> with + +<ul> +<li><a href="#pens-and notebooks">notebook</a></li> +<li>eink reader / tablet 8”</li> +<li>laptop (see <a href="#devices">laptop</a> section)</li> +<li>clothes for 7 days, on the 6th day you do laundry, so you can travel long periods of time, virtually indefinite</li> +</ul></li> +</ul> + +<div style="text-align:center;"> +<a target="_blank" href="img/posts/tools/backpack.jpg"><img style="width:80%;" src="img/posts/tools/backpack.jpg"></img></a> +</div> + +<p>With this backpack setup I’ve traveled across the world (6 continents), with stays ranging from few days to 2 months, all with same backpack setup.</p> + +<p><br></p> + +<p>Notes about the picture above: this picture is from >2 years ago, some changes I did since then:</p> + +<ul> +<li>laptop: the Surface Go 2 has been replaced by the Chuwi Minibook X (both of 10”), see <a href="#devices">laptop</a> section for more details</li> +<li>tablet: the 10” tablet has been replaced by a 8” tablet</li> +<li>emulator: the <a href="https://www.goretroid.com/products/retroid-pocket-4-handheld">Retroid Pocket 4 Pro</a> has been replaced by the <a href="https://retrogamecorps.com/2022/05/15/miyoo-mini-v2-guide/">Myioo Mini Plus</a> (less powerful, so less console options, but smaller in the backpack; ideal for GBA games!)</li> +</ul> + +<h3>pens and notebooks</h3> + +<p>In the recent years I’ve discovered the joy of using <a href="https://www.reddit.com/r/fountainpens">fountain pens</a>. They are not essential, but is one of those little joys of life.</p> + +<p>I’ve had the opportunity to try many different models, both from buying some of them and also from trying them in-store (in Seoul there is <a href="https://maps.app.goo.gl/quDc2oAfb8Kn6Rug7">a very nice shop</a> where for a small fee you have all the time you need to roam through the shop trying more than 200 pens!)</p> + +<div style="text-align:center;"> +<a target="_blank" href="img/posts/tools/penshop-bestpen.png"><img style="width:80%;" src="img/posts/tools/penshop-bestpen.png"></img></a> + <br> + <i style="font-size:75%">In purple t-shirt, enjoying 2h of trying many many pens at <a href="https://maps.app.goo.gl/quDc2oAfb8Kn6Rug7" target="_blank">BestPen</a>, Seoul (South Korea).</i> +</div> + +<p>Beware, once you enter the fountain pens rabbit hole, the next worry is the paper, hence a natural following fixation are the notebooks to use with the fountain pens. Not all papers are good enough for fountain pens, but there are <a href="https://md-product.com/en/collections/md-notebook">some</a> very <a href="https://www.clairefontaine.com/gb/3525-10811-carnet-brochure-age-bag-795401c.html#/1182-rulings-o_uni/1756-dimension-145_x_21_cm/1971-couleur_produit-gris">good</a> paper <a href="https://www.youtube.com/watch?v=c_UvVavl-eE">quality</a> notebooks. Similarly inks are another <a href="https://mountainofink.com">rabbit hole</a>.</p> + +<div style="text-align:center;"> +<a target="_blank" href="img/notebooks.jpg"><img style="width:80%;" src="img/notebooks.jpg"></img></a> +</div> + + </div> + + <footer style="text-align:center; margin-top:100px;margin-bottom:50px;"> + <div class="container"> + <br> + <a href="/blog">Go to main</a> + <br><br> + <div class="row"> + <ul class="list-inline"> + <li><a href="https://twitter.com/arnaucube" + style="color:gray;text-decoration:none;" + target="_blank">twitter.com/arnaucube</a> + </li> + <li><a href="https://github.com/arnaucube" + style="color:gray;text-decoration:none;" + target="_blank">github.com/arnaucube</a> + </li> + </ul> + </div> + <div class="row" style="display:inline-block;"> + Blog made with <a href="http://github.com/arnaucube/blogo/" + target="_blank" style="color: gray;text-decoration:none;">Blogo</a> + </div> + </div> + </footer> + + <script> + </script> + <script src="js/external-links.js"></script> + <script>hljs.initHighlightingOnLoad();</script> + <script defer src="js/katex/katex.min.js"></script> + <script defer src="js/katex/auto-render.min.js"></script> + <script> + document.addEventListener("DOMContentLoaded", function() { + renderMathInElement(document.body, { + displayMode: false, + // customised options + // • auto-render specific keys, e.g.: + delimiters: [ + {left: '$$', right: '$$', display: true}, + {left: '$', right: '$', display: false}, + {left: "\\[", right: "\\]", display: true}, + {left: "\\(", right: "\\)", display: false}, + {left: "\\begin{equation}", right: "\\end{equation}", display: true} + ], + // • rendering keys, e.g.: + throwOnError : true + }); + }); + + /// + let theme = localStorage.getItem("theme"); + if ((theme === "light-theme")||(theme==null)) { + theme = "light-theme"; + document.getElementById("themeSwitcher").checked = false; + } else if (theme === "dark-theme") { + theme = "dark-theme"; + document.getElementById("themeSwitcher").checked = true; + } + document.body.className = theme; + localStorage.setItem("theme", theme); + + function switchTheme() { + theme = localStorage.getItem("theme"); + if (theme === "light-theme") { + theme = "dark-theme"; + document.getElementById("themeSwitcher").checked = true; + } else { + theme = "light-theme"; + document.getElementById("themeSwitcher").checked = false; + } + document.body.className = theme; + localStorage.setItem("theme", theme); + + console.log(theme); + } + </script> + <script> + function tagLinks(tagName) { + var tags = document.getElementsByTagName(tagName); + for (var i=0, hElem; hElem = tags[i]; i++) { + if (hElem.parentNode.className=="row postThumb") { + continue; + } + hElem.id = hElem.innerHTML.toLowerCase().replace(" ", "-"); + hElem.innerHTML = "<a style='color:black!important;' href='#"+hElem.id+"'>"+hElem.innerHTML+"</a>"; + } + } + tagLinks("h2"); + tagLinks("h3"); + tagLinks("h4"); + tagLinks("h5"); + </script> + <script> +function generateTOC(tocContainerId = "toc") { + const tocContainer = document.getElementById(tocContainerId); + if (!tocContainer) return; + + const headings = document.querySelectorAll("h2, h3, h4, h5, h6"); + if (!headings.length) return; + + // Generate IDs if missing + headings.forEach((heading, index) => { + if (!heading.id) { + heading.id = + heading.textContent + .trim() + .toLowerCase() + .replace(/[^\w\s-]/g, "") + .replace(/\s+/g, "-") + + "-" + + index; + } + }); + + const toc = document.createElement("ul"); + let currentLevel = 1; + let currentList = toc; + const stack = [toc]; + + headings.forEach((heading) => { + const level = parseInt(heading.tagName.substring(1), 10); + + const li = document.createElement("li"); + const a = document.createElement("a"); + + a.href = "#" + heading.id; + a.textContent = heading.textContent; + + li.appendChild(a); + + if (level > currentLevel) { + for (let i = currentLevel; i < level; i++) { + const nestedList = document.createElement("ul"); + + const lastLi = + currentList.lastElementChild || + currentList.appendChild(document.createElement("ul")); + + lastLi.appendChild(nestedList); + + stack.push(nestedList); + currentList = nestedList; + } + } else if (level < currentLevel) { + for (let i = currentLevel; i > level; i--) { + stack.pop(); + } + currentList = stack[stack.length - 1]; + } + + currentList.appendChild(li); + currentLevel = level; + }); + + tocContainer.innerHTML = ""; + tocContainer.appendChild(toc); +} + +document.addEventListener("DOMContentLoaded", () => { + generateTOC(); +}); + </script> + <script src="js/mermaid.min.js"></script> + + </body> +</html> +
@@ -16,6 +16,17 @@ "outsideArticle": "https://raw.githubusercontent.com/arnaucube/math/master/notes_hash-based-signatures.pdf",
"date": "2025-12-27" }, { + "thumb": "labrador_thumb.md", + "md": "labrador.md", + "metadescr": "Overview of how the post-quantum lattice-based SNARK scheme Labrador works." + }, + { + "thumb": "tools_thumb.md", + "md": "tools.md", + "metadescr": "A non-exhaustive overview of tools I use daily.", + "metaimg": "img/posts/tools/tools-preview.png" + }, + { "thumb": "ai-connections_thumb.md", "md": "ai-connections.md", "metadescr": "A short note on aiccelerationism, hyperreality, and the recovery of human connections.",
@@ -78,7 +78,7 @@
.container[style*='max-width:800px'], #markdown.container { margin-top: 30px !important; - max-width: 760px !important; + max-width: 800px !important; padding: 0 18px; }@@ -96,16 +96,19 @@ padding-top: 0;
line-height: 1.35; } -h1 { font-size: 1.95rem; } -h2 { font-size: 1.5rem; } -h3 { font-size: 1.24rem; } +h1 { font-size: 1.95rem!important; } +h2 { font-size: 1.5rem!important; } +h3 { font-size: 1.24rem!important; } +h4 { font-size: 1.0rem!important; } +h5 { font-size: 0.84rem!important; } +h6 { font-size: 0.60rem!important; } h1 a, h2 a, h3 a, h4 a, h5 a { - color: inherit !important; + color: black !important; text-decoration: none !important; }@@ -141,7 +144,7 @@ }
blockquote { background: transparent; - border-left: 2px solid var(--border); + border-left: 6px solid var(--border); margin: 1.3em 0; padding: 0.3em 0 0.3em 0.9em; color: var(--muted);
@@ -148,7 +148,7 @@ if (hElem.parentNode.className=="row postThumb") {
continue; } hElem.id = hElem.innerHTML.toLowerCase().replace(" ", "-"); - hElem.innerHTML = "<a style='text-decoration:none;color:black;' href='#"+hElem.id+"'>"+hElem.innerHTML+"</a>"; + hElem.innerHTML = "<a style='color:black!important;' href='#"+hElem.id+"'>"+hElem.innerHTML+"</a>"; } } tagLinks("h2");@@ -156,8 +156,77 @@ tagLinks("h3");
tagLinks("h4"); tagLinks("h5"); </script> - <script src="js/mermaid.min.js"></script> + <script> +function generateTOC(tocContainerId = "toc") { + const tocContainer = document.getElementById(tocContainerId); + if (!tocContainer) return; + + const headings = document.querySelectorAll("h2, h3, h4, h5, h6"); + if (!headings.length) return; + + // Generate IDs if missing + headings.forEach((heading, index) => { + if (!heading.id) { + heading.id = + heading.textContent + .trim() + .toLowerCase() + .replace(/[^\w\s-]/g, "") + .replace(/\s+/g, "-") + + "-" + + index; + } + }); + + const toc = document.createElement("ul"); + let currentLevel = 1; + let currentList = toc; + const stack = [toc]; + + headings.forEach((heading) => { + const level = parseInt(heading.tagName.substring(1), 10); + + const li = document.createElement("li"); + const a = document.createElement("a"); + + a.href = "#" + heading.id; + a.textContent = heading.textContent; + + li.appendChild(a); + + if (level > currentLevel) { + for (let i = currentLevel; i < level; i++) { + const nestedList = document.createElement("ul"); + const lastLi = + currentList.lastElementChild || + currentList.appendChild(document.createElement("ul")); + + lastLi.appendChild(nestedList); + + stack.push(nestedList); + currentList = nestedList; + } + } else if (level < currentLevel) { + for (let i = currentLevel; i > level; i--) { + stack.pop(); + } + currentList = stack[stack.length - 1]; + } + + currentList.appendChild(li); + currentLevel = level; + }); + + tocContainer.innerHTML = ""; + tocContainer.appendChild(toc); +} + +document.addEventListener("DOMContentLoaded", () => { + generateTOC(); +}); + </script> + <script src="js/mermaid.min.js"></script> </body> </html>
@@ -20,7 +20,7 @@ real money (not part of *reality*), while few hackers already saw them as a valid
form of currency. Another example was the [usage of "AFK"](https://youtu.be/KCAGb7oSwDs?t=72) by -the ThePirateBay founders, to refer to the world offline (instead of "IRL"): +the ThePirateBay founders, to refer to the offline world (instead of "IRL"): > *"We don't use the expression IRL, we say 'Away From Keyboard", AFK; we think the internet is for real"* - Peter Sunde, 2009
@@ -0,0 +1,670 @@
+# Notes on Labrador, a lattice-based SNARK scheme +*2026-06-11* + +I was curious on how (post-quantum) lattice-based SNARKs would work, and since I had a bit of time these past days, I've been reading the [LaBRADOR paper](https://eprint.iacr.org/2022/1341.pdf), by Ward Beullens and Gregor Seiler. This post tries to explain the intuition and steps not explicitly shown in the paper, which helped me understanding it. + +The structure of this post is the following: + +<div id="toc"></div> + +## Notation + +The paper uses $\vec{z} \in R^n$ to indicate a vector of length $n$; for simplicity, in this post I'll use $z \in R^n$, since it is already clear that we're working with a vector since we use $\in R^n$. + +Also, will use $i \in [n]$ to indicate $i = 1, \ldots, n$. And $(a_{ij})$ denotes the matrix with all the $a_{ij}$ values as entries. + +## A bit of background on lattices + + +### Lattices intro + +Intuitively, think of lattices as a discrete version of finite-dimensional vector spaces. In the same way that vector spaces span over some basis vectors $\{ b_i ~|~ i \in [n] \}$ in a field, lattices span over the basis in a ring: + +$$\mathcal{L} = \mathcal{L}(B) := B \cdot \mathbb{Z}^k = \{ \sum_{i=1}^k z_i \cdot b_i ~|~ z_i \in \mathbb{Z} \}$$ + +for a basis $B = \{ b_1, \ldots, b_k \}$ (which since they are a basis, they are linearly independent). $k$ is the rank of the basis, and we restrict our attention to *full-rank* lattices where $k=n$. + +Since we're at the lattices realm, we work with the polynomial ring $R_q = \mathbb{Z}_q[X]/(X^d + 1)$, and particularly in Labrador we pick $q$ and $d$ such that $X^d + 1$ splits in two irreducible factors $\pmod q$. + +<br> + +To provide a bit of more intuition, we can relate it to the *algebraic coding theory* concept of *sphere packing problem*, where in lattices we use short vectors and in coding theory we're interested into low weighted codewords. Both in lattices and coding theory we have q-ary {lattices/matrices} respectively and also parity check {lattices/matrices} respectively. + +<div style="text-align:center;"> +<img style="width:70%" +src="img/posts/labrador/lattice-spherepackingproblem.png"></img> +<br> +<i></i> +</div> + +### MSIS problem +*MSIS* problem stands for *Module Short Integer Solution* problem, and it is as follows: + +Given a uniformly random matrix $A \in^R R_q^{n \times m}$, with columns $a_i \in R_q^n$, find a non-zero integer vector $z \in R^m$ of norm $||z|| \leq \beta$ such that + +$$A \cdot z = \sum_i a_i \cdot z_i = 0 \in R_q^n$$ + +> *Remarks:* +> +> - without $||z|| \leq \beta$, it is easy to find a solution via [Gaussian elimination](https://math.libretexts.org/Courses/Palo_Alto_College/College_Algebra/05:_Systems_of_Equations_and_Inequalities/5.04:_Solving_Systems_with_Gaussian_Elimination). +> - $\beta < q$, because otherwise $z=(q, 0, \ldots, 0) \in R^m$ would always be a valid solution. +> - $\beta$ and $m$ must be large enough so that a solution is guaranteed to exist, usually $\beta \geq \sqrt{\hat{m}}$ and $m \geq \hat{m}$ where $\hat{m}$ is the smallest integer greater than $n \log q$. + +So overall when doing lattice-based cryptography, we will be interested into keeping the values *short*, aka. of small norm. + +Specifically, Labrador relies on the hardness of finding short solutions to linear equations over polynomial rings. + +<br> + +With this in mind, usually a common trick used when doing operations is to decompose the values with small bases, so that we can represent them while keeping the norm low. We will also use this in Labrador. + + +### Ajtai commitments + +We use a variant of the Ajtai commitments, where the messages are ring elements with small norms. + +For a sampled random matrix $A \in^R R_q^{n \times m}$, and given $x \in R^m$ as input, where $||x|| \leq \beta$, the Ajtai commitment is defined as $cm := A \cdot x \in R_q^n$. + +In other words, Ajtai commitments are essentially MSIS instances. + +Notice that this is binding but it is not hiding, but we could make it hiding by appending a small random veector to $x$. + +## Labrador constraints + +As usual in SNARK schemes, we have the witness, denoted $s= s_1, \ldots, s_r$, where each $s_i \in R_q^n$, ie. the witness is a vector of $r$ vectors of ring elements. + +And since we're at the lattices world and we're using the assumptions of SIS and Module-SIS, the witness values are "short", that is, their norm is bounded by some parameter: $\sum_{i=1}^r || s_i ||_2^2 \leq \beta^2$. + + +While in other SNARK systems we worked with constraints in the shape of R1CS or Plonkish, LaBRADOR defines its constraints as + +$$\boxed{ +f(s) = \sum_{i, j}^r a_{ij} \langle s_i, s_j \rangle + \sum_{i=1}^r \langle \phi_i, s_i \rangle + b = 0 +} +$$ + +where $s_i, \phi_i \in R_q^n$, and $a_{ij}, b \in R_q$. + +There are two different families of constraints, which are evaluated differently: + +- $\mathcal{F}$ family, which are those constraints where the entire polynomial must evaluate to zero, ie. $f(s) = 0$ +- $\mathcal{F}~'$ family, which are those constraints where we're only interested in their constant term being zero, ie. $ct( f'(s) ) = 0$ + +To avoid future doubts, let's write down what are some constant values: + +- $r$: amount of witness vectors $s_1, \ldots, s_r$ +- $K = |\mathcal{F}|$: amount of original constraints of fully vanishing dot products +- $L = |\mathcal{F}~'|$: amount of original constraints where the constant term is zero + + +<!-- +(HIDDEN) +> *Side derailed observation*: since Labrador works with $\mathbb{Z}_q[X]/(X^d + 1)$, where $q, d$ are such that $X^d + 1$ splits in two irreducible factors $\pmod q$, this is a ring, the polynomial ring $R_q$. +> +> But imagine for a moment (only imagine, since in many lattice-based cryptography this is not the case) that we pick $q, d$ parameters such that $X^d+1$ is irreducible, then we would have $\mathbb{Z}_q[X]/(X^d+1)$ being a finite field, $\mathbb{F}_{q^d}$; and then, the set of solutions satisfying the family $\mathcal{F}$ would be an *algebraic variety*, which is an interesting algebraic object, although not so useful in the context where we're now. But this is not the case, and in our case the $X^d+1$ that splits into two irreducible factors so that it ensures that through the protocol we can find challenges that are both computationally *short* and invertible when subtracted ([iacr:2017-523](https://eprint.iacr.org/2017/523)). +--> + + +## Protocol +For the rest of this post, I'll use colours to easily identify the different concepts across the various equations of the protocol. + +We start from the *original* constraints. Those are both, the fully vanishing constraints of the family $\mathcal{F}$ and the constraints where the constant term is zero of the family $\mathcal{F}~'$: + +$$ +f(s) = \sum_{i, j}^r \textcolor{magenta}{a_{ij}} \langle s_i, s_j \rangle + \sum_{i=1}^r \langle \textcolor{blue}{\phi_i}, s_i \rangle - \textcolor{orange}{b} = 0 +$$ + +$$ +f'(s) = \sum_{i, j}^r \textcolor{magenta}{a'_{ij}} \langle s_i, s_j \rangle + \sum_{i=1}^r \langle \textcolor{blue}{\phi'_i}, s_i \rangle - \textcolor{orange}{b'} ~~such~that~~ ct(f'(s))=0 +$$ + +In this blog post we will not cover how do we get from the R1CS constraints into the Labrador constraints shape, it can be found in the section 6 of the paper, but the main idea you need to have is that for different kinds of R1CS constraints (binary and non-binary constraints) we can obtain the $f(s)$ and $f'(s)$ Labrador constraints. + +Once we have the original constraints, the idea is to do some reductions adding some more constraints, and then do multiple aggregation steps in order to end up having fewer constraints. + + +<div style="text-align:center;"> +<img style="width:60%" +src="img/posts/labrador/constraint-polynomials-evolution.jpg"></img> +<br> +<i style="color:#374072">$f(s), f'(s)$ are original polynomials, from the mentioned families $\mathcal{F}$ and $\mathcal{F}~'$ respectively,<br> +$f_{proj}(s), f''(s)$, $F(s)$ are polynomials that we will describe in the following sections.</i> +</div> + + +<br> +Labrador reduces proof size through four transformations unfolded over the next sections: + +1. Commit to the witness +2. Reduce norm checking with Johnson-Lindenstrauss projections +3. Aggregate many constraints into one +4. Amortize openings +5. Recurse + + +### 1. Committing to the witness +We name *inner commitments* the commitments to the original witness. Those commitments are + +$$cm(s_i) = t_i = A \cdot s_i \in R_q^k ~~ \forall i \in [r]$$ + +But beware, sending all those $r$ commitments would be too costly, so what the protocol does is it commits to them. The issue then is that $t_i$ (the inner commitments) have coefficients that are $\pmod q$, whose norm would be too big. The trick that the protocol uses, is the one we mentioned at the [*'a bit of background on lattices'* section](#a-bit%20of%20background%20on%20lattices), this is, to decompose them into $t_1 \geq 2$ parts with small base $b_1$ before committing, ie. + +$$t_i = t_i^{(0)} + t_i^{(1)} b_1 + \ldots + t_i^{(t_1-1)} \cdot b_1^{t_1 -1}$$ +with $|| t_i^{(k)} ||_{\infty} \leq b_1/2$. + +Now, let $t \in R_q^{r \cdot t_1 \cdot k}$ be the concatenation of all decomposition parts of $t_i^{(k)}$. + +The commitment to the original commitment, is named *outer commitment*, and it is computed as + +$$u_1 = B \cdot t$$ + +that is + +$$u_1 = \sum_{i=1}^r \sum_{k=0}^{t_1-1} B_{ik} \cdot t_i^{(k)}$$ + +with $||t|| \leq \gamma_1$ for a fixed norm parameter $\gamma_1$. + + +Now, we want to show that $s=(s_1, \ldots, s_r)$ satisfies the norm bound that we mentioned at the [setup](#setup) section. But sending $r$ witness to the verifier would be again too costly, so the protocol reduces it through a projection. + +### The σ₋₁ trick + +Before going forward with the protocol, we will overview a trick that Labrador uses in order to compute inner products within the polynomial ring realm. + +$\sigma_{-1}(\pi_i)$ allows us to convert the coefficient-wise vector inner product into a polynomial multiplication relation. + +Within the protocol, we want to compute $p_j = \sum_{i=1}^r \langle \pi_i^{(j)}, s_i \rangle$, but we want to compute it using ring operations, concretely using polynomial multiplication. + +Notice that in Labrador, vectors are encoded as polynomials in $R_q$, ie + +$$a=(a_1, \ldots, a_{n}) ~\longleftrightarrow~ a(x) = \sum_{k=1}^n a_k X^k \in R_q$$ + +Let $a(x) = \sum_k a_k X^k,~~ b(x) = \sum_k b_k X^k$, then when computing $a(x) \cdot b(x)$ + +$$a(x) b(x) = (\sum_k a_k X^k) \cdot (\sum_k b_k X^k) = \sum_{i,j} a_i b_j X^{i+j}$$ + +which is different from the dot product that we're interested in. + +We want to only multiply together the coefficients of the equal-index terms, ie. $a_0 b_0 + a_1 b_1 + \ldots + a_{n-1} b_{n-1}$. Thus we need some trick to force getting only the values when $i=j$. + +The trick is with $\sigma_{-1}$ and getting the constant term of the result of multiplying $\sigma_{-1}(a) \cdot b$. + +Define the automorphism $\sigma_{-1}$ as $\sigma_{-1}(X) = X^{-1}$, so that $\sigma_{-1}(a(X)) = a(X^{-1}) = \sum_i a_i X^{-i}$. + +Then, when multiplying + +$$\sigma_{-1}(a(X)) \cdot b(X) = \sum_{i,j} a_i b_j X^{j-i}$$ + +So, when $j=i$, $X$ will be $0$; hence the result we're interested in, the dot product result, will be contained in the constant terms of the result of that multiplication, where the $X^{j-i}=X^0$. + +In other words, $\langle a, b \rangle = ct(\sigma_{-1}(a(X)) \cdot b(X))$, where $ct$ denotes getting the constant term. That is, applied to the projection case, $\langle \pi_i^{(j)}, s_i \rangle= ct(\sigma_{-1}(\pi_i^{(j)}) \cdot s_i)$. + + +### 2. Johnson-Lindenstrauss projection + +The main idea here, is that we will use random linear projections that almost preserve the $l_2$-norm while allowing us to reduce the amount of equations to check, while keeping the bounds in place. + +This relies on the Johnson-Lindenstrauss Lemma, which basically is used as instead of revealing all the witnes values $s_1, \ldots, s_r$, the verifier samples a random linear map $\Pi: \mathbb{Z}_q^{nd} \longrightarrow \mathbb{Z}_q^{256}$, and then the prover reveals only $\Pi \cdot s$, which has many less elements than the original $s$ vector. + +And the Johnson-Lindenstrauss Lemma guarantees that $||\Pi s||$ contains almost the same norm information as $||s||$ with very high probability. + +In practice, verifier samples $\Pi_i = (\pi_i^{(j)})_j \in \chi^{256 \times nd}$, the prover computes $p_j = \sum_{i=1}^r \langle \pi_i^{(j)}, s_i \rangle$, and the verifier checks that $||p|| \leq \sqrt{128} \beta$. + + +So at this point, in the family $\mathcal{F}~'$, we have the original constraints, without any projection related stuff: + +$$ +f'(s) = \sum_{i, j}^r \textcolor{magenta}{a'_{ij}} \langle s_i, s_j \rangle + \sum_{i=1}^r \langle \textcolor{blue}{\phi'_i}, s_i \rangle + \textcolor{orange}{b'} = 0 +$$ + +And the projection constraints, which encode the check for the J-L projection in the Labrador constraints shape: + +Notice that the projection equation, $p_j = \sum_{i=1}^r \langle \sigma_{-1}(\pi_i^{(j)}), s_i \rangle$ is also + +$$\sum_{i=1}^r \langle \sigma_{-1}(\pi_i^{(j)}), s_i \rangle - p_j = 0$$ + +which looks almost like the Labrador canonical constraints, we just need to add the $a_{ij}$ part. We do it by setting $a_{ij}=0$: + +$$ +f_{proj}(s) = \sum_{i, j}^r \textcolor{magenta}{0} \cdot \langle s_i, s_j \rangle + \sum_{i=1}^r \langle \textcolor{blue}{\sigma_{-1}(\pi_i^{(j)})}, s_i \rangle - \textcolor{orange}{p_j} = 0 +$$ + +this is, we set $\textcolor{magenta}{a_{proj,ij}=0}$, $\textcolor{blue}{\phi_{proj,~i}=\sigma_{-1}(\pi_i^{(j)})}$ and $\textcolor{orange}{b_{proj} = -p_j}$. In this way we have the projection constraints encoded as Labrador constraints. + +Recall that both the original $f'(s)$ constraints, and the projection constraints $f_{proj}(s)$ are in the family $\mathcal{F}~'$, so that we care about their constant term being zero, not the full polynomial vanishing. + + +### 3. Aggregation steps +At this point of the protocol, in the family $\mathcal{F}~'$, we have $L$ original constraints $f'(s)$ and $256$ projection constraints $f_{proj}(s)$. The idea now is to linearly combine them into fewer constraints. + +The verifier sends the challenges $\psi^{(k)} \in^R (\mathbb{Z}_q)^L$ and $w^{(k)} \in^R (\mathbb{Z}_q)^{256}$, which the prover will use to compute the random linear combinations of the constraints we have so far, which preserves the validity while reducing the number of constraints. + +#### 3.1. 1st aggregation + +The prover linearly combines + +- the $L$ original constraints from $f'(s)$ +- the $256$ constraints that prove the Johnson-Lindenstrauss projection, $f_{proj}(s)$ + +as follows + +$$ +f''^{(k)}(s) = +\sum_{l=1}^L \psi_l^{(k)} \cdot f'^{(l)}(s) ++ +\sum_{j=1}^{256} w_j^{(k)} \cdot f_{proj}(s) += +\\ += \underbrace{ \sum_{l=1}^L \psi_l^{(k)} \cdot f'^{(l)}(s) }_{(i.)~ \text{lin.comb. of the original constraints}} ++ +\underbrace{ \sum_{j=1}^{256} w_j^{(k)} \cdot ( \langle \sigma_{-1}(\pi_i^{(j)}), s_i \rangle -p_j) }_{(ii.)~ \text{lin.comb. of the 256 projection constraints}} +$$ + +First we will tackle $(i.)$. Recall that $f'(s) = \sum_{i, j}^r a_{ij}' \cdot \langle s_i, s_j \rangle + \sum_{i=1}^r \langle \phi_i', s_i \rangle + b'$, so by distributing the challenge $\psi_l^{(k)}$, + +$$ +(i.):~ +\sum_l \psi_l^{(k)} \cdot f'^{(l)}(s) = +\sum_l \psi_l^{(k)} \cdot \left( + \sum_{i,j} a_{ij}^{'(l)} \cdot \langle s_i, s_j \rangle + + \sum_i \langle \phi_i^{'(l)}, s_i \rangle - b_0^{'(k)} +\right)\\ += +\underbrace{ +\sum_{i,j} \left( \sum_l \psi_l^{(k)} \cdot a_{ij}^{'(l)} \right) \cdot \langle s_i, s_j \rangle +}_{(i.A)} +\underbrace{ ++ \sum_i \left( \sum_l \psi_l^{(k)} \cdot \phi_i^{'(l)} \right) \cdot s_i +- \sum_l \psi_l^{(k)} \cdot b_0^{'(l)} +}_{(i.B)} +$$ + +For the $(i.A)$ part, we can set + +$$\textcolor{magenta}{a_{ij}^{''(k)}} = \sum_{l=1}^L \psi_l^{(k)} \cdot a_{ij}^{'(l)}$$ + +so that: + +$$ +(i.A):~ + \sum_{i,j} \left( \sum_l \psi_l^{(k)} \cdot a_{ij}^{'(l)} \right) \cdot \langle s_i, s_j \rangle ~=~ \sum_{i,j} \textcolor{magenta}{a''^{(k)}_{ij}} \cdot \langle s_i, s_j \rangle +$$ + + +Now tackle $(ii.)$: + +$$ +(ii.):~ \sum_{j=1}^{256} w_j^{(k)} \cdot \big( \langle \sigma_{-1}(\pi_i^{(j)}), s_i \rangle - p_j \big) = \sum_i \langle \sum_j w_j^{(k)} \sigma_{-1}(\pi_i^{(j)}),~ s_i \rangle +- \sum_j w_j^{(k)} \cdot p_j +$$ + +Now let's put together $(i.B)$ and $(ii)$: + +$$ +\overbrace{ + \textcolor{green}{ + \sum_i ( \sum_l \psi_l^{(k)} \cdot \phi_i^{'(l)} ) \cdot s_i + } + \textcolor{blue}{ + - \sum_l \psi_l^{(k)} \cdot b_0^{'(l)} + } +}^{(i.B)} ++ +\overbrace{ + \textcolor{orange}{ + \sum_i \langle \sum_j w_j^{(k)} \sigma_{-1}(\pi_i^{(j)}),~ s_i \rangle + } + \textcolor{red}{ + - \sum_j w_j^{(k)} \cdot p_j + } +}^{(ii.)} +=\\~\\ += + \sum_i +\textcolor{green}{ + \big\langle + \sum_l \psi_l^{(k)} \cdot \phi_i^{'(l)} + } + + + \textcolor{orange}{ + \sum_l w_j^{(k)} \cdot \sigma_{-1}(\pi_i^{(j)}), s_i + \big\rangle + } + + + \textcolor{blue}{ + \sum_l \psi_l^{(k)} \cdot b_0^{'(l)} + } + + + \textcolor{red}{ + \sum_j w_j^{(k)} \cdot p_j + } +$$ + +without colors: + +$$ += +\sum_i + \big\langle + \underbrace{ + \sum_l \psi_l^{(k)} \cdot \phi_i^{'(l)} + + + \sum_l w_j^{(k)} \cdot \sigma_{-1}(\pi_i^{(j)}) + }_{\phi_i^{''(k)}}, + s_i + \big\rangle + + + \underbrace{ + \sum_l \psi_l^{(k)} \cdot b_0^{'(l)} + + + \sum_j w_j^{(k)} \cdot p_j + }_{b_0^{''(k)} ~\text{(constant term)}} + \\ + = + \sum_{i=1}^r \langle \textcolor{blue}{\phi_i^{''(k)}}, s_i \rangle - \textcolor{orange}{b_0^{''(k)}} +$$ + +So by putting it together with the $(i.A)$ part, we get the full $f''^{(k)}(s)$ aggregated constraint: + +$$ +\boxed{ +f''^{(k)}(s) = +\overbrace{ +\sum_{i,j=1}^r \textcolor{magenta}{a_{ij}^{''(k)}} \cdot \langle s_i, s_j \rangle +}^{(i.A)} ++ +\overbrace{ +\sum_{i=1}^r \langle \textcolor{blue}{\phi_i^{''(k)}}, s_i \rangle - \textcolor{orange}{b_0^{''(k)}} +}^{(i.B) + (ii.)} +} +$$ + +such that $ct(f''^{(k)}(s)) = 0$. + + +<br> + +Right now, $f''^{(k)}(s)$ is a constraint where only the constant term evaluates to zero (ie. it is a constraint from the $\mathcal{F}'$ family). However, for the next aggregation step, we need it to be a *completely vanishing* polynomial, that is, a function from the $\mathcal{F}$ family. + +To do so, the prover takes the integer constant $\textcolor{orange}{b_0^{''(k)}}$ and extends it into a full polynomial $\textcolor{orange}{b''^{(k)}}$ so that the newly formed function $f''^{(k)}(s)$ evaluates exactly to zero. The prover then sends these full $b''^{(k)}$ polynomials to the verifier. + +To ensure that the prover isn't cheating by just making up a polynomial that matches the equation, the verifier will check that the constant coefficient of the prover's $b''^{(k)}$ corresponds to the $b_0^{''(k)}$ value that the verifier computes from the public values and challenges, that is $b_0^{''(k)} \stackrel{?}{=} \langle w^{(k)}, p \rangle + \sum_{l=1}^L \psi_l^{(k)} b_0^{'(l)}$. + +<br> + +Recall that along these past steps, we've defined the new $a_{ij}^{''(k)},~ \phi_i^{''(k)},~ b''$ as + +$$\textcolor{magenta}{a_{ij}^{''(k)}} = \sum_{l=1}^L \psi_l^{(k)} \cdot \textcolor{magenta}{a_{ij}^{'(l)}}$$ + +$$\textcolor{blue}{\phi_i^{''(k)}} = \sum_{l=1}^L \psi_l^{(k)} \textcolor{blue}{\phi_i^{'(l)}} + \sum_{j=1}^{256} w_j^{(k)} \cdot \sigma_{-1}(\textcolor{blue}{\pi_i^{(j)}})$$ + +$$\textcolor{orange}{b''} = \sum_{i,j=1}^r \textcolor{magenta}{a_{ij}^{''(k)}} \cdot \langle s_i, s_j \rangle + \sum_{i=1}^r \langle \textcolor{blue}{\phi_i^{''(k)}}, s_i \rangle$$ + + +#### 3.2. 2nd aggregation +Till here, the 1st aggregation step has taken a random linear combination of the original constant-vanishing constraints and the projection constraints, and linearly combined them into Labrador-shaped constraints. + +Now the idea is to random linearly combine the original fully vanishing constraints $f(s)$, and separately the aggregated $f''(s)$ constraints that come from the original constant-vanishing constraints ($f'(s)$) and projection constraints ($f_{proj}(s)$). + +The verifier sends the challenges $\alpha \in^R R_q^K,~ \beta \in^R R_q^{\lceil 128/\log{q} \rceil}$. + +Then the prover will compute + +$$ +F(s) = \sum_{k=1}^K \alpha_k \cdot f^{(k)}(s) + \sum_{k=1}^{\lceil 128/ \log q \rceil} \beta_k \cdot f''^{(k)}\\ += +\sum_{k=1}^K \alpha_k \cdot \left( \sum_{i, j}^r a_{ij} \langle s_i, s_j \rangle + \sum_{i=1}^r \langle \phi_i, s_i \rangle + b \right) ++ +\sum_{k=1}^{\lceil 128/ \log q \rceil} \beta_k \cdot +\left( + \sum_{i,j}^r a_{ij}^{''(k)} \langle s_i, s_j \rangle + \sum_i^r \langle \phi_i^{''(k)}, s_i \rangle - b_0^{''(k)} +\right) +$$ + +distribute $\alpha_k$ and $\beta_k$ while keeping the witness values separated + +$$ += \sum_{i,j=1}^r +\underbrace{ + \left( \sum_{k=1}^K \alpha_k a_{ij}^{(k)} + + + \sum_{k=1}^{\lceil 128/\log q \rceil} \beta_k a_{ij}''^{(k)} + \right) +}_{a_{ij}} +\langle s_i, s_j \rangle + \sum_{i=1}^r \langle \underbrace{ \left( \sum_{k=1}^K \alpha_k \phi_i^{(k)} + \sum_{k=1}^{\lceil 128/\log q \rceil} \beta_k \phi_i''^{(k)} \right) }_{\phi_i}, s_i \rangle +\\ +- \underbrace{ \left( \sum_{k=1}^K \alpha_k b^{(k)} + \sum_{k=1}^{\lceil 128/\log q \rceil} \beta_k b''^{(k)} \right) }_{b} +$$ + +$$ +\boxed{ + F(s) = \sum_{i,j} a_{ij} \langle s_i, s_j \rangle + \sum_i \langle \phi_i, s_i \rangle - b +} +$$ + +again, a Labrador-shaped constraint, which in this case encodes the linear combinations of the original $f^{(k)}(s)$ constraints and the $f''^{(k)}(s)$ aggregated constraints, which for a valid witness it satisfies $F(s)=0$. + + +### 4. Amortization + +Now, at this point we have $r$ witness vectors, which in practice it is too much to deal with. Thus, instead of opening the individual inner commitments $t_i$ by sending all the $s_i$ and then checking the constraint $F(s)$, the prover opens a random linear combination + +$$z = \sum_{i=1}^r c_i \cdot s_i$$ + +and its respective committed version + +$$\overline{z} = \sum_{i=1}^r c_i \cdot t_i$$ + + +with challenge polynomials $c_i \in R_q$. + +Then the verifier checks + +$$A \cdot z = \sum_{i=1}^r c_i \cdot t_i$$ + +which should match since $t_i$ are the commitments to $s_i$ such that $t_i = A s_i$, so that + +$$A \cdot z = A \cdot \sum_i^r c_i s_i = \sum_i c_i \cdot (A s_i) = \sum_i c_i t_i$$ + + +And also checks that $||z|| \leq \gamma$ which comes from the instantiation parameters, to ensure small norm. + +<br> + +The following step is verifying that $F(s)=0$, but recall that $F(s) = \sum_{i,j} a_{ij} \langle s_i, s_j \rangle + \sum_i \langle \phi_i, s_i \rangle - b$, and the verifier does not know the values of $s_i$ hence can not compute it. + +To do the check, both prover and verifier use what the paper calls *garbage polynomials*, but intuitively we can think about them as some auxiliary polynomials to do this final check. + +Let the *garbage polynomials* be + +$$g_{ij}= \langle s_i, s_j \rangle,~~~~ h_{ij} = \frac{1}{2} ( \langle \phi_i, s_j \rangle + \langle \phi_j, s_i \rangle )$$ + +for $i,j \in [r]$. + + +The prover will commit to those polynomials early on, before the challenges $c_i$ are generated, so that the prover is not able to select the garbage polynomials based on knowledge of the challenges, which would allow the prover to cheat. + +> Observation: the paper does the commitment to the "garbage" polynomials $g$ and $h$ differently at the protocol conceptual explanation of page 14 and at the protocol description of Figure 2 at page 17. +> +> The conceptual explanation describes the second outer commitment $u_2$ as +> +> $$u_2 = C g + D h$$ +> +> but later at the protocol description, the commitment to $g$ is moved to the outer commitment $u_1$, leaving only the commitment to $h$ at $u_2$. So the outer commitments end up being +> +> $$u_1 = B \cdot t + C \cdot g = \sum_{i=1}^r \sum_{k=0}^{t_1-1} B_{ik} t_i^{(k)} + \sum_{i \leq j} \sum_{k=0}^{t_2-1} C_{ijk} g_{ij}^{(k)}$$ +> +> $$u_2 = D \cdot h = \sum_{i \leq j} \sum_{k=0}^{t_1-1} D_{ijk} h_{ij}^{(k)}$$ +> +> This is done because the $g_{ij}$ polynomials are independent of all challenges, thus they can be computed at the very beginning of the protocol and included in $u_1$, allowing a slightly better security proof. + +Then, the verifier will do the following checks: + +$$(i.)~~~ <z, z> = \sum_{i,j=1}^r g_{ij} c_i c_j$$ + +$$(ii.)~~~ \sum_{i=1}^r \langle \phi_i, z \rangle \cdot c_i = \sum_{i,j=1}^r h_{ij} c_i c_j$$ + +$$(iii.)~~~ \sum_{i,j=1}^r a_{ij} g_{ij} + \sum_{i=1}^r h_{ii} -b = 0$$ + +Where $(i.)$ and $(ii.)$ are to check the correctness of $g_{ij}$ and $h_{ij}$ respectively, and $(iii.)$ is checking the $F(s)=0$ equality through the garbage polynomials $g_{ij}, h_{ij}$. + + + +### 5. Recursion + +At this point, the prover has sent the *proof* which consists of $(z, t, h, g)$, and the norm checks get grouped into a single norm check $||z||^2 + ||t||^2 + ||g||^2 + ||h||^2 \leq \gamma^2 + \gamma_1^2 + \gamma_2^2$. + + +The idea now, is to recurse, encoding the protocol checks into new Labrador constraints, and then performing again the protocol. + +First, we simplify the target relation, let $v = t || g || h \in R_q^m$, so that the norm check becomes + +$$||z||^2 + ||v||^2 \leq \gamma^2 + \gamma_1^2 + \gamma_2^2$$ + + +Now we decompose the witness $z = z^{(0)} + b \cdot z^{(1)}$, with $z^{(0)}, z^{(1)} \in R_q^n$, to keep the coefficients with small norm. + +Then, the quadratic dot product becomes + +$$\langle z, z \rangle = \langle z^{(0)}, z^{(0)} \rangle + 2b \langle z^{(0)}, z^{(1)} \rangle + b^2 \langle z^{(1)}, z^{(1)} \rangle$$ + +and we can do the norm check as $||z^{(0)}||^2 + ||z^{(1)}||^2 + ||v||^2 \leq \frac{2}{b^2} \gamma^2 + \gamma_1^2 + \gamma_2^2 = (\beta')^2$, which implies $||z|| = ||z^{(0)} + b z^{(1)} || \leq (1+b)\beta'$. + + +Write the vectors $z^{(0)}, z^{(1)}, v$ as concatenations of vectors: + +$$ +z^{(0)} = s_1 || s_2 || \ldots || s_{\nu}\\ +z^{(1)} = s_{\nu+1} || \ldots || s_{2\nu}\\ +v = s_{2\nu+1} || \ldots || s_{2\nu+ \mu} +$$ + +that is $r' = 2 \nu + \mu$ vectors in total. + + +Observe that the final verifications from the previous section, the checks $(i.),~ (ii.),~ (iii.)$ can be done as dot product constraints (ie. Labrador constraints): + +#### encoding check (i.) + +from the check $(i.)$, $\langle z, z \rangle = \sum_{i,j=1}^r g_{ij} \cdot c_i \cdot c_j$, since we've decomposed $z = z^{(0)} + b z^{(1)}$ then we have + +$$ +\langle z, z \rangle = \langle z^{(0)} + b z^{(1)}, z^{(0)} + b z^{(1)} \rangle\\ += \langle z^{(0)}, z^{(0)} \rangle + 2b \langle z^{(0)}, z^{(1)} \rangle + b^2 \langle z^{(1)}, z^{(1)} \rangle +$$ + +that is, three separate dot products. Then we place + +$$ +z^{(0)} ~\text{into}~ s_1', \ldots, s'_{\nu}\\ +z^{(1)} ~\text{into}~ s_{\nu+1}', \ldots, s'_{2\nu} +$$ + +so that + +$$\langle z^{(0)}, z^{(0)} \rangle = \langle s_1', s_1' \rangle + \langle s_2', s_2' \rangle + \ldots + \langle s_{\nu}', s_{\nu}' \rangle$$ + +and similarly with $\langle z^{(1)}, z^{(1)} \rangle$ + +$$\langle z^{(1)}, z^{(1)} \rangle = \langle s_{\nu+1}', s_{\nu+1}' \rangle + \ldots + \langle s_{2\nu}', s_{2\nu}' \rangle$$ + +and + +$$\langle z^{(0)}, z^{(1)} \rangle = \langle s_1', s_{\nu+1}' \rangle + \langle s_2', s_{\nu+2}' \rangle + \ldots + \langle s_{\nu}', s_{2\nu}' \rangle$$ + + +So that now, it is a matter of setting appropriate $a_{ij}$ values to reshape the original check into a Labrador-shaped constraint: + +- from $1$ to $\nu$: if $i=j$, set $a_{ij}=1,~ 0$ otherwise, +- from $\nu+1$ to $2\nu$: if $i=j$, set $a_{ij}=b^2,~ 0$ otherwise, +- from $2\nu$ to $2\nu+\mu$: if $j=i+\nu$, set $a_{ij}=b$. And since the $(a_{ij})$ matrix is symmetric, then $a_{ji}=b$, which implies that when both combined we get $2b$. + +Therefore, we can perform the $\langle z, z \rangle$ with the decompositions $z^{(0)},~ z^{(1)}$. + + +#### encoding check (ii.) + +The next check is $(ii.)$, $\sum_{i=1}^r \langle \phi_i, z \rangle \cdot c_i = \sum_{i, j = 1}^r h_{ij} c_i c_j$. Reshape it into + +$$\sum_{i=1}^r \langle \phi_i \cdot c_i, z \rangle - \sum_{i, j = 1}^r (c_i c_j)\cdot h_{ij} = 0$$ + +and, recall that we set + +$$ +z^{(0)} = s_1 || s_2 || \ldots || s_{\nu}\\ +z^{(1)} = s_{\nu+1} || \ldots || s_{2\nu}\\ +v = s_{2\nu+1} || \ldots || s_{2\nu+ \mu} +$$ + +also recall that $v=t || g || h$. Thus, similarly as for the check $(i.)$, + +- from $1$ to $\nu$: $\phi_i^{(k)} = \sum \phi_i c_i$, +- from $\nu+1$ to $2\nu$: $\phi_i^{(k)} = \sum \phi_i c_i \cdot b$, +- from $2\nu$ to $2\nu+\mu$: $\phi_i^{(k)} = - (c_i c_j)$. + + +#### encoding check (iii.) + +For the check $(iii.)$, $\sum a_{ij} g_{ij} + \sum h_{ii} - b =0$, recall again that $g_{ij}, h_{ij}$ are inside $v$, so it is just a matter of setting $a_{ij}=a_{ij}$ and $a_{ij}=1$ at their corresponding positions, and $b^{(k)}=b$. + +With this, we can encode the checks $(i.),~ (ii.),~ (iii.)$ into the Labraddor constraint shape + +$$g^{(k)}(s') = \sum_{i,j=1}^{r'} a_{ij}^{(k)} \langle s_i', s_j' \rangle + \sum_i^{r'} \langle \phi_i^{(k)}, s_i' \rangle - b^{(k)} = 0$$ + +with $s' := z^{(0)} || z^{(1)} || v$, where $v=t ||g||h$. + + +#### encoding commitments checks + +Similarly we encode he commitments checks. We had $Az = \sum c_i t_i$, which we can do in a dot product constraint as + +$$\langle A_{row}, z \rangle - \sum c_i \cdot ~ (t_i)_{row} = 0$$ + +which in a similar fashion as the checks $(i.), (ii.), (iii.)$, we can reshape it into a Labrador constraint, since the inner commitments $t_i$ are placed inside $v$. + +<br> + +And for the outer commitments $u_1,~ u_2$, as we saw in the section [[4. Amortization](#4.-amortization)] we want + +$$ +u_1 = B \cdot t + C \cdot g\\ +u_2 = D \cdot h +$$ + +which we can encode as + +$$ +\langle B_{row}, t \rangle + \langle C_{row}, g \rangle = (u_1)_{row}\\ +\langle D_{row}, h \rangle = (u_2)_{row} +$$ + +which, as before, we can also reshape into the dot product Labrador constraints. + +<br> + +The paper mentions that [6 or 7](https://giphy.com/gifs/67-six-seven-john-chungus-08uBcURaMq6vA93TGc) iterations of the recursive protocol give the best results, where at each recursion the witness shrinks down from $|s^{(i)}|$ into $|s^{(i)}|^{2/3}$. + + +## Conclusions + +It has been very interesting to see how a lattice-based SNARK works, its machinery and tricks. +Also, Labrador is of special interest because it was a big step forward on lattice-based SNARKs since it has good proof sizes; asymptotically of $\mathcal{O}(\log n)$, and for $2^{16}$ R1CS constraints, the paper reports proof sizes of 54 kbytes. + +<div style="text-align:center;"> +<a target="_blank" href="img/posts/labrador/proof-sizes-table.png"> + <img style="width:80%" + src="img/posts/labrador/proof-sizes-table.png"></img> +</a> +<br> +<i style="color:#374072">table from the Labrador paper</i> +</div> + +<br> + +An interesting thing in Labrador is the capacity to mix binary and non-binary R1CS constraints, which get reshaped into the $f(s)$ and $f'(s)$ Labrador constraints. This is nice, because for many zk-circuits we want to prove binary-based operations (such as traditional hashes), while doing arithmetic logic, and in Labrador we can mix both in parallel. + +Other post-quantum safe SNARK schemes are based on hashes (eg. STARKs), widely used in the industry; but what seems interesting of the lattice-based cryptography is that lattices have more algebraic structure, and while a bit less developed in production than hash based proofs, they have interesting potential. + +I've enjoyed studying Labrador, and definitely I'll keep exploring other lattice-based schemes while also going a bit down to the math behind them. + +
@@ -0,0 +1,5 @@
+### Notes on Labrador, a lattice-based SNARK scheme + +Overview of how the (post-quantum) lattice-based SNARK scheme "Labrador" works. + +*2026-06-11*
@@ -0,0 +1,151 @@
+# Tools +*2026-06-08* + +A non-exhaustive overview of tools I use daily. + +*I had this post half-written for a couple of years, finally I've put the time to 'complete' it enough to publish it. See it as a kind of email to a friend sharing my ways of approaching everyday tools.* + +## in general +- Always be willing to **customize and optimmize** the tools you use +- Usually closed source tools come with 'defaults' that may 'suit the majority' (or that it's acceptable enough), but does not mean that you have to conform with it. This is another reason (from many) to use freesoftware or if not, opensource. +- when willing to use a tool, if it is closed source, always check **free software / opensource** alternatives, usually they exist and have an active community around them +- prefer **terminal** tools over GUI tools. In this way, working remotely inside a server feels the same as working locally in your laptop, just the terminal and the tool in there +- prefer offline tools than online tools +- In physical objects optimize for **portability**; also usually is good to not centralize many functions in a single object + +## computer +- in general + - maximize keyboard shortcuts, and minimize keyboard surface + - I use a **40% keyboard, [corne](https://github.com/foostan/crkbd)** keyboard + - the idea is that instead of moving your fingers to the keys, you want to move the keys under your fingers (eg. for numbers & symbols, F1 keys, etc) + <div style="text-align:center;"> + <a target="_blank" href="img/posts/tools/corne.png"><img style="width:60%" src="img/posts/tools/corne.png"></img></a> + <br> + <i style="font-size:75%">the Corne keyboard I use, self-assembled and soldered</i> + </div> + - **minimize mouse** usage: + - using the mouse means lifting the fingers from the keyboard + - mouse limits an entire hand to only click buttons, you have many more fingers to use + - so, naturally, **[vim](https://www.vim.org) & [neovim](https://neovim.io)** as code and text editors + - also this has the benefit that working in my laptop or working in the server feels the same + - window manager: till last year I was using [i3](https://i3wm.org), now I'm using [sway](https://swaywm.org). The main idea is to be able to move quick across windows and panels, while avoiding touching the mouse. + - automate: + - this is, bash & python scripts are very useful; for almost every task that has more than 3 iterations of the same steps + - OS: of course, Linux. For the past 3 years I've been using [NixOS](https://nixos.org), attracted by it's imperative philosophy: you have a set of config files, format a laptop, load your config, and you have all your software with your configurations ready to use (ie. not manually {apt/pacman}-installing each software package) + +This is not only for code files; for example, when working on a $LaTeX$ document, edit it in neovim with [vimtex](https://github.com/lervag/vimtex), so that at save it re-renders the pdf and reloads it in [zathura](https://pwmt.org/projects/zathura/). Additionally, this works great while being offline, compared to mainstream options such as overleaf. +Similarly, when working on a markdown document, use [md-live-server](https://github.com/arnaucube/md-live-server) locally, splitting the screen between the terminal for editing and [qutebrowser](https://qutebrowser.org/), so that no mouse is needed any time. + +<div style="text-align:center;"> +<a target="_blank" href="img/posts/tools/desk-setup.jpg"><img style="width:75%" src="img/posts/tools/desk-setup.jpg"></img></a> +<br> +<i style="font-size:75%">a bit old (~2023) desk setup, with my 'old' Thinkpad Carbon X1</i> +</div> + +#### devices + +<!-- TODO picture corne + screens --> + +- At home: + - I build a ~850 euros server (year 2024), with AMD Ryzen 9 5900XT 3.3 GHz 16-Core, 64 GB of RAM. + - to work, I usually [mosh](https://mosh.org/) (ssh) into it, then open a [tmux](https://github.com/tmux/tmux) and [neovim](https://neovim.io/), and work there, so that I can use any (lightweight) laptop while remotely running everything in the powerful-enough server. + Another point for using vim/neovim as default code editor, is that when working remotely on a server, the setup remains the same. Another perspective is that you treat your laptop as a server for those kinds of tasks, so that when you work remotely on a server there is almost no difference. + <div style="text-align:center;"> + <a target="_blank" href="img/posts/tools/server.jpg"><img style="width:60%" src="img/posts/tools/server.jpg"></img></a> + </div> +- Outside home: + - for many years I used a Thinkpad x270 of 12.5" screen, then a Thinkpad Carbon X1 of 13", but then few years ago I started using smaller laptops of 10" + - 10" laptop: I was using a 2nd hand [Surface Go 2](https://support.microsoft.com/en-us/surface/surface-go-2-specs-and-features-0fc6a657-2851-484f-6f82-bd3c589ed92c) that I bought for 200 euros, but recently I've replaced it with a [Chuwi Minibook X](https://www.chuwi.com/product/items/chuwi-minibook-x-n150.html) which costs 300 euros (brand new). Both laptops have 10". + - 8" tablet + notebook; or if in the sun, instead of the tablet, a eink reader (if so, a [hacked one](https://koreader.rocks)) + +<div style="text-align:center;"> +<a target="_blank" href="img/posts/tools/big-slingbag-contents.jpg"><img style="width:60%;" src="img/posts/tools/big-slingbag-contents.jpg"></img></a> +<br> +<i style="font-size:75%">8" tablet and 10" laptop</i> +</div> + +A benefit of using lightweight laptops combined with a remote *powerful* server, is that the battery lasts longer than doing everything in the laptop, since you're not doing many computations locally in the laptop! + + +## services +Useful to have some raspberrypi/minipc at home with some services to make your life more comfortable (for example, with: [docker compose configuration file](https://github.com/arnaucube/configs/blob/master/server-dockers/docker-compose.yml)). + +For about 2 years I've been running multiple media services in a raspberry pi 5, recently replaced it with a Beelink S13 NUC, where I can support many users of non-technical friends. +<div style="text-align:center;"> +<a target="_blank" href="img/posts/tools/media-setup.png"><img style="width:80%;" src="img/posts/tools/media-setup.png"></img></a> +</div> + +Useful services: + +- [qbittorrent-nox](https://github.com/qbittorrent/qBittorrent): the server version of qbittorrent, managed through a web UI +- [Jellyfin](https://jellyfin.org/): stream all the downloaded content (shows, movies, music) to your devices (tv, tablet, phone, laptop, etc) +- [slskd](https://github.com/slskd/slskd/): server client for the [Soulseek network](https://en.wikipedia.org/wiki/Soulseek), in order to download individual songs or albums that are not found through torrent + +Highly recommended: with the previous services, you have all the minimal setup to enjoy media at home; but there is more dimension to it; the various projects within the *arr family, eg: +[sonarr](https://sonarr.tv/), [radarr](https://radarr.video/), [bazarr](https://www.bazarr.media/), [prawlarr](https://prowlarr.com/), etc. + +Then you can tie all those services through a main page with [homepage](https://gethomepage.dev), so that all links are accessible with a single click. + + +Also, using browser [adblockers](https://github.com/gorhill/uBlock) has become a must over the years, additionally having a [pi-hole](https://pi-hole.net) connected to your router filters out adds in general on your home network (on all devices!), while also filtering out a big portion of trackers. + + +## AFK / on the go +*(AFK: away from keyboard, when I go outside of home)* + +- sometimes I bring a slim-foldable keyboard +- during the day (if I don't require a laptop): slingbag with + - notebook and fountain pens + - eink reader (ie. hacked kindle) or a 8" tablet + + +<div style="text-align:center;"> +<a target="_blank" href="img/posts/tools/small-slingbag-contents.jpg"><img style="width:60%;" src="img/posts/tools/small-slingbag-contents.jpg"></img></a> +</div> + +<br> +<div style="text-align:center;"> +<a target="_blank" href="img/posts/tools/notebook-kindle-outside.jpg"><img style="width:60%" src="img/posts/tools/notebook-kindle-outside.jpg"></img></a> + <br> + <i style="font-size:75%">can just randomly pop out of home and just sit anywhere to keep studying</i> +</div> + +- travel: [30L backpack](https://www.patagonia.com/product/refugio-daypack-30-liters/47928.html) with + - [notebook](#pens-and notebooks) + - eink reader / tablet 8" + - laptop (see [laptop](#devices) section) + - clothes for 7 days, on the 6th day you do laundry, so you can travel long periods of time, virtually indefinite + +<div style="text-align:center;"> +<a target="_blank" href="img/posts/tools/backpack.jpg"><img style="width:80%;" src="img/posts/tools/backpack.jpg"></img></a> +</div> + +With this backpack setup I've traveled across the world (6 continents), with stays ranging from few days to 2 months, all with same backpack setup. + +<br> + +Notes about the picture above: this picture is from >2 years ago, some changes I did since then: + +- laptop: the Surface Go 2 has been replaced by the Chuwi Minibook X (both of 10"), see [laptop](#devices) section for more details +- tablet: the 10" tablet has been replaced by a 8" tablet +- emulator: the [Retroid Pocket 4 Pro](https://www.goretroid.com/products/retroid-pocket-4-handheld) has been replaced by the [Myioo Mini Plus](https://retrogamecorps.com/2022/05/15/miyoo-mini-v2-guide/) (less powerful, so less console options, but smaller in the backpack; ideal for GBA games!) + + +### pens and notebooks +In the recent years I've discovered the joy of using [fountain pens](https://www.reddit.com/r/fountainpens). They are not essential, but is one of those little joys of life. + +I've had the opportunity to try many different models, both from buying some of them and also from trying them in-store (in Seoul there is [a very nice shop](https://maps.app.goo.gl/quDc2oAfb8Kn6Rug7) where for a small fee you have all the time you need to roam through the shop trying more than 200 pens!) + +<div style="text-align:center;"> +<a target="_blank" href="img/posts/tools/penshop-bestpen.png"><img style="width:80%;" src="img/posts/tools/penshop-bestpen.png"></img></a> + <br> + <i style="font-size:75%">In purple t-shirt, enjoying 2h of trying many many pens at <a href="https://maps.app.goo.gl/quDc2oAfb8Kn6Rug7" target="_blank">BestPen</a>, Seoul (South Korea).</i> +</div> + + +Beware, once you enter the fountain pens rabbit hole, the next worry is the paper, hence a natural following fixation are the notebooks to use with the fountain pens. Not all papers are good enough for fountain pens, but there are [some](https://md-product.com/en/collections/md-notebook) very [good](https://www.clairefontaine.com/gb/3525-10811-carnet-brochure-age-bag-795401c.html#/1182-rulings-o_uni/1756-dimension-145_x_21_cm/1971-couleur_produit-gris) paper [quality](https://www.youtube.com/watch?v=c_UvVavl-eE) notebooks. Similarly inks are another [rabbit hole](https://mountainofink.com). + + +<div style="text-align:center;"> +<a target="_blank" href="img/notebooks.jpg"><img style="width:80%;" src="img/notebooks.jpg"></img></a> +</div>
@@ -0,0 +1,4 @@
+### Tools +A non-exhaustive overview of tools I use daily. + +*2026-06-08*
@@ -164,11 +164,20 @@ </div>
<br><br> <div style="margin:30px;"> + Cryptography engineer with experience implementing cryptography protocols (zkSNARKs, hashes, ciphers, folding schemes, zk-circuits, ...). + On my free time I enjoy self-studying <a target="_blank" href="/blog/notes.html">some math topics</a>. + + <br><br><br> Some of the projects I've enjoyed working on over these past years:<br> <ul> <li> <i>Folding schemes</i> implementations such as Nova, HyperNova, ProtoGalaxy, CycleFold in the <a href="https://github.com/privacy-ethereum/sonobe" target="_blank">Sonobe</a> library + </li> + <li> + Elliptic curve <i>signature schemes</i> such as + <a href="https://github.com/arnaucube/ring-signatures-rs" target="_blank">bLSAG ring signatures</a> + and <a href="https://github.com/aragonzkresearch/ark-ec-blind-signatures" target="_blank">blind signatures</a> </li> <li> <i>Commitment schemes</i> such as@@ -177,11 +186,6 @@ <a href="https://github.com/arnaucube/fri-commitment" target="_blank">FRI</a>,
<a href="https://github.com/arnaucube/ipa-rs" target="_blank">IPA</a> </li> <li> - Elliptic curve <i>signature schemes</i> such as - <a href="https://github.com/arnaucube/ring-signatures-rs" target="_blank">bLSAG ring signatures</a> - and <a href="https://github.com/aragonzkresearch/ark-ec-blind-signatures" target="_blank">blind signatures</a> - </li> - <li> other <i>cryptographic primitives</i> such as <a href="https://github.com/arnaucube/go-snark-study" target="_blank">go-snark</a> lib, <a href="https://github.com/vocdoni/arbo" target="_blank">arbo</a> (CPU-parallelized MerkleTree),@@ -191,10 +195,11 @@ <li>
zk <i>circuits</i> such as <a href="https://github.com/vocdoni/keccak256-circom" target="_blank">keccak256-circom</a>, -<a href="https://github.com/vocdoni/zk-franchise-proof-circuit/tree/9d25884142f0018746326d5f8c3ac714fe844edd" target="_blank">census-proof</a> +<a href="https://github.com/vocdoni/zk-franchise-proof-circuit/tree/9d25884142f0018746326d5f8c3ac714fe844edd" target="_blank">census-proof</a>, + and plonky2's <a href="https://github.com/0xPARC/plonky2-crypto-gadgets" target="_blank">aes-gcm cipher & poseidon-cipher</a> </li> <li> - some voting system <i>designs & implementations</i> <a + some voting system (rollup style) <i>designs & implementations</i> <a href="https://github.com/aragonzkresearch/research/blob/main/ovote/ovote.pdf" target="_blank">OVOTE</a> and <a href="https://github.com/aragonzkresearch/research/blob/main/blind-ovote/blind-ovote.pdf"@@ -226,7 +231,7 @@ <div class="column" style="font-size:100%;">
<h2>Recent <b>Work</b>:</h2> <div class="recent-work-box"> - <h4 class="gray">2023 - now</h4> + <h4 class="gray">2023 - 2026</h4> <!-- 2023 --> <h3 style="display:inline;"><a href="https://research.0xparc.org" target="_blank" class="recentwork">0xPARC Research</a> -</h3> <i style="display:inline;">Applied cryptography research</i>@@ -234,7 +239,7 @@ <br>
Research and implementations of folding schemes. <ul> <li> - <span class="gray">privacy-ethereum/</span><a href="https://github.com/privacy-ethereum/sonobe" target="_blank">sonobe</a>: folding schemes modular library. Implemented Nova, CycleFold, HyperNova and part of ProtoGalaxy. Also designed the onchain verification approach which allowed Sonobe to be the first folding schemes library to achieve verifying folding proofs in Ethereum. + <span class="gray">privacy-ethereum/</span><a href="https://github.com/privacy-ethereum/sonobe" target="_blank">sonobe</a>: folding schemes modular library. Implemented Nova, CycleFold, HyperNova and ProtoGalaxy. Also designed the onchain verification approach which allowed Sonobe to be the first folding schemes library to achieve verifying folding proofs in Ethereum. <ul><li><a href="https://github.com/privacy-ethereum/sonobe" target="_blank">sonobe</a>, <a href="https://privacy-ethereum.github.io/sonobe-docs" target="_blank">sonobe-docs</a></li></ul> </li> <li>@@ -243,6 +248,13 @@ </li>
<li> <span class="gray">privacy-scaling-explorations/</span><a href="https://github.com/privacy-scaling-explorations/multifolding-poc" target="_blank">multifolding-poc</a>: HyperNova folding scheme proof of concept implementation </li> + <li> + <span class="gray">0xPARC/</span><a href="https://github.com/0xPARC/pod2" target="_blank">pod2</a>: helped with low level primitives and related circuits such as merkletrees and recursion framework. + </li> + <li> + <span class="gray">0xPARC/</span><a href="https://github.com/0xPARC/plonky2-crypto-gadgets" target="_blank">plonky2-crypto-gadgets</a>: plonky2 circuits for AES-GCM encryption, Poseidon cipher. + </li> + </ul> </div> <!-- <hr style="width:40%;margin-left:0;margin-bottom:30px;margin-top:20px;"> -->@@ -337,7 +349,7 @@ <li>
<span class="gray">iden3/</span><a href="https://github.com/iden3/go-iden3-core" target="_blank">go-iden3-core</a>: core implementation of the iden3 system </li> <li> - <span class="gray">iden3/</span><a href="https://github.com/iden3/go-circom-prover-verifier" target="_blank">go-circom-prover-verifier</a>: implementation of the Groth16 zkSNARK Prover and Verifier compatible with Circom + <span class="gray">iden3/</span><a href="https://github.com/iden3/go-circom-prover-verifier" target="_blank">go-circom-prover-verifier</a>: Go lang implementation of the Groth16 zkSNARK Prover and Verifier compatible with Circom </li> <li> <span class="gray">iden3/</span><a href="https://github.com/iden3/circuits" target="_blank">circuits</a>: zkSNARK circuits used by the iden3 core protocol@@ -410,7 +422,7 @@ <li>
<a href="https://github.com/arnaucube/miksi-core" target="_blank">miksi</a>: Ethereum zk-coin-mixer where all the computation & constructions are done offchain and then proved inside a zkSNARK to the smart-contract. <i><a href="https://arnaucube.github.io/miksi-app/" target="_blank">(live demo on testnet)</a></i> </li> <li> - <a href="https://github.com/arnaucube/eth-kzg-ceremony-alt" target="_blank">eth-kzg-ceremony-alt</a>: Alternative implementation of the contributor-client for the Ethereum KZG Trusted Setup Ceremony + <a href="https://github.com/arnaucube/eth-kzg-ceremony-alt" target="_blank">eth-kzg-ceremony-alt</a>: Alternative implementation of the contributor-client for the Ethereum KZG Trusted Setup Ceremony. I used this code to do my contribution to Ethereum's KZG ceremony. </li> </ul>