A collection of reading questions and exercises to help check for understanding.
- What's a cryptographic hash? What are its important properties?
- What's a digital signature? Properties?
- They both seem to take some input and scramble it to some output. Can we reuse the same function for both?
- How does public-key cryptography relate?
- Given a cryptographic hash function
h
, can you define an associative hash functionH
using another functionf
?- You pick
f
H(a, b) = f(h(a), h(b))
H
should generally satisfyH(a, H(b, c)) <=> H(H(a, b), c)
- You pick
The answer is no, we can't. To start, a cryptographic hash function should be irreversible, whereas a digital signature has to be reversible (otherwise how would you determine what was "signed"?)
That's a bit literal, so a higher-level answer is that cryptographic hash functions and digital signature have different requirements, which in turn requires the mathematical functions they use to have different properties.
Reversibility happens to be one property directly in conflict.
The intuition is to pick an f
you already know is associative.
Let f
be the associative string concatenation operator (||
):
H(a, H(b, c))
<=> f(h(a), f(h(b), h(c)))
<=> h(a) || (h(b) || h(c))
<=> h((a) || h(b)) || h(c) # by the associative property of `||`
<=> f((h(a), h(b)), h(c))
<=> H(H(a, b), c)
There may be other f
: concatenation is used here because it is simple and intuitive.
It is also directly relevant to the exercise below on Merkle Trees.
Merkle trees hash the concatenation itself, so `h(h(a) || h(b)).
Follow-up: is concatenating two hashes cryptographically secure? Can H
be a cryptographic hash function?
One way to answer is to revisit the above question "important properties of a cryptographic hash function" and think about whether concatenation would violate any of them.
Describe a blockchain (data structure). What's the use-case?
A blockchain is a linked list with hash pointers. Its components:
.prev
pointer.prev_hash
pointer (cryptographic hash of the previous node aka "block")
The use-case is tamper detection. If any node in the blockchain is altered, we'll know because the hash will no longer match. Therefore, you can always check the blockchain is valid by iterating from the head of the list
Pseudocode: for every node curr
, hash prev
and check that result hash equals curr.prev_hash
What's a Merkle tree? Use-case?
Merkle tree is a clever data structure to reduce time complexity by leveraging the fact hashes are composable.
You can combine two hashes H(h1, h2)
to produce a third hash h3
. If either h1
or h2
change, h3
changes.
The use-case is efficient verification. It takes O(n)
time to verify a block is part of a blockchain, where n
is the number of blocks.
This is because you have to start from the head and check the hashes until you get to that given block.
With a Merkle tree, you overlay a tree on the blockchain such that all the leaves of the tree correspond to the original blocks. Each parent is a composite hash, and the parent's parent is a composite of composite hashes, and so on. Diagram.
Now, to verify a block is part of the chain, you only need a path through the tree. This is O(h)
, where h
is the height of the tree.
Then, by keeping the tree balanced, the complexity is logarithmic.
You are the designer of a currency Doxxcoin
, and you want to implement a protocol Anoncoin
that allows Doxxcoin
holders to "anonymize" their coins.
- The definition of anonymize is to disassociate a given
Doxxcoin
from all its prior transactions and addresses. Doxxcoin
works likeBitcoin
, so you can trace all a given coin's addresses using the transaction ledger.- You have the ability to mint and burn
Doxxcoin
andAnoncoin
. - Users should still be able to transact with
Doxxcoin
after anonymization. - For the sake of simplicity, disregard units or amounts.
Assume also you have the means to construct a zero-knowledge proof that satisfies the predicate Zk(f, x): ∃x, f(x) ∈ {s1, s2, ... , sn}
- Predicate: "There exists an
x
such thatf(x)
is in the setS = {s1, s2, ..., sn}
" - Zero-knowledge proof: "I know
x
such thatf(x)
is inS
, without giving awayx
" - You can pick what
f
andx
are. - The Zk-proof can be treated as a black-box and used anywhere in the scheme.
Can you come up with a cryptographic scheme Anoncoin
that anonymizes Doxxcoin
?
commitment, escrow pool, burn to mint, trust-less
- What if you pooled money together?
- How can you leverage your treasury superpowers? You can issue (mint) or remove (burn) currency from circulation.
- Can this scheme be completely trust-less? How might the Zk-proof help?
The key idea is to anonymize by pooling Doxxcoin
together into a collective Anoncoin
escrow pool then redeeming Doxxcoin
from that pool.
The coin you get out is not the same coin you put in.
More importantly, the coin you get out cannot be associated in any way with the coin you put in.
Imagine you put physical cash in an envelope along with some proof of ownership (identity / public key), and add it to a pool of envelopes. This physical cash has your fingerprints, as well as those of everyone before you (analogous to public keys and tx input / output addresses).
The safest way to ensure anonymity is to put the envelopes in a vault, taking that money out of circulation ("burning") and minting new money to replace it. You can't just shuffle the envelopes and give back someone else's cash, because that still leaks information about history.
Now you have some freshly-minted money which has no transaction history.
It's still Doxxcoin
so you can spend it just like you otherwise would.
In order for this to work, the owner / custodian of the pool needs some way to determine whether you have added money before handing out new Doxxcoin
.
The obvious solution is that the custodian opens the envelope when you ask to redeem Doxxcoin
from the pool (recall you stored your identity in the envelope to indicate that it belongs to you).
They do this to verify your ownership before crediting you with newly-minted Doxxcoin
.
The problem is this requires trust: that owner now has linked the newly-minted Doxxcoin
with your original, and it's no longer anonymous.
Centralization and trust is what we want to avoid!
This is where ZK-proofs come in. The ZK-proof allows you to prove you own one of the envelopes without ever opening it.
The other thing we need to do is model this notion of an "envelope" with cryptography. We can do this using a commitment. Indeed, a commitment is often explained using this envelope analogy.
The Anoncoin
scheme is as follows:
- Anonymize:
- Create a transaction with two inputs
commit(identity)
andDoxxcoin
. - Mint an
Anoncoin
which represents the commitmentcommit(identity)
and add it to a pool ofAnoncoin
. - The input
Doxxcoin
is burned.
- Create a transaction with two inputs
- Redeem:
- Construct a Zk-proof
Zk(f, x)
, wheref = commit
andx = identity
- Then, the proof shows "I know
x
, wheref(x) = commit(identity)
andf(x)
is in the pool of commitments{s1, s2, ...}
" (see problem statement) - Here the commitment pool is in fact a pool of
Anoncoin
. The Zk-proof proves you minted one of theAnoncoin
without revealing your identity (x
). - The protocol (pool custodian) verifies
Zk(f, x)
. - The protocol then outputs newly-minted
Doxxcoin
. Because it's zero-knowledge, the protocol does not learn what your identity (x
) is at any point, thus anonymizing.
- Construct a Zk-proof
The beauty of Zk-proofs is everything can be done with pure cryptography. No trusted third-party needed.
This scheme is based on the Zerocoin protocol, with details omitted for simplicity.
There is a problem with the scheme described in the solution above. Can you spot it?
UTXO
There's a double-spend problem. I can keep constructing Zk-proofs, and getting newly-minted Doxxcoin
.
This is because the Zk-proof
makes it such that my envelope is never opened: in fact the protocol doesn't even know which envelope is mine.
I could have multiple envelopes, so it can't just restrict me to redeem / spend once.
The solution is to include as part of the commitment a unique mint_id
.
- Anonymize: the
mint_id
parameter is added tocommit
socommit(mint_id, identity)
- Redeem: the
mint_id
is also included with theZk-proof
.Anoncoin
protocol keeps trackmint_id
has never been used in a priorredeem
operation