Skip to content

Commit

Permalink
fix wrong bpfUpgradeableLoader layout (#704)
Browse files Browse the repository at this point in the history
  • Loading branch information
nonergodic authored Sep 25, 2024
1 parent 1249937 commit 94d966a
Showing 1 changed file with 27 additions and 15 deletions.
42 changes: 27 additions & 15 deletions platforms/solana/src/utils/utils/bpfLoaderUpgradeable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,33 +15,45 @@ export function deriveProgramDataAddress(programId: PublicKeyInitData): PublicKe
);
}

//the program data pda coincides with the address that's stored in the program id account (i.e. the
// account that's found at the program id address), which is of type UpgradeLoaderState::Program:
// https://docs.rs/solana-program/latest/src/solana_program/bpf_loader_upgradeable.rs.html#40-43
export function programDataAddress(programId: PublicKeyInitData) {
return PublicKey.findProgramAddressSync([new PublicKey(programId).toBytes()], BPF_LOADER_UPGRADEABLE_PROGRAM_ID)[0];
}

const onChainUint = { binary: "uint", endianness: "little" } as const;

const pubKeyConversion = { //TODO find a better place for this
to: (encoded: Uint8Array) => new PublicKey(encoded),
from: (decoded: PublicKey) => decoded.toBytes(),
} as const satisfies CustomConversion<Uint8Array, PublicKey>;

//neither anchor nor solana web3 have a built-in way to parse this
//see here: https://docs.rs/solana-program/latest/solana_program/bpf_loader_upgradeable/enum.UpgradeableLoaderState.html
//Describes the layout of an account that holds a UpgradeableLoaderState::ProgramData enum:
// https://docs.rs/solana-program/latest/src/solana_program/bpf_loader_upgradeable.rs.html#45-52
// because neither Anchor nor Solana web3 seem to have a built-in way to parse this.
//The bpf_loader_upgradeable program uses Rust's serde crate and bincode to serialize its structs,
// which encodes enum variants as 4 byte little endian uints:
// https://github.com/serde-rs/serde/blob/9f8c579bf5f7478f91108c1186cd0d3f85aff29d/serde_derive/src/ser.rs#L399-L408
// and Options with a single byte 0 or 1 tag:
// https://docs.rs/bincode/latest/src/bincode/ser/mod.rs.html#137-147
//However, even if the program is made immutable the bpf_loader_upgradeable program will keep the
// last value of the enum variant and only set the option byte tag to 0, presumably so they don't
// have to memcopy the entire subsequent bytecode (they didn't really think that one through).
//See https://explorer.solana.com/address/GDDMwNyyx8uB6zrqwBFHjLLG3TBYk2F8Az4yrQC5RzMp
// as an example of an immutable program data account.
export const programDataLayout = [
{ name: "slot", binary: "uint", endianness: "little", size: 8 },
{ name: "programDataEnumVariant", ...onChainUint, size: 4, custom: 3, omit: true },
{ name: "slot", ...onChainUint, size: 8 },
{
name: "upgradeAuthority",
binary: "switch",
idSize: 1,
idTag: "isSome",
layouts: [
[[0, false], []],
[
[1, true],
[
{
name: "value",
binary: "bytes",
size: 32,
custom: pubKeyConversion,
},
],
],
[[0, false], [{ name: "_lastValueBeforeImmutability", binary: "bytes", size: 32 }]],
[[1, true], [{ name: "value", binary: "bytes", size: 32, custom: pubKeyConversion }]],
],
},
{ name: "bytecode", binary: "bytes" },
] as const satisfies Layout;

0 comments on commit 94d966a

Please sign in to comment.