-
Notifications
You must be signed in to change notification settings - Fork 30
Define IConfidentialToken base interface #843
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: main
Are you sure you want to change the base?
Conversation
This defines a basic confidential UTXO token interface that could potentially be implemented by both Noto and Zeto (and any other similar tokens in the future). Signed-off-by: Andrew Richardson <[email protected]>
* Emits a {Transfer} event. | ||
*/ | ||
function transfer( | ||
bytes32 txId, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would like to better understand the need for the txId, which as I understand it is used here as a form of idempotency key, rather than replay attacks (replay attacks are protected against by checking the unspent status of the inputs and the uniqueness of the outputs).
There are some discussions in #676, which I'll summarize here. Essentially the idempotency key is to prevent a client like Paladin from re-submitting a new transaction to the contract in an attempt to fulfill a request on the private API ("please transfer 100 of my private UTXOs to Andrew"), in the event that the client lost track of the original submission. Without an idempotency key, duplicate transactions may end up getting submitted by a client if the client believes the original transaction has failed to reach the blockchain.
The argument against using an idempotency key based on onchain states is that:
- it's costly because all txIds get saved onchain
- idempotency keys are for preventing "honest mistakes" made by the client, rather than "malicious attacks". we need to ensure it's impossible to achieve on the client side, before resorting to onchain enforcement
bytes32 lockId, | ||
bytes32[] calldata lockedInputs, | ||
bytes32[] calldata lockedOutputs, | ||
bytes32[] calldata outputs, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems these 3 parameters can be consolidated under a LockStates
object similar to the lock()
function
* @param id the UTXO identifier | ||
* @return unspent true or false depending on whether the identifier is in the unspent map | ||
*/ | ||
function isUnspent(bytes32 id) external view returns (bool unspent); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When using nullifiers, the result should be "unknown" by design. there's another status "does not exist". so maybe instead of a simple bool
, this should be an enum with 4 values: SPENT, UNSPENT, UNKNOWN, NONEXISTENT
:
SPENT/UNSPENT
for all existing UTXOs in non-nullifier tokensUNKNOWN
for all existing UTXOs in nullifier tokensNONEXISTENT
for all non-existing UTXOs in any token implementation
); | ||
|
||
/** | ||
* @dev Spend UTXOs and create new UTXOs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we're going a bit far leaning all the way to UTXO terminology here, rather than just "states".
When you consider FHE solutions use account states that mutated, it's a bit hard conceptually to think of this as a "UTXO model at the base EVM layer" ... when it's definitely not a UTXO model at the higher levels.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suspect we may have to accept that this proposal is limited to "UTXO based" token designs, rather than accommodating both UTXO and account based designs like those using FHE tends to be (e.g Zama confidential tokens, which are essentially equivalent to an ERC20 interface, that spell only the accounts and the delta, rather than inputs and outputs).
As an example, a typical FHE based implementation for a transfer()
:
function transfer(address to, bytes calldata encryptedAmount)
*/ | ||
function transfer( | ||
bytes32 txId, | ||
bytes32[] calldata inputs, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about "read" and "info" states that are needed for other data not mutated.
Few examples:
- Allow-lists for ZKPs ("read")
- Account states for private EVM execution that are not modified ("read")
- Transaction input payload reference ("info")
- Transaction manifest for establishing subsets of states visible to each party ("info")
This defines a basic confidential UTXO token interface that could potentially be implemented by both Noto and Zeto (and any other similar tokens in the future).