Protect validated transaction inputs with threshold encryption - #2393
Conversation
| CREATE TABLE private_records ( | ||
| record_id BLOB NOT NULL, | ||
| transaction_id BLOB NOT NULL, | ||
| chain_id BLOB NOT NULL, | ||
| key_epoch BLOB NOT NULL, | ||
| setup_context_id BLOB NOT NULL, | ||
| schema_version BIGINT NOT NULL, | ||
| block_num BIGINT, | ||
| cipher_id BIGINT NOT NULL, | ||
| cipher_nonce BLOB NOT NULL, | ||
| encrypted_record BLOB NOT NULL, | ||
| wrapped_content_key BLOB NOT NULL, |
There was a problem hiding this comment.
Would be good to add comments about what these fields are. Also, a few questions:
- Why do we need
record_id? Isn'ttransaction_ida primary key here? - What is
chain_id? The validator only works on a single chain - wouldn't this be the same for all records? - What is
setup_context_id? Would it be the same for all records? Or would it be the same for a givenkey_epoch? wrapped_content_keyis the encrypted key that can decrypt a givenencrypted_record? If so, I wonder if we should rename it into something likeencrypted_content_keyor maybeencrypted_record_key.- What is the difference between
schema_versionandchiper_id? - It may be good to store the key share of the validator for a given transaction in the transaction record. This way, the share would be inserted at the same time when the transaction record is inserted, and it wouldn't need to be generated later. Or is doing this computationally intensive?
There was a problem hiding this comment.
Simplified. The transaction ID is also the record identity (edit, this is not going to work), and block height is gone. chain_id is the genesis commitment, kept so exported forensic data identifies its network (e.g. this is where we would differentiate a testnet, a mainnet, a private deployment). setup_context_id identifies the Golden DKG setup needed to combine shares. wrapped_content_key is now encrypted_record_key. One format_version fixes both the context and cipher.
Shares remain generated by the local CLI, which avoids temporary database state that we would later remove. Not to mention the decryption shares are keyed to a specific ciphertext.
There was a problem hiding this comment.
Shares remain generated by the local CLI, which avoids temporary database state that we would later remove. Not to mention the decryption shares are keyed to a specific ciphertext.
Why not store the decryption shares in the table together with their corresponding transaction records? The mapping is 1-to-1 (i.e., each transaction gets a unique share from a validator) - right? This would avoid the need to generate shares via local CLI.
There was a problem hiding this comment.
Because the ciphertexts are distinct. A validator does not know which ciphertext it might have to generate a share for unless it happens to be its own. See commands::tests::two_validators_issue_shares_for_third_validator_record and private_record::tests::independent_writers_with_same_inputs_produce_distinct_ciphertexts
This would avoid the need to generate shares via local CLI.
Why though? Grabbing this via local CLI is simpler than introspecting inside the DB
There was a problem hiding this comment.
Ah - I see. So, each validator has a different ciphertext for the same transaction, and depending on which of these ciphertexts is being decrypted, the shares would be different - right? For some reason I assumed that ciphertext for the same transaction ends up being the same for all validators.
There was a problem hiding this comment.
Thinking more about it - this does imply that a validator cannot generate a share for a given transaction w/o knowing which validator wants to decrypt it - right? If so, the interaction would be:
- Validator
Awants to decrypt some transaction. They send transaction info (including their cyphertext) to validatorB. - Validator
Buses this info and their own keys to generate their own key share and sends the key share to validatorA.
a. SomehowBneeds to make sure that the chipertext thatAsent is for the transaction they claim? Acombines the share fromBwith their own share, and decrypts the transaction.
a. If I understood correctly, the same shares cannot be used byBto decrypt its ciphertext, right?
3479318 to
2eef4f5
Compare
2eef4f5 to
785caeb
Compare
ab7a179 to
6219bdd
Compare
| -- Signing public key of the validator that produced this record. | ||
| validator_id BLOB NOT NULL, |
There was a problem hiding this comment.
nit: if this stores the public key, shouldn't we name it validator_key (or validator_pub_key)?
There was a problem hiding this comment.
The signing public key is the validator’s stable ID in PrivateRecordId. I would keep validator_id so the database, record bundle, and CLI use the same term, and clarify this in the SQL comment.
There was a problem hiding this comment.
The signing public key is the validator’s stable ID in
PrivateRecordId.
This is true until we let validators rotate their keys - right?
There was a problem hiding this comment.
Just fyi these are node's docs that get published to the miden docs website. Unclear if this belongs in there.
There was a problem hiding this comment.
The required Golden startup settings belong in the operator guide because the validator will not start without them. The temporary demo commands do not. I’ll remove the demo section and leave those details in CLI help and local demo notes.
Summary
After validation, the validator encrypts
TransactionInputsunder a threshold key. Each record uses a fresh content key. The validator stores the encrypted record and the validated transaction in one database write.Operators provide the shared setup and one secret share per validator. The new storage schema indexes records by record id, transaction id, key epoch, setup context, and block height.
A local admin command issues a decryption share for one stored record. Operators can combine enough valid shares for the same record to recover its inputs. Shares for another record, context, or key epoch are rejected. The command has no gRPC endpoint and requires direct access to the validator files.
This requires #2389, #2390, and the first Golden crate release. The Golden dependencies use local paths until that release is available. CI failures therefore expected
Closes #2374
Changelog