Skip to content

Commit 534458d

Browse files
committed
solana: fix governance action byte and add serde test from guardian
1 parent bdca974 commit 534458d

File tree

3 files changed

+112
-47
lines changed

3 files changed

+112
-47
lines changed

solana/Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

solana/programs/wormhole-governance/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,6 @@ solana-program.workspace = true
3333
wormhole-anchor-sdk.workspace = true
3434
wormhole-io.workspace = true
3535
wormhole-sdk.workspace = true
36+
37+
[dev-dependencies]
38+
hex.workspace = true

solana/programs/wormhole-governance/src/instructions/governance.rs

+108-47
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,62 @@ impl GovernanceMessage {
109109
0x50, 0x75, 0x72, 0x70, 0x6F, 0x73, 0x65, 0x47, 0x6F, 0x76, 0x65, 0x72, 0x6E, 0x61, 0x6E,
110110
0x63, 0x65,
111111
];
112+
113+
pub fn read_body<R>(reader: &mut R, governance_program_id: Pubkey) -> io::Result<Self>
114+
where
115+
R: io::Read,
116+
{
117+
let program_id: Pubkey = Pubkey::new_from_array(Readable::read(reader)?);
118+
let accounts_len: u16 = Readable::read(reader)?;
119+
let mut accounts = Vec::with_capacity(accounts_len as usize);
120+
for _ in 0..accounts_len {
121+
let pubkey: [u8; 32] = Readable::read(reader)?;
122+
let is_signer: bool = Readable::read(reader)?;
123+
let is_writable: bool = Readable::read(reader)?;
124+
accounts.push(Acc {
125+
pubkey: Pubkey::new_from_array(pubkey),
126+
is_signer,
127+
is_writable,
128+
});
129+
}
130+
let data_len: u16 = Readable::read(reader)?;
131+
let mut data = vec![0; data_len as usize];
132+
reader.read_exact(&mut data)?;
133+
134+
Ok(GovernanceMessage {
135+
governance_program_id,
136+
program_id,
137+
accounts,
138+
data,
139+
})
140+
}
141+
142+
/// Serialises the governance packet's body. This is the part of the packet
143+
/// that will be fed into the guardian node (the header part is populated by
144+
/// the node itself).
145+
pub fn write_body<W>(&self, writer: &mut W) -> io::Result<()>
146+
where
147+
W: io::Write,
148+
{
149+
let GovernanceMessage {
150+
governance_program_id: _,
151+
program_id,
152+
accounts,
153+
data,
154+
} = self;
155+
156+
program_id.to_bytes().write(writer)?;
157+
u16::try_from(accounts.len())
158+
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "accounts length overflow"))?
159+
.write(writer)?;
160+
for acc in accounts {
161+
acc.pubkey.to_bytes().write(writer)?;
162+
acc.is_signer.write(writer)?;
163+
acc.is_writable.write(writer)?;
164+
}
165+
(data.len() as u16).write(writer)?;
166+
writer.write_all(data)
167+
}
112168
}
113169

114170
#[test]
@@ -160,31 +216,8 @@ impl Readable for GovernanceMessage {
160216
"Invalid GovernanceMessage chain",
161217
));
162218
}
163-
164-
let governance_program_id: Pubkey = Pubkey::new_from_array(Readable::read(reader)?);
165-
let program_id: Pubkey = Pubkey::new_from_array(Readable::read(reader)?);
166-
let accounts_len: u16 = Readable::read(reader)?;
167-
let mut accounts = Vec::with_capacity(accounts_len as usize);
168-
for _ in 0..accounts_len {
169-
let pubkey: [u8; 32] = Readable::read(reader)?;
170-
let is_signer: bool = Readable::read(reader)?;
171-
let is_writable: bool = Readable::read(reader)?;
172-
accounts.push(Acc {
173-
pubkey: Pubkey::new_from_array(pubkey),
174-
is_signer,
175-
is_writable,
176-
});
177-
}
178-
let data_len: u16 = Readable::read(reader)?;
179-
let mut data = vec![0; data_len as usize];
180-
reader.read_exact(&mut data)?;
181-
182-
Ok(GovernanceMessage {
183-
governance_program_id,
184-
program_id,
185-
accounts,
186-
data,
187-
})
219+
let governance_program_id = Pubkey::new_from_array(Readable::read(reader)?);
220+
Self::read_body(reader, governance_program_id)
188221
}
189222
}
190223

@@ -205,26 +238,11 @@ impl Writeable for GovernanceMessage {
205238
where
206239
W: io::Write,
207240
{
208-
let GovernanceMessage {
209-
governance_program_id,
210-
program_id,
211-
accounts,
212-
data,
213-
} = self;
214-
215241
Self::MODULE.write(writer)?;
216242
GovernanceAction::SolanaCall.write(writer)?;
217243
u16::from(Chain::Solana).write(writer)?;
218-
governance_program_id.to_bytes().write(writer)?;
219-
program_id.to_bytes().write(writer)?;
220-
(accounts.len() as u16).write(writer)?;
221-
for acc in accounts {
222-
acc.pubkey.to_bytes().write(writer)?;
223-
acc.is_signer.write(writer)?;
224-
acc.is_writable.write(writer)?;
225-
}
226-
(data.len() as u16).write(writer)?;
227-
writer.write_all(data)
244+
self.governance_program_id.to_bytes().write(writer)?;
245+
self.write_body(writer)
228246
}
229247
}
230248

@@ -258,6 +276,49 @@ fn test_governance_message_serde() {
258276
assert_eq!(msg, msg2);
259277
}
260278

279+
#[test]
280+
fn test_governance_message_parse_guardian() {
281+
// hex dumped from guardian node with the following protoxt:
282+
// ```
283+
// current_set_index: 4
284+
// # generic solana call
285+
// messages: {
286+
// sequence: 4513077582118919631
287+
// nonce: 2809988562
288+
// solana_call: {
289+
// chain_id: 1
290+
// governance_contract: "wgvEiKVzX9yyEoh41jZAdC6JqGUTS4CFXbFGBV5TKdZ"
291+
// encoded_instruction: "00000000000000010000000000000000000000000000000000000000000000000002000000000000000200000000000000000000000000000000000000000000000001010000000000000003000000000000000000000000000000000000000000000000000100050102030405"
292+
// }
293+
// }
294+
// ```
295+
// TODO: once this program is moved into the monorepo, do an e2e integration test
296+
let h = hex::decode("000000000000000047656e6572616c507572706f7365476f7665726e616e63650200010e027fbc6b1e61365d4b0680a3179f791b15796f93e24e9b441e3fa04ccda4a000000000000000010000000000000000000000000000000000000000000000000002000000000000000200000000000000000000000000000000000000000000000001010000000000000003000000000000000000000000000000000000000000000000000100050102030405").unwrap();
297+
let actual = GovernanceMessage::deserialize(&mut h.as_slice()).unwrap();
298+
299+
let accounts = vec![
300+
Acc {
301+
pubkey: Pubkey::try_from("1111111ogCyDbaRMvkdsHB3qfdyFYaG1WtRUAfdh").unwrap(),
302+
is_signer: true,
303+
is_writable: true,
304+
},
305+
Acc {
306+
pubkey: Pubkey::try_from("11111112D1oxKts8YPdTJRG5FzxTNpMtWmq8hkVx3").unwrap(),
307+
is_signer: false,
308+
is_writable: true,
309+
},
310+
];
311+
let data = vec![1, 2, 3, 4, 5];
312+
let expected = GovernanceMessage {
313+
governance_program_id: crate::ID,
314+
program_id: Pubkey::try_from("1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM").unwrap(),
315+
accounts,
316+
data,
317+
};
318+
319+
assert_eq!(actual, expected)
320+
}
321+
261322
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
262323
/// The known set of governance actions.
263324
///
@@ -285,12 +346,12 @@ impl Readable for GovernanceAction {
285346
R: io::Read,
286347
{
287348
match Readable::read(reader)? {
288-
0 => Ok(GovernanceAction::Undefined),
349+
0u8 => Ok(GovernanceAction::Undefined),
289350
1 => Ok(GovernanceAction::EvmCall),
290351
2 => Ok(GovernanceAction::SolanaCall),
291-
_ => Err(io::Error::new(
352+
n => Err(io::Error::new(
292353
io::ErrorKind::InvalidData,
293-
"Invalid GovernanceAction",
354+
format!("invalid action {}", n),
294355
)),
295356
}
296357
}
@@ -307,8 +368,8 @@ impl Writeable for GovernanceAction {
307368
{
308369
match self {
309370
GovernanceAction::Undefined => Ok(()),
310-
GovernanceAction::EvmCall => 1.write(writer),
311-
GovernanceAction::SolanaCall => 2.write(writer),
371+
GovernanceAction::EvmCall => 1u8.write(writer),
372+
GovernanceAction::SolanaCall => 2u8.write(writer),
312373
}
313374
}
314375
}

0 commit comments

Comments
 (0)