|
| 1 | +As TFHE-rs is the underlying library of the Zama Confidential Blockchain Protocol, to illustrate real-world performance, |
| 2 | +consider an ERC20 transfer that requires executing the following sequence of operations: |
| 3 | +```rust |
| 4 | +fn erc20_transfer_whitepaper( |
| 5 | + from_amount: &FheUint64, |
| 6 | + to_amount: &FheUint64, |
| 7 | + amount: &FheUint64, |
| 8 | +) -> (FheUint64, FheUint64) { |
| 9 | + let has_enough_funds = (from_amount).ge(amount); |
| 10 | + |
| 11 | + let mut new_to_amount = to_amount + amount; |
| 12 | + new_to_amount = has_enough_funds.select(&new_to_amount, to_amount); |
| 13 | + |
| 14 | + let mut new_from_amount = from_amount - amount; |
| 15 | + new_from_amount = has_enough_funds.select(&new_from_amount, from_amount); |
| 16 | + |
| 17 | + (new_from_amount, new_to_amount) |
| 18 | +} |
| 19 | +``` |
| 20 | +This is one way to compute an encrypted ERC20 transfer, but it is not the most efficient. |
| 21 | +Instead, it is possible to compute the same transfer in a more efficient way by not using the `select` operation: |
| 22 | +```rust |
| 23 | +fn erc20_transfer_no_select( |
| 24 | + from_amount: &FheUint64, |
| 25 | + to_amount: &FheUint64, |
| 26 | + amount: &FheUint64, |
| 27 | +) -> (FheUint64, FheUint64) { |
| 28 | + let has_enough_funds = (from_amount).ge(amount); |
| 29 | + |
| 30 | + let amount = amount * FheType::cast_from(has_enough_funds); |
| 31 | + |
| 32 | + let new_to_amount = to_amount + &amount; |
| 33 | + let new_from_amount = from_amount - &amount; |
| 34 | + |
| 35 | + (new_from_amount, new_to_amount) |
| 36 | +} |
| 37 | +``` |
| 38 | +An even more efficient way to compute an encrypted ERC20 transfer is to use the `overflowing_sub` operation as follows: |
| 39 | +```rust |
| 40 | +use tfhe::FheUint64; |
| 41 | +fn erc20_transfer_overflow( |
| 42 | + from_amount: &FheUint64, |
| 43 | + to_amount: &FheUint64, |
| 44 | + amount: &FheUint64, |
| 45 | +) -> (FheUint64, FheUint64) { |
| 46 | + let (new_from, did_not_have_enough) = (from_amount).overflowing_sub(amount); |
| 47 | + let did_not_have_enough = &did_not_have_enough; |
| 48 | + let had_enough_funds = !did_not_have_enough; |
| 49 | + |
| 50 | + let (new_from_amount, new_to_amount) = rayon::join( |
| 51 | + || did_not_have_enough.if_then_else(from_amount, &new_from), |
| 52 | + || to_amount + (amount * FheType::cast_from(had_enough_funds)), |
| 53 | + ); |
| 54 | + (new_from_amount, new_to_amount) |
| 55 | +} |
| 56 | +``` |
| 57 | +In a blockchain protocol, the FHE operations would not be the only ones used to compute the transfer: |
| 58 | +ciphertext compression and decompression, as well as rerandomization, would also be used. |
| 59 | +Network communications would also introduce significant overhead. |
| 60 | +For the sake of simplicity, here the focus is only placed on the performance of the FHE operations. |
| 61 | +The latency and throughput of these three ERC20 FHE transfer implementations are compared in the following table: |
| 62 | + |
| 63 | +TODO add SVG |
| 64 | + |
| 65 | +The throughput shown here is the maximum that can be achieved with TFHE-rs on CPU, in an ideal scenario. |
| 66 | +In a blockchain protocol, the throughput would be limited by the latency of the network. |
0 commit comments