Open JakkuSakura opened 1 month ago
Thanks for creating the issue but Anchor itself doesn't have any alignment assumptions when using #[account(zero_copy)]
(internal bytemuck
does).
Solana usually keeps layouts consistent — it might be worth creating an issue in https://github.com/anza-xyz/llvm-project.
The issue isn't that the Solana layout changed -- it's that it didn't change while everyone else's did.
If you are trying to deserialize #[zero_copy(unsafe)]
anchor data containing a u128
or i128
with a Rust 1.77+ program, it won't work because the data was serialized using an old Rust layout that's no longer compatible.
Anchor itself doesn't have any alignment assumptions when using #[account(zero_copy)] (internal bytemuck does).
Older Anchor programs (and new ones using #[zero_copy(unsafe)]
) are not defined as repr(C)
, so by default they use repr(Rust)
. That is the assumption causing the issue here.
While technically bytemuck is doing the heavy lifting its anchor that adds the repr
. And as per the bytemuck docs:
The type needs to be repr(C) or repr(transparent).
Anchor fixed this issue in 0.20 but a lot of existing programs cannot change their representations. There are a lot of important programs using #[zero_copy(unsafe)]
(and thus repr(Rust)
) because they were deployed before Anchor 0.20 was released.
I pushed a self-contained reproduction here: https://github.com/matt-allan/anchor-layout-issue
In this case the issue between 1.77
and stable
is not the change in alignment but that the order of the fields changed.
On 1.77
I get the expected value for the first field:
TickArray {
start_tick_index: -1232,
The offset is 0
, which is expected:
Offset of start_tick_index in TickArray: 0
Alignment of TickArray: 1
Alignment of Tick: 1
Offset of liquidity_gross in Tick: 17
On stable I don't get the expected value:
TickArray {
start_tick_index: 0,
The offset of the field has changed:
Offset of start_tick_index in TickArray: 9944
Alignment of TickArray: 1
Alignment of Tick: 1
Offset of liquidity_gross in Tick: 17
9944 is the size of Tick
(113) x the # of array elements (88), so it's moved.
Long story short. The previous versions of rust and llvm treat u128 as 8 alignments incorrectly on x86_64. so does
#[Account(zero_copy)]
. A recent update fixed this issue, but breaks all existing#[account(zero_copy)]
with u128I'm surprised that nobody mentioned this issue here. So I'm just links related issues. https://blog.rust-lang.org/2024/03/30/i128-layout-update.html https://github.com/drift-labs/protocol-v2/issues/891 https://github.com/rust-lang/rust/issues/54341
A workaround would be defining a
struct u128([u64; 2])
and replace all primitive type u128, inspired by https://solana.stackexchange.com/questions/7720/using-u128-without-sacrificing-alignment-8