-
Notifications
You must be signed in to change notification settings - Fork 18
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
Considerations for locked states #119
Comments
Thanks @awrichar for initiating the discussion. A fundamental assumption of the observation above is the fact that a UTXO can only have one state transfer verb, "spend". I would argue that this assumption has already been violated with the usage of nullifiers. When using nullifiers, a UTXO's state doesn't change at all, but instead a separate mechanism of tracking nullifiers are utilized to avoid double spending without anybody knowing which UTXOs have been "spent", except for the UTXO owner, or a centralized trusted party such as the notary in the case of the Noto token implementation. This means an indexer with special knowledge beyond the simple "spending" verb already has to be built to support the special lifecycle needs for a privacy preserving token. So the argument that sticking to a "spend-only" state model would simplify the construction of the indexer isn't very strong. |
@jimthematrix I don't think nullifiers change the execution graph of the UTXO model - they just change the mechanism by which you spend. A state is only spendable if you can generate the proper nullifier for it. To facilitate this, in Paladin we have taught it about this specific spending rule, and it is turned on at a contract level (to say "all states in this contract must be spent by generating nullifiers"). Another option of course would be to have more "generalized" spending rules spelled in a DSL, such as other UTXO models have adopted - but that's a big lift and we haven't yet decided to take it on. But that's a huge difference from states that change their spending rules on the fly. I can't have a state that was generated without requiring nullifiers, and change the spending rule to say that from this point, it will have to be spent with a nullifier instead. This is the big difference I see with the current locking behavior - the rules around spending UTXOs are not fixed throughout the life of the UTXO, but they can change independent of the UTXO itself. |
Correct, which I think is the same when a UTXO is spent and the new UTXO in the output has different spending rules than the ones that just got spent. The indexer still has to react on a per UTXO basis based on each UTXO's spending rules. Not sure I see yet a big difference b/w the current implementation of keeping the same UTXO id but updating the spending rules vs. the proposal to spend and create a new UTXO with a different spending rule and a different ID. |
I think the following are generally needed to be able to index UTXOs:
I believe you're saying that to satisfy (1), an indexing system could elect to identify UTXOs by the union of (UTXO id, locked status). Therefore if However, if I think (2) is a pretty fundamental rule of a UTXO system, so I'm having trouble coming up with an indexing model that doesn't require it to be true. |
No argument with the above assertions. I think we are narrowly defining all state transitions in an UTXO systems as spending, so there can be no other types of state transitions such as locking which can be reverted without spending the UTXO. I tried to search for formal UTXO definitions in the academia space, such as researchgate.net and mdpi.com, but haven't found any unfortunately. If there are such definitions that clearly spells out the above assumptions, I think we have an easy decision to make. In the absence of that, I'm trying to discover the benefit of the above assumption. One obvious benefit is the fact that it leads to a DAG type state transition graph that is more efficient to manage than the graphs that have cycles. For instance in the DB storage design for implementing a Zeto client, we can have an append-only scheme by inserting new records to reflect state transitions, rather than relying on updates. Such schemes result in higher performance. This is the current design of Paladin. |
It's not particularly academic, but I asked ChatGPT about it and generally got what I'd expect: https://chatgpt.com/share/6776f6c1-c090-8012-9338-ce1d73120415 I don't know of any current UTXO systems that allow for a transition other than "spend", or that allow for cycles in a state graph. |
and
I think the above are the most relevant part of the ChatGPT response. However they are in the context of a blockchain protocol based on UTXO. In a UTXO system built on EVM, immutability and determinism are guaranteed by the underlying protocol (account based) already. So I don't think having a UTXO design that can alter its spending rules fundamentally breaks anything (again this is without having authoritative sources on what UTXOs should be). |
That said, I'm leaning toward changing the lock mechanism to spending rather than modifying as you have proposed, for the benefits discussed so far related to client implementations. |
Right - we definitely could have a system that is UTXO-like, and yet has more complex rules (such as having states other than spent/unspent, and more allowable transitions between states). Would it still be called UTXO? Maybe. Like you said, there's really no authority that will determine yes or no. That's a more academic question when you get down to it. I'm mostly concerned with 1) properly describing the state model to others, and 2) making it easy for anyone to efficiently index Zeto contracts. Adhering to basic UTXO and ensuring indexing can be built in an append-only fashion definitely help on these counts. |
Great thoughts.
Also, locking can be implemented without using this optional function:
In the conversation, I saw the following threads that have been discussed:
I think |
@Chengxuan I think some form of locking and delegation must be standardized and provided in base Zeto. Otherwise there will be no standard way to implement any form of atomic settlement using Zeto. Zeto tokens can only be held by EOAs, as you must generate BJJ keys and ZKPs in order to move them. They can't be held by other smart contracts in the way that ERC20 can, for example. Therefore, the only way to provide programmable atomic constructs is to let an EOA set up the proofs and states, and lock them to be finalized by a particular address (which may be an EOA or smart contract). Similar to how most ERC token standards are built, I believe a token needs a minimum of two capabilities:
|
I do think |
@Chengxuan correct. But with Zeto I think there's no way to say "A can spend any amount of my tokens in any number of different transactions, totaling up to a max of 80" - at least not in a way that is guaranteed in the smart contract. @jimthematrix is more of an authority on what's possible with the ZKPs, but because of the way value is tracked, I don't think we can do this in the same way it's done on ERC-20. What we can do is say "I approve A to spend exactly this set of UTXO states", and pre-select a set of states totaling 80. We've landed on "locking" the states while the approval is in effect, simply because it's much too easy to accidentally go and spend those states on something else and therefore invalidate the approval (that was the focus of #110). You could argue for a less reliable form of approval where A has no idea if the states will continue to be valid or not - I'm not saying it's useless, but I don't know under what scenario you would want that instead. There's another form of delegation sometimes called "approval for all" that's used in ERC-1155 and some other standards. It's something we haven't explored in detail, but I think Zeto could potentially support this pattern of delegation as well, if there was a use for it. But it would be a different ZKP. In summary I think we have:
I believe 1 & 3 are possible, and 2 is not possible. |
@awrichar my understanding is also I think it worth clarifying the problem we are trying to solve there. Reading this issue I think what we are trying to solve is enabling a smart contract address to spend Zetos even though a smart contract address:
In addition, if a locking is amount based, there will need to be some translation between the amount and UTXO. E.g. I want to lock 1 (value), but the owner only has one UTXO with 10, what should I do. (this translation is often a client-side job for a UTXO-based system). Another important aspect to consider is: any ownership/transaction information should not be revealed in the Zeto contract due to the locking. One possible solution could be: the locking method consumes all the input UTXOs and generates 1 UTXO (change) to the owner (A) and 1 locked UTXO that can be owned by either A or B when finalized. the "finalize" function
@jimthematrix @awrichar ^^ wonder whether that's something feasible and worth exploring/ aligns with your thinking. I've not thought through the whole data flow to ensure the parameters needed for the lock and finalize method don't reveal UTXO ownership/transfer information. |
Yes, I believe this aligns with where we're going. You've summarized mostly how the Noto implementation of locks is now proposed to work on Paladin, and based on discussion here and #118, it feels like Zeto is trending in a similar direction ("locking" means spending the inputs and generating some number of "locked" outputs along with a refunded "remainder", if necessary). All preparation of input/output states and proofs has to be done by a client, but spending of locked value can be delegated/finalized by a smart contract address. One major goal here is to enable Zeto to be atomically swapped with any other token (either an ERC20, or Noto, etc). So you're exactly right with |
agreeing with the above conversation @Chengxuan @awrichar. No additional comments to add. Something I'd like to call out as a potential significant decision as I got further with the idea to "spend the UTXOs and create locked UTXOs in the output". @awrichar and I have had some preliminary discussions about this. When I initially implemented the new locking function for the non-nullifier token implementations, both for fungible and non-fungible tokens, it was pretty straightforward to do:
This works pretty well, with a small overhead in all transfer() calls, to check the inputs against the locked UTXOs map. The benefit is that we can use a single transfer() function for consuming both unlocked and locked states. However when I got to tokens using nullifiers, I realized that to keep using a single transfer() function, it would have to proof two things:
Both statements are required for every transfer() call. This is going to make the transfer() function very expensive, especially on the prover side. On the other hand, if we maintain the locked UTXOs in a separate list, and have two separate functions for spending unlocked vs. locked UTXOs, then each transfer function only needs to deal with one inclusion proof. I'm leaning toward tracking unlocked and locked UTXOs separately and having two separate functions for spending unlocked vs. locked UTXOs. BTW regardless of the approach, for checking against the locked list in a nullifier based token, we need the merkel tree to record the UTXO hash as the key and the delegate address as the value. In the proof to demonstrate inclusion in the locked UTXOs, the proof must use the delegate address as a public input. |
In reviewing the latest "lock states" functionality (added in #110), I'm trying to understand the current overall state model of Zeto, and whether it can be considered a UTXO model or something else entirely.
For the most part, Zeto follows UTXO - states have a value and an owner, and they can be spent exactly once as an input to a transaction. However, with the addition of "locked states", each state can now have additional metadata specifying whether or not the state is "locked". In the UTXO world, this seems similar to a "spending rule" or "script", as used in systems like Bitcoin and Cardano. However, the Zeto implementation appears to be novel because you can attach or alter this metadata without spending the state. Consider:
A
- $100 coin, spendable by whoever can generate a valid proofA'
- same as A, but only spendable by one specific addressAs of #110, it's possible to transform
A
toA'
without spendingA
. And from there it may transform back toA
, or toA''
, etc. This presents problems when trying to index Zeto coins. A normal UTXO indexer deals in very "final" operations - a state comes into existence exactly once, and gets spent exactly once. With this notion of mutable spending rules, a much more sophisticated indexer would be needed - one which tracks thatA
may become spendable or not spendable at various moments throughout its lifetime, based on a set of changing rules.Given this, I believe there is a fundamental choice to be made in terms of Zeto's future design around "locks":
A
is spent to generateA'
, and we explore the implications for ZKPs under this constraint (for instance, can we create a proof that is valid to spend eitherA
orA'
, because it relies only on the owner/value ofA
and not on the "locked" status?)The text was updated successfully, but these errors were encountered: