feat(relayer): context aware user decrypt threshold#2317
feat(relayer): context aware user decrypt threshold#2317manoranjith wants to merge 6 commits intofeat/RFC013from
Conversation
- Issue: host_chains config, new in this release, failed with "invalid type: map, expected sequence" when set via indexed K8s env vars - Fix: add map-or-seq deserializer and try_parsing(true) for string-to-numeric coercion - Prevention: add env override test for all fields, which uncovered the same issue in backoff intervals and histogram buckets — fixed with a single generic deserializer applied to all Vec fields Refs zama-ai/fhevm-internal#1191
- Previously: doc comment and design doc claimed RFC 7231 absolute timestamps for 429 responses - Now: correctly documents relative seconds, matching the actual implementation
- Resolve user decrypt threshold per context_id from ProtocolConfig contract instead of static config - Parse context_id from extra_data in gateway response; 0 falls back to static default - Cache thresholds in moka with dedup and retry on failure - Persist resolved_threshold in DB; read side uses COALESCE for backward compatibility - New config parameter: protocol_config
79721c3 to
5c36885
Compare
- Previously: threshold was u16 in config, usize in resolver, i64 at DB boundary — mixed widths with implicit casts - Now: u32 end-to-end from config → resolver → handler → repo, with explicit i64 conversion only at the DB BIGINT boundary
- Previously: path dep pulled host-contracts into cargo fmt scope - Now: git dep pinned to 2a6cf82 (ProtocolConfig bindings)
| -- Stores the dynamic threshold used when the write side marked the request as completed. | ||
| -- Nullable for backward compatibility: old software ignores this column, new rows get the value. | ||
| -- NULL means "use static config default" (for rows created before this migration). | ||
| ALTER TABLE user_decrypt_req ADD COLUMN resolved_threshold BIGINT; |
There was a problem hiding this comment.
nit: maybe to enforce the NOT NULL constraint rule, we can set the current "static config default" instead of leaving it as NULL
| pub input_verification_address: String, | ||
| /// Number of shares required for user decryption threshold consensus | ||
| pub user_decrypt_shares_threshold: u16, | ||
| pub user_decrypt_shares_threshold: u32, |
There was a problem hiding this comment.
nit: just wondering why not leave it as u16, or even lower like u8?
|
|
||
| impl ThresholdResolver { | ||
| pub async fn new( | ||
| config: &ProtocolConfigSettings, |
There was a problem hiding this comment.
nit: naming this argument as protocol_config_settings could help to reduce a bit of the developer's mental load
| /// Thresholds are cached permanently (no TTL) since they don't change after | ||
| /// context creation. Context ID 0 is pre-seeded with the static config default. | ||
| pub struct ThresholdResolver { | ||
| contract: HostProtocolConfig, |
There was a problem hiding this comment.
| contract: HostProtocolConfig, | |
| protocol_config_contract: HostProtocolConfig, |
| /// Cached thresholds keyed by context ID. The on-chain uint256 is narrowed | ||
| /// to u32 at the contract fetch boundary with an explicit range check. | ||
| /// The repository layer handles u32 ↔ i64 conversion for DB storage (BIGINT). | ||
| context_thresholds: Cache<U256, u32>, |
There was a problem hiding this comment.
| context_thresholds: Cache<U256, u32>, | |
| context_thresholds_cache: Cache<U256, u32>, |
|
|
||
| let context_thresholds = Cache::builder().max_capacity(max_capacity).build(); | ||
|
|
||
| // Pre-seed default: context_id 0 is invalid and maps to the static config value |
There was a problem hiding this comment.
If context_id 0 is an invariant violation, why intentionally introduce it in the cache? Also, this could be error-prone somehow since we shouldn't have such a default threshold but only what our source of truth dictates, no?
|
|
||
| protocol_config: | ||
| ethereum_http_rpc_url: "http://host-node:8545" | ||
| address: "0x1234567890123456789012345678901234567890" |
There was a problem hiding this comment.
| address: "0x1234567890123456789012345678901234567890" | |
| address: "0x52054F36036811ca418be59e41Fc6DD1b9e4F4c8" |
|
|
||
| protocol_config: | ||
| ethereum_http_rpc_url: "http://host-node:8545" | ||
| address: "0x1234567890123456789012345678901234567890" |
There was a problem hiding this comment.
| address: "0x1234567890123456789012345678901234567890" | |
| address: "0x52054F36036811ca418be59e41Fc6DD1b9e4F4c8" |
isaacdecoded
left a comment
There was a problem hiding this comment.
A few nit changes/suggestions here and there.
Summary
Draft implementation of context aware user decrypt threshold feature.
Refs zama-ai/fhevm-internal#1260
Done
context_idis extracted fromextra_dataand the corresponding user decrypt shares threshold isfetched from the
ProtocolConfigcontract on the Ethereum chain.ProtocolConfigcontract on the next share arrival.extra_data = 0x00(or empty), the threshold value from config is used, ensuring backward compatibility for older requests duringthe migration period.
result.
COALESCE(resolved_threshold, static_config)is used for the share LIMIT, so pre-migration rows(where
resolved_thresholdis NULL) fall back to the static config value.resolved_threshold BIGINTcolumn (backward-compatible with old software).protocol_config.Tests
Unit tests — extra_data parsing (
extra_data.rs,threshold_resolver.rs):0x00extra_data returns default context (zero)Mock-based tests — threshold resolution (
threshold_resolver_test.rs, usingethereum_rpc_mock):Pending (should not block testing)
fhevm_host_bindingsto git tag before merge