<?xml version="1.0" encoding="utf-8"?><rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>thork.net</title><link>https://thork.net/</link><description>Thor's personal site and blog</description><generator>Hugo 0.92.2 https://gohugo.io/</generator><language>en</language><managingEditor>thorck@protonmail.com (Thor)</managingEditor><webMaster>thorck@protonmail.com (Thor)</webMaster><lastBuildDate>Fri, 24 Jan 2025 19:39:17 +0000</lastBuildDate><atom:link rel="self" type="application/rss+xml" href="https://thork.net/rss.xml"/><item><title>designer babies no one asked for: protein cryptography paper speedrun</title><link>https://thork.net/posts/2025-01-23-protein-cryptography-paper/</link><guid isPermaLink="true">https://thork.net/posts/2025-01-23-protein-cryptography-paper/</guid><pubDate>Thu, 23 Jan 2025 00:00:00 +0000</pubDate><author>thorck@protonmail.com (Thor)</author><description>&lt;h1 id="designer-babies-no-one-asked-for-protein-cryptography-paper-speedrun">designer babies no one asked for: protein cryptography paper speedrun&lt;/h1>
&lt;p>cryptographers are finally getting around to answering our deepest, most pressing questions&lt;/p>
&lt;blockquote>
&lt;p>but what if we used babies as encrypted storage format?&lt;sup id="fnref:1">&lt;a href="https://thork.net/posts/2025-01-23-protein-cryptography-paper/#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>&lt;/p>
&lt;/blockquote>
&lt;p>i surely cannot imagine encoding dad-jokes in my future childrens' proteins.&lt;/p>
&lt;p>&lt;img src="https://thork.net/photos/2025-01-23-protein/baby-dna-cryptography-protein.jpeg" alt="">&lt;/p>
&lt;p>but no one is actually talking about using babies as a substrate for cryptography (yet). a &lt;a href="https://eprint.iacr.org/2025/089">paper posted to IACR this week&lt;/a> explores the idea of using proteins as an encrypted data storage format. we&amp;rsquo;re going to speedrun explain that paper.&lt;/p>
&lt;p>&lt;em>thanks to &lt;a href="https://keonigandall.com/">Keoni Gandall&lt;/a> for comments.&lt;/em>&lt;/p>
&lt;h2 id="wat">wat&lt;/h2>
&lt;p>okay so three big questions i had going in:&lt;/p>
&lt;ul>
&lt;li>is there some fancy steganographic key hiding that we could do with biology that we could not otherwise do?
&lt;ul>
&lt;li>for example, there exists this &lt;a href="https://arxiv.org/abs/1004.3328">quantum protocol&lt;/a> for tamper-proof one-time-pad distribution, by pre-distributing qubits in superposition, then collapsing these to one-time-pad keys between sender and receiver&lt;sup id="fnref:2">&lt;a href="https://thork.net/posts/2025-01-23-protein-cryptography-paper/#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>how efficiently could we possibly store (possibly encrypted) data in proteins, what&amp;rsquo;s our bit-density in amino-acids, and do we need to pay out the ass for encoding error-rates, and maybe error-correcting codes&lt;/li>
&lt;li>how far from reality are we really here: how expensive in time and dollars would it be to encode and decode&lt;/li>
&lt;/ul>
&lt;p>it turns out the answer is yes.&lt;/p>
&lt;p>the paper only speaks to the first question, which is summarized in the next section; the the rest of this post is my rambling and back of the envelope math.&lt;/p>
&lt;h2 id="fancy-steganography">fancy steganography&lt;/h2>
&lt;p>the only known way to isolate a protein mixed with other proteins, is to purify the target protein using a &lt;a href="https://en.wikipedia.org/wiki/Monoclonal_antibody">monoclonal antibody&lt;/a> (mAb), before sequencing it&lt;sup id="fnref:3">&lt;a href="https://thork.net/posts/2025-01-23-protein-cryptography-paper/#fn:3" class="footnote-ref" role="doc-noteref">3&lt;/a>&lt;/sup>. the adversary must know the correct mAb in advance to isolate the protein.&lt;sup id="fnref:4">&lt;a href="https://thork.net/posts/2025-01-23-protein-cryptography-paper/#fn:4" class="footnote-ref" role="doc-noteref">4&lt;/a>&lt;/sup>&lt;/p>
&lt;p>that is, sequencing (encryption) a protein is easy, but synthesis (decryption) is hard and admits only one destructive attempt.&lt;/p>
&lt;p>the authors further note that there is no known way to clone protein &amp;ldquo;in small amount&amp;rdquo;, which if possible, would allow for multiple synthesis attempts.&lt;/p>
&lt;p>thus we get a basic idea why proteins might be well suited for cryptography; they have some convenient properties:&lt;/p>
&lt;ul>
&lt;li>uncloneability - can&amp;rsquo;t clone the ciphertext payload&lt;/li>
&lt;li>destructive sequencing - attempts to decrypt destroy the payload&lt;/li>
&lt;/ul>
&lt;p>this gives us a basic idea for a symmetric encryption scheme: to encrypt: encode a message into a protein and tag the protein with a header that can be isolated by an mAb held by the receiver; then mix the message protein with other &amp;ldquo;decoy&amp;rdquo; proteins of similar length and composition.&lt;br>
&lt;img src="https://thork.net/photos/2025-01-23-protein/encrypt-protein.jpeg" alt="">&lt;/p>
&lt;p>to decrypt, isolate the protein with the corresponding mAb and synthesize it with mass spectrometry.
&lt;img src="https://thork.net/photos/2025-01-23-protein/decrypt-protein.jpeg" alt="">&lt;/p>
&lt;p>the authors term this the Miftah symmetric encryption scheme. we now set about to answer the question, but how impractical is this really?&lt;/p>
&lt;h2 id="how-efficiently-can-we-pack-data-in-amino-acids">how efficiently can we pack data in amino acids&lt;/h2>
&lt;p>let&amp;rsquo;s first not think about encryption at all, and just try to pack as much data as we can. proteins are made of strings of amino acids. there are 20 natural amino acids, upper bounding us to abt 4.32 bits of information per amino acid. &lt;a href="https://en.wikipedia.org/wiki/Mass_spectrometry">mass spectrometry&lt;/a> can be used to read the string of amino acids, destroying the string in the process.&lt;/p>
&lt;p>we could try to encode arbitrary proteins from amino acids, but most would be unviable. claude estimates that of length 50 amino acid chains, only 1 in $10^{15}$ would be structurally stable, which still gives us about $4.32*50-\log(10^{15})= 166$ bits in a length 50 protein.&lt;/p>
&lt;p>if we take 160 bits in a length 50 protein at face value, we can try to compute for naive density of data.&lt;/p>
&lt;p>mass spectrometry needs between 10^12 and 10^15 identical samples for reliable sequencing. amino acids weigh ~110 daltons. 50 AAs = 5500 daltons = 9.13 × 10^-21 grams. we get 160 bits / 9.13 × 10^-21 grams ≈ 1.75 × 10^22 bits/gram.&lt;/p>
&lt;p>protein headers for the mAb to grab would be maybe 10-20 amino acids, about 25% overhead for headers, and we might also want to extend our proteins by another 25% to allow for error correcting codes.&lt;/p>
&lt;p>supposing optimistically that we only need 10^12 replicas for mass spectrometry, we arrive at around 10^9 bits/gram, which would still be pretty ridiculously good, given that modern hard drives are on the order of 1 × 10^12 bits/gram for naive data storage.&lt;/p>
&lt;p>for encryption, the paper doesn&amp;rsquo;t explore how many decoys would be necessary, but maybe about 100 decoy proteins to every one message protein would be sufficient. that further reduces our information density by another 10^2.&lt;/p>
&lt;p>that brings our final estimate to around 1Mb/g for protein encrypted data, which is still pretty neat.&lt;/p>
&lt;p>so, uh when should we expect this to become viable at cost?&lt;/p>
&lt;h2 id="but-how-expensive-would-it-be-to-embed-a-dad-joke-in-the-proteins-of-my-children">but how expensive would it be to embed a dad joke in the proteins of my children&lt;/h2>
&lt;p>for encryption:&lt;/p>
&lt;ul>
&lt;li>protein synthesis ~$1k, multiplied by 100 decoys for security&lt;/li>
&lt;li>add ~$20k for sequencing a custom mAb&lt;/li>
&lt;li>this all would take on the order of 3 weeks to synthesize the proteins, and 3 months to produce the antibodies&lt;/li>
&lt;/ul>
&lt;p>but decryption is relatively cheap! on the order of \$500 in 1-2 days for protein purification and another $200 in a few hours for mass spectrometry.&lt;/p>
&lt;p>so, pretty practical. the future is here.&lt;/p>
&lt;section class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1" role="doc-endnote">
&lt;p>no one is asking this, the authors make did not mention babies once in their paper, but sure i guess we could do it&amp;#160;&lt;a href="https://thork.net/posts/2025-01-23-protein-cryptography-paper/#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2" role="doc-endnote">
&lt;p>thor has extremely lossy understanding of quantum things, and makes only the weakest claim to have said this correctly&amp;#160;&lt;a href="https://thork.net/posts/2025-01-23-protein-cryptography-paper/#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:3" role="doc-endnote">
&lt;p>a note from Keoni Gandall: there are other ways to purify proteins; &amp;ldquo;you can purify proteins based on their size with columns and such, as well as methods that use salt (taking advantage of the solubility differences in different proteins)&amp;rdquo;&amp;#160;&lt;a href="https://thork.net/posts/2025-01-23-protein-cryptography-paper/#fnref:3" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:4" role="doc-endnote">
&lt;p>the paper does not answer the question of how large the key space of possible mAb&amp;rsquo;s is, but since spectrometry is destructive, the key space doesn&amp;rsquo;t need to be massive. thor estimate: since an mAb has about 120 amino acids in the variable region, with 20 possible amino acids, that the mAb key space is much larger than $2^{128}$.&amp;#160;&lt;a href="https://thork.net/posts/2025-01-23-protein-cryptography-paper/#fnref:4" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/section></description><category domain="https://thork.net/categories/cryptography/">cryptography</category><category domain="https://thork.net/tags/cryptography/">cryptography</category><category domain="https://thork.net/tags/biology/">biology</category><category domain="https://thork.net/tags/speedrun/">speedrun</category></item><item><title>About the Stone Prover</title><link>https://thork.net/posts/about-the-stone-prover/</link><guid isPermaLink="true">https://thork.net/posts/about-the-stone-prover/</guid><pubDate>Tue, 06 Feb 2024 00:00:00 +0000</pubDate><author>thorck@protonmail.com (Thor)</author><description>&lt;p>&lt;img src="https://thork.net/photos/2024-02-06-About/Stone-prover-stone-sand.jpeg" alt="">&lt;/p>
&lt;p>&lt;em>What follows an introduction to the &lt;a href="https://github.com/starkware-libs/stone-prover/">Stone Prover&lt;/a>. &lt;a href="https://github.com/thor314/pebble-stark">Pebble Stark&lt;/a> is my WIP re-implementation of the Stone Prover as a set of modular Rust libraries targeting web-assembly compilation, thereby enabling client-side proving (proving Cairo from a browser or phone). The target audience for this post are those familiar with verfiable computation at a high level, looking to learn more about the Stone Prover in particular. Thanks to &lt;a href="https://starkware.co/">StarkWare&lt;/a> for supporting this work.&lt;/em>&lt;/p>
&lt;h2 id="background">Background&lt;/h2>
&lt;p>The Stone Prover is a C++ implementation of a STARK&lt;sup id="fnref:1">&lt;a href="https://thork.net/posts/about-the-stone-prover/#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup> proving system developed by StarkWare. There are two architectural approaches for designing proving systems:&lt;/p>
&lt;ul>
&lt;li>Circuit DSL approaches like &lt;a href="https://github.com/0xPolygonZero/plonky2/tree/main?tab=readme-ov-file">Plonky 2&lt;/a>, &lt;a href="https://electriccoin.co/blog/explaining-halo-2/">Halo 2&lt;/a>, and &lt;a href="https://github.com/iden3/circom">circom&lt;/a> allow developers to lay out a circuit representing an arbitrary program, without an intermediate instruction-set representation. These libraries generally allow developers to achieve high levels of circuit performance, but typically offer very low-level developer experience. Circuits are also harder to audit for security and correctness.&lt;/li>
&lt;li>Virtual machine&lt;sup id="fnref:2">&lt;a href="https://thork.net/posts/about-the-stone-prover/#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup> approaches like &lt;a href="https://github.com/starkware-libs/stone-prover/">Stone&lt;/a>, &lt;a href="https://www.risczero.com/">Risc-0&lt;/a>, and &lt;a href="https://polygon.technology/polygon-zkevm">zkEVM&lt;/a> construct circuit equivalents for each instruction in the target instruction set. Some zero knowledge virtual machines target existing instruction sets: Risc-0 and zkEVM target &lt;a href="https://en.wikipedia.org/wiki/RISC-V">RISC-V&lt;/a> and the &lt;a href="https://en.wikipedia.org/wiki/Ethereum#Virtual_machine">EVM&lt;/a> instruction sets respectively; while Stone was designed specifically to prove instances of &lt;a href="https://eprint.iacr.org/2021/1063.pdf">Cairo Assembly&lt;/a> (CASM), designed by the Starkware team for efficient STARK proving. Virtual machines effectively provide an abstraction layer over the the way that a circuit is laid out, allowing for better program modularity and auditability than writing a zk circuit directly.&lt;/li>
&lt;/ul>
&lt;p>The pipeline for producing a proof with Stone is as follows.&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-blockdiag" data-lang="blockdiag">Diagram scaffold
- User writes Cairo program
- Compiles program to CASM (with what?)
- Proves CASM instance (with stone)
- For cost savings, SHARP aggregates multiple STARK proofs into a single proof - (cairo jobs) - aggregates jobs into a train--aggregated jobs
- starknet - produces cairo jobs for sharp to prove
- where the program is deployed ( on starknet )
- Ethereum chain verifies aggregated proof
&lt;/code>&lt;/pre>&lt;p>&lt;em>Proving a Cairo Program&lt;/em>&lt;/p>
&lt;p>Given a program written in Cairo, the program is first compiled to the Sierra IR, then to CASM with the &lt;a href="https://github.com/starkware-libs/cairo-lang/tree/v0.12.0">Cairo compiler&lt;/a>. The Stone Prover executes the CASM program, generating a trace of its execution, and proves the correctness of that trace. The resultant proof may then be aggregated with other proofs, or submitted directly to a verifier.&lt;/p>
&lt;p>In more detail, Stone executes the CASM program, taking snapshots of the program state after the execution of each instruction. The collection of these snapshots of the state of the program is called the &lt;strong>trace&lt;/strong>, which is a sort of table describing the entire execution of the program. You may think of a the trace as describing collection of registers, loading and manipulating information in memory. That is, if we have 8 registers, and execute 200 instructions, the trace is said to have width 8, and height 200. The &lt;strong>trace&lt;/strong> describes the program, but keep in mind that the goal is to prevent a cheating prover from lying about the program execution. Therefore, the &lt;strong>proof&lt;/strong> should show that the &lt;strong>trace&lt;/strong> is consistent with a set of &lt;strong>constraints&lt;/strong>. The &lt;strong>trace&lt;/strong> plus the &lt;strong>constraints&lt;/strong> are called the &lt;strong>arithmetization scheme&lt;/strong>. Stone, and STARKs in general, use an &lt;strong>arithmetization scheme&lt;/strong> called Algebraic Intermediate Representation, &lt;strong>AIR&lt;/strong>.&lt;/p>
&lt;p>Now, that we have the AIR&amp;ndash;the trace and constraints&amp;ndash;we need to prove that the trace satisfies the constraints with &lt;strong>FRI&lt;/strong> (Fast Reed-solomon IOPPs). Relative to the two other common proving schemes, KZG and IPA, &lt;strong>FRI&lt;/strong> proofs employed in STARKs generate proofs quickly (the prover does less work), but generate larger proofs, on the order of 100kB. However, as we&amp;rsquo;ll see in the next step, there are tricks to amortize the cost of verifying larger proofs with recursion. You may think of FRI as a gadget for creating the proof that the trace satisfies the constraints.&lt;/p>
&lt;p>That is, the Stone Prover:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>inputs&lt;/strong> a program consisting of CASM instructions&lt;/li>
&lt;li>&lt;strong>executes the program&lt;/strong> to generate the trace&lt;/li>
&lt;li>&lt;strong>lays out the trace in an AIR&lt;/strong>, representing the constraints between each row of the execution trace&lt;/li>
&lt;li>and finally, &lt;strong>proves that the trace satisfies&lt;/strong> the constraints with FRI.&lt;/li>
&lt;/ul>
&lt;p>The Stone Prover&amp;rsquo;s work is done, but for the sake of completeness, we will briefly mention two further steps in the pipeline of proof generation: proof-aggregation, and verification.&lt;/p>
&lt;p>Now suppose we have a STARK proof, demonstrating the correct execution of some program. We could directly verify the STARK proof, &lt;strong>but&lt;/strong> if the verifier must verify the proofs corresponding to more than one program, we can save the verifier work by combining the proofs in a clever way. Proof Aggregation is exactly this, a way to combine proofs together. The &lt;a href="https://docs.cairo-lang.org/sharp.html">SHARP&lt;/a> proof aggregator implements proof aggregation for Stone. Conveniently, aggregation of FRI proofs can be parallelized.&lt;/p>
&lt;p>&lt;img src="https://thork.net/photos/2024-02-06-About/stark-ex-proof-aggregation.jpeg" alt="">
&lt;em>Proof aggregation batching proof instances; &lt;a href="https://docs.starkware.co/starkex/spot/shared/how-cairo-is-used-in-starkex.html">source&lt;/a>&lt;/em>&lt;/p>
&lt;p>Finally, the proof is verified; verification performs the same algorithm regardless of whether the proof is aggregated. Recall that one of the goals of verifiable computation is that the verifier perform sub-linear work to verify the proof, relative to naively computing the program. That is, if the verifier would have perfomed $N$ steps to compute a program naively, the proof will require fewer than $N$ steps to check. Further, if the proof is the aggregated result of $k$ proofs, then the verifier savings on computation are further increased by about a factor of $k$. The work-saving in proof verification is well suited to compute constrained environments, such as blockchains.&lt;/p>
&lt;h2 id="footnotes">footnotes&lt;/h2>
&lt;section class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1" role="doc-endnote">
&lt;p>&lt;a href="https://www.iacr.org/archive/crypto2019/116940201/116940201.pdf">Scalable Transparant ARguments of Knowledge&lt;/a> (STARKs) are proof systems where (1) the prover time is at most quasilinear (at most $O(n\log n)$ time), with (2) sublinear/polylogarithmic verifier time (at most $O(log^k(n)$ ), and (3) without a &lt;a href="https://a16zcrypto.com/posts/article/snark-security-and-performance/#section--2">trusted setup&lt;/a> phase.&amp;#160;&lt;a href="https://thork.net/posts/about-the-stone-prover/#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2" role="doc-endnote">
&lt;p>But what is a virtual machine? A virtual machine (VM) is really just a non-native instruction set, meaning that your machine can evaluate programs in an assembly language differing from it&amp;rsquo;s own. The two most common architectures are Intel x86 and ARM, and your system typically implements instructions at the hardware level for speed. Machines on these hardware architectures may still run programs targeting other instruction sets, by &lt;strong>software emulation&lt;/strong>. That is, even though my machine runs Intel x86, I may run programs in ARM, EVM, or RISC-V via software emulation. The &amp;ldquo;virtual machine&amp;rdquo; is the component that does the software emulation. In the case of zkVMs, I may emulate the execution of a program in a particular VM that produces proofs of correct execution. Outside of the context of zkVMs, VMs are also used for server virtualization (VMware vSphere cloud-compute server), reproducible testing and development (Docker), and to run different operating systems on a single hardware platform.&amp;#160;&lt;a href="https://thork.net/posts/about-the-stone-prover/#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/section></description><category domain="https://thork.net/categories/cryptography/">cryptography</category><category domain="https://thork.net/tags/cryptography/">cryptography</category><category domain="https://thork.net/tags/stone-prover/">stone-prover</category><category domain="https://thork.net/tags/zk/">zk</category><category domain="https://thork.net/tags/explainer/">explainer</category><category domain="https://thork.net/tags/starkware/">starkware</category></item><item><title>Solving ZK Hack Puzzle 3 - Chaos Theory</title><link>https://thork.net/posts/2024-02-05-solving-zk-hack-puzzle-3-chaos-theory/</link><guid isPermaLink="true">https://thork.net/posts/2024-02-05-solving-zk-hack-puzzle-3-chaos-theory/</guid><pubDate>Mon, 05 Feb 2024 00:00:00 +0000</pubDate><author>thorck@protonmail.com (Thor)</author><description>&lt;p>&lt;img src="https://thork.net/photos/2024-02-05-solving/puzzle-chaos-swirling.jpeg" alt="">&lt;/p>
&lt;p>&lt;em>What follows is a solution to &lt;a href="https://zkhack.dev/zkhackIV/puzzleF3.html">ZK Hack IV, Puzzle 3&lt;/a>. ZK Hack is a series of zero-knowledge cryptography CTFs, workshops, hackathons, and study groups. Thanks to the ZK Hack organizers for creating this CTF, Geometry for this puzzle, and the ZK Hack community. You might also be interested in &lt;a href="https://thork.net/posts/2024-01-24-solving-zk-hack-iv-puzzle-2-supervillain/">solution for puzzle 2&lt;/a>. If reposting elsewhere, find this post on my blog &lt;a href="http://thork.net/posts/2024-02-05-solving-zk-hack-puzzle-3-chaos-theory/">here&lt;/a>&lt;/em>.&lt;/p>
&lt;p>You might be interested in this post if you&amp;rsquo;re interested in:&lt;/p>
&lt;ul>
&lt;li>an thought experiment on how building an authentication scheme based in elliptic curve pairings can go horribly awry!&lt;/li>
&lt;li>Some background on ElGamal encryption and BLS signatures&lt;/li>
&lt;li>The process of (cryptographic) puzzle solving&lt;/li>
&lt;/ul>
&lt;p>Of the three ZK Hack puzzles, this was by far the most straight-forward, so this will be a shorter post than the prior two.&lt;/p>
&lt;h2 id="puzzlesolve">&lt;code>puzzle.solve()&lt;/code>&lt;/h2>
&lt;h3 id="background">Background&lt;/h3>
&lt;p>&lt;em>all the knowledge nuggets you need to solve the puzzle&lt;/em>&lt;/p>
&lt;p>In this puzzle, we are presented with a novel scheme for the age-old cryptographic primitive, encryption plus authentication. Our Bob combines ElGamal encryption with BLS signatures. The details of each algorithm don&amp;rsquo;t much matter, but the way Bob re-uses his encryption key for BLS signing very much does. Our goal is to decrypt the ciphertext, without access to the decryption algorithm.&lt;/p>
&lt;p>We visit BLS and Elgamal schemes in more depth in the next section, but familiarity with these algorithms is unrequired for working this problem.&lt;/p>
&lt;p>Elliptic curve points are denoted in capitals, scalars in lower case. The sender&amp;rsquo;s secret and private keys are denoted $sk, PK=sk*G$, and the receiver&amp;rsquo;s are denoted $sk',PK'$. We have 3 algorithms:&lt;/p>
&lt;ul>
&lt;li>Encryption (&lt;code>send&lt;/code>): $c=(PK, sk*PK' + M)\gets E_{sk}(M, PK')$&lt;/li>
&lt;li>Authentication (&lt;code>authenticate&lt;/code>): $\sigma= sk*H(c) \gets A_{sk}(c)$&lt;/li>
&lt;li>Auth Verification (&lt;code>check_auth&lt;/code>): $b\in {0,1}=e(G,\sigma)=_? e(PK, H(c)) \gets V(PK, c, \sigma)$&lt;/li>
&lt;/ul>
&lt;p>Where the final authentication checks satisfies, since:
$$\begin{aligned}
e(G,\sigma)&amp;amp;=e(PK,H(c)) \\
sk* e(G, H(c)) &amp;amp;= sk * e(G, H(c))
\end{aligned}$$&lt;/p>
&lt;p>We also are given a list of messages that Bob may choose to send.&lt;/p>
&lt;p>We now demonstrate the exploit.&lt;/p>
&lt;p>==🔴Stop! This is the 🚨puzzle solving police🚨! You have been given everything you need to find your own solution! Take ten minutes, pull out your pen, and reap the joy and mathematical gainz of problem solving🔴==&lt;/p>
&lt;p>.&lt;/p>
&lt;p>.&lt;/p>
&lt;p>.&lt;/p>
&lt;p>.&lt;/p>
&lt;p>.&lt;/p>
&lt;p>.&lt;/p>
&lt;p>.&lt;/p>
&lt;p>.&lt;/p>
&lt;p>.&lt;/p>
&lt;h3 id="the-sploit">the &amp;lsquo;sploit&lt;/h3>
&lt;p>&lt;em>The puzzle solution in short&lt;/em>&lt;/p>
&lt;p>Let&amp;rsquo;s revisit those 3 algorithms:&lt;/p>
&lt;ul>
&lt;li>Encryption (&lt;code>send&lt;/code>): $c=(PK, sk*PK&amp;rsquo; + M)\gets E_{sk}(M, PK')$&lt;/li>
&lt;li>Authentication (&lt;code>authenticate&lt;/code>): $\sigma= sk*H(c) \gets A_{sk}(c)$&lt;/li>
&lt;li>Auth Verification (&lt;code>check_auth&lt;/code>): $b\in {0,1}=e(G,\sigma)=_? e(PK, H(c)) \gets V(PK, c, \sigma)$&lt;/li>
&lt;/ul>
&lt;p>Bob&amp;rsquo;s only innovation is to re-use the encryption secret key for signing. Because we have the list of messages he may have sent, we can try to peel the message off of $c_2$. By testing each possible message $M_i$, we may compute $sk&lt;em>PK'$:
$$
X_i = sk&lt;/em>PK' = c_2 - M_i = (sk*PK' + M) - M_i
$$&lt;/p>
&lt;p>We have a line-up. One of these $A_i$&amp;rsquo;s is our guy. How will we differentiate them? We haven&amp;rsquo;t done anything creative or unusual yet. But unlike secure encryption+authentication, now we have a pairing at our disposal.&lt;/p>
&lt;p>We have that, for some $i$, $X=X_i = sk*PK' = sk * sk' G$. That looks a lot like a public key, corresponding to secret key $x=sk * sk'$.&lt;/p>
&lt;p>Can we shove $X$ into our verification step?
$$\begin{aligned}
e(G,\sigma)&amp;amp;=_? e(X_i, H(c)) &amp;amp;\gets V(X_i, c, \sigma) \\
sk* e(G, H(c)) &amp;amp;\not= sk*sk' * e(G, H(c))
\end{aligned}$$
Almost. We need to find a way to squeeze $sk'$ onto the left-hand-side. If we can multiply $G$ or $\sigma$ by $sk'$, we&amp;rsquo;re done, and the former is just the public key, $PK'$.&lt;/p>
&lt;p>We can&amp;rsquo;t re-use the provided auth verification algorithm, but we can easily write that new auth-verification ourselves:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="k">pub&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">check_auth_evil&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">s1s2_G&lt;/span>: &lt;span class="nc">G1Affine&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">c&lt;/span>: &lt;span class="kp">&amp;amp;&lt;/span>&lt;span class="nc">ElGamal&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">s&lt;/span>: &lt;span class="nc">G2Affine&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">receiver_pk&lt;/span>: &lt;span class="nc">G1Affine&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="kt">bool&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// let lhs = { Bls12_381::pairing(G1Projective::generator(), s) };
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// (sk_r * G1, sk_s * H(.))
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">lhs&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Bls12_381&lt;/span>::&lt;span class="n">pairing&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">receiver_pk&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">s&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">};&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// let lhs = { receiver_pk, s) };
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">hash_c&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">hash_to_curve&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// (sk_r*sk_s * G1, H(.))
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rhs&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Bls12_381&lt;/span>::&lt;span class="n">pairing&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">s1s2_G&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">hash_c&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">};&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">lhs&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rhs&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;em>&lt;a href="https://github.com/thor314/puzzle-chaos-theory/blob/840635f84c90eb6d39edb5f442fbf4540c202b90/src/main.rs#L148-L159">repo link&lt;/a>&lt;/em>&lt;/p>
&lt;p>Plug that bad-boy in, and we&amp;rsquo;re done:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sender_pk&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">blob&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">sender_pk&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">blob&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">clone&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">c1&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">blob&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="mf">1.&lt;/span>&lt;span class="n">clone&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">s&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">blob&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">s&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rec_pk&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">blob&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">rec_pk&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">m&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">in&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">_messages&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">into_iter&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">enumerate&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">c1&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">m&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">into_affine&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">check_auth_evil&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">s&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rec_pk&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">warn&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;found message at index {}&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">warn&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;win&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;img src="https://thork.net/photos/2024-02-05-solving/zkhack-puzzle-3-lineup.jpeg" alt="">
&lt;em>line em up&lt;/em>&lt;/p>
&lt;h2 id="puzzledeets">&lt;code>puzzle.deets()&lt;/code>&lt;/h2>
&lt;p>&lt;em>Extended background on BLS and ElGamal encryption. This isn&amp;rsquo;t technically required to solve the problem, but the interested reader may enjoy the background context.&lt;/em>&lt;/p>
&lt;h3 id="bls">BLS&lt;/h3>
&lt;p>The BLS signature scheme is a simple signature aggregation scheme, relying on a elliptic curve pairing. It consists of 3 algorithms, (sign, aggregate_signatures, verify), where verify checks either any individual&amp;rsquo;s signature, or the whole aggregated signature. We don&amp;rsquo;t use aggregation at all in this puzzle.&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>Individual signing&lt;/strong> - Each individual signature $\sigma_i=sk_i\cdot H(M)$ is curve point on $\mathbb G_2$. Each public key $pk_i=sk_i\cdot G_1$ is a curve point on $\mathbb G_1$.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Individual signature verification&lt;/strong> and &lt;strong>aggregated verification&lt;/strong> - Verification for any individual signature $\sigma_i$ is the same as for the entire signature, so we drop the indices:
$$e(pk, H(M)) = e(G_1,\sigma)$$
Note that we don&amp;rsquo;t typically verify the individual signatures, only the final aggregated signature. The point of a signature aggregation scheme is that &lt;em>the aggregated signature is valid if and only if each component signature is valid&lt;/em>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Signature aggregation&lt;/strong> - Simply compute the sum of the signatures and sum of public keys:&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>$$\begin{aligned}
\sigma_{\text{agg}} &amp;amp;= \sum\limits_{i=1}^{n} \sigma_i=(\sum\limits sk_i)H(M) \\
pk_{\text{agg}}&amp;amp;=\sum\limits pk_i=(\sum\limits sk_i)G_1
\end{aligned}$$&lt;/p>
&lt;h3 id="elgamal">ElGamal&lt;/h3>
&lt;p>ElGamal encryption is a secure but non-standard public-key cryptosystem based on the Diffie-Hellman key exchange. It was described by Taher Elgamal in 1985. ElGamal can be used for both encryption and digital signatures. The security of the ElGamal encryption system is based on the difficulty of solving the discrete logarithm problem. ElGamal encryption generally produces larger ciphertexts compared to the original plaintext size, effectively doubling the message size. This can be inefficient in terms of bandwidth and storage.&lt;/p>
&lt;p>Denoting scalars in lower case, and elliptic curve points in upper case:&lt;/p>
&lt;p>&lt;strong>Key Generation:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Private Key:&lt;/strong> Select a random number $x$ as the recipient private key.&lt;/li>
&lt;li>&lt;strong>Public Key:&lt;/strong> Multiply the generator point $G$ of the curve by $x$ to get $Q = xG$. The recipient public key is $Q$.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Encryption:&lt;/strong> (message, pk) -&amp;gt; ciphertext&lt;/p>
&lt;ul>
&lt;li>Given a plaintext message $M$ represented as a point on the curve:&lt;/li>
&lt;li>Choose a random number $r$.&lt;/li>
&lt;li>Compute $C_1 = rG$.&lt;/li>
&lt;li>Compute $C_2 = M + rQ$.&lt;/li>
&lt;li>The ciphertext is the pair $C=(C_1, C_2)$.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Decryption:&lt;/strong> (ciphertext, sk) -&amp;gt; message&lt;/p>
&lt;ul>
&lt;li>Compute $M = C_2 - xC_1$.&lt;/li>
&lt;li>This works because: $C_2 - xC_1 = M + rQ - x(rG) = M + r(xG) - xrG = M$.&lt;/li>
&lt;/ul>
&lt;h2 id="puzzlelog">&lt;code>puzzle.log()&lt;/code>&lt;/h2>
&lt;p>&lt;em>My puzzle solving log, lightly edited for readability. Keeping a log helps navigate overwhelming context dumps, and to avoid doing wrong and/or stupid things repeatedly. I use Obsidian to take these notes, for which I have written &lt;a href="https://github.com/thor314/obsidian-setup">an extensive setup and usage guide&lt;/a>. My repo can be found &lt;a href="https://github.com/thor314/puzzle-supervillain/blob/main/src/main.rs">here&lt;/a>. For taking math notes, I generally use a whiteboard or ipad for my scratch space before writing down my clean notes here, which helps to think n scratch faster. This puzzle in particular may have been faster to just work out on a whiteboard, without a log at all.&lt;/em>&lt;/p>
&lt;p>&lt;img src="https://thork.net/photos/2024-02-05-solving/zkhack-puzzle-3-pirate-log.jpeg" alt="">
&lt;em>it be time for logging&lt;/em>&lt;/p>
&lt;h3 id="code-runthrough">Code runthrough&lt;/h3>
&lt;ul>
&lt;li>hasher - same hash as last puzzle it looks like, sha256 based&lt;/li>
&lt;li>&lt;code>struct ElGamal&lt;/code> representing ciphertext. Over a pair of G1Affine elements, let&amp;rsquo;s tag it with Debug for convenience.
&lt;ul>
&lt;li>&lt;code>hash_to_curve(self)&lt;/code> to create a G2 element&lt;/li>
&lt;li>we&amp;rsquo;re implementing our own hash to curve? check pre-image resistance (not collision resistance based on goal context). Sha256 over 128 bits is still pretty strong, so it seems fine&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>struct Message&lt;/code> over &lt;code>G1Affine&lt;/code>&lt;/li>
&lt;li>&lt;code>struct Sender&lt;/code> over a secret key in &lt;code>Fr&lt;/code> and a pk
&lt;ul>
&lt;li>verify that pk is correctly constructed&lt;/li>
&lt;li>&lt;code>send&lt;/code> a message to receiver&lt;/li>
&lt;li>&lt;code>authenticate&lt;/code> generate the tag &lt;code>s&lt;/code> in &lt;code>G2Affine&lt;/code>: $s = H(c)*sk$&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>struct Receiver&lt;/code> gets a pk&lt;/li>
&lt;li>&lt;code>Auditor&lt;/code>
&lt;ul>
&lt;li>&lt;code>check_auth&lt;/code> takes a the sender pk, the cipher-text, and the authentication tag $s$
&lt;ul>
&lt;li>encrypt-then-sign is fine, unless the encryption and authentication are malleable?&lt;/li>
&lt;li>lhs: $e(G_1, s)$&lt;/li>
&lt;li>rhs: $e(pk, H(c))$ , where $H$ is our sketchy hash function&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>I think we have everything we need already to work this out. To the whiteboard!&lt;/p>
&lt;p>&lt;em>See above notes for whiteboard observations.&lt;/em>&lt;/p></description><category domain="https://thork.net/categories/cryptography/">cryptography</category><category domain="https://thork.net/tags/cryptography/">cryptography</category><category domain="https://thork.net/tags/puzzle/">puzzle</category><category domain="https://thork.net/tags/zk/">zk</category><category domain="https://thork.net/tags/pairing/">pairing</category><category domain="https://thork.net/tags/zkhack/">zkhack</category></item><item><title>new project: git-merkle (gm)</title><link>https://thork.net/posts/2024-01-31-git-merkle/</link><guid isPermaLink="true">https://thork.net/posts/2024-01-31-git-merkle/</guid><pubDate>Wed, 31 Jan 2024 00:00:00 +0000</pubDate><author>thorck@protonmail.com (Thor)</author><description>&lt;p>GM!
&lt;img src="https://thork.net/photos/2024-01-31-merkle.md/gm-tree.jpeg" alt="">&lt;/p>
&lt;p>Just a short note describing a recent new project of mine, &lt;a href="https://github.com/thor314/gm">git merkle (gm)&lt;/a>!&lt;/p>
&lt;p>&lt;code>gm&lt;/code> is a recursive tree of all the repos I&amp;rsquo;ve worked on since 2017 after a little spring cleaning; I removed about 40% of my pre-existing github repo history in the process of writing &lt;code>gm&lt;/code>. There are about 90 repos contained in &lt;code>gm&lt;/code>, and counting. I removed about 60.&lt;/p>
&lt;h2 id="why-did-you-do-this">Why did you do this?!&lt;/h2>
&lt;p>This is a very real question. Organizing and cleaning up about 6 years of git history, plus writing a &lt;a href="https://github.com/thor314/.cron/blob/main/git_merkle.fish">script&lt;/a> to keep them organized going forward, took about 6 hours across two days. Submodules are somewhat infamously finicky, and the last thing I want to be doing is managing merge conflicts on a nested tree of my commit history.&lt;/p>
&lt;p>That is, if a commit is made to a submodule at depth $d$ in the tree, say in my repo &lt;code>helix&lt;/code>, for which the path is &lt;code>gm/linux/.files/helix&lt;/code>, then $d$ new commits are added to the git-merkle tree.&lt;sup id="fnref:1">&lt;a href="https://thork.net/posts/2024-01-31-git-merkle/#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>&lt;/p>
&lt;p>I have a few reasons. One is that it&amp;rsquo;s very nice to have a copy of the full history of my work on hand to &lt;a href="https://github.com/BurntSushi/ripgrep">ripgrep&lt;/a> through. Another is that it&amp;rsquo;s a nice way to keep my work presentable for others. But most especially, this is one representation of all the work I&amp;rsquo;ve done for the last 6 years, and I&amp;rsquo;m proud of it. This repo is a sort of monument to the pride I feel in the all the work I&amp;rsquo;ve done. As a cryptography engineer, I find it apropos to represent that work as the workhorse data structure of blockchain cryptography, the merkle tree.&lt;/p>
&lt;p>&lt;img src="https://thork.net/photos/2024-01-31-merkle.md/git-merkle-tree-retro.jpeg" alt="">
GM.&lt;/p>
&lt;pre tabindex="0">&lt;code>root = h(n1, n2)
| \
n1=h(n3,n4) n2=h(n5,n6)
| | | |
n3 n4 n5 n6
&lt;/code>&lt;/pre>&lt;p>&lt;em>a merkle tree where the leaves are where data is stored and the root is a single hash&lt;/em>&lt;/p>
&lt;section class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1" role="doc-endnote">
&lt;p>For those of you who are rusty on cryptographic data structures 101, a merkle tree is a tree data structure where the leaves contain the data, and every intermediate node is the hash of it&amp;rsquo;s leaves, see above. It&amp;rsquo;s useful for committing to a bunch of data in a single hash (256 bytes), that cryptographically represents the compressed state of the entire set of data, even if there&amp;rsquo;s a ton of data. Each git commit is produces the hash of the state of the repo, so the submodule tree is a just a tree of commit hashes.&amp;#160;&lt;a href="https://thork.net/posts/2024-01-31-git-merkle/#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/section></description><category domain="https://thork.net/categories/programming/">programming</category><category domain="https://thork.net/tags/git/">git</category><category domain="https://thork.net/tags/programming/">programming</category><category domain="https://thork.net/tags/organization/">organization</category><category domain="https://thork.net/tags/merkle-tree/">merkle-tree</category><category domain="https://thork.net/tags/art/">art</category></item><item><title>Solving ZK Hack IV puzzle 2 - Supervillain</title><link>https://thork.net/posts/2024-01-24-solving-zk-hack-iv-puzzle-2-supervillain/</link><guid isPermaLink="true">https://thork.net/posts/2024-01-24-solving-zk-hack-iv-puzzle-2-supervillain/</guid><pubDate>Wed, 24 Jan 2024 00:00:00 +0000</pubDate><author>thorck@protonmail.com (Thor)</author><description>&lt;p>&lt;img src="https://thork.net/photos/2024-01-24-Solving/supervillain-zkhack-puzzle.jpeg" alt="">&lt;/p>
&lt;p>&lt;em>What follows is a solution to &lt;a href="https://zkhack.dev/zkhackIV/puzzleF2.html">ZK Hack IV, Puzzle 2&lt;/a>. ZK Hack is a series of zero-knowledge cryptography CTFs, workshops, hackathons, and study groups. Thanks to the ZK Hack organizers for creating this CTF, Geometry for this puzzle, and the ZK Hack community. You might also be interested in &lt;a href="https://thork.net/posts/2024-01-18-solving-zk-hack-iv-puzzle-1/">solution for puzzle 1&lt;/a>. If reposting elsewhere, find this post on my blog &lt;a href="http://thork.net/posts/2024-01-24-solving-zk-hack-iv-puzzle-2-supervillain/">here&lt;/a>&lt;/em>.&lt;/p>
&lt;p>You might be interested in this post if you&amp;rsquo;re interested in:&lt;/p>
&lt;ul>
&lt;li>a smattering of zk-related trivia about elliptic curve pairing assumptions and proofs of knowledge&lt;/li>
&lt;li>The process of (cryptographic) puzzle solving&lt;/li>
&lt;/ul>
&lt;p>&lt;em>Recommended reading order&lt;/em>:&lt;/p>
&lt;ul>
&lt;li>read this post top to bottom for a concise description and solution of the puzzle, then some mathematical detail nuggets to hold onto, and finally the stream of consciousness log of problem solving.&lt;/li>
&lt;li>read this post back to front for the order in which it was actually written (problem solving log, then cleaned up and bloggified knowledge nuggets).&lt;/li>
&lt;/ul>
&lt;h2 id="puzzlesolve">&lt;code>puzzle.solve()&lt;/code>&lt;/h2>
&lt;h3 id="background">Background&lt;/h3>
&lt;p>&lt;em>all the knowledge nuggets you need to solve the puzzle&lt;/em>&lt;/p>
&lt;p>In this puzzle we are tasked with exploiting a BLS Signature scheme with a home-baked proof of knowledge argument.&lt;/p>
&lt;h4 id="bls-background">BLS background&lt;/h4>
&lt;p>The BLS signature scheme is a simple signature aggregation scheme, relying on a elliptic curve pairing. It consists of 3 algorithms, (sign, aggregate_signatures, verify), where verify checks either any individual&amp;rsquo;s signature, or the whole aggregated signature.&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>Individual signing&lt;/strong> - Each individual signature $\sigma_i=sk_i\cdot H(M)$ is curve point on $\mathbb G_2$. Each public key $pk_i=sk_i\cdot G_1$ is a curve point on $\mathbb G_1$.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Individual signature verification&lt;/strong> and &lt;strong>aggregated verification&lt;/strong> - Verification for any individual signature $\sigma_i$ is the same as for the entire signature, so we drop the indices:
$$e(pk, H(M)) = e(G_1,\sigma)$$
Note that we don&amp;rsquo;t typically verify the individual signatures, only the final aggregated signature. The point of a signature aggregation scheme is that &lt;em>the aggregated signature is valid if and only if each component signature is valid&lt;/em>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Signature aggregation&lt;/strong> - Simply compute the sum of the signatures and sum of public keys:&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>$$\begin{aligned}
\sigma_{\text{agg}} &amp;amp;= \sum\limits_{i=1}^{n} \sigma_i=(\sum\limits sk_i)H(M) \\
pk_{\text{agg}}&amp;amp;=\sum\limits pk_i=(\sum\limits sk_i)G_1
\end{aligned}$$&lt;/p>
&lt;h4 id="proof-of-knowledge-argument-background">Proof of knowledge argument background&lt;/h4>
&lt;p>As noted above, we don&amp;rsquo;t verify the individual signatures. A malicious party could therefore choose public key without even knowing the corresponding secret key. The proof of knowledge intends to prevent that.&lt;/p>
&lt;p>Instead, we require each party $i$ to provide a proof of knowledge $\pi_i$ that the party knows their secret key. The proof relies on a pairing check:&lt;/p>
&lt;p>$$e(pk_i, R_i)= e(G_1, \pi_i)$$
Where $R_i$, is randomly generated within $\mathbb G_2$. That is, we check that the party knows $sk_i$ such that they can compute:&lt;/p>
&lt;p>$$\pi_i = sk_i*R_i$$&lt;/p>
&lt;h4 id="back-to-the-problem-at-hand">Back to the problem at hand&lt;/h4>
&lt;p>In the case of this problem, the implementer modifies the BLS signature verification algorithm, negating the message hash:&lt;/p>
&lt;p>$$\begin{aligned}
e(pk, -H(M)) &amp;amp;= e(G_1,\sigma) \\
e\bigg(\sum\limits(sk_i)G_1, -H(M)\bigg) &amp;amp;= e\bigg(G_1,\sum\limits(sk_i)H(M)\bigg)
\end{aligned}$$
This constrains the choice of our secret key $sk'$ such that (note that we start with 8 given public keys, and we are to choose the 9th):&lt;/p>
&lt;p>$$sk = sk' + \sum\limits^7_0 sk_i$$
and&lt;/p>
&lt;p>$$-sk * e(..) = sk * e(..)\implies 2 * sk = p$$&lt;/p>
&lt;p>where $p$ is the prime modulus of our field. Since $p$ is odd, the group $sk=0$ and $pk= sk * G = \mathcal O$. We don&amp;rsquo;t have the others' secret keys, but we do have their public keys, from which we may construct our own key $pk'$:&lt;/p>
&lt;p>$$pk'= \mathcal O - \sum\limits_0^7 pk_i$$
And the aggregate signature:&lt;/p>
&lt;p>$$\sigma_{agg} = sk_{agg} * H(M) = 0 * H(M) = \mathcal O$$&lt;/p>
&lt;p>However, we do not have a way obtain our own secret key $sk'$ without breaking the discrete log assumption, leads to issues with the proof-of-knowledge if the PoK is &lt;a href="https://github.com/thor314/puzzle-supervillain/blob/main/src/main.rs#L27C1-L44C2">correctly implemented&lt;/a>.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="sd">/// generate random point R
&lt;/span>&lt;span class="sd">&lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">derive_point_for_pok&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">i&lt;/span>: &lt;span class="kt">usize&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="nc">G2Affine&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rng&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="k">mut&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ark_std&lt;/span>::&lt;span class="n">rand&lt;/span>::&lt;span class="n">rngs&lt;/span>::&lt;span class="n">StdRng&lt;/span>::&lt;span class="n">seed_from_u64&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">20399&lt;/span>&lt;span class="k">u64&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">G2Affine&lt;/span>::&lt;span class="n">rand&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rng&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">mul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Fr&lt;/span>::&lt;span class="n">from&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">u64&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)).&lt;/span>&lt;span class="n">into&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="sd">/// verify proof of knowledge
&lt;/span>&lt;span class="sd">&lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">pok_verify&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pk&lt;/span>: &lt;span class="nc">G1Affine&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>: &lt;span class="kt">usize&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">proof&lt;/span>: &lt;span class="nc">G2Affine&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="fm">assert!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Bls12_381&lt;/span>::&lt;span class="n">multi_pairing&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">pk&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">G1Affine&lt;/span>::&lt;span class="n">generator&lt;/span>&lt;span class="p">()],&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">derive_point_for_pok&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">neg&lt;/span>&lt;span class="p">(),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// -R_i, not R_i
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">proof&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">is_zero&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Note that instead of:&lt;/p>
&lt;p>$$e(pk_i, R_i) = e(G_1, \pi_i)$$
We have:&lt;/p>
&lt;p>$$e(pk_i, -R_i)= e(G_1, \pi_i)$$
So we actually need $\pi = -sk_i * R_i$, not $\pi=sk_i*R_i$. Take a moment consider whether this changes anything.&lt;sup id="fnref:1">&lt;a href="https://thork.net/posts/2024-01-24-solving-zk-hack-iv-puzzle-2-supervillain/#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>&lt;/p>
&lt;p>We now demonstrate the exploit.&lt;/p>
&lt;p>==🔴Stop! This is the 🚨puzzle solving police🚨! You have been given everything you need to find your own solution! Take ten minutes, pull out your pen, and reap the joy and mathematical gainz of problem solving🔴==&lt;/p>
&lt;p>.&lt;/p>
&lt;p>.&lt;/p>
&lt;p>.&lt;/p>
&lt;p>.&lt;/p>
&lt;p>.&lt;/p>
&lt;p>.&lt;/p>
&lt;p>.&lt;/p>
&lt;p>.&lt;/p>
&lt;p>.&lt;/p>
&lt;h3 id="the-sploit">the &amp;lsquo;sploit&lt;/h3>
&lt;p>&lt;em>The puzzle solution in short&lt;/em>
Let&amp;rsquo;s revisit that randomness algorithm.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="sd">/// generate random point R
&lt;/span>&lt;span class="sd">&lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">derive_point_for_pok&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">i&lt;/span>: &lt;span class="kt">usize&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="nc">G2Affine&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rng&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="k">mut&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ark_std&lt;/span>::&lt;span class="n">rand&lt;/span>::&lt;span class="n">rngs&lt;/span>::&lt;span class="n">StdRng&lt;/span>::&lt;span class="n">seed_from_u64&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">20399&lt;/span>&lt;span class="k">u64&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">G2Affine&lt;/span>::&lt;span class="n">rand&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rng&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">mul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Fr&lt;/span>::&lt;span class="n">from&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">u64&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)).&lt;/span>&lt;span class="n">into&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>That is, let $R\gets\mathbb G_2$, then:&lt;/p>
&lt;p>$$R_i = (i+1)R$$
ₒₕ ₙₒ&lt;/p>
&lt;p>You thought this would be harder didn&amp;rsquo;t you? I certainly did. Well, a sanity-check test is in &lt;a href="https://github.com/thor314/puzzle-supervillain/blob/8b967f9e8640c25fb26e5df13c0cdd50c4d503fc/src/main.rs#L167C1-L183C2">order&lt;/a>:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="cp">#[test]&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">test_rng&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// let G = G1Projective::generator().into_affine();
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rng&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="k">mut&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ark_std&lt;/span>::&lt;span class="n">rand&lt;/span>::&lt;span class="n">rngs&lt;/span>::&lt;span class="n">StdRng&lt;/span>::&lt;span class="n">seed_from_u64&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">20399&lt;/span>&lt;span class="k">u64&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">G2Affine&lt;/span>::&lt;span class="n">rand&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rng&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">R_1&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">mul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Fr&lt;/span>::&lt;span class="n">from&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">R_9&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">mul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Fr&lt;/span>::&lt;span class="n">from&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="fm">assert_eq!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">derive_point_for_pok&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">R_1&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// pass
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="fm">assert_eq!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">derive_point_for_pok&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">9&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">R_9&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// pass
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">use&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ark_ff&lt;/span>::&lt;span class="n">Field&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">five_inv&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Fr&lt;/span>::&lt;span class="n">from&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="k">u64&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">inverse&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">unwrap&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">R_9_to_1&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">R_9&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">into_affine&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">mul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">five_inv&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">into_affine&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="fm">assert_eq!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R_1&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">R_9_to_1&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// pass
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;em>a test on the malleability in the randomness&lt;/em>&lt;/p>
&lt;p>This shows that, by the malleability of the randomness, we can solve for our $\pi_9$ by a little algebra on ${\pi_i}$.&lt;sup id="fnref:2">&lt;a href="https://thork.net/posts/2024-01-24-solving-zk-hack-iv-puzzle-2-supervillain/#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup> We have:&lt;/p>
&lt;p>$$\begin{aligned}
\pi_9 &amp;amp;= sk_9 \cdot R_9 \\
&amp;amp;= (-\sum\limits_1^8 sk_i)\cdot((1+9)R) \\
&amp;amp;= -10*\sum\limits_1^8 sk_i\frac{R_i}{i+1}\qquad \text{as}\ R=R_i/(i+1)\\
&amp;amp;= -10*\sum\limits_1^8 \frac{\pi_i}{i+1}\qquad \text{as}\ \pi_i=sk_i\cdot R_i\\
\end{aligned}$$
Which we may then simply &lt;a href="https://github.com/thor314/puzzle-supervillain/blob/8b967f9e8640c25fb26e5df13c0cdd50c4d503fc/src/main.rs#L142C1-L161C1">implement&lt;/a>:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="k">fn&lt;/span> &lt;span class="nf">extract_proof&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">new_key&lt;/span>: &lt;span class="kp">&amp;amp;&lt;/span>&lt;span class="nc">G1Affine&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">pis&lt;/span>: &lt;span class="nb">Vec&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">G2Affine&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="nc">G2Affine&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">use&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ark_ff&lt;/span>::&lt;span class="n">Field&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">pi_sum&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">pis&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">into_iter&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">enumerate&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">map&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">pi_i&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">coef&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">debug&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;i+1: {}&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">inv&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Fr&lt;/span>::&lt;span class="n">from&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">u32&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="k">u32&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">inverse&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">unwrap&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Fr&lt;/span>::&lt;span class="n">from&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">inv&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">};&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">pi_i&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">coef&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">})&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">sum&lt;/span>::&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">G2Projective&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">into_affine&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// pi_9 = -sum(pi_i * (10/(i+1))
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">pi_sum&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>And then enjoy a pamplemousse sparkling water to celebrate our triumph.
&lt;img src="https://thork.net/photos/2024-01-24-Solving/pamplemousse_glory.jpeg" alt="">
&lt;em>dalle, upon being asked for triumphant pamplemousse&lt;/em>&lt;/p>
&lt;h2 id="puzzledeets">&lt;code>puzzle.deets()&lt;/code>&lt;/h2>
&lt;p>&lt;em>extended background on the B-KEA and KOSK assumptions&lt;/em>&lt;/p>
&lt;p>The puzzle gives a pair of resources that I didn&amp;rsquo;t mention. Resources like these tend to assume things like &amp;ldquo;you won&amp;rsquo;t stick a knife in your gut on purpose by choosing a malleable source of randomness&amp;rdquo;. But we did do that, so the resources are maybe only helpful for a little background context for the problem.&lt;/p>
&lt;p>Therefore, the following notes are short and somewhat irrelevant to solving the problem, but may be interesting tidbits to take with you.&lt;/p>
&lt;p>Mary Maller&amp;rsquo;s paper gives the Bilinear Knowledge of Exponent Assumption (B-KEA). The B-KEA can be succinctly defined as follows: Given a bilinear group $(G_1, G_2, G_T)$ with a bilinear map $e: G_1 \times G_2 \rightarrow G_T$, and elements $G \in G_1, H \in G_2, xH \in G_2$ for some unknown $x$, if there exists an algorithm that outputs $yG \in G_1$ and $T \in G_T$ such that $e(yG, H) = T$, then there exists an extractor that can compute $y$ given $yG$ and $T$. This assumption implies that the algorithm &amp;ldquo;knows&amp;rdquo; the exponent $y$ used to compute $yG$. That is, the B-KEA assumption says that, unless we can break the DLP, we&amp;rsquo;re not going to be able to extract our secret key $sk&amp;rsquo;$ from our chosen public key $pk'$.&lt;/p>
&lt;p>This is maybe a bummer for hacking the problem, but on the whole, net good that we can use pairings in cryptography and not break all our private keys.&lt;/p>
&lt;p>The second paper gives the Knowledge of Secret Key (KOSK) assumption, that it is assumed that the possession and use of a secret key is proof of the identity of the user.&lt;/p>
&lt;p>Formally, let $SK$ be a secret key and $PK$ be the corresponding public key. The KOSK Assumption states that if an entity can perform cryptographic operations requiring $SK$ (like decrypting a message encrypted with $PK$ or signing a message verifiable with $PK$), then this entity possesses $SK$.&lt;/p>
&lt;p>In other words, if someone can use $SK$, they are assumed to be the legitimate owner of $SK$.&lt;/p>
&lt;p>The remainder of Paper 2 gives a series of remarks on multiparty signature schemes with a more in depth coverage of BLS' security than is absolutely necessary to be familiar with.&lt;/p>
&lt;p>&lt;img src="https://thork.net/photos/2024-01-24-Solving/algebra_hammer.jpeg" alt="">&lt;/p>
&lt;h2 id="puzzlelog">&lt;code>puzzle.log()&lt;/code>&lt;/h2>
&lt;p>&lt;em>My puzzle solving log, lightly edited for readability. You might be interested in this as a map of my state of mind while thinking about the puzzle. Keeping a log helps navigate overwhelming context dumps, and to avoid doing wrong and/or stupid things repeatedly. I use Obsidian to take these notes, for which I have written &lt;a href="https://github.com/thor314/obsidian-setup">an extensive setup and usage guide&lt;/a>. My repo can be found &lt;a href="https://github.com/thor314/puzzle-supervillain/blob/main/src/main.rs">here&lt;/a>. For taking math notes, I generally use a whiteboard or ipad for my scratch space before writing down my clean notes here, which helps to think n scratch faster.&lt;/em>&lt;/p>
&lt;h3 id="initial-problem-description">initial problem description&lt;/h3>
&lt;p>We are tasked with forging a signature.&lt;/p>
&lt;blockquote>
&lt;p>Bob has been designing a new optimized signature scheme for his L1 based on BLS signatures. Specifically, he wanted to be able to use the most efficient form of BLS signature aggregation, where you just add the signatures together rather than having to delinearize them. In order to do that, he designed a proof-of-possession scheme based on the B-KEA assumption he found in the the Sapling security analysis paper by Mary Maller [1]. Based the reasoning in the Power of Proofs-of-Possession paper [2], he concluded that his scheme would be secure. After he deployed the protocol, he found it was attacked and there was a malicious block entered the system, fooling all the light nodes&amp;hellip;&lt;/p>
&lt;/blockquote>
&lt;p>1: &lt;a href="https://github.com/zcash/sapling-security-analysis/blob/master/MaryMallerUpdated.pdf">https://github.com/zcash/sapling-security-analysis/blob/master/MaryMallerUpdated.pdf&lt;/a>&lt;/p>
&lt;p>2: &lt;a href="https://rist.tech.cornell.edu/papers/pkreg.pdf">https://rist.tech.cornell.edu/papers/pkreg.pdf&lt;/a>&lt;/p>
&lt;p>noted keywords:&lt;/p>
&lt;ul>
&lt;li>delinearize bls signatures&lt;/li>
&lt;li>B-KEA - 1&lt;/li>
&lt;li>proof of possession - 2&lt;/li>
&lt;/ul>
&lt;p>Immediate candidates for bugs:&lt;/p>
&lt;ul>
&lt;li>&lt;code>pok_verify&lt;/code> - perhaps there&amp;rsquo;s a corner case that &lt;code>pok_verify&lt;/code> doesn&amp;rsquo;t check&lt;/li>
&lt;li>aggregation and verification - does it match the BLS spec aggregation step?&lt;/li>
&lt;/ul>
&lt;p>The PoK seems more suss than bls, since the we&amp;rsquo;ve been informed that the implementer designed their own PoK scheme.&lt;/p>
&lt;h3 id="initial-things-to-look-at-after-skimming-code">initial things to look at, after skimming code&lt;/h3>
&lt;ul>
&lt;li>aggregates public keys correctly? - yes&lt;/li>
&lt;li>aggregate_signature - huh, we construct this, instead of just publishing our individual signature? that&amp;rsquo;s one vector for not actually knowing our secret, which leaves &lt;code>pok_verify&lt;/code> as the only other measure&lt;/li>
&lt;li>&lt;code>bls_verify&lt;/code> verifies sig agg correctly? - yes&lt;/li>
&lt;li>&lt;code>pok_verify&lt;/code> verifies individually proofs correctly? - yes&lt;/li>
&lt;/ul>
&lt;h3 id="notes-on-bls-signatures">notes on BLS signatures&lt;/h3>
&lt;p>&lt;em>a good portion of this comes from copy-pasting my existing notes (e.g. on BLS signatures), and/or telling chatGPT to write a good note on topic for me and editting it&lt;/em>&lt;/p>
&lt;p>BLS signature aggregation alg:&lt;/p>
&lt;h4 id="individual-signing">Individual signing&lt;/h4>
&lt;p>Each individual signature $\sigma_i=sk_i\cdot H(M)$ is curve point on G2. Each public key $pk_i=sk_i\cdot G$ is a curve point on G1.&lt;/p>
&lt;h4 id="individual-verification-and-bls-verification">Individual verification and BLS verification&lt;/h4>
&lt;p>Verification for any individual signature $\sigma_i$ is the same as for the entire signature, so we drop the indices:
$$e(pk, H(M) = e(G_1,\sigma)$$&lt;/p>
&lt;h4 id="signature-aggregation">Signature aggregation&lt;/h4>
&lt;p>Simply compute the sum of the signatures and sum of public keys:
$$\sigma_{\text{agg}} = \sum\limits_{i=1}^{n} \sigma_i=(\sum\limits sk_i)H(M)$$
$$pk_{\text{agg}}=\sum\limits pk_i=(\sum\limits sk_i)G$$&lt;/p>
&lt;h3 id="bls-code-check">BLS code check&lt;/h3>
&lt;p>We don&amp;rsquo;t test the individual signatures for correctness. This seems weird, as noted above.&lt;/p>
&lt;h4 id="public-keys-look-correctly-aggregated">public keys look correctly aggregated&lt;/h4>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">aggregate_key&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">public_keys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">iter&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">fold&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">G1Projective&lt;/span>::&lt;span class="n">from&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">new_key&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="n">acc&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pk&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">_&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">acc&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">pk&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">into_affine&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>But we get to choose the aggregated signature, rather than submitting an individual signature, which seems like an attack vector for sure.&lt;/p>
&lt;h4 id="bls-signing-check">bls signing check&lt;/h4>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="cp">#[allow(dead_code)]&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">bls_sign&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sk&lt;/span>: &lt;span class="nc">Fr&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">msg&lt;/span>: &lt;span class="kp">&amp;amp;&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="kt">u8&lt;/span>&lt;span class="p">])&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="nc">G2Affine&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">hasher&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">hash&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">msg&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">unwrap&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">mul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sk&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">into_affine&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>matches signing algorithm given above, $\sigma_i=sk_i*H(M)$&lt;/p>
&lt;h4 id="bls-verify-check">bls verify check&lt;/h4>
&lt;p>recall that we don&amp;rsquo;t verify individual signatures. We have aggregated $pk=G * sk$ and $\sigma=sk * H(M)$. We compute 2 pairings to check:&lt;/p>
&lt;p>$$e(pk,H(M))= sk* e(G,H(M))=e(G,\sigma)$$&lt;/p>
&lt;p>bls verification code:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="sd">/// multi_pairing: vec&amp;lt;g1&amp;gt;.zip(vec&amp;lt;g2&amp;gt;).map(pairing) and then subtracts one pairing from the other
&lt;/span>&lt;span class="sd">&lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">bls_verify&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pk&lt;/span>: &lt;span class="nc">G1Affine&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sig&lt;/span>: &lt;span class="nc">G2Affine&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">msg&lt;/span>: &lt;span class="kp">&amp;amp;&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="kt">u8&lt;/span>&lt;span class="p">])&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="c1">// what&amp;#39;s deal with `neg`? Looks off
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="fm">assert!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Bls12_381&lt;/span>::&lt;span class="n">multi_pairing&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="n">pk&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">G1Affine&lt;/span>::&lt;span class="n">generator&lt;/span>&lt;span class="p">()],&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">hasher&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">hash&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">msg&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">unwrap&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">neg&lt;/span>&lt;span class="p">(),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// what?
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sig&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">])&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">is_zero&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Looks like we&amp;rsquo;re taking a negative for some reason. That means our pairing looks like:
$$e(pk, -H(M)) = e(G,\sigma)$$
but $\sigma=sk * H(M)$, not $-sk * H(M)$, so there&amp;rsquo;s only a few values $sk$ could have:
$$sk = -sk \mod N \implies 2sk = N $$
where $N$ is the modulus of field &lt;code>Fr&lt;/code> that $sk$ lies in. Since $N$ is an odd prime (has no 2 divisor), we have that the aggregate $sk=0$. Our agg sig is then:
$$\sigma= sk*H(M) = 0 * H(M)=\mathcal O$$
our shared public key is also just $\mathcal O$, so we choose our individual public key:
$$pk_9 = \mathcal O - \sum\limits_0^8 pk_i$$&lt;/p>
&lt;h4 id="first-shot">first shot&lt;/h4>
&lt;p>Do we win?!&lt;sup id="fnref:3">&lt;a href="https://thork.net/posts/2024-01-24-solving-zk-hack-iv-puzzle-2-supervillain/#fn:3" class="footnote-ref" role="doc-noteref">3&lt;/a>&lt;/sup>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="k">pub&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">get_hack&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">pks&lt;/span>: &lt;span class="nb">Vec&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">G1Affine&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">G2Affine&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">msg&lt;/span>: &lt;span class="kp">&amp;amp;&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="kt">u8&lt;/span>&lt;span class="p">],&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="p">(&lt;/span>&lt;span class="n">G1Affine&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">G2Affine&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">G2Affine&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">new_key&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">pks&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">iter&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">fold&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">G1Projective&lt;/span>::&lt;span class="n">zero&lt;/span>&lt;span class="p">(),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="n">acc&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pk&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">_&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">acc&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">pk&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">into&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// dunno about proof
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">new_proof&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">G2Projective&lt;/span>::&lt;span class="n">generator&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">mul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Fr&lt;/span>::&lt;span class="n">zero&lt;/span>&lt;span class="p">()).&lt;/span>&lt;span class="n">into_affine&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">aggregate_signature&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">G2Affine&lt;/span>::&lt;span class="n">zero&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">debug&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;new_key: {:?}&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">new_key&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">new_key&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">new_proof&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">aggregate_signature&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;em>&lt;a href="https://github.com/thor314/puzzle-supervillain/blob/main/src/main.rs#L198">first attempt&lt;/a> at soln&lt;/em>&lt;/p>
&lt;p>Still need to figure out what to put in proof $\pi$.&lt;/p>
&lt;p>Do we at least satisfy the &lt;a href="https://github.com/thor314/puzzle-supervillain/blob/8b967f9e8640c25fb26e5df13c0cdd50c4d503fc/src/main.rs#L90C1-L102C60">BLS check&lt;/a>?&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="cm">/* Enter solution here */&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">new_key&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">new_proof&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">aggregate_signature&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">get_hack&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">public_keys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">clone&lt;/span>&lt;span class="p">(),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="cm">/* End of solution */&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="c1">// pok_verify(new_key, new_key_index, new_proof); // not happy
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="n">debug&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;pok verified successfully&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">aggregate_key&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">public_keys&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">iter&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">fold&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">G1Projective&lt;/span>::&lt;span class="n">from&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">new_key&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="n">acc&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pk&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">_&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">acc&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">pk&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">into_affine&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="n">debug&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;agg key created successfully&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="n">bls_verify&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">aggregate_key&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">aggregate_signature&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="n">warn&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;win&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;em>puzzle main, sans pok_ver which we haven&amp;rsquo;t gotten yet&lt;/em>&lt;/p>
&lt;p>We do. Very cool. Note that we &lt;a href="https://github.com/thor314/puzzle-supervillain/blob/8b967f9e8640c25fb26e5df13c0cdd50c4d503fc/src/main.rs#L226C1-L264C2">tried some other stuff&lt;/a> to see if we could satisfy &lt;code>bls_verify&lt;/code> in some other creative ways too.&lt;/p>
&lt;h3 id="but-how-do-we-satisfy-pok_verify">but how do we satisfy &lt;code>pok_verify&lt;/code>&lt;/h3>
&lt;h4 id="pok-verify-code">pok verify code:&lt;/h4>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="k">fn&lt;/span> &lt;span class="nf">pok_verify&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pk&lt;/span>: &lt;span class="nc">G1Affine&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>: &lt;span class="kt">usize&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">proof&lt;/span>: &lt;span class="nc">G2Affine&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="fm">assert!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Bls12_381&lt;/span>::&lt;span class="n">multi_pairing&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="n">pk&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">G1Affine&lt;/span>::&lt;span class="n">generator&lt;/span>&lt;span class="p">()],&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">derive_point_for_pok&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">neg&lt;/span>&lt;span class="p">(),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">proof&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">])&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">is_zero&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>which corresponds to
$$e(pk_9, -R_9 = e(G,\pi_9)$$
So we need $\pi_9= -sk_9*R_9$ (note that 9 is the index of my key).&lt;/p>
&lt;p>They actually give us a nice convenience helper to write proofs, thanks Kobi!&lt;sup id="fnref:4">&lt;a href="https://thork.net/posts/2024-01-24-solving-zk-hack-iv-puzzle-2-supervillain/#fn:4" class="footnote-ref" role="doc-noteref">4&lt;/a>&lt;/sup>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="cp">#[allow(dead_code)]&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">pok_prove&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sk&lt;/span>: &lt;span class="nc">Fr&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>: &lt;span class="kt">usize&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="nc">G2Affine&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">derive_point_for_pok&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">mul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sk&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">into&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>which would imply that $\pi =R*sk$, dropping the negative. Huh. Idk. There&amp;rsquo;s very possibly a negative bug somewhere in my life.&lt;/p>
&lt;p>We either need to derive $pk_9$ by means unknown, or else, if the proofs are malleable by some twisted algebra, we win. The proofs don&amp;rsquo;t look malleable, and the rng doesn&amp;rsquo;t look game-able.&lt;sup id="fnref:5">&lt;a href="https://thork.net/posts/2024-01-24-solving-zk-hack-iv-puzzle-2-supervillain/#fn:5" class="footnote-ref" role="doc-noteref">5&lt;/a>&lt;/sup>&lt;/p>
&lt;p>To the reading!&lt;/p>
&lt;h3 id="he-looks-to-the-reading">he looks to the reading&lt;/h3>
&lt;p>revisit keywords:&lt;/p>
&lt;ul>
&lt;li>delinearize BLS signatures
&lt;ul>
&lt;li>neither paper uses the word. chatGPT is reluctant and handwavey to define it. only few &lt;a href="https://github.com/w3f/bls">mentions&lt;/a> appear in a search. chat suggests that it involves checking an additional pairing constraint on a secondary hash function, meaning our OG hash function could be underconstrained.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>B-KEA - feed the paper to chat, who says:
&lt;ul>
&lt;li>The Bilinear Knowledge of Exponent Assumption (B-KEA) is defined over group elements and a bilinear map. It states that, if there exists an algorithm that can produce a certain group element in a bilinear group setting, then there also exists an extractor that can compute a related exponent.&lt;/li>
&lt;li>That basically says, if we can produce the faulty proof $\pi$, then we can extract $sk$ and break the discrete log problem. That seems bearish.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>proof of possession - feed the paper to chat, who says:
&lt;ul>
&lt;li>Proofs of Possession (PoPs) are mechanisms used to demonstrate that a party possesses a secret key corresponding to a given public key, without revealing the secret key itself. PoPs typically involve the party generating a cryptographic proof, such as a signature using their private key, and then presenting this proof alongside their public key. The verifier checks the proof to ensure that it was indeed created with the corresponding private key. This method ensures that a party cannot claim to own a public key without actually possessing the associated private key.
&lt;ul>
&lt;li>that is indeed what we&amp;rsquo;re trying to break. We want $\pi=sk*-R$, which we can only compute if already have &lt;code>sk&lt;/code>. We have &lt;code>pk&lt;/code> we want to derive &lt;code>sk&lt;/code> to break the PoP.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h4 id="interlude">Interlude&lt;/h4>
&lt;p>&lt;em>I read the papers some more, and found them not entirely helpful. Their proofs asserted that everything was fine, which seemed untrue. So I screwed around on the whiteboard a bit more to commit the equations to heart before going to bed. In the morning, as I lay in bed dreaming of algebra, there was insight. I ran to the algebra machine&lt;/em>.&lt;/p>
&lt;h4 id="honey-get-the-randomness-malleability-hammer">honey, get the randomness malleability hammer&lt;/h4>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="c1">// on second glance, the random nonces are less random than they once seemed 🙊
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="cp">#[test]&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">test_rng&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// let G = G1Projective::generator().into_affine();
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rng&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="k">mut&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ark_std&lt;/span>::&lt;span class="n">rand&lt;/span>::&lt;span class="n">rngs&lt;/span>::&lt;span class="n">StdRng&lt;/span>::&lt;span class="n">seed_from_u64&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">20399&lt;/span>&lt;span class="k">u64&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">G2Affine&lt;/span>::&lt;span class="n">rand&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rng&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">R_1&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">mul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Fr&lt;/span>::&lt;span class="n">from&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">R_9&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">mul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Fr&lt;/span>::&lt;span class="n">from&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="fm">assert_eq!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">derive_point_for_pok&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">R_1&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// pass
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="fm">assert_eq!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">derive_point_for_pok&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">9&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">R_9&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// pass
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">use&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ark_ff&lt;/span>::&lt;span class="n">Field&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">five_inv&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Fr&lt;/span>::&lt;span class="n">from&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="k">u64&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">inverse&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">unwrap&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">R_9_to_1&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">R_9&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">into_affine&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">mul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">five_inv&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">into_affine&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="fm">assert_eq!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R_1&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">R_9_to_1&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// pass
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// oh fun, we nearly done
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">//
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// we can construct stuff.
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;em>&lt;a href="https://github.com/thor314/puzzle-supervillain/blob/8b967f9e8640c25fb26e5df13c0cdd50c4d503fc/src/main.rs#L166C1-L183C2">not so random&lt;/a> anymore!&lt;/em>&lt;/p>
&lt;p>described in greater depth in &lt;code>puzzle.solve()&lt;/code> above.&lt;/p>
&lt;h2 id="changelog">changelog&lt;/h2>
&lt;ul>
&lt;li>2024-01-23 - majority of log written&lt;/li>
&lt;li>2024-01-24
&lt;ul>
&lt;li>solved the problem while sleeping, woke up with divine insight&lt;/li>
&lt;li>copy log over, edited for readability&lt;/li>
&lt;li>wrote solve() and deets()&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="footnotes">footnotes&lt;/h2>
&lt;section class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1" role="doc-endnote">
&lt;p>It doesn&amp;rsquo;t. We can choose $\pi$ however we want.&amp;#160;&lt;a href="https://thork.net/posts/2024-01-24-solving-zk-hack-iv-puzzle-2-supervillain/#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2" role="doc-endnote">
&lt;p>Sorry, I play a little fast and loose with whether I start from 1 or 0, so don&amp;rsquo;t pay too much attention to the indices.&amp;#160;&lt;a href="https://thork.net/posts/2024-01-24-solving-zk-hack-iv-puzzle-2-supervillain/#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:3" role="doc-endnote">
&lt;p>He did not win.&amp;#160;&lt;a href="https://thork.net/posts/2024-01-24-solving-zk-hack-iv-puzzle-2-supervillain/#fnref:3" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:4" role="doc-endnote">
&lt;p>Woe unto thee, Kobi, architect of ledgers and guardian of chains! Kobi you fool, for your help, I shall betray you! Your fields shall burn as I deceive your L2 light clients! We hack-eth your PoK and thus rain fire and brimstone upon nodes! With this hammer, I do decentralize your walls and scatter shards, crypto-Canaan anew. As Pharaoh&amp;rsquo;s heart was hardened, so shall be the fate of thy PoK.&amp;#160;&lt;a href="https://thork.net/posts/2024-01-24-solving-zk-hack-iv-puzzle-2-supervillain/#fnref:4" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:5" role="doc-endnote">
&lt;p>Hard enough, he did not look.&amp;#160;&lt;a href="https://thork.net/posts/2024-01-24-solving-zk-hack-iv-puzzle-2-supervillain/#fnref:5" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/section></description><category domain="https://thork.net/categories/cryptography/">cryptography</category><category domain="https://thork.net/tags/cryptography/">cryptography</category><category domain="https://thork.net/tags/puzzle/">puzzle</category><category domain="https://thork.net/tags/zk/">zk</category><category domain="https://thork.net/tags/pairing/">pairing</category><category domain="https://thork.net/tags/zkhack/">zkhack</category></item><item><title>Solving ZK Hack IV puzzle 1 - Gamma Ray</title><link>https://thork.net/posts/2024-01-18-solving-zk-hack-iv-puzzle-1-gamma-ray/</link><guid isPermaLink="true">https://thork.net/posts/2024-01-18-solving-zk-hack-iv-puzzle-1-gamma-ray/</guid><pubDate>Thu, 18 Jan 2024 00:00:00 +0000</pubDate><author>thorck@protonmail.com (Thor)</author><description>&lt;p>&lt;img src="https://thork.net/photos/2024-01-18-Solving/twisted-elliptic-curve-moon-puzzle-1.jpeg" alt="">&lt;/p>
&lt;p>&lt;em>What follows is a solution to &lt;a href="https://zkhack.dev/zkhackIV/puzzleF1.html">ZK Hack IV, Puzzle 1&lt;/a>. ZK Hack is a series of zero-knowledge cryptography CTFs, workshops, hackathons, and study groups. Thanks to the ZK Hack organizers for creating this CTF, Geometry for this puzzle, and the ZK Hack community. Thanks to Paul Gafni and Daira Hopwood for discussion.&lt;/em>&lt;/p>
&lt;p>You might be interested in this post if you&amp;rsquo;re interested in:&lt;/p>
&lt;ul>
&lt;li>a smattering of zk-related trivia about nullifiers and elliptic curves&lt;/li>
&lt;li>The process of (cryptographic) puzzle solving&lt;/li>
&lt;/ul>
&lt;p>&lt;em>Recommended reading order&lt;/em>:&lt;/p>
&lt;ul>
&lt;li>read this post top to bottom for a concise description and solution of the puzzle first, some mathematical nuggets to hold onto, and finally the messy stream of consciousness log of problem solving.&lt;/li>
&lt;li>read this post back to front for the order in which it was actually written (messy problem solving log, then cleaned up and bloggified knowledge nuggets).&lt;/li>
&lt;/ul>
&lt;h2 id="puzzlesolve">&lt;code>puzzle.solve()&lt;/code>&lt;/h2>
&lt;h3 id="background">background&lt;/h3>
&lt;p>&lt;em>all the knowledge nuggets you need to solve the puzzle&lt;/em>&lt;/p>
&lt;ul>
&lt;li>We have a &lt;a href="https://en.wikipedia.org/wiki/Merkle_tree">merkle tree&lt;/a> constructed with the &lt;a href="https://eprint.iacr.org/2019/458.pdf">poseidon hash function&lt;/a> over elliptic curve &lt;a href="https://eprint.iacr.org/2014/595.pdf">MNT_753_4&lt;/a>, with public keys stored at the leaves.&lt;/li>
&lt;li>Miyaji–Nakabayashi–Takano (MNT) curves are pairs of elliptic curves suited for &lt;a href="https://www.zellic.io/blog/what-are-elliptic-curve-pairings/">elliptic curve pairings&lt;/a>; that is, the (scalar, base) field for MNT_753_4 correspond to the (base, scalar) field for MNT_753_6.&lt;/li>
&lt;li>A &lt;a href="https://zips.z.cash/protocol/protocol.pdf#nullifierconcept">nullifier&lt;/a> in Zcash is a unique hash of the public keys stored at the leaves of the merkle tree. For curve generator point $G$ and secret scalar $s$, the public key $pk$ is computed simply, $pk=sG$. Thus the nullifier may be computed: $N=H(pk)$.&lt;sup id="fnref:1">&lt;a href="https://thork.net/posts/2024-01-18-solving-zk-hack-iv-puzzle-1-gamma-ray/#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>&lt;/li>
&lt;li>The prover in the nullifier argument provides a ZK proof of knowledge of $s$ such that the nullifier hash is correct:&lt;sup id="fnref:2">&lt;a href="https://thork.net/posts/2024-01-18-solving-zk-hack-iv-puzzle-1-gamma-ray/#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup>
&lt;ul>
&lt;li>$N=_?H(pk)=H(sG)$&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>In this puzzle, we use the &lt;a href="https://eprint.iacr.org/2016/260.pdf">Groth16&lt;/a> prover to construct a zk proof of the correctness of the hash.&lt;/li>
&lt;li>Zcash uses the &lt;a href="https://zips.z.cash/protocol/protocol.pdf#jubjub">Jubjub&lt;/a> elliptic curve for their nullifier argument. Jubjub does not support a pairing function.&lt;/li>
&lt;li>As remarked on in &lt;a href="https://zips.z.cash/protocol/protocol.pdf#concreteextractorjubjub">Zcash spec lemma 5.4.7&lt;/a>:&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>&lt;strong>Lemma 5.4.7&lt;/strong>. Let $P=(u,v) \in \mathbb J^{(r)}.$ Then $(u,-v) \not \in \mathbb J^{(r)}$ (subgroup of Jubjub of order r).&lt;/p>
&lt;/blockquote>
&lt;p>The lemma lends itself to a storage optimization for nullifiers over Jubjub: if $P=pk=(u,v)$, (note the switch to $u,v$ denotes that we are using &lt;a href="https://en.wikipedia.org/wiki/Twisted_Edwards_curve">Twisted Edwards&lt;/a> form, rather than &lt;a href="https://en.wikipedia.org/wiki/Elliptic_curve">Weierstrass form&lt;/a>, expanded on in more detail below). We may choose to only store the $u$ coordinate of $pk$, to avoid redundancy.&lt;/p>
&lt;p>That is, instead of taking the hash $H(pk)$ as suggested above, the Zcash nullifier takes the hash of only one coordinate: $H(pk.x)$. This optimization is particular to Jubjub, not universal to all curves; and in particular, is not true of MNT_753_4.&lt;/p>
&lt;p>We now demonstrate the exploit.&lt;/p>
&lt;p>==🔴Stop! This is the 🚨puzzle solving police🚨! You have been given everything you need to find your own solution! Take ten minutes, pull out your pen, and reap the joy and mathematical gainz of problem solving🔴==&lt;/p>
&lt;p>.&lt;/p>
&lt;p>.&lt;/p>
&lt;p>.&lt;/p>
&lt;p>.&lt;/p>
&lt;p>.&lt;/p>
&lt;p>.&lt;/p>
&lt;p>.&lt;/p>
&lt;p>.&lt;/p>
&lt;p>.&lt;/p>
&lt;h3 id="the-sploit">the &amp;lsquo;sploit&lt;/h3>
&lt;p>&lt;em>The puzzle solution in short&lt;/em>&lt;/p>
&lt;p>Suppose $pk = sG = (x,y)$, and nullifier $N=H(sG.x)$. Recall that nullifiers must be unique to avoid double-spends. We give a constructive algorithm to obtain $s&amp;rsquo;$ such that $N=H((s&amp;rsquo;G).x)$.&lt;/p>
&lt;p>Let $s&amp;rsquo;G=(x,-y)$, &lt;em>assuming such a point exists on our subgroup&lt;/em>. Let the order of generator $G$ be denoted $n$. Then:
$$\begin{aligned}
s&amp;rsquo;G &amp;amp;= \mathcal O - sG \\
&amp;amp;= nG - sG \\
&amp;amp;= (n-s)G \\
\therefore s'&amp;amp;=n-s
\end{aligned}$$&lt;/p>
&lt;p>If there exists $s'$ such that $s&amp;rsquo;G=(x,-y)$ is a point on the curve, then nullifier $N$ may not be unique, that is:
$$H(sG.x) = H((x,y).x) = H(x) = H((x,-y).x)= H(s&amp;rsquo;G.x) $$&lt;/p>
&lt;p>Which we may implement as follows:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="sd">/// given a leaked secret, return the double spend secret
&lt;/span>&lt;span class="sd">&lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">get_hack&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">leaked_secret&lt;/span>: &lt;span class="nc">MNT4BigFr&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="nc">MNT4BigFr&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">MNT4BigFr&lt;/span>::&lt;span class="n">from&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">MNT6BigFr&lt;/span>::&lt;span class="n">MODULUS&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">leaked_secret&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;em>&lt;a href="https://github.com/thor314/puzzle-gamma-ray/blob/main/src/main.rs">github link&lt;/a>&lt;/em>&lt;/p>
&lt;p>One hiccup. We assumed (hoped) that $(x,-y)$ lay on the curve. On Jubjub, it does not. Why does it exist on &lt;code>MNT_753_4&lt;/code>? It&amp;rsquo;s time to put our twisted Edwards hats on.&lt;/p>
&lt;p>&lt;img src="https://thork.net/photos/2024-01-18-Solving/elliptic-curve-hat.jpeg.jpeg" alt="">&lt;/p>
&lt;h2 id="puzzledeets">&lt;code>puzzle.deets()&lt;/code>&lt;/h2>
&lt;p>&lt;em>extended background on twisted Edwards elliptic curves, Zcash lemma 5.4.7&lt;/em>&lt;/p>
&lt;h3 id="twisted-edwards-and-proof-zcash-lemma-547httpszipszcashprotocolprotocolpdfconcreteextractorjubjub-with-added-notes">Twisted Edwards, and proof &lt;a href="https://zips.z.cash/protocol/protocol.pdf#concreteextractorjubjub">Zcash Lemma 5.4.7&lt;/a> with added notes&lt;/h3>
&lt;p>&lt;em>This lemma was initially confusing; thanks to several folks who helped clarify, including the author of the lemma herself. Historical record of my confusion notes &lt;a href="https://hackmd.io/@cryptograthor/BycdetdFT">here&lt;/a>.&lt;/em>&lt;/p>
&lt;p>It may be helpful to first recall the twisted Edwards (tE) curve equation[^3]:
$$au^2 + v^2 = c^2(1 + du^2v^2)$$&lt;/p>
&lt;p>The point addition law:
$$\begin{aligned}
u_3 &amp;amp;= \frac{u_1v_2 + v_1u_2}{1 + du_1u_2v_1v_2}\\
v_3 &amp;amp;= \frac{v_1v_2 - au_1u_2}{1 - du_1u_2v_1v_2}
\end{aligned}$$&lt;/p>
&lt;p>And the point doubling law:
$$\begin{aligned}
u' &amp;amp;= \frac{2uv}{1 + du^2v^2}\\
v' &amp;amp;= \frac{v^2 - au^2}{1 - du^2v^2}
\end{aligned}$$&lt;/p>
&lt;p>Finally, note that on a tE curve, $-P=(-u,v)$, not $(u,-v)$. Further, all of $(\pm u, \pm v)$ lie on the curve, but may or may not lie on the given subgroup. The point at infinity is denoted $\mathcal O = (0,1)$.&lt;/p>
&lt;p>&lt;strong>Lemma 5.4.7&lt;/strong>. Let $P=(u,v) \in \mathbb J^{(r)}.$ Then $(u,-v) \not \in \mathbb J^{(r)}$ (subgroup of Jubjub of order r).&lt;/p>
&lt;p>&lt;strong>Proof&lt;/strong>
If $P=\mathcal O_J$ is the point at infinity then $(u,v)=(0,1),$ but $(u,-v)=(0,-1)$ which does not lie on the subgroup. All other points $P$ have odd order. &lt;em>Because $P$ lies on some subgroup of order $r$, chosen to be an odd prime&lt;/em>.&lt;/p>
&lt;p>Further, $v\ne 0$, since if $v=0$:
$$au^2+0^2=1\implies u=\pm \sqrt {1/a}$$
An application of the group doubling law, gives:
$$
\begin{aligned}
u' &amp;amp;= \frac{0}{1 + 0}=0 \\
v' &amp;amp;= \frac{0 - au^2}{1 - 0}=-au^2= -a(1/a)=-1
\end{aligned}
$$&lt;/p>
&lt;p>Which implies that $[2]P=(0,-1)$ (which does not lie on the subgroup, as argued above) then $&lt;a href="%5B2%5DP">2&lt;/a>=(0,1)=O$, which obtains $P$ of even order, a further contradiction.&lt;/p>
&lt;p>Now, anticipating contradiction, let $P=(u,v), Q=(u,-v)$ be points on the subgroup. By the doubling formula, we have that $[2]Q=-[2]P$:
$$\begin{aligned}
[2]Q.u &amp;amp;= \frac{2u(-v)}{1 + du^2v^2}=-\frac{2uv}{1 + du^2v^2}=(-[2]P).u\\
[2]Q.v &amp;amp;= \frac{v^2 - au^2}{1 - du^2v^2}=[2]P.v=(-[2]P).v
\end{aligned}$$&lt;/p>
&lt;p>But also, $&lt;a href="-P">2&lt;/a>=-[2]P$. Therefore either:&lt;/p>
&lt;ul>
&lt;li>$Q=-P$, a contradiction, since $u=0$ only for $\mathcal O$&lt;/li>
&lt;li>Or, doubling is not injective on the subgroup, which contradicts the subgroup&amp;rsquo;s having odd order.&lt;/li>
&lt;/ul>
&lt;h3 id="lemma-547-applied-to-mnt_753_4">Lemma 5.4.7 applied to &lt;code>MNT_753_4&lt;/code>&lt;/h3>
&lt;p>Returning to the problem, we might ask why we were able to assume that, given $P=(x,y)$ over &lt;code>MNT_753_4&lt;/code>, why $(x,-y)$ existed? Because the Prover was defined over good old affine, Weierstrass point coordinates, not Twisted Edwards coordinates!&lt;/p>
&lt;p>In the Weierstrass affine coordinate system, we&amp;rsquo;re working over an entirely different group law, and if $(x,y)$ exists on the subgroup, $(x,-y)$ exists too.&lt;/p>
&lt;p>&lt;em>now we steal all the coins, ahem, submit a whitehat bug report&lt;/em>
&lt;img src="https://thork.net/photos/2024-01-18-Solving/black-dude-behind-tree-hungry.jpeg.jpeg" alt="">&lt;/p>
&lt;h2 id="puzzlelog">&lt;code>puzzle.log()&lt;/code>&lt;/h2>
&lt;p>&lt;em>My puzzle solving log, cut short for readability. You might be interested in this as a map of my state of mind while thinking about the puzzle. Keeping a log helps navigate overwhelming context dumps, and to avoid doing wrong and/or stupid things repeatedly. I use Obsidian to take these notes, for which I have written &lt;a href="https://github.com/thor314/obsidian-setup">an extensive setup and usage guide&lt;/a>. To see the full log, see &lt;a href="https://hackmd.io/@cryptograthor/ByU_8Sdta">this hackmd&lt;/a>.&lt;/em>&lt;/p>
&lt;p>After a bit of information gathering, we know a few things:&lt;/p>
&lt;ul>
&lt;li>curves: we&amp;rsquo;re using the MNT4/6 cycle of curves for our nullifier, which support a pairing function. Zcash uses the BLS12-381 (pairing-supporting) curves for the nullifier argument. No actually, they use Jub-jub for this, which is pairing-unfriendly.
&lt;ul>
&lt;li>&lt;del>both are pairing friendly&lt;/del>.&lt;/li>
&lt;li>MNT embedding degree 4 and 6, &lt;del>while the BLS curves have embedding degree 12. Larger embedding degree means less efficient pairing computation, and a harder DLP&lt;/del>.&lt;/li>
&lt;li>&lt;del>BLS has a smaller field size&lt;/del>.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>nullifier argument:
&lt;ul>
&lt;li>$N:= H(sk,r)$ where N is the nullifier, H the poseidon hash, r a random nonce, and sk the secret key
&lt;ul>
&lt;li>It does not look like there is a random nonce included included in the hash eval; we see &lt;code>LEAFH::evaluate(&amp;amp;leaf_crh_params, [leaked_secret])&lt;/code>, where leaf params are just the poseidon config, and leaked secret is the secret key.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>the zcash-style proof should show:
&lt;ul>
&lt;li>knowledge of (sk, r) such that the nullifier hash is correct&lt;/li>
&lt;li>knowledge of (v,a,r) such that H(v,a,r)=C - that the coin exists
&lt;ul>
&lt;li>v the note&amp;rsquo;s value, a the address, C a commitment to the coin&amp;rsquo;s existence&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>that nullifier N has not already been published&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Poseidon is an algebraic hash function used in zk circuits. Its parameter selection here looks a little different from the parameters that Zcash selected, though a vuln in Poseidon parameter selection would be surprising to me, given the hints. Two differences noted in param selection, from the zcash spec:
&lt;ul>
&lt;li>partial_rounds = 56 - &lt;strong>diff; partial_rounds = 29&lt;/strong>&lt;/li>
&lt;li>alpha = 5 - &lt;strong>diff; alpha = 17&lt;/strong>&lt;/li>
&lt;li>didn&amp;rsquo;t check the vector of raw values, or the MDS matrix&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Groth16 - the only element of the Groth16 proving system that factors in here is the specification of the SpendCircuit, so it seems unlikely that we&amp;rsquo;ll find any Groth16-related bugs. The circuit checks:
&lt;ul>
&lt;li>outputvar from root&lt;/li>
&lt;li>paramsvar&amp;rsquo;s for leaf_params and two_to_one_params (which are the same)&lt;/li>
&lt;li>fpvar witness for secret&lt;/li>
&lt;li>outputvar from nullifier&lt;/li>
&lt;li>g1var constant for g1 generator&lt;/li>
&lt;li>pathvar witness for proof&lt;/li>
&lt;li>to construct leaf_g:
&lt;ul>
&lt;li>assign g1var constant for G1 generator (base)&lt;/li>
&lt;li>construct public key from secret key&lt;/li>
&lt;li>leaf_g is the public key&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>assert that:
&lt;ul>
&lt;li>secret &amp;lt; MNT6&amp;rsquo;s modulus&lt;/li>
&lt;li>nullifier hash is correctly calculated&lt;/li>
&lt;li>leaf_g lies in the merkle tree at index 2
&lt;ul>
&lt;li>recall leaf_g = g1_generator * secret&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Shape of the problem, we have:
&lt;ul>
&lt;li>a merkle tree, with the target leaked secret at index 2, configured to hash with poseidon over the MNT4BigFr Field&lt;/li>
&lt;li>a nullifier constructed from the hash of the leaked secret&lt;/li>
&lt;li>we want to construct a cheater-secret such that:
&lt;ul>
&lt;li>secret &amp;lt; MNT6&amp;rsquo;s modulus&lt;/li>
&lt;li>nullifier hash is correctly calculated:
&lt;ul>
&lt;li>&lt;code>N == LeafHG::evaluate(&amp;amp;params, &amp;amp;[my_secret])?;&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>leaf_g lies in the merkle tree at index 2
&lt;ul>
&lt;li>recall &lt;code>leaf_g = (g1_generator * my_secret).x&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>in other words, we have: want construct a secret that obtains a hash collision:
&lt;ul>
&lt;li>&lt;code>h(params, cheat_secret) == h(params, secret)&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;pre tabindex="0">&lt;code> root
/ \
h(h0,h1) h(h2,h3)
| | | |
h0 h1 h2 h3 (this row: leaves.bin)
| | | |
l0 l1 l2 l3
^(leaked_secret.bin)
&lt;/code>&lt;/pre>&lt;h3 id="issue-how-are-we-going-to-construct-a-hash-collision">Issue: how are we going to construct a hash collision?&lt;/h3>
&lt;p>&lt;a href="https://twitter.com/__zkhack__/status/1747574583538925572">hint 1&lt;/a> updated: see zcash spec lemma 5.4.7 for bob&amp;rsquo;s reasoning on why to only use the x coordinate when storing public keys.&lt;/p>
&lt;p>&lt;strong>Lemma 5.4.7&lt;/strong>. Let $P=(u,v) \in \mathbb J^{(r)}.$ Then $(u,-v) \not \in \mathbb J^{(r)}$.&lt;/p>
&lt;ul>
&lt;li>initial thought: the hint suggests that the x coordinate for the point on the chosen curve may not be unique, i.e. that for $P=(x,y)$, there may exist $y'$ such that $Q=(x,y')$. Then we would have:
&lt;ul>
&lt;li>a new nullifier: $N=H(s)$ and $N'=H(s')$&lt;/li>
&lt;li>but with leaf: $l=(s&amp;rsquo;G).x=(sG).x$, such that the merkle proof verifies correctly.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>by the hint, it seems likely that the point we want is $s&amp;rsquo;G=(x,-y)$.&lt;/li>
&lt;/ul>
&lt;p>Given leaked secret $s$ such that $k=(sG).x$, we want to obtain $s'$ such that:
$$k'=(s&amp;rsquo;G).x=(x,-y).x = x = (x,y).x = (sG).x =k$$
Is it possible that $(-s)G=(x,-y)$? Seems unlikely. Checks: nope. Let&amp;rsquo;s read the proof very carefully.&lt;/p>
&lt;p>It&amp;rsquo;s not immediately obvious how we will peel off $s'$, i.e. solve the discrete log problem. Jubjub, the zcash curve which the lemma describes, does not have a pairing. Can we exploit the pairing?
$$e(s'*G_1, G_2)=e(G_1, s'*G_2)= s'*e(G_1,G_2)=s&amp;rsquo;G_T$$
Maybe if solving the discrete log problem in $G_T$ or $G_2$ is easy, but still, that feels unlikely to be it. But Jubjub isn&amp;rsquo;t pairing friendly; mnt4/6 is, so that feels like a big hint that we just haven&amp;rsquo;t figured out how to use yet. Might be worth throwing an hour at the whiteboard.&lt;/p>
&lt;p>Still, we&amp;rsquo;ve made some progress. The state of the problem is now:&lt;/p>
&lt;ul>
&lt;li>we want to obtain $s'$ such that $s&amp;rsquo;G=(x,-y).x$ where $(x,y).x=sG$. Solving for $s'$ directly would involve solving the discrete log problem.&lt;/li>
&lt;li>we&amp;rsquo;re pretty sure that there&amp;rsquo;s an exploit on the pairing somehow to help us solve for $s'$.&lt;/li>
&lt;/ul>
&lt;h3 id="lemma-and-proof">Lemma and proof&lt;/h3>
&lt;p>&lt;strong>Lemma 5.4.7&lt;/strong>. Let $P=(u,v) \in \mathbb J^{(r)}.$ Then $(u,-v) \not \in \mathbb J^{(r)}$ (subgroup of Jubjub of order r). Proof:&lt;/p>
&lt;ul>
&lt;li>if P is the point at infinity (u,v)=(0,1), but -P=(0,-1) which does not lie on the curve.&lt;/li>
&lt;li>all other points P have odd order.
&lt;ul>
&lt;li>further, $v\ne 0$. if v=0, then $au^2=1$ then $[2]P=(0,-1)$ then $&lt;a href="%5B2%5DP">2&lt;/a>=(0,1)=O$, which would have even order.
&lt;ul>
&lt;li>&lt;em>unsure what the $au^2=1$ argument is saying. $a$ is part of the curve equation, $y^2=x^3 +ax + b$&lt;/em>. Check: $0=u^3+au+b$&amp;ndash;nope. Maybe there&amp;rsquo;s another curve representation they&amp;rsquo;re working over, something twisted-ed related. Oh, u,v denotes twisted edwards notation.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>if $v\ne 0$ then $v\ne -v$.&lt;/li>
&lt;li>Let $Q=(u,-v)$ be a point on the curve. We will show $Q=-P$ is a contradiction.
&lt;ul>
&lt;li>Then $[2]Q=-[2]P=&lt;a href="-P">2&lt;/a>$, but $Q.v=-P.v$ is contradiction since $-v \ne v$
&lt;ul>
&lt;li>&lt;em>what? Should expand on that&lt;/em>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Therefore either $Q=-P$ or else doubling is not injective on the curve, which contradicts the curve&amp;rsquo;s odd order.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>Hint 2 released:&lt;/p>
&lt;blockquote>
&lt;p>Note that the MNT curves are represented in Weierstrass from in the circuit, and use the fact that both (x,y) and (x,-y) are valid points for spending&lt;/p>
&lt;/blockquote>
&lt;p>Which confirms my suspicion that we want $s'=(x,-y)$. Let&amp;rsquo;s find it.&lt;/p>
&lt;h3 id="algebraic-insight">algebraic insight!&lt;/h3>
&lt;ul>
&lt;li>$P=sG=(x,y)$&lt;/li>
&lt;li>$-P=O-P=O-sG=s&amp;rsquo;G=(x,-y)$&lt;/li>
&lt;/ul>
&lt;p>We observe that for the order $n$ of generator $G$, we have $nG=O$, therefore:&lt;/p>
&lt;p>$$\begin{aligned}
s&amp;rsquo;G &amp;amp;= O-sG = nG-sG\\
&amp;amp;=(n-s)G\\
\therefore s'&amp;amp;=(n-s)
\end{aligned}$$
However, note that $s$ lives in the &lt;code>mnt4::Fr&lt;/code> field, but the order $n$ of generator $G$ will live in &lt;code>mnt4::Fp&lt;/code>, so we&amp;rsquo;ll have to do a type conversion.&lt;/p>
&lt;h2 id="changelog">changelog&lt;/h2>
&lt;ul>
&lt;li>2024-01-22 - final draft&lt;/li>
&lt;li>2024-01-24 - zk hack solution published; fix typos in blog and publish&lt;/li>
&lt;/ul>
&lt;h2 id="footnotes">footnotes&lt;/h2>
&lt;p>^3]: Why do we use twisted Edwards curves at all? Speed. Every Montgomery form elliptic curve has a birational Twisted Edwards equivalent, though not necessarily a simple Edwards equivalent. Edwards curves have fast addition algorithms, and Twisted Edwards curves are nearly as fast, enabling faster point operations for many curves currently in use.&lt;/p>
&lt;section class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1" role="doc-endnote">
&lt;p>Unrelated to this puzzle, the Zcash nullifier argument also includes a nonce, so that a public key can be used more than once: $N=H(pk,r)$.&amp;#160;&lt;a href="https://thork.net/posts/2024-01-18-solving-zk-hack-iv-puzzle-1-gamma-ray/#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2" role="doc-endnote">
&lt;p>Unrelated to this puzzle, the Zcash nullifier must prove two other conditions: (1) that nullifier $N$ has not already been published&amp;ndash;this is not included in the proof for the puzzle, but is checked elsewhere in the puzzle driver. Would need to be checked in the proof in real life. And (2) the existence of the note(s) (coins) that will be spent, via proof of knowledge of: ($v,a,r$); the value of the note, address possessing the note, and random nonce, respectively.&amp;#160;&lt;a href="https://thork.net/posts/2024-01-18-solving-zk-hack-iv-puzzle-1-gamma-ray/#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/section></description><category domain="https://thork.net/categories/cryptography/">cryptography</category><category domain="https://thork.net/tags/cryptography/">cryptography</category><category domain="https://thork.net/tags/puzzle/">puzzle</category><category domain="https://thork.net/tags/zk/">zk</category><category domain="https://thork.net/tags/elliptic-curves/">elliptic-curves</category><category domain="https://thork.net/tags/zkhack/">zkhack</category></item><item><title>Walking Through Distributed Key Generation (DKG)</title><link>https://thork.net/posts/2022_4_21_dkg/</link><guid isPermaLink="true">https://thork.net/posts/2022_4_21_dkg/</guid><pubDate>Thu, 21 Apr 2022 00:00:00 +0000</pubDate><author>thorck@protonmail.com (Thor)</author><description>&lt;h1 id="walking-through-distributed-key-generation-dkg">Walking Through Distributed Key Generation (DKG)&lt;/h1>
&lt;p>This post is a follow-along to a talk on Distributed Key Generation at DevConnect, Amsterdam 2022 (&lt;a href="https://drive.google.com/file/d/1ejnOVwu6JPaUl72jiDGglndYFIIT0Rdh/view?usp=sharing">slides here&lt;/a>). It also functions as a stand-alone resource.&lt;/p>
&lt;p>The post is intended to briefly introduce the motivation for threshold signatures, relating them to multisignatures, and clarify a Distributed Key Generation protocol introduced by cryptographer Torben Pedersen &lt;a href="https://link.springer.com/chapter/10.1007/3-540-46766-1_9">in 1991&lt;/a>, and applied by Gennaro and Goldfeder in their threshold scheme, &lt;a href="https://eprint.iacr.org/2020/540.pdf">GG20&lt;/a>, S3.1, p14.&lt;/p>
&lt;p>Implementing the GG20 protocol has been a core component of my work at &lt;a href="https://entropy.xyz">Entropy&lt;/a>, the trustless decentralized asset custodian.&lt;/p>
&lt;p>A mathematically complete description of the protocol begins in the latter half of the document; the first half remains technically high level, and should be accessible to non-technical readers.&lt;/p>
&lt;p>Questions and corrections are invited via &lt;a href="https://twitter.com/cryptograthor">Twitter&lt;/a>.&lt;/p>
&lt;h2 id="knowledge-check">Knowledge Check!&lt;/h2>
&lt;p>It will be helpful to be familiar with several concepts:&lt;/p>
&lt;ul>
&lt;li>Langrange interpolation, on which Shamir Secret Sharing relies&lt;/li>
&lt;li>Shamir Secret Sharing Scheme&lt;/li>
&lt;li>Commitment Schemes, in particular Pedersen&amp;rsquo;s Commitment Scheme&lt;/li>
&lt;/ul>
&lt;p>(everything in cryptography is a scheme, except for programs which are invariably in Rust)&lt;/p>
&lt;p>Each have reasonably good Wikipedia articles.&lt;/p>
&lt;p>Pallier encryption, Schnorr&amp;rsquo;s zk proof of knowledge of exponent, and the Feldman modification to Shamir secret sharing, which are used in the signing protocol, will not be necessary here.&lt;/p>
&lt;h2 id="what-threshold-signature-and-briefly-what-multisignatures">What Threshold Signature? And Briefly, What Multisignatures&lt;/h2>
&lt;p>A t-of-n secret sharing of some secret $u$ produces $n$ &amp;ldquo;shares&amp;rdquo; of $u$, of which any $t+1$ can reconstruct $u$. There is some notation confusion whether t-of-n should refer to a minimum of $t+1$ parties or $t$ parties; $t+1$ is the convention used by GG20, so we will use it here.&lt;/p>
&lt;p>A similar construction to the threshold signature is the t-of-n multisignature:&lt;/p>
&lt;ul>
&lt;li>$n$ parties hold private keys&lt;/li>
&lt;li>Some trusted entity (eg. a smart contract) collects $t+1$ unique signatures on some message, (eg. a transaction), and then signs that message itself&lt;/li>
&lt;/ul>
&lt;p>In a t-of-n threshold signature:&lt;/p>
&lt;ul>
&lt;li>Key Generation: Some secret key $u$, secret to all parties, is constructed. At the end of a DKG, every party possesses some secret share from which $u$ may be reconstructed. No party knows another party&amp;rsquo;s share.&lt;/li>
&lt;li>Any $t+1$ parties can collaborate to sign a message, producing a single signature. No party learns of any other party&amp;rsquo;s share. The protocol effectively reconstructs the private key, $u$, in zero knowledge, and uses it to sign a message.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Relative Advantages of a multisignature&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Simplicity of implementation: the trusted party simply must store and verify $t+1$ signatures, before constructing its own&lt;/li>
&lt;li>No new cryptography&lt;/li>
&lt;li>Async: parties do not need to all be online at the same time&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Relative Advantages of a threshold signature&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Space-efficiency, verifier-efficiency: Only a single signature need be stored and verified&lt;/li>
&lt;li>Modularity: The implementation logic of a threshold signature can be isolated from the environment that verifies the signature, allowing a threshold signature to be computed entirely off-chain&lt;/li>
&lt;/ul>
&lt;h2 id="but-what-if-we-justcentralized-it">But what if we just&amp;hellip;centralized it?&lt;/h2>
&lt;p>In this centralized setting, a trusted party, Alice, will construct all the shares, and simply send them to the $n$ parties. Alice may simply pick her favorite secret sharing scheme (eg. Shamir&amp;rsquo;s) to do so.&lt;/p>
&lt;p>In the centralized setting, Alice is a back door to the threshold signature, as she may simply construct signatures using the private key. In the next section we will eliminate Alice as a trusted party for true DKG.&lt;/p>
&lt;p>The centralized scheme has one &amp;ldquo;key&amp;rdquo; property: Alice can bring her own private key to the threshold signature, where the rest of the threshold signing protocol continues as if Alice were a normal player. That is to emphasize that, in the DKG model, an entirely new secret key is constructed that no player possesses.&lt;/p>
&lt;p>Therefore, if Alice were to delete her private key, then the setting would be identical to the next section. Since it is impossible to verify whether Alice has kept a copy, we turn to cryptography to construct Distributed Key Generation.&lt;/p>
&lt;h2 id="cryptography-time">Cryptography time!&lt;/h2>
&lt;p>This section follows S3.1 in GG20. In several places, I&amp;rsquo;ve added notation not in the original paper. These are explicitly labelled, noted to avoid creating confusion.&lt;/p>
&lt;p>The DKG proceeds in 3 phases. Participants in the protocol will be called &amp;ldquo;players&amp;rdquo;.&lt;/p>
&lt;p>A word of warning: there will be many moving parts here. Note-taking is encouraged.&lt;/p>
&lt;h3 id="phase-1">Phase 1&lt;/h3>
&lt;p>Each player selects their private share, and broadcasts a commitment to it. The $:=$ notation is used here to denote a calculation done by a given player.&lt;/p>
&lt;p>Each player, $P_i$ constructs:&lt;/p>
&lt;ul>
&lt;li>$u_i$, an initial private share, selected at random by the player&lt;/li>
&lt;li>$(KGD_i,KGC_i):=(g^{u_i}, Com(g^{u_i}))$, a commitment &lt;strong>to the hiding&lt;/strong> of $u_i$ to send to each other player. Note that this is essentially &lt;strong>a commitment to a commitment of a secret.&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>Each player receives:&lt;/p>
&lt;ul>
&lt;li>$KGC_j\space \forall j$, the commitment broadcast by all other players&lt;/li>
&lt;li>$E_j\space \forall j$, the (Pallier) public key, broadcast by all players. This will be used to encrypt messages sent to player $P_j$, and to perform the signing protocol. That it is a Pallier cryptosystem public key is irrelevant for the scope of this post.&lt;/li>
&lt;/ul>
&lt;h3 id="phase-2">Phase 2&lt;/h3>
&lt;p>Players first decommit their public keys (broadcasting $g^{u_i}$), then perform a secret sharing and determine the group secret sharing.&lt;/p>
&lt;p>Denote the $(t,n)$-secret sharing scheme of the secret $u_i$ (my notation):
$$SS_{t,n}(u_i:\text{secret},z:\text{evaluation index})=u_i+a_{i,1}z+&amp;hellip;+a_{i,t}z^t$$
and the the jth evaluation of the polynomial,
$$s_{i,j} =SS_{t,n}(u_i,j)=u_i+a_{i,1}j+..+a_{i,t}j^t=u_i+\sum_k a_{j,k}j^k$$
Where the coefficients $a_{i,k}$ are selected at random by $P_i$. The reader should verify that $P_i$ can compute $s_{i,j}$ for all $j$.&lt;/p>
&lt;p>Each player, $P_i$:&lt;/p>
&lt;ul>
&lt;li>assigns $y_j=KGD_j\space \forall j&amp;lt;t$, the hiding of the of each other player&amp;rsquo;s secret. Henceforth, the symbol $\forall$ denotes for all players.&lt;/li>
&lt;li>computes $y:=\prod y_i=g^{\sum u_i}$, the &amp;ldquo;public key&amp;rdquo; of the group; the group&amp;rsquo;s &amp;ldquo;secret key&amp;rdquo; will be the sum of shares, $\sum u_i$.&lt;/li>
&lt;li>selects random coefficients to construct $SS_{t,n}(u_i,z)$, the temporary secret sharing polynomial for their own secret.&lt;/li>
&lt;li>computes shares $s_{i,j}\space \forall j$ to privately send to each other player. Player $P_i$ receives $s_{j,i}$ from each other player $P_j$.&lt;/li>
&lt;li>computes $x_i:=\sum_j s_{j,i}$&lt;/li>
&lt;li>computes the hiding of $x_i$, $X_i=g^{x_i}$&lt;/li>
&lt;/ul>
&lt;p>Denote the group secret $x=\sum u_i$.&lt;/p>
&lt;p>We will now show that Player $P_i$ has computed a $(t,n)$ secret share of $x$:
$$x_i:=\sum_j s_{j,i} = \sum_j SS_{t,n}(u_j,i)$$
$$=(\sum_j u_j)+(\sum_j a_{j,1})i+&amp;hellip;+(\sum_j a_{j,t})i^t$$
$$=SS_{t,n}(\sum_j u_j: \text{group secret}, i: \text{player index})$$
Note that coefficients of this polynomial are shared by all players, but not known to any player. Therefore, each player $P_i$ now posseses in $x_i$ a $(t,n)$-secret-sharing of $x$.&lt;/p>
&lt;h3 id="phase-3-verifiability">Phase 3: Verifiability&lt;/h3>
&lt;p>Suppose Player $P_i$ were able to lie to player $P_j$ about the value of $X_i$, the hiding of their secret key share. This would allow player $P_i$ to volunteer to participate in the signing protocol, but prevent the creation of a valid signature, without anyone knowing!&lt;/p>
&lt;p>By adopting the Feldman VSS, which extends the Shamir SS, Player $P_i$ gains the ability to calculate each other player $P_j$&amp;rsquo;s commitment $X_j$. This step prevents any player from cheating in the signature protocol.&lt;/p>
&lt;p>In the Feldman VSS, each party also publishes the hiding of each coefficient of their polynomial, $g^{a_{i,k}}\space \forall k$. For convenience, denote the coefficients of our final polynomial:
$$b_k=\sum_j a_{j,k} \quad \text{and} \quad b_0 = \sum u_j$$&lt;/p>
&lt;p>Then player $P_i$&amp;rsquo;s share can be expressed:
$$SS_{t,n}(\sum_j u_j,i)=\sum_j b_ji^j$$&lt;/p>
&lt;p>By exploiting commitments from Feldman&amp;rsquo;s, any player $P_i$ may calculate $g^{b_k}\space \forall k$:
$$g^{b_k}=g^{\sum_j a_{j,k}}=\prod_j g^{a_{j,k}}$$&lt;/p>
&lt;p>Where each player broadcast the hiding of their coefficients $g^{a_{i,k}}$ as part of the Feldman protocol.
Player $P_i$ exploits the above lemma to compute $g^{b_k}$, and may thereby compute $X_j$:
$$X_j=g^{x_j}=g^{\sum_k b_kj^k}=\prod_k {(g^{b_k})}^{j^k}$$&lt;/p>
&lt;p>There are several checks from Phase 3 that are elided. They are straightforward, but would require introducing Pallier encryption, Schnorr&amp;rsquo;s zk proof-of-knowledge-of-exponent, and a square-free proof, which I feel would bloat the scope of this post.&lt;/p>
&lt;h2 id="wrap-up">Wrap up&lt;/h2>
&lt;p>Yay, you made it here! Things you may have learned:&lt;/p>
&lt;ul>
&lt;li>An application of repeated secret key sharing to construct a distributed secret&lt;/li>
&lt;li>What threshold signatures are, and how they compare to multisignatures&lt;/li>
&lt;li>Taking good notes is hard!&lt;/li>
&lt;/ul></description><category domain="https://thork.net/categories/cryptography/">cryptography</category><category domain="https://thork.net/tags/cryptography/">cryptography</category><category domain="https://thork.net/tags/explainer/">explainer</category><category domain="https://thork.net/tags/mpc/">MPC</category></item><item><title>Experiment: How Effective is Prototyping? Measuring Developer productivity in Python and Rust with Project Euler</title><link>https://thork.net/posts/20211015_how_effective_prototyping_euler50/</link><guid isPermaLink="true">https://thork.net/posts/20211015_how_effective_prototyping_euler50/</guid><pubDate>Sat, 16 Oct 2021 00:00:00 +0000</pubDate><author>thorck@protonmail.com (Thor)</author><description>
&lt;p>
I was interested in the time-effectiveness of problem-solving different programming languages. I looked for research around comparative developer productivity across languages, but came up mostly empty-handed, so I constructed an personal experiment using &lt;a href="https://projecteuler.net/">Project Euler&lt;/a>.&lt;/p>
&lt;p>
Professionally, I write Rust for the smart contract platform &lt;a href="https://near.org/">NEAR&lt;/a>. Years ago, I learned to program in Python, though have not written much for several years, and wanted an excuse to brush up.&lt;/p>
&lt;div id="outline-container-headline-1" class="outline-3">
&lt;h3 id="headline-1">
In Short:
&lt;/h3>
&lt;div id="outline-text-headline-1" class="outline-text-3">
&lt;p>I solved the first 50 problems in Project Euler in Rust and Python, randomizing the language I started in, then translating the solution to the remaining language. I timed each part of the process. After an adjustment period to regain Python familiarity, I found that solving problems in Python was substantially faster than solving the problems in Rust, even despite my greater experience with the Rust programming language.&lt;/p>
&lt;p>
My data suggests that any problem taking &lt;strong>35 minutes&lt;/strong> or more to develop a solution in Rust alone could plausibly be &lt;strong>solved quicker by first developing a solution in Python, then translating the solution to Rust.&lt;/strong>&lt;/p>
&lt;p>
I note that there are confounding factors to my case study whose effect sizes I have little capacity to estimate the impact of; readers are advised to refrain from over-extrapolating from a case study by a single developer on his sample size of 50 (actually 24, see Confounding Factors below) scope-constrained problems.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-2" class="outline-3">
&lt;h3 id="headline-2">
Results (&lt;a href="https://docs.google.com/spreadsheets/d/1CJIizjl2PEgeTu3wbmF9wxItOhcdYtNh3fnXZdqDh-8/edit?usp=sharing">see spreadsheet&lt;/a>, &lt;a href="https://github.com/thor314/euler-rs-py">see code&lt;/a>):
&lt;/h3>
&lt;div id="outline-text-headline-2" class="outline-text-3">
&lt;ul>
&lt;li>Prototyping was worthwhile for any task of reasonable (&lt;strong>&amp;gt;35 minutes&lt;/strong>) complexity; ie. where the solution was not immediately obvious&lt;/li>
&lt;li>Python-first implementations averaged &lt;strong>20 minutes&lt;/strong>, Rust-first implementations averaged &lt;strong>34.7 minutes&lt;/strong>, a &lt;strong>~40% time difference&lt;/strong>&lt;/li>
&lt;li>Solution translation time averaged &lt;strong>12.5 minutes&lt;/strong>, both from Rust to Python and vice versa.&lt;/li>
&lt;li>The most effective way to increase the time on any problem was to be interrupted repeatedly, followed by trying to solve the problem in a dumb way, and having to re-approach the problem.&lt;/li>
&lt;li>Imperative-style solutions were generally harder to debug than Functional-style solutions. Debugging accounted roughly half (not measured) of the total time spent programming. Minimizing the number of mutable variables was useful for debugging, as was preemptively writing debugging logs.&lt;/li>
&lt;li>Usage of the Python REPL was less cumbersome than writing Rust tests for testing pieces of code.&lt;/li>
&lt;li>My Python fluency increased over the course of the experiment, while my Rust fluency appeared to stay roughly constant.&lt;/li>
&lt;li>I hypothesize that language minutia (eg. references, mutability constraints, types) competes with attention toward the total solution, reducing developer productivity for complexity.&lt;/li>
&lt;li>Further, I hypothesize that simply whiteboarding out a solution before implementation may have even greater returns on efficiency, as whiteboarding has the least language minutia of all languages :I&lt;/li>
&lt;/ul>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-3" class="outline-3">
&lt;h3 id="headline-3">
Data and Graphs
&lt;/h3>
&lt;div id="outline-text-headline-3" class="outline-text-3">
&lt;div id="outline-container-headline-4" class="outline-4">
&lt;h4 id="headline-4">
Data
&lt;/h4>
&lt;div id="outline-text-headline-4" class="outline-text-4">
&lt;table>
&lt;tbody>
&lt;tr>
&lt;td>&lt;/td>
&lt;td class="align-right">mins: solve&lt;/td>
&lt;td class="align-right">mins: translate&lt;/td>
&lt;td class="align-right">mins: rs solve&lt;/td>
&lt;td class="align-right">mins: py solve&lt;/td>
&lt;td class="align-right">mins: rs from py&lt;/td>
&lt;td class="align-right">mins: py from rs&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AVERAGES:&lt;/td>
&lt;td class="align-right">25.9&lt;/td>
&lt;td class="align-right">12.3&lt;/td>
&lt;td class="align-right">28.8&lt;/td>
&lt;td class="align-right">22.4&lt;/td>
&lt;td class="align-right">12.5&lt;/td>
&lt;td class="align-right">12.1&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AVERAGE: first 25&lt;/td>
&lt;td class="align-right">23.3&lt;/td>
&lt;td class="align-right">12.1&lt;/td>
&lt;td class="align-right">22.0&lt;/td>
&lt;td class="align-right">24.7&lt;/td>
&lt;td class="align-right">12.6&lt;/td>
&lt;td class="align-right">11.7&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AVERAGE: 10-25&lt;/td>
&lt;td class="align-right">30.9&lt;/td>
&lt;td class="align-right">16.6&lt;/td>
&lt;td class="align-right">28.1&lt;/td>
&lt;td class="align-right">37.3&lt;/td>
&lt;td class="align-right">20.9&lt;/td>
&lt;td class="align-right">14.6&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AVERAGE: last 25&lt;/td>
&lt;td class="align-right">28.2&lt;/td>
&lt;td class="align-right">12.5&lt;/td>
&lt;td class="align-right">34.7&lt;/td>
&lt;td class="align-right">20.0&lt;/td>
&lt;td class="align-right">12.4&lt;/td>
&lt;td class="align-right">12.5&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-5" class="outline-4">
&lt;h4 id="headline-5">
Graphs
&lt;/h4>
&lt;div id="outline-text-headline-5" class="outline-text-4">
&lt;p>&lt;img src="https://thork.net/photos/prototyping_effectiveness/2021-10-15_13-19-35_screenshot.png" alt="/photos/prototyping_effectiveness/2021-10-15_13-19-35_screenshot.png" title="/photos/prototyping_effectiveness/2021-10-15_13-19-35_screenshot.png" />&lt;/p>
&lt;p>
&lt;img src="https://thork.net/photos/prototyping_effectiveness/2021-10-15_13-19-22_screenshot.png" alt="/photos/prototyping_effectiveness/2021-10-15_13-19-22_screenshot.png" title="/photos/prototyping_effectiveness/2021-10-15_13-19-22_screenshot.png" />&lt;/p>
&lt;/div>
&lt;/div>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-6" class="outline-3">
&lt;h3 id="headline-6">
Looking but not Finding: The Curse of Walled Garden Research Journals
&lt;/h3>
&lt;div id="outline-text-headline-6" class="outline-text-3">
&lt;p>On Google Scholar and Arxiv, I searched for:&lt;/p>
&lt;ul>
&lt;li>&lt;code class="verbatim">&amp;#34;programming language&amp;#34; efficiency OR productivity&lt;/code>&lt;/li>
&lt;li>&lt;code class="verbatim">&amp;#34;programming language&amp;#34; AND (productivity OR efficiency OR effectiveness OR prototyping)&lt;/code>&lt;/li>
&lt;li>&lt;code class="verbatim">comparative &amp;#34;programming language&amp;#34; AND (productivity OR efficiency OR effectiveness)&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>among several other permutations of keywords.&lt;/p>
&lt;p>
Unfortunately, results from Google Scholar of any relevance were universally behind journal paywalls, while results from Arxiv were irrelevant. &lt;a href="https://www.sciencedirect.com/science/article/abs/pii/S2210650220303734">Several&lt;/a> &lt;a href="https://ieeexplore.ieee.org/abstract/document/5615739">results&lt;/a> &lt;a href="https://journals.sagepub.com/doi/abs/10.1177/1094342004048537">looked&lt;/a> reasonably promising, but lacking a subscription to SAGE, Elsevier, or IEEE, the closest I got to any substantial research about the comparative effectiveness of programming languages in terms of developer time was &lt;a href="https://redmonk.com/dberkholz/2013/03/25/programming-languages-ranked-by-expressiveness/">an article by Donnie Berkholz&lt;/a> on &amp;#34;Programming languages ranked by expressiveness&amp;#34; from 2013, which was reasonably interesting, but entirely off topic for this review.&lt;/p>
&lt;p>
I&amp;#39;m not a professional researcher. If someone with more research cred has insight about how I might do better research in the future without paying for journal subscription, I&amp;#39;d love to receive an email from you at thorck at protonmail dot com.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-7" class="outline-3">
&lt;h3 id="headline-7">
Confounding Factors
&lt;/h3>
&lt;div id="outline-text-headline-7" class="outline-text-3">
&lt;p>Over the course of the experiment, my life was at least a standard deviation over the mean Level of Chaos (the other LoC). Travel, a job change, and personal life changes extended the length of the experiment and disrupted a couple problems. If disrupted, I stopped my running timer and added the taken time to the remaining time to complete the problem. Nevertheless, the variance effects of my meatspace environment on my programmatic attention is difficult to account for. I dropped two problems (18 and 26) from analysis, as they were subject to repeated interruptions and false starts.&lt;/p>
&lt;p>
My sample size amounts to 50 problems, of which the first 10 were unusually easy to complete. My python fluency returned over the course of the experiment, and the earlier questions are unlikely to reflect the actual results of the experiment. The final 24 problems are a better indicator on effectiveness of Python implementation, and from what I draw my results. This is admittedly, not a large sample size.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-8" class="outline-3">
&lt;h3 id="headline-8">
Further Questions
&lt;/h3>
&lt;div id="outline-text-headline-8" class="outline-text-3">
&lt;p>If I return to Project Euler for problems 51-X, I would like to test my hypotheses that whiteboarding would likely be as effective as, if not more effective, prototyping a solution in Python.&lt;/p>
&lt;p>
I&amp;#39;d also be interested in swapping Rust out for another language. Among my industry&amp;#39;s lingua franca are Rust, Go, Typescript, and Solidity, though I maintain a personal fascination with more functional languages. Of these, I&amp;#39;d probably choose to swap out Rust for Go (admittedly a very unfunctional language).&lt;/p>
&lt;p>
Finally, Project Euler problems are reasonably small-scope, and well defined by programming standards. Research on the value of prototyping, for instance, a command-line tool, or a website would be interesting. How significant would differences in language libraries be in confounding the value of prototyping? Is prototyping a better tool for closely scoped problems in general, where the translation from one language to another is reasonably direct? I would guess that, the greater the difference between language libraries, the less worthwhile using another language to prototype would be, as I have done here. But for problems of simple algorithm definition, prototyping seems likely to be at a local maximum for developer utility.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-9" class="outline-3">
&lt;h3 id="headline-9">
Actionable Takeaways
&lt;/h3>
&lt;div id="outline-text-headline-9" class="outline-text-3">
&lt;ul>
&lt;li>Outlining Matters. Prototyping was found to be worthwhile when the problem was well-scoped but sufficiently complex. Any problem taking more than 35 minutes to develop a solution for in Rust was worth prototyping in Python. If given choice of language to take a programming interview, I would consider choosing Python over Rust. If taking an interview in Rust, I would emphasize the importance of sketching out a solution on a whiteboard, or as code stubs, before implementing the solution.&lt;/li>
&lt;li>Debugging Sucks, So Don&amp;#39;t Write Bugs! (or catch them quickly). Anticipating bugs by setting up tests and debugging logs before setting up implementation details was useful for reducing time spent locating problems. Generally prefer functional solutions to imperative solutions. A single mutable data structure is easier to debug than a collection of mutable variables.&lt;/li>
&lt;li>&amp;#34;Assembly of &lt;a href="https://www.amazon.com/Zen-Art-Motorcycle-Maintenance-Inquiry/dp/0060839872/ref=sr_1_1?dchild=1&amp;amp;keywords=zen+and+art+of+motorcycle&amp;amp;qid=1634413147&amp;amp;sr=8-1">Japanese bicycle&lt;/a> require great peace of mind!&amp;#34; Distractions are still the Antichrist. I dropped two outlier problems from the dataset. What they had in common: they were started in Rust, I had to rewrite each, and I was distracted and/or pulled away from each of them at least once. My bias to over-value attention health feels justified by the data. Start a problem with a clear mind, without distractions, or else get back to that state of mind.&lt;/li>
&lt;li>Think First: Solve the Right Problem with the Right Stuff. Not thinking first is as bad as getting distracted, and is a likely sign that I already am distracted. Reaching the right tools means actively thinking about implementation options before diving in. The reward is not having to rewrite my crappy code, and enjoying more concise and run with lower asymptotic bounds.&lt;/li>
&lt;/ul>
&lt;/div>
&lt;/div></description><category domain="https://thork.net/categories/programming/">programming</category><category domain="https://thork.net/tags/experiment/">experiment</category><category domain="https://thork.net/tags/programming/">programming</category><category domain="https://thork.net/tags/rust/">rust</category><category domain="https://thork.net/tags/python/">python</category></item><item><title>NFT Hype: What’s What and What’s to Come</title><link>https://thork.net/posts/2020_nft_hype/</link><guid isPermaLink="true">https://thork.net/posts/2020_nft_hype/</guid><pubDate>Fri, 26 Mar 2021 12:16:49 +0100</pubDate><author>thorck@protonmail.com (Thor)</author><description>
&lt;blockquote>
&lt;p>What&amp;#39;s that? Anna asked.&lt;/p>
&lt;p>
&amp;#34;It&amp;#39;s anything you want it to be,&amp;#34; said Ted the Tokenmaker.&lt;/p>
&lt;p>
&amp;#34;But that&amp;#39;s not a thing,&amp;#34; said Anna.&lt;/p>
&lt;p>
&amp;#34;That&amp;#39;s the magic of crypto,&amp;#34; said Olly the Optimist, &amp;#34;the more people believe
in these, the more they are.&amp;#34;&lt;/p>
&lt;p>
…&lt;/p>
&lt;p>
&amp;#34;What&amp;#39;s that?&amp;#34; said Alex.&lt;/p>
&lt;p>
&amp;#34;It&amp;#39;s anything you want it to be,&amp;#34; said Anna.&lt;/p>
&lt;p>
&amp;#34;Sounds like magic beans,&amp;#34; said Alex.&lt;/p>
&lt;p>
&amp;#34;They are until they&amp;#39;re not, unless they are, but I like them so who cares?&amp;#34; said Anna.&lt;/p>
&lt;/blockquote>
&lt;p>
This article responds to the &lt;a href="https://medium.com/zeroknowledge/nfts-so-hot-but-would-you-buy-one-31fe280d7cb7">thoughtful piece&lt;/a> by Anna Rose, which is recommended
pre-reading, as this piece will attempt to respond to the posed questions:
What exactly are NFTs for, and what would make me buy an NFT?&lt;/p>
&lt;p>
How to price NFTs is beyond the scope of this article, but I recommend &lt;a href="https://medium.com/@nic__carter/why-nfts-are-hard-to-explain-48f0ab0a35bf">Nic Carter&lt;/a> and
&lt;a href="https://newsletter.banklesshq.com/p/how-to-value-nfts">William Peaster&lt;/a> for some useful takes.&lt;/p>
&lt;div id="outline-container-headline-1" class="outline-3">
&lt;h3 id="headline-1">
What are NFTs for, and Why you might want one
&lt;/h3>
&lt;div id="outline-text-headline-1" class="outline-text-3">
&lt;p>Much of the noise around NFTs stems from the question, what the hell am I going
to do with this? An NFT is a sufficiently general technology that, in theory,
there are &lt;a href="https://twitter.com/jbrukh/status/1367471632932085760">many&lt;/a> uses for NFTs; yet in practice, art and gaming have attracted the
greatest share of attention. Looming behind these is the spectre of rampant
price speculation, divorcing many NFT products from any semblance of intuitive
valuations. At its &lt;a href="https://www.theverge.com/2021/3/11/22325054/beeple-christies-nft-sale-cost-everydays-69-million">highest echelons&lt;/a>, the NFT investment game is being played by
&lt;a href="https://cryptounit.com/2021/01/25/flamingodao-acquires-cryptopunk-nft-for-761k-in-ether/">DAOs&lt;/a> and whales playing a game of chicken, taking turns in bidding increasingly
unbelievable amounts, in turn raising the overall value of their impressive
collections (which may then be quietly sold for a &lt;a href="https://twitter.com/pranksyNFT/status/1366589669568360457">tidy profit&lt;/a>).&lt;/p>
&lt;p>
NFTs aren&amp;#39;t just for speculation. They&amp;#39;re also for status signalling, as &lt;a href="https://medium.com/@nic__carter/why-nfts-are-hard-to-explain-48f0ab0a35bf">Nic
Carter&lt;/a> of Castle Island Ventures points out:&lt;/p>
&lt;blockquote>
&lt;p>Most likely, I’m betting that newly-rich Ethereum enthusiasts will see the NFTs
as a kind of totem of status, signalling membership in an exclusive club (people
that had the foresight to buy the first NFTs on Ethereum, or the financial clout
to buy them once they got popular.) My best thesis here is that these are
effectively a form of resalable social signalling, like a digital Hermes Birkin
bag. The actual contents of the NFT is largely irrelevant.&lt;/p>
&lt;/blockquote>
&lt;p>
We see this playing out on Crypto Twitter, where &lt;a href="https://www.larvalabs.com/cryptopunks">CryptoPunks&lt;/a> and &lt;a href="https://mooncatrescue.com/">MoonCats&lt;/a> are
becoming increasingly common avatars for prominent crypto community members.
Paying the exorbitant price to acquire one of these cements one&amp;#39;s identity as a
crypto OG, or at least as a community member willing to pay for their status.&lt;/p>
&lt;p>
Finally though art NFT prices are wild, prices go to supporting artists and
artist communities (assuming you&amp;#39;re not being duped into &lt;a href="https://cryptobriefing.com/crypto-art-fraud-rarible-sparks-governance-discussion/">fraudulent art&lt;/a>!). Much
like any piece of art, if a piece on an NFT marketplace like &lt;a href="https://mintbase.io/">Mintbase&lt;/a>, &lt;a href="https://opensea.io/">OpenSea&lt;/a>,
or &lt;a href="https://rarible.com/">Rarible&lt;/a> catches your eye enough to warrant purchase, do some research about
the artist to verify the piece is non-fraudulent before buying. If you do
purchase an art NFT, virtual reality worlds like CryptoVoxels and Somnium Space
allow users to show off NFTs inside their virtual property (which of course, you
may purchase with an NFT).&lt;/p>
&lt;p>
It&amp;#39;s worth taking a time-out to address one criticism of NFTs that seems
undeserved. &lt;a href="http://cryptoart.wtf/">Environmentalist&lt;/a> &lt;a href="https://joanielemercier.com/the-problem-of-cryptoart/">critics&lt;/a> have accused NFT artists consuming
excessive amounts of energy on Proof of Work chains. But as artist &lt;a href="https://sterlingcrispin.blogspot.com/2021/02/crypto-art-sky-is-not-falling.html">Stirling
Crispin points out&lt;/a> in an excellently researched essay, NFTs account for about &lt;a href="https://etherscan.io/gastracker">3%&lt;/a>
of gas consumption on Ethereum, which in turn uses 0.02% of the total Earth CO2
footprint. There are greater obstacles to energy neutrality than a small
community of talented people exploring ways to monetize their work:&lt;/p>
&lt;blockquote>
&lt;p>The real problem is the giant 72%…it’s fossil fuel subsidies, its coal power
plants, its fracking. If your number one motivation is the environment, and
you’re behaving rationally, it makes sense to focus on the big things most and
the little things least.&lt;/p>
&lt;/blockquote>
&lt;p>
Further, NFT platforms are not married to Proof-of-Work blockchains. &lt;a href="https://near.mintbase.io/">Mintbase&lt;/a>
gives our users the tools to deploy their own NFT contracts. We opted to add
support for the Proof-of-Stake chain &lt;a href="https://near.org/">NEAR&lt;/a>, so that our users may minimize their
transaction costs and environmental impact.&lt;/p>
&lt;p>
So we&amp;#39;ve got speculation, status, and support for art and artist communities. I
haven&amp;#39;t exactly painted a glamorous picture of NFTs, so you&amp;#39;d be excused for the
suspicion that NFTs aren&amp;#39;t actually that cool. But you&amp;#39;d be WRONG. Downstream
from the hype cycle, some great projects are taking form, with NFTs at their
core.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-2" class="outline-3">
&lt;h3 id="headline-2">
Like what?
&lt;/h3>
&lt;div id="outline-text-headline-2" class="outline-text-3">
&lt;p>NFTs are a tool for collaboration and creation. The NFT might be best understood
as a &lt;a href="https://en.wikipedia.org/wiki/Public-key_cryptography">Public Key&lt;/a> for an item or service, on a public database. The departure from
a private database model allows for ease of collaboration, while guaranteeing
users to the longevity of their data.&lt;/p>
&lt;p>
Gaming NFTs, like &lt;a href="https://zkga.me/">Dark Forest&lt;/a>, &lt;a href="https://axieinfinity.com/">Axie Infinity&lt;/a>, and &lt;a href="https://godsunchained.com/">Gods Unchained&lt;/a> scratch the
surface of what&amp;#39;s possible, by tokenizing in-game assets, which are easily
exchanged on on-chain marketplaces. Further, gaming NFTs can do more than one
thing. Imagine if after spending 100 hours grinding in World of Warcraft, the
Sword of Coolness item you earned gave you a rare playing card in Hearthstone, a
skin in Fortnite, and a ticket to a League of Legends esports event. Since NFTs
live on a public ledger, a single NFT can do as many things as there are
services willing to attach value to that NFT. Storing game data as an NFT,
thereby on a public database, is a departure from prior models, where all data
is stored privately. Private databases limit not only users&amp;#39; ability to access
and profit from their efforts and data but also limits opportunities for
cross-pollination between projects.&lt;/p>
&lt;p>
Beyond gaming application NFTs in the decades to come are likely to be a force
for standardizing an extensible concept of ownership. Extensible, as the base
concept of an NFT is &lt;a href="http://erc721.org/">very simple&lt;/a>. Simplicity that can be extended as the use
case demands, by way of extending the base contract, or else by attaching
services to the ownership of an NFT. Much like smartphones have simplified the
interface to communication, transportation, and social media, NFTs simplify the
interface to ownership of access to goods and services.&lt;/p>
&lt;p>
The NFT-maximal future doesn&amp;#39;t have much in common with the NFT speculation
today, but for that goods and services that can be represented as NFTs will have
incentive to do so, so as to take advantage of ease of issuance, verification,
integration with other NFT-enabled businesses, and to reap royalty payments for
NFT transfers. Users will be able to view, manage, buy, and sell goods and
services across domains. Imagine if your gym membership, blog subscriptions,
rewards programs, event tickets, and more, could all be managed, bought, and
sold within a single application.&lt;/p>
&lt;p>
In place of the myriad interfaces we use daily to prove our right of access to
goods and services, from tickets to titles to coupons to login credentials, the
(1) public, uncensorable verifiability of NFTs, (2) ease of transfer of NFTs,
and (3) shared interface to each of these application areas mean that the realm
of possibilities of NFT-enabled technology is far-reaching.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-3" class="outline-3">
&lt;h3 id="headline-3">
Wrap up
&lt;/h3>
&lt;div id="outline-text-headline-3" class="outline-text-3">
&lt;p>To recap. What are NFTs for? At the moment, speculation, status signalling, and
artwork are the ascendant NFT applications. But NFTs are &lt;a href="https://threadreaderapp.com/thread/1365390455957950464.html">incredibly flexible&lt;/a> as
technological tools, and could someday be for lots of things, or at least, much
more than what they&amp;#39;re currently used for. Gaming applications are allowing
users to reap dividends from their play-time, by representing in-game assets as
NFTs. And an NFT future could involve much more (maybe after a hype cycle or
two).&lt;/p>
&lt;p>
The &lt;a href="https://twitter.com/cryptograthor">author&lt;/a> is a developer at Mintbase, a platform giving users an interface to
create and their own NFT minting contracts. We&amp;#39;re on &lt;a href="https://mintbase.io/">Ethereum&lt;/a> and have recently
launched on &lt;a href="https://near.mintbase.io/">NEAR testnet&lt;/a>! If you&amp;#39;re exploring what&amp;#39;s possible with NFTs, check
us out.&lt;/p>
&lt;p>
&lt;em>first posted at the [[&lt;a href="https://medium.com/zeroknowledge/nft-hype-whats-what-and-what-s-to-come-9a7642defcb1">https://medium.com/zeroknowledge/nft-hype-whats-what-and-what-s-to-come-9a7642defcb1&lt;/a>
][ZKPodcast blog]]&lt;/em>&lt;/p>
&lt;/div>
&lt;/div></description><category domain="https://thork.net/categories/crypto/">crypto</category><category domain="https://thork.net/tags/nft/">nft</category><category domain="https://thork.net/tags/crypto/">crypto</category><category domain="https://thork.net/tags/predictions/">predictions</category></item><item><title>Zero Knowledge: The Game</title><link>https://thork.net/posts/2020_zero_knowledge_game/</link><guid isPermaLink="true">https://thork.net/posts/2020_zero_knowledge_game/</guid><pubDate>Thu, 11 Feb 2021 12:16:49 +0100</pubDate><author>thorck@protonmail.com (Thor)</author><description>
&lt;p>
What makes a game a “blockchain game”? A simple definition: some part of the
state of the game is stored on a blockchain and is therefore publicly visible.
Think chess or tic-tac-toe: each move is a state change on a publicly visible
ledger. Compare this to games like League of Legends or Call of Duty, where the
game state lives entirely on a private game server.&lt;/p>
&lt;p>
Suppose we wanted to store the entire game state on-chain. But then, every
player would be able to see the entire “game board” and the position of every
opposing player. For games like chess, that’s fine. But can we build a game with
hidden information like Civilization where players have only a partial view of
their environment and opponents?&lt;/p>
&lt;p>
The game Dark Forest is exactly that. By using zkSNARK cryptography (zero
knowledge Succinct Non-interactive ARguments of Knowledge), the developers of
Dark Forest have designed a universe and created a game where the state is
completely stored on-chain. And yet each coordinate is hidden behind a hash that
players may compute. This way all participants can explore and conquer a corner
of the universe, and what they discover is consistent with what other
participants see when they explore that region.&lt;/p>
&lt;p>
This article will focus on the game mechanics that the Dark Forest team were
able to implement by applying SNARKs, and explore how these schemes push the
limit of possible game interaction beyond the limits of prior art, such as
commit-reveal schemes.&lt;/p>
&lt;div id="outline-container-headline-1" class="outline-3">
&lt;h3 id="headline-1">
The Game, Succinctly
&lt;/h3>
&lt;div id="outline-text-headline-1" class="outline-text-3">
&lt;p>In the game of Dark Forest, players compete with one another for interplanetary
territory. Players begin in complete darkness. A fog of war covers the universe
surrounding their home planet. To explore, the browser makes use of players’
local computing power to compute a hash for each coordinate. Thereafter, that
coordinate is revealed to the player and the map is slowly explored. Most
coordinates contain nothing but empty space, but some contain planets, either
uninhabited, or owned by other players. Once a planet is revealed, the browser
can check on-chain to see if another player has staked a claim to the revealed
rock, and see if other players try to take that planet.&lt;/p>
&lt;p>
Beginning with the one planet, a player (let’s call her Alice) publishes state
transitions from her original planet to other planets. A state transition moves
resources (population and silver) from an owned planet to the discovered planet,
thereby colonizing a new planet, or attacking another player’s planet. Moving
population between planets is limited by the origin planet’s range and
population to move. Players can also upgrade their planets with silver — an
in-game resource generated on certain planets that is used to purchase upgrades.&lt;/p>
&lt;p>
Planets gradually accumulate resources and, as players discover one another,
wars break out over high value planets. However, a player is not guaranteed to
know where an attack came from; only if the player has revealed the coordinates
of an attacking planet will they be able to see the red line indicative of an
enemy movement.
&lt;img src="https://thork.net/photos/zkg/zkg1.png" alt="/photos/zkg/zkg1.png" title="/photos/zkg/zkg1.png" />&lt;/p>
&lt;p>
What makes the game possible is &lt;strong>valid state transitions&lt;/strong>: how does the game
move resources between planets, as on-chain data, without revealing the data, or
committing invalid transitions?&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-2" class="outline-3">
&lt;h3 id="headline-2">
What can we do without SNARKing?
&lt;/h3>
&lt;div id="outline-text-headline-2" class="outline-text-3">
&lt;p>Here’s the problem: Alice has a planet X with population N, and wants to send n
people from X to planet Y, without revealing information about X, N, n, or Y to
the rest of the world (state transitions on silver are in effect identical to
population, so we’ll ignore them). Alice will have to prove:&lt;/p>
&lt;ul>
&lt;li>Ownership of X&lt;/li>
&lt;li>n &amp;lt; N&lt;/li>
&lt;li>A ship from planet X with n population has enough range to reach Y&lt;/li>
&lt;/ul>
&lt;p>Without SNARKs, Alice and company can still set their computers to the task of
de-fogging the map: hashing each coordinate, looking for planets. There are two
schemes that would allow Alice and company to play: 1) a centralized
coordinator, or 2) a commit reveal scheme.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-3" class="outline-3">
&lt;h3 id="headline-3">
Centralized Coordinator Scheme
&lt;/h3>
&lt;div id="outline-text-headline-3" class="outline-text-3">
&lt;p>In the centralized coordinator scheme, Alice and company submit state
transitions to a centralized coordinator with a god’s eye view. The coordinator
can see X, N, n, and Y, and easily verify that each transition is valid. Most
existing games rely on a centralized coordinator.&lt;/p>
&lt;p>
However, the centralized coordinator is a target for attacks and collusion,
either of which could halt the game or reveal the universe to the
attacking/colluding party. It’s not really a “Dark Forest” if someone has a
universal floodlight, as is the norm with games on hosted game servers! As a
cultural side note, companies who host games on servers assuage players’ fears
by creating strong punishments for those caught cheating or attempting to cheat
by hacking or other measures: players must trust the company’s promise to seek
out and ban cheating players.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-4" class="outline-3">
&lt;h3 id="headline-4">
Commit-Reveal
&lt;/h3>
&lt;div id="outline-text-headline-4" class="outline-text-3">
&lt;p>In a commit-reveal scheme, each participant has some secret s that they want to
hide from other players until some event occurs, or after a time delay T.
Players publish the hash of their secret, H(s), and reveal the secret after the
delay. Poker provides an example: players commit the hash of their cards at the
beginning of the round, publishing H(s), and a player reveals the value of s if
the hand does not terminate early.&lt;/p>
&lt;p>
In Dark Forest, players could agree on a time elapse to reveal their actions. If
a player revealed an invalid action after the time delay, they could have some
or all of their actions reverted, and optionally be punished in some other way
(removal, temporary deactivation, etc). In each period, players would commit the
hashes of their actions, H(a_1, …, a_k), and the game could proceed, except that
no action would be publicly visible until after the period elapsed, along with
X, N, n, and Y.&lt;/p>
&lt;p>
The core problem with a Commit-Reveal scheme is that information eventually must
be revealed. Actions are only private up to the point of the next period, not
for the entire duration of the game. This incentivizes a longer period of play,
but a long period slows game pace, without resolving the fundamental issue of
keeping game state private. Revealing concealed information at the end of each
interval is fine for round-based games like poker, but not for games with a vast
global state with a fog-of-war element (or for that matter, any application that
would like to keep mutable state elements indefinitely concealed).&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-5" class="outline-3">
&lt;h3 id="headline-5">
SNARKing through the Fog of Space
&lt;/h3>
&lt;div id="outline-text-headline-5" class="outline-text-3">
&lt;p>By applying a SNARK, Dark Forest players can submit proofs to the Dark Forest
contract that X, N, n, and Y satisfy the necessary conditions. The Dark Forest
contract verifies the proof, without ever having to know the values of X, N, n,
and Y. Therefore, a player can submit moves and upgrade actions to the smart
contract without allowing other players to know the contents of their actions.&lt;/p>
&lt;p>
This is almost perfect zero-knowledge. Other players are still able to see the
number of transactions submitted by their rivals, and the block at which each
transaction was included. From that on-chain information, a rival could
determine when a player was most and least likely to be online, thereby
receiving hints about when to attack. To eliminate even that information, Dark
Forest would have to operate on a blockchain with shielded addresses or a
privacy-enabled Layer 2.&lt;/p>
&lt;p>
&lt;img src="https://thork.net/photos/zkg/zkg2.png" alt="/photos/zkg/zkg2.png" title="/photos/zkg/zkg2.png" />&lt;/p>
&lt;p>
It’s important to note that this applies to more than just games. Zero knowledge
technology has broad application beyond Dark Forest. Experiments with zero
knowledge systems are underway in voting systems, financial privacy,
authentication without identification schemes, and even nuclear disarmament. In
a broader context, the power of zero knowledge schemes is to conceal
participants’ private information, while guaranteeing honest system-interaction.
The Dark Forest zero knowledge game may serve as an approachable introduction to
a field that has been respectfully referred to as “moon math”, in reference to
its complexity. By bringing broader awareness to this technology, Dark Forest is
a harbinger of novel zero-knowledge enabled experiments and applications,
introducing the advantages of greater privacy and security in new ways.&lt;/p>
&lt;p>
&lt;em>first posted at the &lt;a href="https://medium.com/zeroknowledge/zero-knowledge-the-game-688ec3709b41">ZKPodcast blog&lt;/a>&lt;/em>&lt;/p>
&lt;/div>
&lt;/div></description><category domain="https://thork.net/categories/crypto/">crypto</category><category domain="https://thork.net/tags/zeroknowledge/">zeroknowledge</category><category domain="https://thork.net/tags/cryptography/">cryptography</category><category domain="https://thork.net/tags/game/">game</category><category domain="https://thork.net/tags/explainer/">explainer</category></item><item><title>Tipping Cows, a Primer on Rust’s Most Bovine Data Structure</title><link>https://thork.net/posts/2020_cows_in_the_wild/</link><guid isPermaLink="true">https://thork.net/posts/2020_cows_in_the_wild/</guid><pubDate>Sun, 05 Jul 2020 00:00:00 +0000</pubDate><author>thorck@protonmail.com (Thor)</author><description>
&lt;p>
The year was 2020. An epidemic swept across the globe, driving all human life
indoors. Unrest concerning police action and longstanding racial inequality in
the US drove the very same back into the streets. Like cattle, driven back and
forth we were, with global sociopolitical and health trends our ranger.&lt;/p>
&lt;p>
In other words, there was never a better time to read an primer on Cows. Yes,
those magical moo-ers. No, that was a lie. This is going to be about the &lt;a href="https://doc.rust-lang.org/std/borrow/enum.Cow.html">Cow&lt;/a>
data structure in Rust, &lt;a href="https://www.infoworld.com/article/3560970/c-and-rust-programming-languages-continue-to-rise.html">systems programming language and global phenomenon&lt;/a>,
sorry for the confusion.&lt;/p>
&lt;p>
So now that I&amp;#39;ve lied to you once, I hear you asking &amp;#34;Why should I stay?&amp;#34; Well.
I offer ye nothing less than knowledge of dark bovine arts. Along the way you&amp;#39;ll
be sprinkled with Rust language insights and regular attempts at cow-humor.&lt;/p>
&lt;p>
When you&amp;#39;ve finished this cool glass of milk, you will know what a Cow is doing
when you spot one (in Rust that is, for the alternative consult a local farmer),
see several examples of Cow in practice, and maybe even raise your own Cows.
&lt;a href="https://www.youtube.com/watch?v=FavUpD_IjVY">Like this&lt;/a>. If you&amp;#39;re unfamiliar with smart pointers, lifetimes, or the
borrowing/ownership system in Rust, don&amp;#39;t panic. They&amp;#39;re totally on the docket.
Cowabunga.&lt;/p>
&lt;p>
But if you&amp;#39;re new to Rust, you might consider first visiting &lt;a href="https://medium.com/better-programming/rust-ownership-and-borrowing-9cf7f081ade0">one&lt;/a> &lt;a href="https://medium.com/better-programming/rust-ownership-and-borrowing-9cf7f081ade0">of&lt;/a> &lt;a href="https://doc.rust-lang.org/1.8.0/book/references-and-borrowing.html">the&lt;/a> many great
explainers of how the compiler helps us avoid careless reference errors by way
of its borrow checker. But if you&amp;#39;re not here to click links, here&amp;#39;s the basic
idea:&lt;/p>
&lt;p>
Similar to C and C++, Rust doesn&amp;#39;t have a garbage collector: a process that runs
in a program&amp;#39;s runtime that tracks which variables reference what data, and when
that data will no longer be used, so that memory can be freed up. Most languages
(Python, Java, Javascript, C#, Go, etc.) have a garbage collector, which is how
they manage memory. In C and C++, memory and references are generally managed by
the programmer (and sometimes, programmers make memory management mistakes that
may not be obvious until things break). In Rust, the borrow checker does memory
management for us, which is ergonomic (don&amp;#39;t have to manually manage memory),
safe (prevents security errors and issues, especially in concurrent programs),
and fast (on par with C and C++). By strictly enforcing a couple rules about
mutable and immutable references, we get these nice things.&lt;/p>
&lt;div id="outline-container-headline-1" class="outline-3">
&lt;h3 id="headline-1">
Enter Cows
&lt;/h3>
&lt;div id="outline-text-headline-1" class="outline-text-3">
&lt;p>If we run over to the &lt;a href="https://doc.rust-lang.org/std/borrow/enum.Cow.html">Zoo of Rust&amp;#39;s Creatures&lt;/a>, we find a Cow in captivity:&lt;/p>
&lt;div class="src src-rust">
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="k">pub&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">enum&lt;/span> &lt;span class="nc">Cow&lt;/span>&lt;span class="o">&amp;lt;&amp;#39;&lt;/span>&lt;span class="na">a&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">B&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">where&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">B&lt;/span>: &lt;span class="o">&amp;#39;&lt;/span>&lt;span class="na">a&lt;/span> &lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">ToOwned&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="nb">Sized&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Borrowed&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&amp;#39;&lt;/span>&lt;span class="na">a&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">B&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Owned&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">B&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">ToOwned&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>::&lt;span class="n">Owned&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;/div>
&lt;p>The zoo helpfully informs us,&lt;/p>
&lt;pre class="example">
_________________________________________
/ The type Cow is a smart pointer \
| providing clone-on-write functionality: |
| it can enclose and provide immutable |
| access to borrowed data, and clone the |
| data lazily when mutation or ownership |
| is required. The type is designed to |
| work with general borrowed data via the |
\ Borrow trait. /
-----------------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
&lt;/pre>
&lt;p>
Moo. That&amp;#39;s a lot to parse. There&amp;#39;s roughly three concepts baked into that
definition:&lt;/p>
&lt;ul>
&lt;li>What is clone-on-write functionality, and what&amp;#39;s the diff to copy-on-write&lt;/li>
&lt;li>Why this elitist smart pointer&lt;/li>
&lt;li>Where do lifetimes enter in&lt;/li>
&lt;/ul>
&lt;p>
If you know the answer to all of those questions, you can probably skip ahead to
examples. Don&amp;#39;t know? Read on!&lt;/p>
&lt;p>
Let&amp;#39;s start with why we care about these concepts: what do we win if we collect
them all? Well, in simplest form, we win an runtime optimization technique, for
when the data we&amp;#39;re handling will sometimes be acceptable to immutably borrow
(which is cheap), but other times will have to be owned, which would happen if
we had to mutate the data in some way. Back to the concepts.&lt;/p>
&lt;div id="outline-container-headline-2" class="outline-4">
&lt;h4 id="headline-2">
Copy-on-Write vs Clone-on-Write
&lt;/h4>
&lt;div id="outline-text-headline-2" class="outline-text-4">
&lt;p>&lt;a href="https://doc.rust-lang.org/std/marker/trait.Copy.html">Copy&lt;/a> and &lt;a href="https://doc.rust-lang.org/std/clone/trait.Clone.html">Clone&lt;/a> in Rust mean different things. In the non-Rust world, copying
usually means reproducing the data at a new location. This is cloning in Rust.
Copying is simpler and less expensive: reproduce the data if it&amp;#39;s statically
sized (ints, floats, bools, etc), or reproduce the pointer if the data is
dynamically sized (Vec, String, HashMap, etc). Copying usually happens
implicitly; meaning the compiler just does it for us, no muss, no fuss. Cloning,
not so: we have to explicitly tell the compiler to clone the data. Any data that
can be copied (only fixed size) can also be cloned (fixed or dynamically sized);
the reverse is not true. Copy is cheap, Clone is expensive.&lt;/p>
&lt;div class="src src-rust">
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="c1">// u32 implements Copy
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="w"> &lt;/span>: &lt;span class="kt">u32&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">42&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i_copy&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// copy occurs implicitly
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="fm">println!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Can use original int and copy: {}, {}&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i_copy&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i_clone&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">clone&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="fm">println!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Can use original int and clone: {}, {}&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i_clone&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="c1">// String implements Clone but not Copy.
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">s&lt;/span>: &lt;span class="nb">String&lt;/span>::&lt;span class="nb">From&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Don&amp;#39;t Panic&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">s_clone&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">s&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">clone&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="fm">println!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Can use original string and clone: {}, {}&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">s&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">s_clone&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="c1">// this will fail, since String does not implement Copy
&lt;/span>&lt;span class="c1">// let copy_s = s;
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;/div>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-3" class="outline-4">
&lt;h4 id="headline-3">
Smart Pointers are Runtime Friends
&lt;/h4>
&lt;div id="outline-text-headline-3" class="outline-text-4">
&lt;p>They say runners are are smart (don&amp;#39;t ask who &amp;#34;they&amp;#34; are, I&amp;#39;m sure they&amp;#39;re out
there somewhere). And Smart Pointers are your running friends. Smart pointers
were introduced by C++ in the 1990s as a tool to manage resources related to the
memory they&amp;#39;re pointing to. Like Rust, C++ doesn&amp;#39;t have automatic garbage
collection, and smart pointers were invented to prevent memory leak situations.
Rust does it&amp;#39;s best to manages memory without a garbage collector at
compile-time with it&amp;#39;s ownership system, but at runtime, it&amp;#39;s all smart
pointers. That&amp;#39;s why smart pointers aren&amp;#39;t generally a necessary concept to
anyone coming from a garbage collected language, like Java or Python (though
Java does have a &lt;a href="https://javamex.com/tutorials/synchronization_concurrency_8_copy_on_write.shtml">cow&lt;/a>).&lt;/p>
&lt;p>
In the context of Cows, the Cow smart pointer acts like a normal pointer when it
merely borrows data, but at runtime, the Cow smart pointer can take ownership of
the borrowed data. This is more expensive than borrowing the data: a borrow only
requires the borrower to keep a reference to the data, wherever it is. But
taking ownership requires the data to be cloned, meaning the runtime will have
to reproduce the data on the Heap (whenever we allocate memory in runtime, it&amp;#39;s
usually safe to assume it&amp;#39;s happening on the heap). If the data is particularly
large, (a long string or text file, for instance), lazily cloning only when it
becomes obviously necessary is a useful optimization.&lt;/p>
&lt;div class="src src-rust">
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="k">use&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">std&lt;/span>::&lt;span class="n">borrow&lt;/span>::&lt;span class="n">Cow&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">lazy_abs&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">input&lt;/span>: &lt;span class="kp">&amp;amp;&lt;/span>&lt;span class="nc">mut&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Cow&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="kt">i32&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">in&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="o">..&lt;/span>&lt;span class="n">input&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">len&lt;/span>&lt;span class="p">(){&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">v&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">input&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">];&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">v&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// Clone into vector if not already owned
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">input&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">to_mut&lt;/span>&lt;span class="p">()[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">v&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;/div>
&lt;p>From &lt;a href="https://doc.rust-lang.org/std/borrow/enum.Cow.html">Rust&amp;#39;s Cow documentation&lt;/a>.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-4" class="outline-4">
&lt;h4 id="headline-4">
Where do lifetimes enter
&lt;/h4>
&lt;div id="outline-text-headline-4" class="outline-text-4">
&lt;p>&lt;a href="https://doc.rust-lang.org/rust-by-example/scope/lifetime.html">Lifetimes&lt;/a> tell the borrow checker when a borrow is going to end. When a Cow
borrows some data, the Cow should never outlive the data. Further, if the Cow
takes ownership of the data with a clone, it makes sense that the cloned data
still shouldn&amp;#39;t outlive the original data. Rememer how we defined Cow? You don&amp;#39;t
have to, here it is again.&lt;/p>
&lt;div class="src src-rust">
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="k">pub&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">enum&lt;/span> &lt;span class="nc">Cow&lt;/span>&lt;span class="o">&amp;lt;&amp;#39;&lt;/span>&lt;span class="na">a&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">B&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// Cow doesn&amp;#39;t outlive data with lifetime &amp;#39;a
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="k">where&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// if Cow takes ownership, stay with the herd, keep lifetime &amp;#39;a
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">B&lt;/span>: &lt;span class="o">&amp;#39;&lt;/span>&lt;span class="na">a&lt;/span> &lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">ToOwned&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="nb">Sized&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Borrowed&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&amp;#39;&lt;/span>&lt;span class="na">a&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">B&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// Borrow a generic reference to data B, with lifetime &amp;#39;a
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Owned&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">B&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">ToOwned&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>::&lt;span class="n">Owned&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// We haven&amp;#39;t gotten here yet.
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;/div>
&lt;/div>
&lt;/div>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-5" class="outline-3">
&lt;h3 id="headline-5">
Putting it all together
&lt;/h3>
&lt;div id="outline-text-headline-5" class="outline-text-3">
&lt;p>You made it this far cowpoke. Hold onto your milk, because it&amp;#39;s time for a pop
quiz.&lt;/p>
&lt;p>
Suppose we&amp;#39;ve got a struct containing an immutable generic vector. How
would we update it to wrap a Cow?&lt;/p>
&lt;div class="src src-rust">
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="k">struct&lt;/span> &lt;span class="nc">VecWrapper&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">v&lt;/span>: &lt;span class="nb">Vec&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">impl&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">VecWrapper&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">new&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">v&lt;/span>: &lt;span class="nb">Vec&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="nc">Self&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">VecWrapper&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">v&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;/div>
&lt;p>
Well, for starters, we&amp;#39;re going to need to import Cow, add lifetimes, and modify
some definitions. Let&amp;#39;s sprinkle lifetimes
everywhere a generic definition appears, and wrap our Vector in a Cow.&lt;/p>
&lt;div class="src src-rust">
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="k">use&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">std&lt;/span>::&lt;span class="n">borrow&lt;/span>::&lt;span class="n">Cow&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">struct&lt;/span> &lt;span class="nc">VecWrapper&lt;/span>&lt;span class="o">&amp;lt;&amp;#39;&lt;/span>&lt;span class="na">a&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">v&lt;/span>: &lt;span class="nc">Cow&lt;/span>&lt;span class="o">&amp;lt;&amp;#39;&lt;/span>&lt;span class="na">a&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">Vec&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&amp;gt;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">impl&lt;/span>&lt;span class="o">&amp;lt;&amp;#39;&lt;/span>&lt;span class="na">a&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">VecWrapper&lt;/span>&lt;span class="o">&amp;lt;&amp;#39;&lt;/span>&lt;span class="na">a&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">new&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">v&lt;/span>: &lt;span class="nc">Cow&lt;/span>&lt;span class="o">&amp;lt;&amp;#39;&lt;/span>&lt;span class="na">a&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">Vec&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&amp;gt;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="nc">Self&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">VecWrapper&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">v&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;/div>
&lt;pre class="example">
error[E0277]: the trait bound `T: std::clone::Clone` is not satisfied
--&amp;gt; src/lib.rs:5:3
|
5 | v: Cow&amp;lt;&amp;#39;a, Vec&amp;lt;T&amp;gt;&amp;gt;,
| ^^^^^^^^^^^^^^^^^^ the trait `std::clone::Clone` is not implemented for `T`
|
= note: required because of the requirements on the impl of `std::clone::Clone` for `std::vec::Vec&amp;lt;T&amp;gt;`
= note: required because of the requirements on the impl of `std::borrow::ToOwned` for `std::vec::Vec&amp;lt;T&amp;gt;`
help: consider restricting type parameter `T`
|
2 | struct VecWrapper&amp;lt;&amp;#39;a, T: std::clone::Clone&amp;gt;
| ^^^^^^^^^^^^^^^^^^^
...
&lt;/pre>
&lt;p>
Well, it was a noble first try. In the wise words of Rust Sage &lt;a href="https://rust-unofficial.github.io/too-many-lists/">Gankra&lt;/a>, &amp;#34;It
should be noted that the &lt;em>authentic&lt;/em> Rust learning experience involves writing
code, having the compiler scream at you, and trying to figure out what the heck
that means.&amp;#34; We&amp;#39;re living that dream. But the rust compiler is actually pretty
helpful here. We need to put a trait bound on T, so that our pet Cow can clone T
if and when it needs to.&lt;/p>
&lt;div class="src src-rust">
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="k">use&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">std&lt;/span>::&lt;span class="n">borrow&lt;/span>::&lt;span class="n">Cow&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">struct&lt;/span> &lt;span class="nc">VecWrapper&lt;/span>&lt;span class="o">&amp;lt;&amp;#39;&lt;/span>&lt;span class="na">a&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">T&lt;/span>: &lt;span class="nb">Clone&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">where&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">T&lt;/span>: &lt;span class="nb">Clone&lt;/span>
&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">v&lt;/span>: &lt;span class="nc">Cow&lt;/span>&lt;span class="o">&amp;lt;&amp;#39;&lt;/span>&lt;span class="na">a&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">Vec&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&amp;gt;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">impl&lt;/span>&lt;span class="o">&amp;lt;&amp;#39;&lt;/span>&lt;span class="na">a&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">VecWrapper&lt;/span>&lt;span class="o">&amp;lt;&amp;#39;&lt;/span>&lt;span class="na">a&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">where&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">T&lt;/span>: &lt;span class="nb">Clone&lt;/span>
&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">new&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">v&lt;/span>: &lt;span class="nc">Cow&lt;/span>&lt;span class="o">&amp;lt;&amp;#39;&lt;/span>&lt;span class="na">a&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">Vec&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&amp;gt;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="nc">Self&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">VecWrapper&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">v&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;/div>
&lt;pre class="example">
warning: struct is never constructed: `VecWrapper`
--&amp;gt; src/lib.rs:2:8
|
2 | struct VecWrapper&amp;lt;&amp;#39;a, T: Clone&amp;gt;
| ^^^^^^^^^^
|
= note: `#[warn(dead_code)]` on by default
...
&lt;/pre>
&lt;p>
Success! If we wanted to take our implementation one step further, the
&lt;a href="https://doc.rust-lang.org/std/borrow/enum.Cow.html">documentation&lt;/a> gives an example of wrapping a generic array with a Cow, which
would require a couple more trait bounds.&lt;/p>
&lt;div class="src src-rust">
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="k">use&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">std&lt;/span>::&lt;span class="n">borrow&lt;/span>::&lt;span class="n">Cow&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">struct&lt;/span> &lt;span class="nc">Items&lt;/span>&lt;span class="o">&amp;lt;&amp;#39;&lt;/span>&lt;span class="na">a&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">X&lt;/span>: &lt;span class="o">&amp;#39;&lt;/span>&lt;span class="na">a&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">where&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">X&lt;/span>&lt;span class="p">]&lt;/span>: &lt;span class="nb">ToOwned&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Owned&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">Vec&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">X&lt;/span>&lt;span class="o">&amp;gt;&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">values&lt;/span>: &lt;span class="nc">Cow&lt;/span>&lt;span class="o">&amp;lt;&amp;#39;&lt;/span>&lt;span class="na">a&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">X&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">impl&lt;/span>&lt;span class="o">&amp;lt;&amp;#39;&lt;/span>&lt;span class="na">a&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">X&lt;/span>: &lt;span class="nb">Clone&lt;/span> &lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;#39;&lt;/span>&lt;span class="na">a&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Items&lt;/span>&lt;span class="o">&amp;lt;&amp;#39;&lt;/span>&lt;span class="na">a&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">X&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">where&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">X&lt;/span>&lt;span class="p">]&lt;/span>: &lt;span class="nb">ToOwned&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Owned&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">Vec&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">X&lt;/span>&lt;span class="o">&amp;gt;&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">new&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">v&lt;/span>: &lt;span class="nc">Cow&lt;/span>&lt;span class="o">&amp;lt;&amp;#39;&lt;/span>&lt;span class="na">a&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">X&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="nc">Self&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Items&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">values&lt;/span>: &lt;span class="nc">v&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;/div>
&lt;p>Which ends up looking pretty close to our vector wrapper, but since the Vec type
implements ToOwned for us and the array doesn&amp;#39;t, a we&amp;#39;d have to implement
`ToOwned` for our generic array by hand.&lt;/p>
&lt;p>
Alright, so there&amp;#39;s a lot more that can be done with Cows than we got
into here. But I&amp;#39;m hoping this was enough of a prod to get you up and mooving
with cows. Thanks for joining me, and best of luck in all your future Rust
endeavors.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-6" class="outline-3">
&lt;h3 id="headline-6">
Sources:
&lt;/h3>
&lt;div id="outline-text-headline-6" class="outline-text-3">
&lt;p>&lt;a href="https://doc.rust-lang.org/1.26.1/std/borrow/enum.Cow.html">Documentation std::borrow::Cow&lt;/a>&lt;/p>
&lt;p>
&lt;a href="https://en.wikipedia.org/wiki/Smart_pointer#cite_note-1">Smart Pointers Wikipedia&lt;/a>&lt;/p>
&lt;p>
&lt;a href="https://deterministic.space/secret-life-of-cows.html#fn:deref">Secret Life of Cows&lt;/a>&lt;/p>
&lt;/div>
&lt;/div></description><category domain="https://thork.net/categories/rust/">rust</category><category domain="https://thork.net/tags/rust/">rust</category><category domain="https://thork.net/tags/programming/">programming</category></item><item><title>Leveraging Prediction Markets to Mitigate the Tragedy of the Commons Problem in International Climate Change Agreements</title><link>https://thork.net/posts/2020_prediction_markets_climate/</link><guid isPermaLink="true">https://thork.net/posts/2020_prediction_markets_climate/</guid><pubDate>Thu, 16 Apr 2020 00:00:00 +0000</pubDate><author>thorck@protonmail.com (Thor)</author><description>
&lt;p>I wrote this paper as a course paper in sustainability and innovation at the University of Oslo. In the section before the conclusion I propose an original (possibly not, but I didn&amp;#39;t find it elsewhere) system for coordinating government agreements around international policy.&lt;/p>
&lt;div id="outline-container-headline-1" class="outline-3">
&lt;h3 id="headline-1">
Abstract
&lt;/h3>
&lt;div id="outline-text-headline-1" class="outline-text-3">
&lt;p>Climate change presents a pressing range of tragedy of the commons problems, eg. global sea level and atmospheric carbon dioxide concentration. Barring the reduction of democracy to technocracy, there is a niche for innovation in the methodology employed by political bodies toward assessing solution quality and the probable outcome landscape of implementing a policy or resolution. Multilateral policy agendas coordinated between political actors represents one such opportunity. Attempts at collective action solutions often result in the emergence of tragedy-of-the-commons problems. I will analyze the tragedy-of-the-commons failure as pertaining to international climate summit resolutions, and introduce several implementations of prediction markets as a possible solution to the problems incumbent to global climate agreements, and tragedy-of-the-commons problems more generally.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-2" class="outline-3">
&lt;h3 id="headline-2">
Introduction
&lt;/h3>
&lt;div id="outline-text-headline-2" class="outline-text-3">
&lt;p>Global climate summits an example of the tragedy-of-the-commons problem: political actors employ the ecopragmatist-originated sustainable transformation rhetoric as umbrage for resolution-reneging political failures, while the political incentive to meet international climate summit commitments is regularly outweighed by competing local political factors. Incentivizing multilateral follow-through on climate agreements requires addressing several sub-problems.
I will address and analyze the problems incumbent to the present system of climate resolution. Then I explore technocracy as a naive solution to the political problem of information aggregation. Then I will introduce prediction markets, applied to these problems in two possible implementations. Last, I will discuss some of the more common criticisms with prediction markets.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-3" class="outline-3">
&lt;h3 id="headline-3">
Criticisms of the 2030 Agenda for Sustainable Development
&lt;/h3>
&lt;div id="outline-text-headline-3" class="outline-text-3">
&lt;p>The 2030 Agenda for Sustainable Development and SDGs [a], adopted in 2015 by the UN, outlines 17 goals and 169 targets for reducing poverty and engaging sustainable practices in participating nations. One might infer from the self-congratulatory language used on the website of the European Union that the Agenda’s success is all but assured: “the adoption of the 2030 Agenda was a landmark achievement, providing for a shared global vision towards sustainable development for all.” Claims to the contrary are in evidence.
425 days after the adoption of the Agenda (in 2016), “tangible progress in terms of implementing the SDGs at the country level has been hard to come by” [c], and in the 2019 SDG report, United Nations Secretary-General António Guterres wrote “it is abundantly clear that a much deeper, faster and more ambitious response is needed to unleash the social and economic transformation needed to achieve our 2030 goals” [d].
One obvious problem is the excessive use of vague language in the agenda’s goals, eg. “3. Good Health and Well-Being for people– Ensure healthy lives and promote well-being for all at all ages”, in combination with ludicrously overzealous goals, eg. “1. No Poverty-End poverty in all its forms everywhere.” A child might have written these. For these among other reasons, the SDG goals were called “worse than useless” by The Economist in 2015 [b]. The document continues “All countries have a shared responsibility to achieve the SDGs.” To clarify: the term “shared responsibility” is political umbrage, indicative of no responsibility at all. Political leaders’ motives at summits such as these are not to acquire the relevant information, assemble coherent policy agendas, or otherwise solve any problems at all, but to author rhetorical nonsense, while pretending to be doing something.
A better system for aggregating the relevant information and constructing coherent policy agendas is in need. Before proposing an alternative, I analyze some of the problems inherent to the present system.
[a] &lt;a href="https://ec.europa.eu/environment/sustainable-development/SDGs/index_en.htm">https://ec.europa.eu/environment/sustainable-development/SDGs/index_en.htm&lt;/a>
[b] &lt;a href="https://www.economist.com/leaders/2015/03/26/the-169-commandments">https://www.economist.com/leaders/2015/03/26/the-169-commandments&lt;/a>
[c] &lt;a href="https://www.idea.int/sites/default/files/news/16-11-22-The_slumber_of_the_SDGs.pdf">https://www.idea.int/sites/default/files/news/16-11-22-The_slumber_of_the_SDGs.pdf&lt;/a>
[d] &lt;a href="https://www.un.org/sustainabledevelopment/progress-report/">https://www.un.org/sustainabledevelopment/progress-report/&lt;/a>&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-4" class="outline-3">
&lt;h3 id="headline-4">
Problems Inherent to International Policy Consensus
&lt;/h3>
&lt;div id="outline-text-headline-4" class="outline-text-3">
&lt;p>Three key problems emerge in the UN methodology in designing and implementing goals. The first has to do with the distributed nature of responsibility for implementing policy, resulting in a tragedy-of-the-commons problem. The second has to do with the existing processes by which existing information is aggregated into resolutions and policies. The last has to do with the reporting and accountability process, after resolutions and policies are put in place.
The distributed nature of responsibility among United Nations member nations decreases the degree of accountability any particular nation holds in following through on its commitments, eg. As member countries A and B commit to reducing it’s net carbon footprint, country B benefits from country A reducing its carbon footprint, but pays none of the costs incurred by decreasing country B’s own footprint. Thus country B has incentive to renege on its agreements while saddling country A with the costs. The scale of the problem increases with the number of countries, and their relative power differential. For instance, in our previous example, if country B is economically or militarily dependent on country A, country A has further less incentive to sacrifice immediate economic productivity to satisfy an international agreement. This puts weak nations in an impossible political situation, as Leichenko and Silva identify that “not only are the poorest and most marginalized disproportionately affected, but climate change impacts can also exacerbate existing inequalities” [e]. Note that country A may still enter an agreement with no intention of holding to it, so as to enjoy the benefits of other nations’ carbon footprint reduction while paying none of the costs. This provides context for how all 193 of the member countries of the United Nations committed to the SDG agenda, while funding for achieving these goals is 2.5 trillion dollars short of the 5 trillion per annum price tag [f]. The incentive to defect from agreements is a hallmark of the Tragedy-of-the-Commons problem. Holding to the international agreement is in direct competition with satisfying interest groups, promoting economic activity, and securing local objectives.
Solving tragedy-of-the-commons problems requires optimization in selecting realistically limited objectives, implementing effective policy to resolve those objectives, and measuring results.
Beginning with rational information acquisition, we may ask the question, how well informed with respect to realistic outcomes is current policy? Given that only 2 of the 17 SDG goals are currently on track, it seems safe to conclude: not very. This is a characteristic property of democracy: from the local to the international, voters have little chance to be pivotal in voting, irrational in selecting realistic policy paths, under-informed in policy relevant to them, and tribal in their ultimate policy preference. Their representative politicians, responsible to their constituents, reflect these failures in information aggregation. In this system, relevant information for policy selection is regularly subverted to tribal preference, miscommunicated by media, deprioritized on the basis of technicality, or never brought to light in the first place. The result is the commitment toward ineffective and unrealistic solutions, as in the case of the 17 SDGs. As the power for humans to effect changes on our environment monotonically increases with time, political systems incorporating such failures as these doom the quality of our political decisions.
[e] &lt;a href="https://onlinelibrary.wiley.com/doi/abs/10.1002/wcc.287">https://onlinelibrary.wiley.com/doi/abs/10.1002/wcc.287&lt;/a>
[f] &lt;a href="https://www.nature.com/articles/d41586-019-03907-4">https://www.nature.com/articles/d41586-019-03907-4&lt;/a>&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-5" class="outline-3">
&lt;h3 id="headline-5">
Naive Solution: Technocracy
&lt;/h3>
&lt;div id="outline-text-headline-5" class="outline-text-3">
&lt;p>The most naive solution to the criticisms above, in particular with regards to climate action as a primarily scientific and economic domain of policy, would be to simply reduce the poIr of our representative democracy with respect to policy decisions inside certain domains, reallocating the poIr to shape policy to the set of experts perceived to be most relevant. Political systems in this arrangement are referred to as technocracies, and emerge commonly in businesses requiring technical expertise. Though a full analysis of the merits and weaknesses of technocracy is beyond the scope of this paper, a brief treatment of the most relevant problems of technocracy will prepare the ground for prediction markets.
Technocracy’s primary advantage in practice is an approximation of meritocracy flavored towards elevating the hierarchical importance of fact-based reasoning and technical ability to engage a somewhat predefined domain of technical problems.
The first problem in technocracy one might encounter is a problem with the very definition of what constitutes an “acceptable group of relevant experts.” Each word presents myriad problems. Acceptable to whom? Being acceptable to the public reduces our group of experts to little more than politicians. Being acceptable to other experts (if such a group even exists) exposes age bias, and is still vulnerable to a certain degree of politicking. Does relevant indicate the group should represent a sampling of interdisciplinary fields or should it be more constrained? Should the group’s participants be a dynamic set or a static one? What about overlapping expertise groups? This is to demonstrate that the problem of electing a set of experts in a democracy is quickly reduced to the problem electing politicians. Were it politically popular to elect expects, such would already be the status quo.
Even presupposing a mechanism for expert committee selection, a second class of political problem exists: technocracies are aristocracies with a chrome polish. That is, technocracies are closed political systems; once established, members of a technocracy could gate-keep even other experts out of the technocracy. This is acceptable in business, where gatekeeping takes the form of filtering new hires, but not in government, where a political near-invulnerability to criticism is unacceptable.
Finally, the problem domains in which technocracies thrive often allow for the opportunity for a posteriori improvements to solutions. Political environments are hostile to iterative policy change; that is, the cost of renegotiating policy decisions is high. Further, there are economic productivity losses in volatile policy environments. Thus policy technocracies are subject to the same constraint restriction of the existing democratic system: policy construction must optimally aggregate the available information landscape while navigating the political network. This implies that the information aggregation problem exists in isolation to the chosen political regime, suggesting that there may be better solutions to the problem of information aggregation with respect to the construction of policy decisions than resorting to a cabal of politically unaccountable domain experts.
Prediction Markets in Assessing Policy Directions
To my best knowledge, prediction markets were introduced as having potential as a democratic process by economist Robert Hanson [g]. Hanson proposed that “inferior policies happen because our info institutions fail to induce people to acquire and share relevant info with properly-motivated decision makers.” Hanson identified inferior processes for generating and evaluating information relevant to governing as responsible for economic divergence between democracies, and proposed prediction markets as a solution. Prediction markets are effective at aggregating information about the probable outcomes of a proposed action. By their application to policy, nations may optimize policy selection, while resolving an incentive mechanism to counteract the tragedy-of-the-commons problem. Note that the ability of prediction markets is limited to expressing probable outcomes of policy alternatives, not in expressing what outcomes are qualitatively better than one another.
Participants in a prediction market bet on the outcome of a particular event by an agreed upon point in time. For example, suppose at time t=0, a prediction market opens around whether policy A will achieve event Z by time t=T. Bettor 0 believes the outcome is likely, and bets $70. Then at time t=1, bettor 1 believes the outcome is only 70% likely to occur, so bettor 5 bets $30 against. The betting market now reflects a 70% chance of action success. If no further bettors enter, and the result is positive, the first seven bettors split the $30 counterbet, and if the result is negative, bettor 1 takes the $70. But now suppose an alleged domain expert, bettor 2, sees the 70% betting odds, and thinks the odds are much closer to 40%. If bettor 2 is correct, he will profit off of making any bet moving the odds closer to 40%. He does so by betting $75 against. The prediction market thereby incentivizes all domain experts, regardless of credentialing, to apply their knowledge for the public interest. This system avoids the closed property of technocracies’ only allowing for the expressed opinions of a narrow, preselected group of experts, while simultaneously allowing for democracy to take place around the most engaged, informed, and affected constituents. Statistics reflect this: in one study, prediction markets around elections were found to be 74% more effective than polling over 964 elections, with an even greater advantage in forecasts beyond 100 days [h].
In the context of policy selection, a political committee could propose several policy alternatives to a betting market in reference to their ability to achieve some outcome. After some waiting period (requiring some mechanism to avoid allowing last minute interference bets), the committee could then implement the policy alternative seen as most likely to succeed, and reset all markets of policy alternatives not taken.
One of the most common criticisms of prediction markets as a vector to inform policy decisions is the opportunity for interest groups to tip the scale by betting overwhelmingly in their preferred direction. But placing restrictions on the maximum bet should not be necessary. The event of an interest group betting tremendously in one direction should be seen as a tremendous arbitrage opportunity for prediction market participants, as the interest group is not strictly buying “votes” per se, but expressing an opinion concerning the likelihood of an outcome. For instance, if a petroleum company sought to sabotage a prediction market around the probability that policy A achieves outcome Z by betting overwhelmingly that an alternative policy B achieves outcome Z better than policy A, bettors are incentivized to take the counter-bet of petroleum company on policy B, reaping an arbitrage opportunity. This is because bets are not votes: votes are expressions of preference (which deserve discussion in a paper in their own right), bets are expressions of belief about the state of the world, and the consequences of a given action.
[g] &lt;a href="http://mason.gmu.edu/~rhanson/futarchy2013.pdf">http://mason.gmu.edu/~rhanson/futarchy2013.pdf&lt;/a>
[h] &lt;a href="https://www.sciencedirect.com/science/article/abs/pii/S0169207008000320">https://www.sciencedirect.com/science/article/abs/pii/S0169207008000320&lt;/a>&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-6" class="outline-3">
&lt;h3 id="headline-6">
Prediction Markets as Staking Mechanisms
&lt;/h3>
&lt;div id="outline-text-headline-6" class="outline-text-3">
&lt;p>Bets are mechanisms for forcing alignment between beliefs and actions. Among the problems inherent to the tragedy-of-the-commons is that actors are incentivized to lie about intentions, or to otherwise express under-calibrated opinions. In addition to their role in information aggregation, prediction markets can serve a mechanism for constructing stake in international policy agreements.
A staking mechanism is a procedure in which a participating party accept some quantity of risk, for one or more of the following purposes: (i) as collateral against the party to breaking agreements, (ii) as a signaling mechanism intended to encourage further action by other parties, ie. the public, (iii) as a substitute for, or complement to reputation, in systems where reputation is insufficient in one of the two above ways, (iv) as a bet on a particular outcome with some probability and expectation of returns with positive expected value (v) from the perspective of an observer to the staking process, a mechanism for collecting information about the belief state of participants in the staking system. All but definition (iv) are directly relevant to the discussion of inducing actors to commit to and engage in a more desirable set of actions, in particular, with respect to preserving the environment among other desirable outcomes.
A proposed system for international politics could proceed as follows: First, a desired outcome is determined, for instance, a k% reduction in carbon footprint by each nation within 3 years, where each nation gets to choose their value for k, but exponentially increasing upfront costs to a nation’s choice of k below some value. Nations would be required to bet some minimum percentage of their GDP as stake toward meeting their target. Finally, delegates from each nation would proceed to place bets on each other nation’s objective being met with and against that nation’s staked bet.
This system has several advantages. First, it avoids a one-size-fits-all problem of international politics: policy that is effective or reasonable in one nation may not at all be in another. By calibrating goals with respect to each nation, we avoid the problems inherent to the SDG goals’ being simultaneously vague and exaggerated.
Second, if nation A’s delegates bet that nation B succeeds at meeting its policy goal, nation A is incentivized to pressure and assist nation B in meeting that policy goal, fostering international cooperation, rather than creating opportunity for defection from agenda agreements.
Third, the process creates a reasonably interesting opportunity for popular engagement in international politics. An international betting market between nations would be the international policy equivalent of the Olympics, and would likely draw a great deal of popular attention, raising the stakes for failing to meet commitments.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-7" class="outline-3">
&lt;h3 id="headline-7">
Conclusion
&lt;/h3>
&lt;div id="outline-text-headline-7" class="outline-text-3">
&lt;p>I have analyzed several problems with the international political process, embodied in the 2015 SDGs, and proposed two possible implementations of prediction markets as solutions for rationally aggregating information. I introduced technocracy as a toy solution to better information aggregation, and explored the undesirable properties inherent to that solution as an alternative for eliminating the irrationality inherent to democratic processes. I aimed to attempt to solve the tragedy-of-the-commons problem emergent in international politics, while identifying the economic mechanism failures of the existing process.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-8" class="outline-3">
&lt;h3 id="headline-8">
Bibliography
&lt;/h3>
&lt;div id="outline-text-headline-8" class="outline-text-3">
&lt;p>Berg, J. E., Nelson, F. D., &amp;amp; Rietz, T. A. (2008, April 28). Prediction market accuracy in the long run. Retrieved from &lt;a href="https://www.sciencedirect.com/science/article/abs/pii/S0169207008000320">https://www.sciencedirect.com/science/article/abs/pii/S0169207008000320&lt;/a>
Get the Sustainable Development Goals back on track. (2020, January 1). Retrieved from &lt;a href="https://www.nature.com/articles/d41586-019-03907-4">https://www.nature.com/articles/d41586-019-03907-4&lt;/a>
Hanson, R. (n.d.). Shall We Vote on Values, But Bet on Beliefs? Retrieved from &lt;a href="http://mason.gmu.edu/~rhanson/futarchy2013.pdf">http://mason.gmu.edu/~rhanson/futarchy2013.pdf&lt;/a>
Leichenko, R., &amp;amp; Silva, J. A. (2014, May 2). Climate change and poverty: vulnerability, impacts, and alleviation strategies. Retrieved from &lt;a href="https://onlinelibrary.wiley.com/doi/abs/10.1002/wcc.287">https://onlinelibrary.wiley.com/doi/abs/10.1002/wcc.287&lt;/a>
Sustainable Development Goals Report - United Nations Sustainable Development. (n.d.). Retrieved from &lt;a href="https://www.un.org/sustainabledevelopment/progress-report/">https://www.un.org/sustainabledevelopment/progress-report/&lt;/a>
The 169 commandments. (2015, March 26). Retrieved from &lt;a href="https://www.economist.com/leaders/2015/03/26/the-169-commandments">https://www.economist.com/leaders/2015/03/26/the-169-commandments&lt;/a>
The 2030 Agenda for Sustainable Development and the SDGs. (n.d.). Retrieved from &lt;a href="https://ec.europa.eu/environment/sustainable-development/SDGs/index_en.htm">https://ec.europa.eu/environment/sustainable-development/SDGs/index_en.htm&lt;/a>
Vandemoortele, J. (2016, November 22). How to bring the SDGs out of their current slumber? Retrieved from &lt;a href="https://www.idea.int/sites/default/files/news/16-11-22-The_slumber_of_the_SDGs.pdf">https://www.idea.int/sites/default/files/news/16-11-22-The_slumber_of_the_SDGs.pdf&lt;/a>&lt;/p>
&lt;/div>
&lt;/div></description><category domain="https://thork.net/categories/society/">society</category><category domain="https://thork.net/tags/technocracy/">technocracy</category><category domain="https://thork.net/tags/prediction-markets/">prediction markets</category><category domain="https://thork.net/tags/school/">school</category><category domain="https://thork.net/tags/environmentalism/">environmentalism</category><category domain="https://thork.net/tags/futarchy/">futarchy</category></item><item><title>2020 Jan-Mar Book Review Rundown</title><link>https://thork.net/posts/2020_jan_mar_book_review/</link><guid isPermaLink="true">https://thork.net/posts/2020_jan_mar_book_review/</guid><pubDate>Wed, 08 Apr 2020 00:00:00 +0000</pubDate><author>thorck@protonmail.com (Thor)</author><description>
&lt;p>
23 books, 3 unfinished with no intention to finish anytime soon.
5 fiction, 18 nonfiction (would it be cheeky to count The Little Bitcoin Book as fiction?)&lt;/p>
&lt;p>
Trends:&lt;/p>
&lt;ul>
&lt;li>non-fiction especially about business, identity, and climate science early on&lt;/li>
&lt;li>Biographies and Neal Stephenson towards the end&lt;/li>
&lt;/ul>
&lt;p>I&amp;#39;ll give a couple sentences of my thoughts about each book and a rating out of 10.&lt;/p>
&lt;div id="outline-container-headline-1" class="outline-4">
&lt;h4 id="headline-1">
Climate and Society, Robin Leinchenko and Karen O&amp;#39;Brien
&lt;/h4>
&lt;div id="outline-text-headline-1" class="outline-text-4">
&lt;p>Information sparse, introduces the integrative approach to climate change solutions. Well researched, useful to get a bearing on the complexity of proposing a &amp;#34;solution&amp;#34; to climate change, but prolix. 7/10&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-2" class="outline-4">
&lt;h4 id="headline-2">
The Ecology of Commerce, Paul Hawken, unfinished
&lt;/h4>
&lt;div id="outline-text-headline-2" class="outline-text-4">
&lt;p>Hawken is an ecological warrior whose writing should be dated given its publication date of 1992, yet one could rewrite the book today with few changes. Lends support to the conclusion that the environment is doomed and our static political systems are to blame. Unfinished because the book is repetitive. 6/10&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-3" class="outline-4">
&lt;h4 id="headline-3">
The End of Jobs, Taylor Pearson
&lt;/h4>
&lt;div id="outline-text-headline-3" class="outline-text-4">
&lt;p>The 4 hour work week, but 10 years later and less interesting. Was interested in his blog, not polished or content dense enough to justify a book. 3/10&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-4" class="outline-4">
&lt;h4 id="headline-4">
What You Do is Who You Are, Ben Horowitz
&lt;/h4>
&lt;div id="outline-text-headline-4" class="outline-text-4">
&lt;p>Great read on values, stories about vivid figures from history who demonstrate values in motion, which Horowitz terms &amp;#34;virtues&amp;#34;, borrowed from samurai literature. Less interested in the business anecdotes, but Horowitz is all around a great story teller. May reread. 9/10.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-5" class="outline-4">
&lt;h4 id="headline-5">
A Liberated Mind, Steven C. Hayes
&lt;/h4>
&lt;div id="outline-text-headline-5" class="outline-text-4">
&lt;p>Hayes is the father of Acceptance and Commitment Therapy. All the feely goody bubbly nonsense anecdotes therapy is infamous for, but not without a complete lack of substance–Hayes delivers the principles of ACT effectively, despite himself. 7/10.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-6" class="outline-4">
&lt;h4 id="headline-6">
Sapiens, A Brief History of Humankind, Yuval Noah Harari
&lt;/h4>
&lt;div id="outline-text-headline-6" class="outline-text-4">
&lt;p>Overrated, but not by much. Seemed like a good thing to read to kids, if I had any. 8/10&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-7" class="outline-4">
&lt;h4 id="headline-7">
The Hard Thing about Hard Things, Ben Horowitz, reread
&lt;/h4>
&lt;div id="outline-text-headline-7" class="outline-text-4">
&lt;p>Reread after WYDiWYD. Hard thing about hard things is, as with the next book, about taking the hard things in your hands, communicating honesty in business and in life. Horowitz weaves lessons into the story of his time as CEO of Loudcloud, and a great storyteller he is. 9/10.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-8" class="outline-4">
&lt;h4 id="headline-8">
Models, Attract Women Through Honesty, Mark Manson
&lt;/h4>
&lt;div id="outline-text-headline-8" class="outline-text-4">
&lt;p>An exploration of masculinity, an area of conversation I find underrated, possibly because it only seems to happen when men talk about dating. Book cogently delivers on its premise, investigating the particulars of holding honest as a value, and using it to connect with others (including women). 9/10&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-9" class="outline-4">
&lt;h4 id="headline-9">
Homo Deus, Yuval Noah Harari, unfinished
&lt;/h4>
&lt;div id="outline-text-headline-9" class="outline-text-4">
&lt;p>Worse than Sapiens due to a greater degree of speculation. Harari is more in his element in 21 Lessons and Sapiens. Was often bored. 6/10.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-10" class="outline-4">
&lt;h4 id="headline-10">
Range, Why Generalists Triumph in a Specialized World, David Epstein
&lt;/h4>
&lt;div id="outline-text-headline-10" class="outline-text-4">
&lt;p>This book and the next fall into my &amp;#34;standard MBA business book&amp;#34; category, delivering loosely connected anecdotes and studies as defenses of an over-general principle. Barely managed to finish each of them. This one did it better. I&amp;#39;m squarely in the target audience for each and was constantly bored. 6/10.
Rebel Talent, Why it Pays to Break the Rules at Work and in Life, Francesca Gino
5/10.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-11" class="outline-4">
&lt;h4 id="headline-11">
Tribe, On Homecoming and Belonging, Sebastian Junger, reread
&lt;/h4>
&lt;div id="outline-text-headline-11" class="outline-text-4">
&lt;p>Junger&amp;#39;s message in Tribe is clearer than Lake Tahoe, and timeless as her mountains. Conciseness is underrated; Junger delivers in 180 short pages several well constructed narratives highlighting our separation from one another, and how we may come together again. 10/10.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-12" class="outline-4">
&lt;h4 id="headline-12">
Talking to Strangers, Malcolm Gladwell
&lt;/h4>
&lt;div id="outline-text-headline-12" class="outline-text-4">
&lt;p>Depressingly the opposite of the above. Gladwell&amp;#39;s winding narrative style is acceptable when he has more to say. He remains an excellent, if somewhat roundabout narrator. I found the premise–that we underrate the value of context and overrate the importance of identity–uninspiring. 7/10.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-13" class="outline-4">
&lt;h4 id="headline-13">
Words and Rules, Steven Pinker, unfinished
&lt;/h4>
&lt;div id="outline-text-headline-13" class="outline-text-4">
&lt;p>A book only a linguist could love, or finish. Long, thorough, and by the sixth chapter, too systematic for even a man with Aspergers. 6/10&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-14" class="outline-4">
&lt;h4 id="headline-14">
The Little Bitcoin Book, The Bitcoin Collective
&lt;/h4>
&lt;div id="outline-text-headline-14" class="outline-text-4">
&lt;p>Useful in offering the Bitcoin maximalist system of the world. I&amp;#39;d wanted to get a better idea of what this system of the world was, and especially found it in chapter 5, which offers a pair of speculative anecdotes of the world in 2030 with and without decentralized currency. Conciseness is good. 9/10.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-15" class="outline-4">
&lt;h4 id="headline-15">
Emmy in the Key of Code, Aimee Lucido
&lt;/h4>
&lt;div id="outline-text-headline-15" class="outline-text-4">
&lt;p>What a delightful and unusual children&amp;#39;s book this is, discovered through the Embedded.fm podcast. A collection of poems from the perspective of a musical child struggling to find herself in a new place. Struggles with chauvinism, self discovery, appeals to the similarity of code and poetry, musical references, and an utter delight. 10/10.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-16" class="outline-4">
&lt;h4 id="headline-16">
Hackers and Painters, Paul Graham, reread
&lt;/h4>
&lt;div id="outline-text-headline-16" class="outline-text-4">
&lt;p>The best collection of essays I&amp;#39;ve ever read on what it is to be a hacker, beyond the obvious: we make stuff. The first chapter, about the quality of education (despite my general agreement with most of the premises), was arguably the worst. The remainder of the book is a welcome investigation into value, hacking, and what it is to be a technologist, without fear of delving deep. 10/10.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-17" class="outline-4">
&lt;h4 id="headline-17">
King Lear, &amp;#39;ol Bill Shakespeare
&lt;/h4>
&lt;div id="outline-text-headline-17" class="outline-text-4">
&lt;p>Unrateable: a low rating marks me as uncultured swine, and a high rating suggests a mastery of Shakespeare I am yet to attain. I continue to find reading Shakespeare as challenging and worthwhile a literary exercise as the best mathematical exercise.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-18" class="outline-4">
&lt;h4 id="headline-18">
Elon Musk, Ashlee Vance, reread
&lt;/h4>
&lt;div id="outline-text-headline-18" class="outline-text-4">
&lt;p>Reread this biography a few months after the more recent controversies of Musk&amp;#39;s making an ass out of himself to see if I still resonate with his story and self image as a man possessed to make an impact in the world. I do. It&amp;#39;s interesting that he seems content to piss off people so much, something I feel I nearly understand, but just somehow miss. The biography is excellent, and makes me want to defend him. It also makes me want to be like him, which I&amp;#39;m only somewhat uncomfortable with. 10/10.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-19" class="outline-4">
&lt;h4 id="headline-19">
Steve Jobs, Walter Isaacson
&lt;/h4>
&lt;div id="outline-text-headline-19" class="outline-text-4">
&lt;p>I wanted to compare the Musk biography to another pivotal figure, who exists in the same strata of obscenely multitalented entrepreneur defining and propelling the boundary of technology. I&amp;#39;m unsure if it&amp;#39;s simply a testament to Vance&amp;#39;s ability as a biographer, or if I simply resonate more deeply with the Musk story, but I came away from the Jobs biography with a certain, &amp;#34;why do I care again?&amp;#34; taste in my mouth. The early half of the book felt more alive. Maybe this is my becoming a Wozniak fan. 7/10.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-20" class="outline-4">
&lt;h4 id="headline-20">
Atmosphæra Incognita, Neal Stephenson
&lt;/h4>
&lt;div id="outline-text-headline-20" class="outline-text-4">
&lt;p>Stephenson at his shortest. In 100 pages or so, Stephenson lets loose a short story of the tallest building mankind could ever construct. The story appears to be an allegory for technology, progressively hoisting mankind to uncharted heights, with newfound danger and excitement at each new level. I can&amp;#39;t stop reading Stephenson after this. 10/10.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-21" class="outline-4">
&lt;h4 id="headline-21">
The Diamond Age, Neal Stephenson
&lt;/h4>
&lt;div id="outline-text-headline-21" class="outline-text-4">
&lt;p>Snow Crash (read in December) was fantastic, but only periodically ran as deep as I would have liked. The Diamond Age scratches that itch. Stephenson trots out a gorgeous fragmented world replete with a new element: nanotechnology. Deftly exploring coming of age, what it means to be a hypocrite, moral life in a corrupt society, the education of a subversive, and the difference of having everything one needs and having everything one needs to build that which one needs, I&amp;#39;m captured with The Diamond Age world, and its protagonist. 10/10.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-22" class="outline-4">
&lt;h4 id="headline-22">
Anathem, Neal Stephenson
&lt;/h4>
&lt;div id="outline-text-headline-22" class="outline-text-4">
&lt;p>I wanted The Diamond Age to be Stephenson at his best. I wanted to think, alright, I can stop reading Stephenson now. I especially wanted to avoid reading 2700 pages of the Baroque cycle. But after this, I think I&amp;#39;m doomed to read everything Stephenson ever wrote. I&amp;#39;m absolutely taken by the world building, the allegories to mathematics and technology, the incorporation of astronomical physics and the multiple worlds hypothesis, and of course, Neal Stephenson&amp;#39;s uncanny ability to bring his characters to life. 10/10.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-23" class="outline-3">
&lt;h3 id="headline-23">
Current
&lt;/h3>
&lt;div id="outline-text-headline-23" class="outline-text-3">
&lt;ul>
&lt;li>Snow Crash, Neal Stephenson, rereading&lt;/li>
&lt;li>Turing&amp;#39;s Cathedral, George Dyson, reading slowly&lt;/li>
&lt;li>Disunited Nations, Peter Zeihan, Roy Worley, unsure if I&amp;#39;ll finish&lt;/li>
&lt;/ul>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-24" class="outline-3">
&lt;h3 id="headline-24">
On the &amp;#34;soon&amp;#34; List:
&lt;/h3>
&lt;div id="outline-text-headline-24" class="outline-text-3">
&lt;ul>
&lt;li>The Critique of Pure Reason Kant, forever procrastinating&lt;/li>
&lt;li>Reprogramming the American Dream, Kevin Scott, Greg Shaw&lt;/li>
&lt;li>The Sovereign Individual James Dale Davidson, Lord William Rees-Mogg&lt;/li>
&lt;li>Evidence for Hope, Kathryn Sikkink&lt;/li>
&lt;li>Radical Markets Eric Posner, Glen Weyl, a reread&lt;/li>
&lt;li>The Machinery of Freedom, David D. Friedman&lt;/li>
&lt;li>The Baroque Cycle, trilogy Neal Stephenson&lt;/li>
&lt;/ul>
&lt;/div>
&lt;/div></description><category domain="https://thork.net/categories/book/">book</category><category domain="https://thork.net/tags/book/">book</category><category domain="https://thork.net/tags/review/">review</category></item><item><title>Human Rights: Entertaining the Illusion</title><link>https://thork.net/posts/2020_four_floundering_eras_of_human_rights/</link><guid isPermaLink="true">https://thork.net/posts/2020_four_floundering_eras_of_human_rights/</guid><pubDate>Tue, 10 Dec 2019 00:00:00 +0000</pubDate><author>thorck@protonmail.com (Thor)</author><description>
&lt;div id="outline-container-headline-1" class="outline-3">
&lt;h3 id="headline-1">
Introduction
&lt;/h3>
&lt;div id="outline-text-headline-1" class="outline-text-3">
&lt;p>“The speed with which human rights has penetrated every corner of the globe is astounding. Compared to human rights, no other system of universal values has spread so far so fast…. In what amounts to a historical blink of the eye, the idea of human rights has become the lingua franca of international morality” (Normand 8). With the simultaneous rise of secularism and the middle class in colonial Europe in the 19th Century, so rose the need for the decoupling of moral justifications for imperialism from religious precedents. Human rights left the sacred behind as a construction of universal humanist norms, to inspire the support of a rising middle class in a modernizing Europe. By donning the vestiges of moral supremacy, Europeans gained new justification for civilizing, educating, and employing the next century of colonial labor, while stemming criticisms of the rising bourgeois class. Human rights have always been a farce, and they remain expensive moral umbrage for nations and the elite.&lt;/p>
&lt;p>
The International Criminal Court, established in 2002, took ten years before its first successful prosecution, of Congolese warlord Thomas Lubanga Dyilo. The cost of the ICC, in the first 10 years, exceeds 1 billion dollars. The major international criminal courts in aggregate exceed 6 billion dollars (Stuart 968). The United States, on whom much of the funding and legitimacy for international human rights relies, maintains immunity for its citizens by the 2002 American Service Members’ Protection Act. The rise of political rivals, most prominently Brazil, Russia, India, China, and South Africa, to American and European power, present a valid challenge to the underlying assumption embedded in the most basic premise of human rights: can human rights be truly universal, or are these simply the last vestiges of the once religious, now secular, hypocrite phoenix of moral supremacy? In an examination of the accomplishments of human rights through four eras, I’ll attempt to disentangle the self-aggrandizing mythos of human rights from reality.&lt;/p>
&lt;p>
Ford, S. “How Leadership in International Criminal Law is Shifting from the U.S. to Europe and Asia: An Analysis of Spending on Contributions to International Criminal Courts (2011). Saint Louis University Law Journal, Vol. 55, p. 953.
Normand, R., Zaidi, S. “Human Rights at the UN: The Political History of Universal Justice” (2008). Bloomington: Indiana University Press.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-2" class="outline-3">
&lt;h3 id="headline-2">
1776-1947: Era of The Bourgeoisie, Secularism, and Capitalism
&lt;/h3>
&lt;div id="outline-text-headline-2" class="outline-text-3">
&lt;p>The modern age of human rights very little resembles its prehistory. Democracies and autocracies, including monarchies, have vastly different requirements of their populations and legal frameworks. Democracies are held to higher standards of justification for political action. The overthrow of European monarchs marks one of the more productive eras in de-facto human rights development, though the language of human rights is mostly a post World War II invention.&lt;/p>
&lt;p>
While difficult to quantify, one can reasonably hypothesize that the great advances of this era in human rights is the product of the success of popular uprisings, and the initial successes of expanding democracy, and its deceleration in the 20th century represents conflict of haves and have-nots: wealthy democratic countries have no more incentive than wealthy autocratic ones to preserve the human rights of other nations, especially when these rights conflict with at-home economic interests.&lt;/p>
&lt;p>
At the beginnings of the Age of Exploration, beginning with Portueguese navigators in the 15th and 16th Century, monarchs had little need of moral justification towards the end colonizing and enslaving. In the twin, unrivaled power structures of church and state, were generally sufficient justifications for expansion of empire in the name of God and country. But the trappings of exploration produced rivals to the landed elite: the bourgeoisie, in whom the elites invested.&lt;/p>
&lt;p>
The British, with far-flung colonies and powerful shipping merchants bore witness to this. The Declaration of Independence, penned by American plantation owner Thomas Jefferson, is the product of the realization by American bourgeoisie that they possess sufficient wealth to contest, and even field armies against traditional monarchical power. Jefferson couches protestations towards monarchal Europe in proto-human rights declarations of offenses by the crown, violating “certain unalienable Rights, that among these are Life, Liberty and the pursuit of Happiness.” The American Constitution re-affirms and cements the political rights of American citizens in The Bill of Rights albeit, excluding most of the population.&lt;/p>
&lt;p>
Two decades later, revolutionaries in France mirror the American uprising, and take advantage of the financially drained Louis XVI to declare their rights as justification for revolution in The French Declaration of the Rights of Man. The age of the bourgeoisie, while still exclusionary to the vast majority of citizens, makes the beginnings of human rights discussion possible. But by this point in history, rights are not declared by top-down organizations and governments, but are the product of bottom-up uprisings.&lt;/p>
&lt;p>
The year 1848, known as the Spring of Nations, brought a wave of popular uprisings across over 50 countries in Europe (Evans). The revolutions mostly aimed to overthrow monarchical power, while demanding democracy, freedom for the press, and rights for the working class. These rebellions were mostly secular: religious leaders traditionally worked in concert with monarchs suppress challenges to existing power structures. The revolutions succeeded to varying degrees at establishing democracies, but the agitation for a definitive set of rights, was impossible for elites to ignore. European humanist norms grew out of the revolutions, supporting the rights of the newly established bourgeois.&lt;/p>
&lt;p>
These rights would come under assault in the first half of the 20th Century. The twin cataclysms of the World Wars reduced Western Europe to rubble, allowing the advancing American economy to surpass its European forebears. The devastating effects of the world wars also propelled the advance of democracy and capitalism in Western Europe, and communist dictatorship in Eastern Europe. The positive narrative identifies this struggle as that of competing ideologies in a brawl of how best to assert the political and social rights against a Hobbsian struggle of all against all. The remaining powers needed new language to justify the shattered myths of the previous century, asserting that humanist rights existed, and could not be abused at any time by the existing power structures, as they had been in the world wars.&lt;/p>
&lt;p>
It’s worth noting that each of the participants in the Second World War abused and repressed citizens in a major way. Though this list is hardly exhaustive: Jim Crow in the United States, colonial oppression by Western Powers, barbaric war crimes by the Japanese, and genocides by Nazis and Soviets. These were the powers submitting diplomats to pen the first modern document of Human Rights, the Universal Declaration of Human Rights (UDHR). This stands in sharp contrast to the emergence of rights following popular uprisings in the 18th and 19th Centuries.&lt;/p>
&lt;p>
R.J.W. Evans and Hartmut Pogge von Strandmann, eds., The Revolutions in Europe 1848–1849 (2000) pp. v, 4&lt;/p>
&lt;p>
Declaration of Independence, Paragraph 2 (1776).&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-3" class="outline-3">
&lt;h3 id="headline-3">
1947-1976: Era of The Universal Declaration of Human Rights
&lt;/h3>
&lt;div id="outline-text-headline-3" class="outline-text-3">
&lt;p>
In the aftermath of the Second World war, world leaders gathered in Paris to pen the UDHR. According to international law professor Stephen Hopgood in The Endtimes of Human Rights, the UDHR was “an antidote to a troubling contradiction, the coexistence of progress with intensifying violence, vast social and economic inequality, and fears of “the disenchantment of the world” (Hopgood 1).&lt;/p>
&lt;p>
The modern language of human rights was borne out of the need for a neutral and secular ideology for developed nations to use as a neutered critique of one another, to limit risk of a third world war. European and American leaders sought to weave into the UDHR an ideological alibi for a new globalized economic system designed to promote international collaboration, benefitting the elites, while only supporting an enhanced sentimentality for human rights in tongue. The UDHR, therefore was a set of thirty non-binding articles, agreed upon by world leaders, but not to be taken seriously. Economist Eric Posner writes in The Twilight of Human Rights “the words in the Universal Declaration may have been stirring, but no one believed at the time that they portended a major change in the way international relations would be conducted, nor did they capture the imagination of voters, politicians, intellectuals, leaders of political movements, or anyone else who might have exerted political pressure on governments” (Posner 17).&lt;/p>
&lt;p>
Why were the articles non-binding? The authors of the UDHR were not the surviving revolutionaries seeking to validate the rights of humans against their former governments, but the ambassadors of the most powerful nations, who sought to reaffirm their hegemonic power by a set of tacit agreements to not challenge one another militarily. These diplomats were not defending human rights in the sense that human rights protect people from their governments: they were protecting their nations, developed nations, from the threat of war. Any illusion of possible agreement concerning universal ideals was quickly dispelled: there were clashes between the United States diplomat, Elanor Roosevelt, with the Soviets concerning rights to property and political freedoms, opposition by the Saudis towards articles concerning freedom of religion, Latin American diplomats who wanted God mentioned, and fears of colonial intervention by the British and French, while the Japanese were to be stripped of their recent colonial acquisitions (Loeffler).&lt;/p>
&lt;p>
The effects of the UDHR on human rights around the world were underwhelming. The United Nations established a commission for responding to human rights complaints. In the first ten years of operation, roughly 65,000 letters alleging human rights violations arrived at this newly minted defender of human rights. The commission declined to investigate a single complaint (Loeffler). Issues emerged as larger powers did not want to risk upsetting the power balance between the Soviet Union and the United States, potentially triggering war, and smaller powers had neither the political capital, resources, nor incentive to begin investigations. The UDHR proved an empty set of nice sounding words. This was not unexpected, even at the outset: according to international law expert Hersch Laueterpacht in 1947, “to a lawyer, the enunciation of a right without the provision of a remedy is a judicial heresy…It is clear to me that the declaration does not carry things further and that in some important respects has put the clock back.” (Loeffler)&lt;/p>
&lt;p>
Loeffler, J. (2018, December 21) “Human rights treaties promised a better future. Why did they fail?”. Washington Post.
Hopgood, S. (2015). The Endtimes of Human Rights. Ithaca: Cornell University Press.
Posner, E. (2014). Twilight of Human Rights Law. Oxford University Press.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-4" class="outline-3">
&lt;h3 id="headline-4">
1976-1991: Era of Human Rights Treaty Proliferation and American Money
&lt;/h3>
&lt;div id="outline-text-headline-4" class="outline-text-3">
&lt;p>
The modern age of human rights organizations begins in earnest in the 1970s with the International Covenant on Economic, Social and Cultural Rights (ICESCR) and the International Covenant on Civil and Political Rights (Moyn). The covenants were intended as an expansion of the UDHR, but this time, gave overseeing commissions more power to enforce violations. The covenants were contemplated as early as 1948, by American historian Arthur Holcombe, as “a project for a piece of international legislation, more ambitious and perhaps more important than any other in the history of international law. If supported by suitable measures of implementation, it could be a great triumph of reason over force and violence in the development of human relations.” (Holcombe, 413)&lt;/p>
&lt;p>
Thus, the 1970s represent the shifting tide of human rights from general irrelevance to a proliferation of human rights treaties, to mixed effect. Some of these genuinely treated human rights issues, ie. Convention on the Elimination of Discrimination Against Women (CEDAW), while others were microcosms of the symbolic battles between the United States and the Soviet Union. The United States used human rights law as political leverage against the Soviet Union, while in exchange for ideological concessions, the Soviets bought recognition of autonomy in determining the freedoms of Eastern Europe.&lt;/p>
&lt;p>
The beginnings of American involvement in human rights in the 1970’s signaled the end of a unipolar European human rights era, though for a time human rights became unipolar about the American-centric perspectives, a phenomena that strengthened after the collapse of the Soviet Union, and has only in the last decade seen significant challenges by economic competitors to the United states, most prominently China. The era of American influence saw the demise of what Hopgood describes as European “secular religiosity” and towards the American style of political intervention in the name of defending democracy—where democracy was a thinly veiled stand-in for American economic interests. Recent examples include American ongoing relations with Saudi Arabia, 1990s support Saddam Hussain in Iraq, and strategic support of torture in Guantanamo Bay.&lt;/p>
&lt;p>
The Americans, even under the most ardent human rights supporter, President Carter, were inconsistent allies to the human rights endeavor: “human-rights violating allies like Iran and Saudi Arabia were just too important for American security, and seen as an important counterweight to Soviet influence, so Carter could not consistently follow through on his rhetoric by threatening to withhold diplomatic support or economic resources from some of the worst violators of human rights” (Posner 18). While the United States generally supported and provided funding for major human rights organizations, they seldom ratified human rights treaties, and only with a raft of Reservations, Understandings, and Declarations (RUDs) limiting the United States’ liability to upholding the treaty.&lt;/p>
&lt;p>
Not that this was likely even necessary. Many authoritarian countries ratified the ICCPR, and other human rights treaties, and although these treaties were designed to be more binding, the same issue of political non-incentive for any particular country to take action resurfaced. Further, the treaties often used vague, sometimes self-negating language, or specified requirements that would be aspirational for all but the richest countries. Article 19 of the ICCPR declares the right to freedom of expression, but in the next paragraph, offers governments a get-out clause:&lt;/p>
&lt;ol>
&lt;li>Everyone shall have the right to freedom of expression; this right shall include freedom to seek, receive and impart information and ideas of all kinds, regardless of frontiers, either orally, in writing or in print, in the form of art, or through any other media of his choice.&lt;/li>
&lt;li>The exercise of the rights provided for in paragraph 2 of this article carries with it special duties and responsibilities. It may therefore be subject to certain restrictions, but these shall only be such as are provided by law and are necessary: (a) For respect of the rights or reputations of others; (b) For the protection of national security or of public order (ordre public), or of public health or morals.&lt;/li>
&lt;/ol>
&lt;p>The gates of plausible deniability in the clause, “protection of national security or of public order.. or morals,” swing wide. The failings of the covenants were appreciated at the time. Pakistani legal scholar, Hamid Kizilbash wrote of the covenants in 1976 that “what is most unsatisfactory about the implementation procedure is the fact that the individual has no role in the preparation of reports. States are not called upon to consult or transmit what an individual group within their territory may wish to have included in the report. No system of hearings, public consultation and individual petitions has been provided for. In the absence of such provisions it is clear that the reports will reflect whatever the government of a state wishes to make known” (Kizilbash 57).&lt;/p>
&lt;p>
Holcombe, A.N. &amp;#34;The Covenants on Human Rights&amp;#34;, Law and Contemporary Problems, 14 (Summer 1948), pp. 413-429.
International Covenant on Civil and Political Rights, Article 19 (1966).
Kizilbash, H. “United Nations and Human Rights: A Failure Report” (1974). Pakistan Horizon Vol. 27, No. 1 (First Quarter, 1974), pp. 50-60
Moyn, S. (2010). The Last Utopia. Harvard University Press.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-5" class="outline-3">
&lt;h3 id="headline-5">
1991-now: Era of American Pseudo-Unipolarity
&lt;/h3>
&lt;div id="outline-text-headline-5" class="outline-text-3">
&lt;p>In the wake of the fall of the Soviet Union, one might reasonably assume a raft of advances human rights could proceed, over the corpse of their most powerful political opponent. However, immediately following the fall of the Soviet Union, events made a mockery of such an assumption. First, the horrifying genocide in Rwanda, where more than 800,000 Tutsis were slaughtered by the Hutu majority in Rwanda (Posner). The event was made for macabre spectator sport for the human rights organizations of Europe, demonstrating the irrelevance of the now nearly 50 year old Convention on the Prevention and Punishment of the Crime of Genocide and the commission appointed to advise the UN. The lesson repeated itself one year later in the civil war in Yugoslavia where Serbians attempted to ethnically cleanse Bosnians from the region, shocking Europeans to see genocide return once more in the 20th Century to their own continent.&lt;/p>
&lt;p>
The war criminal trials that followed (the UN was basically immobile during the atrocities) were widely criticized for bias and inconsistency, leading to the establishment of the International Criminal Court in 1998, and official opening in 2002. It has tried few, successfully tried fewer, and those it has tried are exclusively from African nations. The court took 10 years before its first successful prosecution, of the Congolese warlord Thomas Lubanga Dyilo. It is also expensive, costing a billion dollars in its first decade. The United States continues to claim immunity to the court, and refuses to provide funding for the court. Naturally, the court appears mostly irrelevant.&lt;/p>
&lt;p>
According to Freedom House, every year since 2005 has seen a retreat in democracy and an advance of authoritarianism. Modern democracy has seen a corresponding retreat of the notion of the public space where facts exist as universal. Propaganda experts in Russia successfully used data, with the aid of data company Cambridge Analytica to spread disinformation about political events in the United States and the United Kingdom leading up to the 2016 election, and the Brexit referendum. Facebook has set new norms for the public dialogue between fiction and fact, allowing disinformation to be spread, while making surveillance easier than ever before (Snyder). Democracy has succumbed to populism in Hungary, Poland, Brazil, and Bosnia in the last decade. Human rights efforts, by contrast, appear to be stuck in the 19th century.&lt;/p>
&lt;p>
In 2017 the Office of the High Commissioner for Human Rights launched their 70th year anniversary campaign. The campaign feature the hashtag, #standup4humanrights, along with a website proclaiming “we can all be Human Rights Champions.” The website encouraged participants to post stories online, on platforms including Snapchat, Instagram, and yes, Facebook, the last of whom there has received no official comment on, or criticism of, by any of the human rights commissions at the UN. “All it takes, apparently, is posting individual stories online and recording an article of the declaration in one’s own language. There is hardly any mention of law or politics; it suffices to ‘promote, engage and reflect.’” (Loeffler)&lt;/p>
&lt;p>
Snyder, T. (May 21, 2018) “Facism is back. Blame the Internet.” Washington Post.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div id="outline-container-headline-6" class="outline-3">
&lt;h3 id="headline-6">
Conclusion
&lt;/h3>
&lt;div id="outline-text-headline-6" class="outline-text-3">
&lt;p>Throughout this essay, I’ve alluded to the differences between revolutionaries’ claims to rights, and governments’ self-justification via an invented language of international law. Hopgood explains the difference between top-down human and bottom-up human rights as follows, “the local and transnational network of activists who bring publicity to abuses they and their communities face and who try to exert pressure on governments and the United Nations for action, often at tremendous personal cost”, versus “a global structure of laws, courts, norms, organizations that raise money, write reports, run international campaigns, open local offices, lobby governments, and claim to speak with singular authority in the name of humanity as a whole” (Hopgood 2). The criticism reflects the conflict of liberal versus the libertarian, in the question, does greater bureaucracy correspond to gains in human potential, or is the growth that of a cancer, whose purpose is not to serve the body out of which it was conceived, but only to grow?&lt;/p>
&lt;p>
This central conflict plays out while human rights activists engage bottom-up, grassroots endeavors around the world. These countries lie beyond the political purview of the International Criminal Court in the Hague, struggling against the rise of tyranny and nationalism in places like Bolivia, Venezuela, China, and Russia. Resistance movements can only seldom rely on the international bureaucracy to provide the assistance promised in lofty covenants. If human rights organizations had any legitimate influence, the Arab Spring would have been unnecessary, and dictators in Egypt and Syria would have been replaced by human rights leaders. Instead, politicians in countries like the United States and Russia used human rights declarations as pawns to maintain economic interests in the region.&lt;/p>
&lt;p>
What is unconscionable is that human rights organizations continue to ignore criticism of the failings of the movement. Foreign policy analyst David Rieff wrote of this phenomenon of cognitive dissonance, “This is predictable. If your expectations are millenarian — if you believe there is a right side of history, yours, and a wrong side of history that is doomed to defeat — skepticism about the human rights project, let alone voices of opposition, is unlikely to sway your position” (Rieff). Human rights activists need to justify the unmerited and costly expansion of their bureaucracies. Human rights documents need to have more substantial teeth than the possibility that a commission will enter into dialogue with a violating nation. The failures of human rights organizations are costly not only in price but in their deception: allowing nations to do the bare minimum to defend democracy and free people around the world.&lt;/p>
&lt;p>
Rieff, E. “The End of Human Rights?” (April 9, 2018). Foreign Policy Magazine.&lt;/p>
&lt;/div>
&lt;/div></description><category domain="https://thork.net/categories/society/">society</category><category domain="https://thork.net/tags/human-rights/">human rights</category><category domain="https://thork.net/tags/history/">history</category><category domain="https://thork.net/tags/school/">school</category><category domain="https://thork.net/tags/politics/">politics</category></item></channel></rss>