With the adoption of sapling contracts and shielded transactions with Edo, there has been a lot of talk about the integration of confidential transaction into main stream applications.
Sadly there is no “visible” progress in this matter.
The documentations for the sapling integrations includes a test contract for a shielded Tez pool with a 1 to 1 conversion to (unshielded) Tez.
I don’t know if that code’s been audited but it’s very straightforward and only about 40 lines of well documented code, all the heavy lifting happens in SAPLING_VERIFY_UPDATE, not really in the contract itself.
However, it doesn’t really make sense to use it as such, as the absence of delegation would create an incentive to go in and out of the pool which would hurt privacy. This is essentially the type of use case ctez exists for.
storage (sapling_state 8);
parameter (list (pair (sapling_transaction 8) (option address) ) );
code {
# Do not accept tez
AMOUNT;
PUSH mutez 0;
ASSERT_CMPEQ ;
# Get ctez contract entrypoint (or other)
PUSH @ctez address "KT1SjXiUX63QvdNMcM2m492f7kuf8JxXRLp4%transfer" ;
CONTRACT (pair nat (pair address address)); ASSERT_SOME ;
SWAP;
# Stack manipulation
UNPAIR;
NIL operation; DUG 2;
ITER { UNPAIR;
DIP { SWAP };
# We verify the transaction and update the storage if the transaction is
# valid. The shielded transactions are handled here.
# The new state is pushed on top of the stack in addition to the balance
# of the transaction. If the rest of the script goes well, this state
# will be the new state of the smart contract.
SAPLING_VERIFY_UPDATE;
# In the case of an invalid transaction, we stop.
ASSERT_SOME;
UNPAIR;
DUP;
# We have three cases now: unshielding, shielding and transfers.
# If the balance is strictly positive (i.e. unshielding), we send funds
# to the given address.
# If no address is given (see ASSERT_SOME), we stop
IFGT {
ABS @sapling_balance_is_positive;
DIIP { ASSERT_SOME };
SWAP;
DIP {
SWAP ; RENAME @to;
SELF_ADDRESS @from;
PAIR; SWAP; PAIR @param;
DUP 3;
SWAP;
PUSH mutez 0;
SWAP ;
TRANSFER_TOKENS;
CONS };
}
{
DUP ;
IFLT
{
# If the balance is negative, we are shielding. The spend will have to have been authorized in the fa1.2 contract
SWAP ;
DIP
{
ABS ; SELF_ADDRESS @to ;
DIG 2 ; ASSERT_SOME ; RENAME @from ;
PAIR; SWAP ; PAIR @param;
DUP 3;
SWAP;
PUSH mutez 0;
SWAP ;
TRANSFER_TOKENS ;
CONS ;
};
}
{
# Internal transfer
DROP ; SWAP ; ASSERT_NONE ;
};
};
};
DIG 2 ; DROP ; SWAP ; PAIR ;
}
Like @murbard pointed out, most of the complexity of the Sapling protocol is hidden in the Michelson instruction SAPLING_VERIFY_UPDATE, the rest of the contract logic should be auditable by any Michelson developer. No special expertise is needed.
Our goal was to integrate a privacy enhancing solution as generic as possible. We therefore did it as a Michelson instruction. The contract we provided is just a simple example that we developed for testing and educational purposes.
There are many ways in which Sapling can be used inside a product, we are happy to provide support to any interested developer.
With Airgap launching, I had another look at the contract over the weekend. As I’ve said above, it’s pretty straighfoward code, but it also has a subtle issue. SAPLING_VERIFY_UPDATE only returns the unshielded amount, but it doesn’t bind the proof to the output address. This makes unshielding transactions malleable.
While it’s technically possible to use Sapling as is on Tezos with a commit/reveal scheme, writing a safe and practical contract would require a version of SAPLING_VERIFY_UPDATE that binds the proof to a given output, in our case the optional address. It’s not a difficult fix, but it is a protocol level change. I contacted the AirGap team when I spotted the issue and there was only about 68 tez in the pool.
At the end of last week, we at AirGap announced the integration of Sapling into our solution.
This was the first known implementation of Sapling into a product. Unfortunately, an issue with Sapling was spotted by Arthur Breitman - as a precaution we acted quickly to disable this integration.
It appears that with the current integration of Sapling, it is possible that the address to which funds are unshielded to, can be changed while the signed operation is in the mempool. As the current contract that AirGap uses holds only an amount of 68 tez, this flaw was fortunately caught in a timely manner.
We advise any users who have locked funds to reach out to us on Discord, Telegram or by Email.
The Nomadic Labs team is aware of this and has also confirmed they are working on a fix that will be proposed for inclusion in the “J” protocol proposal.
We are excited to reactivate this feature once the fix is implemented so users can use Sapling from our solution.
After the announcement of this vulnerability, we have developed a new, safer integration, for Sapling transactions into Michelson smart contracts. This has already been merged into the Tezos master branch, and will be a part of an upcoming “J” protocol proposal.
Notice that this new version of the integration is part of the Tezos economic protocol, and as such it will only be available if protocol proposal “J” is accepted by the community, and only after it becomes active on mainnet.
Until then, we advise against originating new Sapling contracts on mainnet, and to avoid interacting with existing deployed Sapling contracts.