Add Migration<'info, From, To> account type#4060
Add Migration<'info, From, To> account type#4060jacobcreech merged 17 commits intosolana-foundation:masterfrom
Migration<'info, From, To> account type#4060Conversation
|
@L0STE is attempting to deploy a commit to the Solana Foundation Team on Vercel. A member of the Team first needs to authorize it. |
|
Based on @jacobcreech and @chen-robert inputs I rewrote the migration feature to only accept the strict version. The only thing I wasn't super happy was the fact that we could |
jamie-osec
left a comment
There was a problem hiding this comment.
Overall implementation looks good to me, just some notes about the API.
I'd also potentially suggest an API like
impl<'info, From, To> Migration<'info, From, To>
where From: Default {
pub fn migrate_with<F: FnOnce(From) -> To>(&mut self, func: F) -> Result<()> {
let MigrationInner::From(f) = &mut self.inner else {
return Err(ErrorCode::AccountAlreadyMigrated.into());
};
let from = std::mem::take(f);
self.inner = MigrationInner::To(func(from));
Ok(())
}
}To allow constructing the new type directly from an owned instance of the old one.
Finally, please add a test case demonstrating the usage and edge cases of this account.
The only thing that I'm worried about this is the fact that All other comments have been addressed! |
That is true - potentially we could instead use unsafe to leave the account in an invalid state temporarily, though this would need some careful handling about panic safety. Probably better to leave to a followup if this API is desired in practice. |
Agreed. Anything else that I need to fix to get this into the next version? |
|
I think we can land this in v1/1.1 with the following:
|
|
Hey, changes look good but we're still hitting formatting issues. Can you please fix that? |
|
I thought I fixed it but there was a trailing whitespace that I must have added by accident while committing. I think now it should be good |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
@L0STE @jamie-osec i believe you should add a note somewhere in the docs that its not part of a latest stable release. Was a bit confused when found it in the docs but didnt see it in 0.32.1 |
…n#4060) * Added a migration type * Fixed my midcurved implementation * Fixed some errors + added realloc for migration type * Last nit then up only * Better naming * Updated to always be stricted * Simplified and improved * Last nit then really up only * Additions and Fixes based on comments * clippy * last addition * last nits * Update error.rs * Update migration.rs * fix: fmt error --------- Co-authored-by: Jacob Creech <82475023+jacobcreech@users.noreply.github.com>
What does this do
This PR adds a new account container type that abstracts away the hurdle of migrating Solana accounts from one schema to another.
How it works
The
Migration<'info, From, To>type wraps an account that can be in either the old format (From) or new format (To). It automatically detects which format the account is in during deserialization, lets you migrate with a simple.migrate()call, and guarantees the account is migrated before exit.Example:
Smart Migration
By default, the migration type accepts both
FromandToaccounts. It tries to deserialize asTofirst (already migrated), then falls back toFrom(needs migration).Why? This enables gradual migrations without downtime. Instead of running a costly loop to migrate all existing accounts upfront, you can deploy the migration instruction and let accounts migrate organically as users interact with your protocol.
Some accounts might already be in V2 format (migrated earlier), while others are still V1: the type handles both seamlessly. This is especially useful for live protocols where you can't afford downtime or want to avoid the compute costs and operational complexity of batch migrations.
Calling
.migrate()on an already-migrated account is a no-op; it means that if the account has already migrated it won't fail or corrupt state.The serialization on exit is heavily constrained: if you forget to call
.migrate(), the account exit will fail withAccountNotMigratedpreventing accidentally leaving accounts in an inconsistent state. You can't forget to migrate because the type system forces you to handle it explicitly. This is critical for data integrity in production.Strict mode
Add
#[account(migrate = "strict")]to reject accounts that are already migrated.Sometimes you want to ensure an account hasn't been migrated yet. For example, if you want to enforce that migration happens through a specific code path. Strict mode makes this explicit.
Realloc
We added the
reallocconstraint to work withMigrationtypes since account migrations usually involve resizing. Instead of forcing developers to manually realloc in a separate logic you can do it directly in the account struct like this:What Changed
lang/src/accounts/migration.rswith the coreMigrationtypeAccountAlreadyMigratedandAccountNotMigratederror codesmigrate = "strict"constraint in theparser/codegenreallocwork withMigrationtypesMigration<'info, From, To>in the macroTesting
You can see an example of how this work here: https://github.com/L0STE/anchor-migration-test
And everything compiles successfully with
cargo check -p anchor-lang.