Skip to content

Commit 94d966a

Browse files
authored
fix wrong bpfUpgradeableLoader layout (#704)
1 parent 1249937 commit 94d966a

File tree

1 file changed

+27
-15
lines changed

1 file changed

+27
-15
lines changed

platforms/solana/src/utils/utils/bpfLoaderUpgradeable.ts

+27-15
Original file line numberDiff line numberDiff line change
@@ -15,33 +15,45 @@ export function deriveProgramDataAddress(programId: PublicKeyInitData): PublicKe
1515
);
1616
}
1717

18+
//the program data pda coincides with the address that's stored in the program id account (i.e. the
19+
// account that's found at the program id address), which is of type UpgradeLoaderState::Program:
20+
// https://docs.rs/solana-program/latest/src/solana_program/bpf_loader_upgradeable.rs.html#40-43
21+
export function programDataAddress(programId: PublicKeyInitData) {
22+
return PublicKey.findProgramAddressSync([new PublicKey(programId).toBytes()], BPF_LOADER_UPGRADEABLE_PROGRAM_ID)[0];
23+
}
24+
25+
const onChainUint = { binary: "uint", endianness: "little" } as const;
26+
1827
const pubKeyConversion = { //TODO find a better place for this
1928
to: (encoded: Uint8Array) => new PublicKey(encoded),
2029
from: (decoded: PublicKey) => decoded.toBytes(),
2130
} as const satisfies CustomConversion<Uint8Array, PublicKey>;
2231

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

0 commit comments

Comments
 (0)