Flow encryption is ultimately “just” a form of additively homomorphic threshold encryption. However, usefully integrating that primitive into a ledger and byzantine consensus system requires a number of additional properties, and it’s useful to have a single name that refers to that complete bundle of properties.
In this section, we sketch what those properties are, and the algorithms that a
flow encryption construction should provide, separately from our instantiation
of flow encryption,
eddy, described in the next section.
There are three participants in flow encryption:
- the ledger, which is assumed to be a BFT broadcast mechanism that accepts messages based on some application-specific rules;
- users, who encrypt their individual contributions to some flow of value and submit them to the ledger;
- decryptors, who aggregate encryptions posted to the ledger and decrypt the batched flow.
In this description, we refer to decryptors, rather than validators, in order to more precisely name their role in flow encryption. In Penumbra, the decryptors are also the validators who perform BFT consensus to agree on the ledger state. However, this is not a requirement of the construction; one could also imagine a set of decryptors who jointly decrypted data posted to a common, external ledger such as Juno or Ethereum.
We include the ledger as a participant to highlight that a BFT broadcast mechanism is assumed to be available, and to explicitly define when communication between other participants happens via the ledger, and is thus already BFT, and when communication happens out-of-band, and must be made BFT.
Importantly, users are not required to have any online interactions or perform any protocols with any other participant. They must be able to encrypt and submit their contribution noninteractively. This is essential for the intended role of flow encryption in allowing private interaction with public shared state: the decryption protocol acts as a form of specialized, lightweight MPC, and the encryption algorithm acts as a way for users to delegate participation in that MPC to a set of decryptors, without imposing wider coordination requirements on the users of the system.
Algorithms and Protocols
This is a multi-round protocol performed by the decryptors. On input a set of decryptors and a decryption threshold , this protocol outputs a common threshold encryption key , a private key share for each participant , and a public set of commitments to decryption shares . The exact details of the DKG, its number of rounds, etc., are left to the specific instantiation.
This an algorithm run by users. On input an encryption key and the opening to a Pedersen commitment , this algorithm outputs a ciphertext and a proof which establishes that is well-formed and is consistent, in the sense that it encrypts the same value committed to by .
We assume that all ciphertexts are submitted to the ledger, which verifies along with any other application-specific validity rules. These rules need to check, among other things, that is the correct value to encrypt, and the Pedersen commitment provides a flexible way to do so. The consistency property established by allows application-specific proof statements about (a commitment to) to extend to the ciphertext .
This algorithm describes how to add ciphertexts to output the encryption of their sum.
We also assume that, because ciphertexts are posted to the ledger, all decryptors have a consistent view of available ciphertexts, and of the application-specific rules concerning which contributions should be batched together, over what time period, etc., so that they also have a consistent view of the aggregated ciphertext to decrypt.
In the case of same-block decryption, this assumption requires some care to integrate with the process for coming to consensus on the block containing transactions to batch and decrypt, but this is out-of-scope for flow encryption itself. See Flow Encryption and Consensus for details on this aspect in Penumbra specifically.
On input ciphertext and key share , this algorithm outputs a decryption share and a decryption share integrity proof .
On input a ciphertext and any set of decryption shares and proofs with , output if at least of decryption shares were valid.
We require the following properties of these algorithms:
Ciphertexts must be additively homomorphic:
The flow encryption must ensure conservation of value, from the individual users’ contributions all the way to the decrypted batch total. Establishing value integrity proceeds in a number of steps:
- Application-specific logic proves that each user’s contribution value conserves value according to the application rules, by proving statements about the commitment to .
- The encryption proof extends integrity from to .
- Integrity extends to the aggregation automatically, since the aggregation can be publicly recomputed by anyone with access to the ledger.
- The decryption share integrity proofs extend integrity from to . This requires that, if is the result of decryption using valid decryption shares, than .
- Publication of (any) decryption transcript allows any participant to check the end-to-end property that for (application-)valid .
The decryption process must succeed after receiving any valid decryption shares, so that any decryptor who can receive messages from honest decryptors must be able to compute the correct plaintext.
Unlike the DKG, where we do not impose a constraint on the number of rounds, we require that decryption succeed with only one round of communication. This allows us to integrate the decryption process with the consensus process, so that in the case where decryptors are validators, they can jointly learn the batched flow at the same time as they finalize a block and commit its state changes. This property is important to avoid requiring a pipelined execution model. For more details, see Flow Encryption and Consensus.
Note that we do not require that any specific subset of decryption shares is
used to get the (unique) decryption result in
FlowEnc/Decrypt. This permits a
streaming implementation where all decryptors participate, but decryption
completes for each participant as soon as they receive valid shares,
ensuring that decryption is not bottlenecked on the slowest
The DKG must be verifiable: participants (decryptors) must be able to verify that counterparty participants (other decryptors) are contributing to the DKG honestly, without the use of a trusted dealer. This can be achieved using something similar to Feldman’s Verifiable Secret Sharing protocol, where each participant shares a commitment to their share which is visable to all other participants. In addition, our DKG must be able to tolerate rogue-key attacks: that is, it must tolerate the instance where a validator maliciously chooses their share based on the value of the other validator’s shares in order to cancel out other validator’s keyshares and gain unilateral control over the resulting DKG key. One way this can be prevented is by each validator producing a proof of knowledge of their secret share.
The DKG must have robustness. The DKG should be able to tolerate a byzantine threshold of decryptors intentionally refusing to participate in the DKG round, or intentionally contributing malformed shares during DKG execution, without requiring a full restart of the DKG protocol. This is due to DoS concerns: with a naive, non-robust implementation, a single malicious decryptor could potentially indefinitely delay the beginning of an epoch by refusing to participate in DKG or by contributing invalid shares.