-
Notifications
You must be signed in to change notification settings - Fork 26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Base ledger private trade circuit design [ex-issue-16] #95
Comments
Important categories of private trades to consider:
Separately, we also want to think about private ways to gossip liquidity across the orderbook. |
Additional background reading:
Likely we'll need a more general record system for private asset tracking. I'd like to reason through what it would take to craft a maximally general private trade circuit as well - e.g. this would require private state (only Merkle roots stored), private validity predicates, WASM validation in a circuit, etc. - likely far too expensive but in the far future we'll want this, and a useful basis for comparison. I'm also now reasonably convinced that we need to include direct negotiation in the intent gossip system. |
Notes from pondering:
|
Another note - we should investigate delegated proving, it would be very nice for edge clients. |
So, here's what I've been pondering as well:
OK, now let's address the records system:
Before describing ExchangeSpend and ExchangeOutput further, one important observation. In MASP (and Sapling) the sender of a tx can actually send garbage to the recipient and there is nothing in the protocol to externally detect or prevent this (of course, the recipient immediately realizes they got garbage, and generally one can rely on the sender of a tx to not destroy the asset they're sending). However in a DEX we want to ensure that one party doesn't end up with garbage, in case a party has some incentive to burn their outgoing asset instead of sending. One idea is to check the output note commitments in the ExchangeSpend as well as the ExchangeOutput. In this case, each recipient would verifiably have enough information to spend a note before the tx is processed. So then the shared state includes asset types, amounts, keys, and output note commitments; the tx verifier checks that all ZKPs in the tx have this same shared state as input, and therefore ExchangeSpend can only be used in a tx if accompanied by the correct other proofs. This approach doesn't handle the "cancel" case (as described in ZEXE). In ZEXE style, a tx can also be accepted with a subset of ExchangeSpend proofs along with a Cancel proof, with some consistency checks. This prevents the other party from indefinitely holding a free option to either execute the exchange or not; alternatively ExchangeSpend proofs can also come with an expiration timeout (avoiding tx fees for a Cancel) Unfortunately a Cancel leaks information because the nullifer is revealed in ExchangeSpend, and the same nullifier is revealed later in another ExchangeSpend or Spend. I'm not immediately sure how to cure this issue. |
In terms of delegated proving, it's notable that Sapling (and therefore MASP) allow delegated proving as described in the Sapling protocol spec. It does require revealing viewing keys to the delegated prover as well as spend authority for the note being spent, but I think this existing method would possibly work well for ExchangeSpend as well. |
Possibly worth consulting here w.r.t. checking encrypted data - Henry de Valence's notes on building up encryption from Poseidon using something like the Strobe protocol framework, which as I understand it would then make the encryption relatively inexpensive to check in the circuit itself. |
I think between traditional commitments and building encryption from Poseidon, it should be feasible to do what we want. Now on checking validity predicates: I already observed that there is a limited version of this check already in place in Sapling/MASP as the Some observations about properties we might want:
I'll leave it as an open question whether preprogrammed validity predicates are a good short term idea. Hiding the VP circuit is the primary technical challenge. The tx builder, knowing the circuit, must write a proof of the form "the VP circuit was satisfied by the tx plaintext". Two ideas:
In one-layer composition the tx builder proves the VP circuit, then verifies the proof inside another circuit. I think this will have scaling issues. If the VP circuit is small, then the VP circuit can be proved using PLONK/IPA (say, over JubJub) and then verified in the "main" circuit which is PLONK/KZG over BLS12-381. The size of the largest allowed VP dictates the performance. There is also a mismatch problem between the scalar field of VP circuit and the scalar field of the MASP circuit. Alternatively BLS12-377 can be the inner curve and MNT the outer. This allows much bigger VP at the cost of much more expensive verification. There is a mismatch between the scalar field of VP circuit over BLS12-377 and the scalar field of the MASP circuit over BLS12-381, unless we change MASP to BLS12-377. Alternatively PLONK/IPA over Pasta curves can be used for everything: MASP, VP, etc. This solves all mismatches with a large concrete cost increase which can be amortized away in the Halo 2 style.
|
Another quick note, there is another alternative to PLONK/IPA for one layer proof composition: R1CS via Spartan. This would have some more scalability than PLONK, allowing for larger VP, at a greater engineering cost to design and build such a scheme. |
I think the simplest approach to checking validity predicates right now is blinding the PLONK polynomials, and checking the blinding takes roughly 7 non-native scalar multiplies in circuit. Assuming the blinding details work out. The alternative, which is likely to work well, is to use BLS12-377 over MNT - checking all the proofs of validity predicates as private data. This might also be rather straightforward, since it requires less novel code than blinding PLONK. |
I think this is a reasonable limitation for the initial set of private bartering circuits; later on or more long-term we can try to pursue more advanced options allowing computation over shared private state.
This is fine, but I wonder if we need it - can't records also do shielded transfers (perhaps a bit less efficiently)?
Does the recipient need any other information (besides output note commitments) which we'd have to check, or just those?
I think we want to avoid this specific approach because it requires an extra on-chain transaction to create the record (escrow).
Aye; let's aim to preserve this capability wherever possible.
We should certainly have examples, but ideally users would also be able to write their own (private) VP circuits.
Do we have any idea "how small is small" ?
That is, by accumulating, e.g. all proofs within a single block?
What do we need to do to investigate the possibility of so blinding? If that works, this proposal seems compelling. |
Hopefully, for quite a while; I think a pairwise shared private state model can handle all the cases laid out in the Anoma vision/whitepaper. For example, if an n-party exchange is needed because (for example) exchanging coin A to D via A-B-C-D is only possible by A-B, B-C, C-D intermediate exchanges (with 3 different counterparties), then the only shared private state needs to be pairwise between each party/counterparty; say, party 1 can request A-D from party 2; party 2 negotiates A-B with party 3, B-C with party 3, C-D with party 4, all incorporated into a single tx.
Yeah, you're right; I don't think we need to record intent in the merkle tree.
There's a few components to the note plaintext, but it's really quite small (except for the memo field). It's a relatively easy check and I don't even think it will require anything fancy, but I think it wasn't a requirement of the Sapling security model to require output ciphertext checking whereas here it does.
Sure, I think I better understand this approach now and none of it has to be on-chain except for the final Exchange or Cancel transaction. There can be a timeout on the Exchange operation, if we want, but the Cancel is necessary to avoid leakage of the nullifier.
I think the overall complexity of the system is largely determined by the complexity of the most complex VP, but if supporting complex VPs is important, then we must.
Let's say the biggest VP circuit is negligible compared to the size of the Spend circuit, then checking the VP with PLONK/IPA in the Spend circuit over PLOMK/BLS12-381 only incurs a modest penalty on everyone with simpler VP. Basically one-layer proof composition with no curve issues, at the cost of increased prover time for everyone, up to the biggest VP circuit. I think this is reasonable if validity predicates check simple circuit friendly signatures and such. I can't really envision a use case for a huge VP that has to do many (say) hashes but of course I don't want to limit the system arbitrarily either.
Yes, I think managing the complexity is much easier with accumulation. However, accumulation does put maximum size restrictions on the VP in a similar way since the VP is written in PLONK/IPA. I don't see a straightforward way around this, which is why I hesitate to outright recommend it.
The primary implementation task is probably plookup non-native arithmetic, and the primary brain task is writing out the blinding and proving the relevant soundness and hiding properties. The downside is probably slightly larger tx sizes (1 extra circuit description + 1 VP plonk proof per party, which is probably about 1.5 plonk proof sizes) |
OK, here is a (tentative) plan:
Actually, in this case, the I think the only complication that I don't understand now is that Let's take the "subscription" example from the whitepaper. I want to subscribe to a site that is $1/month, for 12 months, and I have a note worth $15. So I can send one tx that splits my $15 note into 12 $1 notes and one $3 note. Once I have the $1 notes I can send to the site all 12 This achieves the desired goal, but seems a little clunky to me. For one, I need to set up all the notes in advance. Even if the VP only authorized spending part of a note, the new note output from a tx can't be VPSpend before it's produced; hence the need to split my $15 note beforehand. |
We'll need to think a little bit about how this negotiation will work, since the parties want to avoid creating proofs during negotiation which can be used to settle trades that they didn't want (e.g. not the full swap), but presumably this is possible since their validity predicates can tie different pieces together and require that all be present before accepting.
👍
Hmm, can we investigate this a little? Is it possible to craft a way in which potential exchanges can by default timeout (with no transaction settled) without leaking a nullifier, or not?
Let's try this!
This is a little clunky, yes. That said, it's not that different from what our intent gossip network is doing already, so at least architecturally it wouldn't be difficult to provide software which tracks these messages and proofs for the site operator, or even for someone else to track them - as they don't contain any confidential information, right? Actually I think maybe the key distinction on whether this is too clunky or not is whether the roles can be split so that the site operator can outsource this data management to a third party who can receive a small fee for doing so. However, what we'd really like to enable in this case is something different, I think - we want a subscription that is "$1/month, out of this account, until cancelled" - that means that the subscriber needs to sign something authorising splitting their note of "$x" each month into "$1" and "$x-1", until they submit another transaction to the chain which revokes this subscription authorisation. Is this possible, or not? Could this be accomplished by something like the website or third party operator creates the new notes but has to send the "$x-1" note back to the subscriber? |
I wrote a small note on a possibility of blinding the circuit here. |
I think the plan makes sense now. To summarize with more detail:
The nice thing is that for this circuit it should Just Work; no issues with output ciphertext validity, etc. In fact to do an exchange from A-B you can just write an
It's actually an interesting question whether to check the validity predicate in the
The validity predicate works in the following way:
The reason for delegation in 2 is because, in general, full viewing authority is needed to prove the |
Here's my idea for using a second snark to prove the expected VP was used. Say the blinding is done as in Simon's note above:
The commitment In a second proof, Because it's a snark, the Prover is not bound to using the exact same polynomials |
This looks really nice, I think this will be very practical. In fact I wonder if there's a simple way to implement the
I did forget one step here - the circuit needs to check not only knowledge of [q], but that [q] is the correct validity predicate for the owner of the note. I had assumed that [q] would just be signed, but this does not handle the case where someone changes their validity predicate (they cannot undo the old signed VP). So somehow we need to keep track of all the current validity predicates, say in a merkle tree, then the prover has to prove correctness of the
Actually this does beg a question, what is
I think this is ok, what do you think @simonmasson? |
Right, so the original plan is not good enough. The authenticity of the If necessary, we can also require that |
This would be nice, substantial privacy benefits for whichever of transfers or exchanges happens less frequently (esp. at first).
This would be nice. If it's too expensive, a validity predicate could also in principle itself keep in state the verification key for a second circuit, which it could then use to verify, using an additional layer of recursion, right? This is analogous to what we do at the moment where validity predicates can |
The main argument against lumping everything into one circuit is probably increased memory requirements for proving. We might have to evaluate if it becomes prohibitive.
Maybe, though I'm not sure this helps. The main problem is being able to cancel or replace a VP, there has to be some tracking of that. Since the prover can replay old signed validity predicates, there has to be a public input that verifies that the validity predicate is current, without revealing the VP; hence the need for a cryptographic accumulator like a Merkle Tree. I think the current idea is sound but it is just constraint-expensive to check a second Merkle path in circuit; however in the long run maybe this can be optimized with plookup/Sinsemella |
So, I rather like the idea that the merkle trees contain both "regular notes" and "VP notes" (where the note doesn't represent any asset or value, but rather an allowed validity predicate for that address). This makes updates of the VP's part of the same anonymity set as the asset notes, which is nice. Then it's actually quite straightforward to check the blinding - it's almost exactly like spending a regular note, except that if the "VP note" is "spendable" then we know that's an OK validity predicate to use for that address. I should note that @42ndTowel's suggestion about consuming the note is necessary here. When a "VP note" is "spent", which reveals it's nullifier, it cannot be used again, so the transaction has to output a new "VP note" to replace it. Otherwise, there is no way to check if a "VP note" is still valid or not (since the merkle tree of notes is append-only) We still have a serious problem, that I didn't fully appreciate until now: the recipient of notes must receive private information that allows them to spend the notes., probably through the Sapling/Orchard in-band distribution mechanism. The problem? In the Zcash model, the creator of a transaction is not likely to want to burn the output of the transaction by withholding the critical information from the recipient (it's equivalent to them destroying their own money, which they can always do by different means). But if a third party is now constructing transactions, they can use someone's VP to validate the tx, but then skip the distribution step, effectively spending the sender's money but the recipient gets nothing. |
Can we check this (check that the correct private information is encrypted) in the circuit itself? |
This would use the "SNARK-verifiable encryption" idea: there's some symmetrically encrypted payload that we want to verify the contents of. But while it's technically feasible I would rather come up with some clever workaround that doesn't depend on something like that. |
After chatting with Pratyush and str4d I think we should do a verifiable encryption scheme to solve this. Basically Zcash will start using Poseidon as the PRF to get randomness for transcript aggregation, so we might as well start depending on the Poseidon assumption for other things as well. There is a question in my mind whether we go for the quick approach (encrypt the relevant values asset type, value, diversifier, randomness seed directly, without symmetric key encryption - this costs an extra PRF to derive rcm from rseed) or the complex approach (implement a whole Poseidon based AEAD and use that for note encryption). |
Let's see. The Action circuit could be changed in these ways: A validity predicate is a PLONK circuit given by some polynomial commitments together with an owning address/key. A blinded VP is the polynomial commitments, hidden with some blinding factor. Note commitments can be either regular note commitments, or a commitment to a validity predicate (the type of note is indicated by a flag) The Action circuit, and a new CheckVP circuit, takes public input two "blinded" VPs, one for the spent note and one for the new note, and private input the corresponding VP note commitment and blinding factor. The public input to a validity predicate is the ordered list of incoming nullifiers, and outgoing note commitments, for every Action with that blinded VP as public input. The private input is the opening of the note commitments (both incoming and outgoing). An Action circuit is given a regular (value) note to spend, instead of checking the spend authorization, it checks the key in the spending VP note commmitment against the spent note, and checks the blinding of the VP. (Then the same with the output note) A CheckVP circuit is given a VP note to spend, it checks the VP note against the blinding of the VP, and checks that the VP note is in the merkle tree, which ensures that the blinded VP is valid. The CheckVP is given as public input the nullifier of the "spent" VP note, ensuring that the VP note is "current". The output note is the new VP note, whose owning key must be the same as the incoming VP note. Both Action and CheckVP circuits use verifiable encryption on the output notes to ensure proper distribution. We want the following properties:
There's probably at least 5 remaining problems with this:
|
I think I don't like this approach, because it's not using the full power of the accumulation scheme. For example, it's actually not necessary at all to have the blinded PLONK circuit or the VP proof be part of the transaction at all; it would be better to have the VP proof added directly to the accumulator (potentially with a blinding factor; but there would be not need a separate check of the blinding factor, only to include it in the accumulator's proof) However, there are some parts which aren't clear to me at all:
At least for problem 1, I think the solution is still to have a separate CheckVP circuit. Instead of sharing a blinded VP with each Action circuit, we would only need to share the commitment to the VP, which is a lot simpler. (Side note: One problem with a separate CheckVP circuit is that it breaks the anonymity set between regular notes and VP notes; we might want to merge it all into the Action circuit, as long as we can ensure that the VP is checked somewhere) Problem 2 is much trickier. I have no idea how to do this (although I am confident that it is theoretically possible - we just have to figure it out). It might be ok to just have a separate VP proof accumulator for each transaction (that might get accumulated into other accumulators later, but that's besides the point) |
So things are still a bit unclear, except that I think the question I just asked (what do we accumulate on top of?) is actually not important. Each block should have 1 accumulator (or maybe 2; one each for Pallas and Vesta, we'll have to look at what Zcash is planning carefully) which will accumulate all proofs within that block. In the long run, the next block will simply accumulate onto the previous block (this is a separate thing we can discuss independently) But for now, since we already need block-level accumulation, we shouldn't (or at least don't need to) accumulate at the transaction level, which avoids the question of what gets accumulated. This might make transaction size larger, but since they get accumulated into a block, it doesn't consume more space on chain. Therefore, we should probably just go back to the blinded VP approach:
The issues I'm still unsure how to do are:
|
Subprojects and sub-subprojects (thanks @lopeetall for the notes!)
We need to add multiple-asset support to Orchard, either in a similar way that we did to Sapling, or in a way more like Zcash Shielded Assets (zcash/zcash#830). Since Zcash will be adding support anyways, there's some possibility for shared effort, but in the worst case, we're not reliant on their approach.
We will need a proving system for VPs, which at first will be written by hand and later by some tools like Juvix etc. Fortunately we have a couple good options for this - and once the transaction format is finalized we can design some example VP circuits.
We already have some good ideas on how to prove (and check proofs) of blinded VPs, though we need to convert it to the Halo 2/IPA commitments scheme. This includes implementing an in-circuit blinding check (to make sure that the blinded VP is a blinding of a correct VP) and proving that the soundness of the proof system is still ok.
This is the main challenge - we need some kind of metadata processing to ensure consistency among all the circuits (including VPs) in a tx. In Sapling/Orchard, this is the value commitment balancing - each circuit is only concerned with the value of it's own note(s) and none of the circuits actually check that the transaction balances correctly. Instead, the value commitments are added and checked outside of the circuit (avoiding any need for each circuit to consider transaction-level metadata). However, validity predicates aren't homomorphic so this makes it trickier to check. We'll need some clever idea here. In the worst case, the entire transaction should be committed to and opened in each circuit - this is "feasible" but not necessarily "efficient".
Once the circuits are all designed, the transactions are going to be rather large, since Halo 2 proofs are on the scale of kilobytes (and we will have more of them, since VP circuits and proofs are included). So at minimum we will need Halo 2 accumulation at least at the block level: mempool transactions are large, but the block proposer accumulates them so only 1-2 proofs are needed per block. Fortunately, Zcash will be working on this as well, so shared effort is possible (although our situation is more complicated because of accumulating different VP circuits) In the worst case, we can also do transaction level accumulation (accumulate all proofs within a tx) although I don't think this will be necessary.
Ultimately an exchange will only work if some information is leaked - otherwise there is no way to match people to trade (without fancy crypto like FHE/iO). So we have to figure out when and where to leak. Leaking exchange rate publicly is probably not that bad - in fact it could be a feature as it helps the market do price discovery. This can maybe be incorporated with the unshielded intent gossip system. Alternatively this could be a role for a trusted/centralized third party. At least, leaking to a third party is more private than leaking publicly. However, since there's not a perfect way to handle this, we should add as much flexibility as possible.
Since the transaction creator can output invalid note ciphertexts (potentially burning the output notes), the system has to make sure that VP-authorized transaction-creation authority (which so far only requires viewing key authority for the relevant keys) doesn't allow burning-authority as well. Probably the way to do this is with Poseidon-based verifiable encryption of the note ciphertext. In principle this doesn't require any new ideas, however the implementation might be somewhat complex.
Sending to an address now requires knowledge of the recipient VP, which could be included in the address (which would be quite unwieldy), or maybe the VP can be posted transparently on the blockchain (also unwieldy). The large size of VP circuits compared to addresses is a problem. At least for right now, this is more of a "usability" problem than a "technical" problem, although we should try to solve it by engineering some kind of better solution. 8b. How to find the new VP after a former one is consumed? Related to 8a, this is additionally complicated if VPs are not simple static objects but instead are created and consumed with each tx (e.g if VP commitments are stored in a note merkle tree) In short, as you can tell, adding validity predicate support is a major project with subprojects that range from "some implementation required" to "the entire scheme needs to be designed" - and nothing is set in stone yet (which is good since we haven't met all the design requirements, yet!) |
I'm writing down some notes here to help me organize my thoughts on PBC. My main goal is to separate out the core semantics and mathematical functionalities that PBC should achieve without reasoning about the implementation specifics. Some preliminary definitions
Private state kept track by the shielded pool
Transactions
Validity predicates (VPs)
Evaluations against user workflows
|
I think we do, since we want NFTs to be able to have arbitrary properties which can be reasoned about in the validity predicate. Each NFT can still have a unique tag, however.
This may be the case, if we can ensure that the important userflows work without stateful VPs then that's fine. |
OK, these are some good ideas. I had been picturing that intents could be expressed as "user VP" (e.g. a new address for each intent), but maybe it's better to split it out separately as an "intent VP". Here's what I think so far:
The remaining details sound good - I think we should use the MASP PRF model right now, and write the first spec around that. For NFTs (or really any other asset type with extra metadata), the tag does not need to be processed in-circuit and we don't have to define any special behavior in-circuit. |
Sure. Intent VP could persist in the ledger and keep a state. The main conceptual difference between user and intent VP is this: validity of an user VP is proved by the user (in the form of signature or what not), while the validity of the intent VP is proved by the matchmaker. For the example of asset swaps, Alice can construct an intent VP The main benefit of considering intent VPs is to clearly abstract what is being constructed and proved at the client versus by the matchmaker, as well as the exact information leakage from the client to the matchmaker. Intent VP encodes information that is revealed to the matchmaker, which include trade information (trade sizes, NFT properties) but exclude identifying information about the user (Alice). In the finalized ledger transaction, a matchmaker must prove satisfaction of the intent VP (of course in zero-knowledge, hence hiding the trade details from external observers).
This model requires Bob to request payment every time period. The other method (NFT-based subscription) require Alice to pay for an subscription every time period. They are very different approaches realizing the same application. At this stage, we should probably keep the design space open. One advantage that the NFT-based subscription has is that it might remove the need for a stateful VP (that authorizes the payment).
Semantically, tag is any information that identify the NFT, which include properties such as expiration date or color. To enable property based trades, intent VP should take input the tag. More generally, I think tag can serve as a "record" as in Zexe, allowing us to realize the functionality of Zexe. |
OK, so my question right now is how much we should handle this in the protocol level (instead of programming all of this into VPs). I think some of the exact details (e.g. who/what/when/where certain information is revealed) can be entirely handled within VPs without specifically designing it into the base ledger or our circuits - which allows for more flexibility later. Actually, I do have another question. We've been talking about intents (as defined in ZEXE) but, formally, intents themselves are merely tools for matchmaking and not part of the transaction building process itself (in particular, the VPs have nothing to do with this part). Rather, the VPs are only used in the tx building phase where parties who have agreed on terms finalize and commit to the final transaction. So let's see how this might work in practice. One approach is: Alice gossips an intent to trade X for Y at rate R up to max M. Bob accepts this offer and signs a partial tx exchanging some amount of Y for X at rate R, and sends the partial tx to Alice. Alice completes her partial tx and the tx is posted on chain. This finalizes the intent based trade, but it comes at a cost that Alice can drop offline at any time because maybe she just wanted to leak info about Bob, maybe the market shifted (Bob is giving a free option to Alice for some time period) or maybe Alice just wants to frustrate Bob's attempts to trade. A more "order based" approach would be to make intents themselves binding as orders. Alice offers X for Y at rate R up to max M, and encodes in her VP state to accept that rate. Then Alice gives the VP data to a matchmaker or releases it publicly. Bob can complete the whole transaction himself (or give his partial tx to the matchmaker) and either Bob or the matchmaker proves Alice's VP. One approach along these lines is for Alice to reveal (publicly or to MM) her collection of X notes along with her VP and its state. The VP says that anyone can "spend" Alice's X notes but if the value goes to anyone besides Alice, there has to be a correct amount of Y also sent to Alice. (Residual unexchanged X from spent X notes gets returned to Alice) A slightly better way is that Alice moves her X into many small, fixed value notes in a separate payments address. As Alice accumulates "loose change" in a different address, she occasionally manually moves it back to the source address. She can give incoming viewing keys to this address to Bob/marketmakers/etc and doesn't leak too much. So, can we use a special intent VP protocol to simplify this? It feels more like a problem with viewing authority than anything else, actually.
I see - they're basically mirror images. Either Alice submits payment and gets an automatically issued NFT, or Bob automatically deducts payment and issues an NFT in return. Both cases are quite compelling - although the application details might be slightly different. For example, issuing this NFT may or may not need to be stateful - if the subscription is for a physical object there might be a limit to how many can be issued at a time).
Oh, I see. OK, so the Action circuit doesn't need to be aware of this at all (as long as NFTs with distinct tags are distinct assets) but the VP circuit most certainly does need to know these details. |
Here's a tentative framework for how to proceed:
which supports incoming/outgoing VPs and multiple asset types. For now, include a "dummy" check for
which checks that
which basically gives a summary list of Action descriptions to the VP. Action descriptions with I think this is reasonably straightforward to implement, and while it has some major deficiencies (most specifically, checking the VP proofs is not private, since that requires halo 2) |
Right. It seems to me we are aiming for the "order book" approach and partial tx are actionable by MM or a counter party. The interactive approach can be realized utilizing the order book approach, the matching of Alice and Bob can be done externally (to PBC) in that case.
I don't think we need to give MM viewing authority. Let us examine the trade intent VP more closely. Alice wants to spend an note (containing 10 token A) to obtain a new note containing at least 5 token B. What could happen is the following:
After the MM receives the partial tx from Alice and finds a counter order, it will do the following.
Why is C^enc needed? Am I understanding it correctly that a verifiable note encryption is needed since the MM is creating the new note in this model? If yes, then I'm not sure if we need to have the MM create any note. We can design the system so that all notes are basically created by the users, the MM simply modify the homomorphic value commitments (which results in note cm changing) and prove satisfaction of VPs.
It seems to me we would need something more than just a plain Merkle tree for storing the VPs. As you pointed out, we need to make sure there is exactly one VP for each address (and token) at each state. This sounds to be a "hash map" type structure to me. Such verifiable data structures have been studied (e.g. https://eprint.iacr.org/2020/1161). |
Who constructs the Action proof (or similar) for
OK, this means the MM needs to make the Action proof for
Alright, I think I like it :) Maybe a slight simplification, basically Alice and Bob prove the spend of their notes separately, and Alice signs an A little bit more complex is the case of "partial fill". In principle this can be handled by Bob's
Verifiable encryption is mandatory any time a note is spent conditioned on creating the output note, because otherwise it's possible to censor or burn the output note (there might not be incentive to do so, but we shouldn't risk it). In this case I don't think you can avoid it, because someone has to write the Action proof that outputs the note, and if it's Alice, then the MM cannot homomorphically change the value later (without invalidating the proof), so Alice would need to write the proof after the terms were agreed, but that is an additional round of interaction, etc.
I agree this would be great, but it's not clear to me how to do it. The specific construct in that paper seems to require group of unknown order which won't hold in the EC group |
Right. I understand the approach here and the need to verifiable encryption. But I think we can get around this and never have the MM creating notes from scratch. My thinking is that we should redesign the action circuit so that (1) users prove validity of "partial notes" and authorize intent vp, and (2) MM prove that they turn partial notes into valid full notes and that the intent vp is honored. More specifically,
Of course, these are only very high-level intuition sketches at the moment. But I think this should be possible.
Right, we will probably need other constructions. |
Hmm. I'm still a little unsure. The problem in my mind is that it's just super difficult to ensure that Alice actually knows the entire contents of the output note commitment. In the simple case, Alice might be able to infer, but in other cases she might not, for example if the output note is an NFT and the intent VP was property-based, then there might be way too many possibilities. Also, if we don't use verifiable encryption, we can't ever have offline spends (like the subscription example) since those probably will always return at least some kind of NFT or subscription token or something. Let's explore further if there's a circuit efficient key-value store. I was thinking that it's not really a problem if an address has multiple VPs, so Merkle tree is always a backup option, but maybe a stronger guarantee is helpful. I have a bigger issue with the VP merkle tree being different than the note commitment tree. Since meta-analysis of VP updates is probably way easier to do than note tree updates since they're less frequent, there's more potential for information leakage. |
This can work similarly to the example above. NFT simply has one or more commitments that needs to be verified by the intent VP.
This is true. But more in the sense that we can only have one spend per partial tx. In the example above, the partial tx can only be turned into a full tx and processed once. After that, the partial tx is useless, since the "funding" note is already spent. To achieve subscription even if Alice is offline. The previous approach (as I understand it) is having Alice creating a new address with a user vp that authorize spends and giving MM viewing authority. This could still be compatible and built alongside the intent vp example above. All of the other user flows besides subscription currently consider fall into the intent vp example I believe.
But how do you ensure the latest VP for an address is being used? Merkle tree is semantically a set commitment, with efficient opening proofs. The structure we need is a map from address to VP, again with efficient opening proofs. However, maybe there's a way to augment Merkle trees to provide the semantics of a hash map.
Not sure what you mean by meta-analysis. Information leakage could be minimize if a tx always appear to (1) modify notes merkle tree and (2) modify some entries of the VP map. Of course some operations would be "dummy" for real txs. |
https://eprint.iacr.org/2021/1263.pdf <- potential key-value store
Sorry, I still don't understand? Let's suppose that Alice is willing to trade $1 for one of any 2^128 different NFTs with some particular property. She won't know at the time of intent which specific token it is, so the partial note has to be generic. Let's assume the MM creates a full note from this partial note, what prevents the MM from not telling Alice which token it is or the opening of the note commitment?
I'm not sure this is really what we had in mind, because it's not truly offline, it's pre-prepared: Alice needs to construct some set of potential output notes in case her VP authorizes an output to her. In any case, it doesn't seem very straightforward.
There needs to be an authenticated method for addresses to add/update/delete VPs for themselves (I don't really know the best way to to do this yet), but I don't think we need to enforce 1 VP per account. The Merkle tree can just contain valid pairs
Maybe I mean like a timing analysis or something. Because VP updates are likely to be much more rare than creating notes, it's easier to correlate a VP update with some real world activity. |
You are right. For Alice to know the exact property, the commitment should be openable by Alice. So yes, naively the MM needs to verifiable encrypt the commitment randomness to Alice. Alternatively, maybe we could replace the commitment with e.g. ElGammal encryption (directly to the public key of the owner). Of course, the MM needs to again prove that it is done correctly. I agree, it seems some form of verifiable encryption is needed, but we can design it so that it is not of the full note but rather a small part of it (i.e. the part of the note that the MM changes).
Indeed. Perhaps the subscription use case should be taken care of separately.
This might work. Another thing I just realized: Merkle tree plus nullifier approach has the advantage of high parallelism. Multiple txs can be generated against the same root and be processed. Key-value maps might or might not support the same type of parallelism, which is a problem. |
Yeah, it's worth thinking whether we need a full Poseidon-based symmetric key encryption, or if some kind of ElGamal is enough (since the amount of data is pretty small). I started to sketch some estimates in the VE issue but I didn't think about NFTs...so that would probably inflate the size of the encrypted note. I think the biggest problem is not the verifiable encryption part (which is entirely feasible...we'd just have to implement it), it's that I don't think we want to dump the burden of proving output note validity on the MM. Since I think that would be the performance bottleneck if many tx are running through the same MM. |
I'm trying to figure out how intent VP validity should get checked. Now, since the intent VP is probably not in the public commitment tree, Alice also needs to write some kind of "VPCheck" proof which will pass anyway. My initial thought was that it would work something along these lines: Alice commits to the intent VP in The problem is that the RedPallas scheme is not particularly efficient in-circuit - it's designed to have re-randomizable keys so that the signature can be checked efficiently and privately outside the circuit. Except now, the existence of a signature check is public; so VPs from the public VP tree and signed VPs are easily distinguishable. I'm not sure this is a major problem, but it's certainly a bit less elegant than I had hoped. |
I think intent VP would be a separate field of the transaction. User and token VPs are stored (in a merkle tree or other type of structure) and are referred to by notes. Intent VP are provided additional to each partial transaction. I think there needs to be a circuit (additional to the modified action circuit) whose proof is generated by the MM to prove validity of the matched partial transaction. There's also the question of whether we want to "blind" regular sends with more complex n-party transactions completed by MM. So perhaps simple sends now need to involve two proofs as well. We are discussing PBC tomorrow at the retreat. Hopefully the overall structure becomes clearer then. |
After thinking about it, it's only ~96 bytes to include a signature of the VP commitment for every single VPCheck description. In case the signature is valid, the VPCheck circuit checks the signature key rerandomization (in the same way that Action does right now); in case the VP commitment is stored in the public VP tree, the VPCheck circuit can check membership and ignore the randomized public key (the external signature attached can be a random dummy key) This way at minimal cost, from outside the circuit, a signature is verified for each VPCheck and the situation looks identical. The MM needs to prove this VPCheck circuit, though. The big detail remaining is how to prevent replay of this VP commitment signature in later transactions. VPCheck can easily manage state updates for us (either globally, by updating the public VP tree, or locally, by spending/outputting notes that contain VP state) but this is maybe more useful for VPs stored on-chain - it's not so clear to me how state is enforced for ephemeral VPs. |
This looks to be a good approach if we want to "blind" the type of vp involved! However I'm not sure if we need to blind between user and intent vps. The main reason being that intent vp can be stateless (at least according to my consideration) and the "IntentVPCheck" circuits can be simpler than "UserOrTokenVPCheck" circuits. This is the (intuitive) model I have in mind: user (and possible token) VPs are proved by the user, at partial tx preparation time, and intent VPs are proved by the MM at full tx preparation time. User and token vp are stateful, intent vps are not and only refer to value commitments within a tx (and hence can be ephemeral to each transaction). Intent vps are to ensure the fund security of the user when they are providing a partial tx. Intent vps are not present if the user is not engaging in multi-party bartering. I believe this is expressive enough for most applications (besides subscription) and simpler to reason about. |
Hmm, let's assume for now we are ok with splitting the privacy set between intent/barter tx and other tx. I'm still not sure how to prevent an intent VP from being replayed later (for example, Alice gives the MM an intent to exchange X for Y at some rate, and then later gives the MM an intent to exchange X for Y at a different rate, what prevents the MM from using the old rate)
I guess this depends on what we mean by "user". In general (except for when the user VP is a signature check) we should expect the user VP to be proven by someone else; otherwise, there's not really any need for programmability, since the user would just have signature authority (well, maybe some use case for cold storage of keys) |
OK, just to give a quick summary of where I'm at with this. We should distinguish between ephemeral and nonephemeral VPs and data. By nonephemeral, I mean VPs and data whose validity is determined by reference to already-verified public (or on-chain) data; for example, since the note commitment tree (and let's assume the VP commmitment tree) consists of commitments that are known to be valid, is it possible to check a VP or check some data/state by providing (as public input) the known-good Merkle tree root, and doing a lookup in a circuit. By contrast, ephemeral VPs and data/state are ones whose validity cannot be determined directly because they are not long-lived (hence, ephemeral) and it is impractical to do an on-chain update for each one of these things. The most direct example of ephemeral data are intents: the bid/ask of an intent is too short lived to exist on-chain. My first instinct was that ephemeral data is verified by signature. However, I think this is not the best idea (or at least incomplete) because:
I think the limitation is important enough not to ignore. Basically the only way to authorize an intent (either as a maker or taker) is by one-party signature, meaning any of the following cannot authorize intents directly:
since all of the above are dependent on VPs to authorize a spend. Now, there are lots of details that we haven't figured out about how to implement all three of these things, which makes things difficult to reason about. For example, it's not clear at all how the intent should be constructed (at what rate? where does that data come from?). I think there are two feasible paths forward from here:
I think we should make an attempt to do this the Right Way which means (2). Especially because I don't think it's going to be very difficult or complex to do - but it does need some engineering. For example, suppose Alice, Bob, and Charlie have a hedge fund that invests in cryptokitties. The rules of the fund are that Alice, Bob, and Charlie each deposit some of asset X, which can only ever be used to buy and sell cryptokitties, and any 2-of-3 multisig can decide the properties and prices. We can set it up like:
The fundamental issue is linking the intent approved by the fund VP with the intent VP. For example, in a sequence of on-chain transactions, Alice and Bob can trigger the fund can move some X into a temporary account whose (intent) VP is written to specifically authorize X->kitty swaps. The fund VP also updates the (intent) VP state with Alice and Bob's instructions (e.g. 1 X for every blue). Since every transfer or state update is checked as it happens, the MM only needs to prove the intent VP spending the funds in the temporary account, and it all works. The most obvious idea is to allow state updates to be "fast forwarded" in some way. There is nothing special about a sequence of on-chain transactions - we just need to design the system so that we accomplish the same thing without establishing a temporary account with the intent VP and moving X into it. Instead, the fund VP creates some ephemeral VP+state encoding the intent, and the tx verifier checks that the fund VP authorized the ephemeral VP+state, and that the ephemeral VP+state approve the final tx. |
Here's a concrete proposal for how this might work. Let's go back to a past concept we considered - sending notes to VPs instead of addresses. Let a "VP address" be defined something like: VP addresses can be used to solve the intent problem because they allow ephemeral data to be exchanged between VPs in a tx. Continuing the hedge fund example from above: The VPs would be
Alice and Bob can spend a note that looks like Alice and/or Bob can write a proof of The MM finds a counterparty selling a blue for 1X. The intent can be executed by the MM creating more Actions:
The MM can prove Now here is where the magic happens. The Action circuit should not check validity of intent notes at all - it's the VPs that check! So when an intent gets "spent" by intent_addr, we use some random nullifier and the Action statement approves. This is perfectly OK because The best parts of this are:
(There are some details omitted/to be filled in...and also an interesting self-referential loop with |
OK, I think our current plan looks pretty good. I like the current intent and gossip fee proposals, and I think it handles all applications we've come up with. (Though I'm still not convinced that we need to allow new notes to be spent in the same tx, because the exact same outcome can always be achieved by letting the value commitment balance, but we can discuss this more) In terms of next steps:
|
Chain of VP integrity checks: @simonmasson
Guarantees:
The end conclusion (following the chain of cryptographic links) is that the VP proof is valid against |
Also, it is notable that the prover probably must know the blinded VP because the polynomial commitment openings in the VP proof open against the blinded VP polynomial commitments, not the original VP polynomial commitments (even though the evaluations are the same), and also the last two verifier challenges So I meant to say, the VP proof is valid against |
OK, here's a proposal for handling cyclic VP address references. At first, finding a "fixed point" of the address derivation function seems difficult, but taking some inspiration from Kleene's recursion theorem we can actually make some progress. Suppose Suppose we want two VPs which reference each other's addresses. The first VP will be Then Extend the Note that In particular, now Suppose you want to have longer than a 2-cycle, like an n-cycle ( Let Hopefully, complex interdependencies will be rare and the vast majority of dependencies only involve 2-3 addresses. Also, this is the kind of complexity that is ideal to abstract away from the VP writer as much as possible. |
The following statements are sanity checks on whether I understand your proposal: If the sanity checks have passed, I have the following questions:
Footnotes |
Closing but archiving for posterity and entertainment. |
Discussion of private trade circuits we should aim to ship along with Anoma's initial launch.
The text was updated successfully, but these errors were encountered: