Accounts Refactor PR 3: Addinng more call messages to accounts module#2773
Conversation
920da01 to
736575b
Compare
a7156ff to
b48875f
Compare
3c9bfcf to
22aac29
Compare
22aac29 to
bbe926a
Compare
|
New trait discussion: Context PR #2773 made is_authorized_for a mandatory gate on the V0 tx pre-exec Pre-fix, the generic blanket Before PR 3, this mismatch was latent — is_authorized_for wasn't on any What HEAD already has Commit 71e96cf ("Attempt to fix one:") at the current branch HEAD implements
Status verified locally:
The red CI is stale — it ran on bbe926a (before the fix). HEAD The three realistic options Option 1 — Keep current (TryDecodeCredentialId trait) The fix at HEAD. Already committed, compiles, tracing confirms correct. Pros
Cons
Option 2 — Inline check in sov-address + use HyperlaneAddress::from_sender in demo-stf Drop the new trait. Inline a ~3-line bytes[..12] == [0u8; 12] check directly Pros
Cons
Option 3 — Replace From for EthereumAddress with TryFrom Delete the unconditional impl From for EthereumAddress at Pros
Cons
Recommendation Option 1 — keep the current HEAD 71e96cf and push it to re-run CI.
Optional small polish inside Option 1: tighten the demo-stf From |
a887f49 to
30c8c38
Compare
71e96cf to
c14c762
Compare
3898e2e to
3704b1d
Compare
f94b261 to
ce7b152
Compare
c14c762 to
cf6f78e
Compare
a9fcf15 to
8f1d6ea
Compare
6406ec4 to
6e7d22a
Compare
adec893 to
f82f4eb
Compare
Description When `address_override` is `None`, `resolve_authorized_sender()` was re-checking `auth_data.default_address` through `Accounts::is_authorized_for()`. That works when the authenticator's default address matches `credential_id.into()`, but it breaks authenticators whose default address uses a different address form. For `sov-evm` with `MultiAddressEvm`, the authenticator sets `default_address` to `Vm(eth_addr)`, while the canonical fallback in `is_authorized_for()` is `Standard(credential_id.into())`. Ordinary EVM transactions were therefore skipped with "not authorized for resolved address" unless an explicit `(vm_address, credential_id)` mapping had already been inserted. Fix the shared `address_override = None` path to trust the authenticator-selected default address unless that exact `(address, credential_id)` pair is explicitly denied in `account_owners`. Keep `Some(address_override)` strict, and preserve revocation semantics for explicit `false` entries. This restores the default EVM sender path without changing EVM auth semantics or requiring pre-populated account mappings.
c8fba4e to
cefae1f
Compare
theodorebugnet
left a comment
There was a problem hiding this comment.
LGTM if the typescript comment is addressed!
a586c2b
into
theodore/multisig-upgrade
Description
sov_accounts::CallMessagebecomes generic in S: Spec and gains three variants:AddCredentialToAddress { address, credential }— authorize a new credential on an owned address.RemoveCredentialFromAddress { address, credential }— revoke.RotateCredentialOnAddress { address, old_credential, new_credential }— atomic swap (functionally Remove + Add, but one tx, one signing round for a multisig).InsertCredentialIdsemantics are unchanged. All three new variants share one rule:context.sender() == message.address.Why
#2770 added the account_owners map but only wrote to it at genesis and through
InsertCredentialId.PR 2 made V0 and V1 signers declare a
address_override.Without this PR, a multisig that rotates its keys can't keep its address — there's no way to add a new authorized credential or revoke the old one.
Rotate is the primitive that makes M1 → M2 a single operator-friendly tx instead of two.
Tradeoffs / design calls worth reviewing
context.sender() == addressinstead of the plan's "caller's credential authorizes for address" lookup. Context doesn't exposesender_credential_id(), and PR 2's resolver already proves the caller controls sender — so the simpler rule is equally safe and subsumes the "bootstrap" case.account_owners[(addr, cred)] = false, not a delete.falseis a durable revocation that blocks the stateless canonical fallback (credential.into() == address). Without it, removing a credential from its own default address would be silently re-authorized by the fallback. The storage model flips from "present-means-authorized" to "explicit true/false override".accounts[c] = { addr }entry can be added to a different new-model address. V0 routing still uses legacy; V1+target=Some uses the new map. This avoids making PRs 3/4 useless on upgraded chains before PR 5'sDrainLegacyAccountsruns.Wire / consensus
Adjacent changes (outside
sov-accounts)sov-capabilities/src/lib.rs: whenauth_data.address_overrideisNone, the resolver now explicitly callsis_authorized_for()on the canonical default address. This is what gives the newfalserevocation its end-to-end teeth — without this check, a revoked credential would still pass authorization via its canonical-address fallback.serializer.ts:serializeUnsignedTx()now auto-wraps plain unsigned-tx inputs as{ V0: input }unless already versioned (newisVersionedUnsignedTx()guard). Wire format itself is unchanged.uniqueness: { generation }nesting, andMultiAddress→MultiAddressEvmSolanarename.sov-address/src/evm/address.rs: two roundtrip tests for credential ↔ address conversion (credential_from_evm_address_stays_standard,credential_from_native_hash_stays_standard).--
CHANGELOG.mdwith a new entry if my PR makes any breaking changes or fixes a bug. If my PR removes a feature or changes its behavior, I provide help for users on how to migrate to the new behavior.Cargo.tomlchanges before opening the PRs. (Are all new dependencies necessary? Is any module dependency leaked into the full-node (hint: it shouldn't)?)Linked Issues
Testing
New tests added
Docs
Docstrings are updated