@@ -15,33 +15,45 @@ export function deriveProgramDataAddress(programId: PublicKeyInitData): PublicKe
15
15
) ;
16
16
}
17
17
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
+
18
27
const pubKeyConversion = { //TODO find a better place for this
19
28
to : ( encoded : Uint8Array ) => new PublicKey ( encoded ) ,
20
29
from : ( decoded : PublicKey ) => decoded . toBytes ( ) ,
21
30
} as const satisfies CustomConversion < Uint8Array , PublicKey > ;
22
31
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.
25
45
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 } ,
27
48
{
28
49
name : "upgradeAuthority" ,
29
50
binary : "switch" ,
30
51
idSize : 1 ,
31
52
idTag : "isSome" ,
32
53
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 } ] ] ,
45
56
] ,
46
57
} ,
58
+ { name : "bytecode" , binary : "bytes" } ,
47
59
] as const satisfies Layout ;
0 commit comments