chore: validate submission accounts on deserialization#4450
Conversation
There was a problem hiding this comment.
Code Review
This pull request refactors the validation of EIP-7702 submission accounts by enforcing that they must be signers (and not address-only accounts) during deserialization using a new SubmissionAccounts wrapper type. Feedback on this change suggests retaining the validation check within solver::Config::validate() to ensure that programmatic constructions of the configuration remain safe and validated.
| // The invariant that submission accounts are signers (never | ||
| // `Account::Address`) is enforced when the configuration is | ||
| // deserialized, see `config::file::SubmissionAccounts`. |
There was a problem hiding this comment.
While enforcing the invariant at the deserialization layer (config::file::SubmissionAccounts) is excellent for failing fast during config loading, removing the check from solver::Config::validate() leaves programmatic construction of solver::Config (e.g., in unit/integration tests or future programmatic APIs) unvalidated.
To adhere to defensive programming principles and ensure that solver::Config remains valid by construction/validation regardless of how it is instantiated, we should retain this check in validate().
anyhow::ensure!(
self.submission_accounts
.iter()
.all(|account| !matches!(account, Account::Address(_))),
"solver '{}': EIP-7702 submission accounts must be signers; address-only accounts \\
cannot sign delegated settlement transactions",
self.name,
);db90c24 to
36b9ca3
Compare
| #[derive(Debug, Default)] | ||
| struct SubmissionAccounts(Vec<Account>); | ||
|
|
||
| impl SubmissionAccounts { |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
| impl<'de> Deserialize<'de> for SubmissionAccounts { | ||
| fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||
| where | ||
| D: Deserializer<'de>, | ||
| { | ||
| let accounts = Vec::<Account>::deserialize(deserializer)?; | ||
| Self::new(accounts).map_err(serde::de::Error::custom) | ||
| } | ||
| } | ||
|
|
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
36b9ca3 to
2248d96
Compare
|
@failuresmith Thanks for the review! Applied the changes. |
|
Longer term, I think the wrapper may belong in The current PR still improves the file-loading path and keeps |
|
If we want one canonical check, it should live just in the runtime/domain type, not only in file config. The stronger design is: // infra::solver
pub struct SubmissionAccounts(Vec<Account>);
impl SubmissionAccounts {
pub fn new(accounts: Vec<Account>) -> anyhow::Result<Self> {
// reject Account::Address once
}
}Then: pub struct Config {
pub submission_accounts: SubmissionAccounts,
}Now both paths must go through the same constructor:
The tradeoff is scope: moving it into |
Description
submission-accountsis already validated at runtime insolver::Config::validate()(added in #4430). As suggested in this review comment, this additionally enforces the "submission accounts must be signers" invariant at config deserialization via a wrapper type, so an invalid config fails to load and the type can't be constructed in an invalid state.Changes
SubmissionAccounts(Vec<Account>)wrapper inconfig/file/mod.rswhosenew()constructor and customDeserializeimpl reject any address-onlyAccount(only signers can sign EIP-7702 delegated settlements). Config loading now fails fast with a clear error.SubmissionAccountsfor thesubmission_accountsconfig field;load.rsunwraps the validated list viainto_inner().solver::Config::validate()is left in place as defense-in-depth (also guards programmatic construction); no change there.How to test
cargo test -p driver --lib submission_accountssubmission-accountsin a driver config (e.g.submission-accounts = ["0x0000000000000000000000000000000000000002"]) and start the driver — it refuses to start withEIP-7702 submission accounts must be signers; address-only accounts cannot sign delegated settlement transactions. A private-key entry starts normally.Related Issues
Fixes #4449