Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit 2b8c48c

Browse files
authored
Token 22: Add functionality to update extra_account_metas after initializing. (#5894)
* Added update function for ExtraAccountMetaList * Updated interface to handle new update instruction * Updated Cli to handle update command * updated example program to handle updating * Rust fmt trailing whitespace fix * Removed unused variable * Added more explicit update instruction doc comment * Allow for resizing to smaller account size * Removed system program from update instruction * Added helper fn to calculate transfer lamports * Added unit tests for update function * Added unit test for update instruction * removed unnecessary commented out code * re-added checks on initialization * turned of zero_init for realloc for performance * Fixed update doc comments * Used block-scoping rather than explicit drop() * Removed unnecessary convert to vec * refactored updated test into single test * added additional off-chain test of update instruct * made on-chain invoke update test to match original * moved helper function up to others * refactored create and update with helpers * rustfmt: fix * rustfmt: fix * removed commented out system program in update * renamed helpers and removed unnecessary helper * moved test helper up * fixed test attribute location * removed multiple init extra account metas in test * added instruction assert to update test * renamed transfer address to extra account metas * rustfmt: comment fix * clippy: fix * added update test with simple PDA * made more changes to updated metas in test * added check for if extra metas have be initialized * spelling fix * fixed initialized condition
1 parent bb17e28 commit 2b8c48c

File tree

5 files changed

+1130
-43
lines changed

5 files changed

+1130
-43
lines changed

libraries/tlv-account-resolution/src/state.rs

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,22 @@ impl ExtraAccountMetaList {
182182
Ok(())
183183
}
184184

185+
/// Update pod slice data for the given instruction and its required
186+
/// list of `ExtraAccountMeta`s
187+
pub fn update<T: SplDiscriminate>(
188+
data: &mut [u8],
189+
extra_account_metas: &[ExtraAccountMeta],
190+
) -> Result<(), ProgramError> {
191+
let mut state = TlvStateMut::unpack(data).unwrap();
192+
let tlv_size = PodSlice::<ExtraAccountMeta>::size_of(extra_account_metas.len())?;
193+
let bytes = state.realloc_first::<T>(tlv_size)?;
194+
let mut validation_data = PodSliceMut::init(bytes)?;
195+
for meta in extra_account_metas {
196+
validation_data.push(*meta)?;
197+
}
198+
Ok(())
199+
}
200+
185201
/// Get the underlying `PodSlice<ExtraAccountMeta>` from an unpacked TLV
186202
///
187203
/// Due to lifetime annoyances, this function can't just take in the bytes,
@@ -1292,6 +1308,124 @@ mod tests {
12921308
}
12931309
}
12941310

1311+
async fn update_and_assert_metas(
1312+
program_id: Pubkey,
1313+
buffer: &mut Vec<u8>,
1314+
updated_metas: &[ExtraAccountMeta],
1315+
check_metas: &[AccountMeta],
1316+
) {
1317+
// resize buffer if necessary
1318+
let account_size = ExtraAccountMetaList::size_of(updated_metas.len()).unwrap();
1319+
if account_size > buffer.len() {
1320+
buffer.resize(account_size, 0);
1321+
}
1322+
1323+
// update
1324+
ExtraAccountMetaList::update::<TestInstruction>(buffer, updated_metas).unwrap();
1325+
1326+
// retrieve metas and assert
1327+
let state = TlvStateBorrowed::unpack(buffer).unwrap();
1328+
let unpacked_metas_pod =
1329+
ExtraAccountMetaList::unpack_with_tlv_state::<TestInstruction>(&state).unwrap();
1330+
let unpacked_metas = unpacked_metas_pod.data();
1331+
assert_eq!(
1332+
unpacked_metas, updated_metas,
1333+
"The ExtraAccountMetas in the buffer should match the expected ones."
1334+
);
1335+
1336+
let mock_rpc = MockRpc::setup(&[]);
1337+
1338+
let mut instruction = Instruction::new_with_bytes(program_id, &[], vec![]);
1339+
ExtraAccountMetaList::add_to_instruction::<TestInstruction, _, _>(
1340+
&mut instruction,
1341+
|pubkey| mock_rpc.get_account_data(pubkey),
1342+
buffer,
1343+
)
1344+
.await
1345+
.unwrap();
1346+
1347+
assert_eq!(instruction.accounts, check_metas,);
1348+
}
1349+
1350+
#[tokio::test]
1351+
async fn update_extra_account_meta_list() {
1352+
let program_id = Pubkey::new_unique();
1353+
1354+
// Create list of initial metas
1355+
let initial_metas = [
1356+
ExtraAccountMeta::new_with_pubkey(&Pubkey::new_unique(), false, true).unwrap(),
1357+
ExtraAccountMeta::new_with_pubkey(&Pubkey::new_unique(), true, false).unwrap(),
1358+
];
1359+
1360+
// initialize
1361+
let initial_account_size = ExtraAccountMetaList::size_of(initial_metas.len()).unwrap();
1362+
let mut buffer = vec![0; initial_account_size];
1363+
ExtraAccountMetaList::init::<TestInstruction>(&mut buffer, &initial_metas).unwrap();
1364+
1365+
// Create updated metas list of the same size
1366+
let updated_metas_1 = [
1367+
ExtraAccountMeta::new_with_pubkey(&Pubkey::new_unique(), true, true).unwrap(),
1368+
ExtraAccountMeta::new_with_pubkey(&Pubkey::new_unique(), false, false).unwrap(),
1369+
];
1370+
let check_metas_1 = updated_metas_1
1371+
.iter()
1372+
.map(|e| AccountMeta::try_from(e).unwrap())
1373+
.collect::<Vec<_>>();
1374+
update_and_assert_metas(program_id, &mut buffer, &updated_metas_1, &check_metas_1).await;
1375+
1376+
// Create updated and larger list of metas
1377+
let updated_metas_2 = [
1378+
ExtraAccountMeta::new_with_pubkey(&Pubkey::new_unique(), true, true).unwrap(),
1379+
ExtraAccountMeta::new_with_pubkey(&Pubkey::new_unique(), false, false).unwrap(),
1380+
ExtraAccountMeta::new_with_pubkey(&Pubkey::new_unique(), false, true).unwrap(),
1381+
];
1382+
let check_metas_2 = updated_metas_2
1383+
.iter()
1384+
.map(|e| AccountMeta::try_from(e).unwrap())
1385+
.collect::<Vec<_>>();
1386+
update_and_assert_metas(program_id, &mut buffer, &updated_metas_2, &check_metas_2).await;
1387+
1388+
// Create updated and smaller list of metas
1389+
let updated_metas_3 =
1390+
[ExtraAccountMeta::new_with_pubkey(&Pubkey::new_unique(), true, true).unwrap()];
1391+
let check_metas_3 = updated_metas_3
1392+
.iter()
1393+
.map(|e| AccountMeta::try_from(e).unwrap())
1394+
.collect::<Vec<_>>();
1395+
update_and_assert_metas(program_id, &mut buffer, &updated_metas_3, &check_metas_3).await;
1396+
1397+
// Create updated list of metas with a simple PDA
1398+
let seed_pubkey = Pubkey::new_unique();
1399+
let updated_metas_4 = [
1400+
ExtraAccountMeta::new_with_pubkey(&seed_pubkey, true, true).unwrap(),
1401+
ExtraAccountMeta::new_with_seeds(
1402+
&[
1403+
Seed::Literal {
1404+
bytes: b"seed-prefix".to_vec(),
1405+
},
1406+
Seed::AccountKey { index: 0 },
1407+
],
1408+
false,
1409+
true,
1410+
)
1411+
.unwrap(),
1412+
];
1413+
let simple_pda = Pubkey::find_program_address(
1414+
&[
1415+
b"seed-prefix", // Literal prefix
1416+
seed_pubkey.as_ref(), // Account at index 0
1417+
],
1418+
&program_id,
1419+
)
1420+
.0;
1421+
let check_metas_4 = [
1422+
AccountMeta::new(seed_pubkey, true),
1423+
AccountMeta::new(simple_pda, false),
1424+
];
1425+
1426+
update_and_assert_metas(program_id, &mut buffer, &updated_metas_4, &check_metas_4).await;
1427+
}
1428+
12951429
#[test]
12961430
fn check_account_infos_test() {
12971431
let program_id = Pubkey::new_unique();

0 commit comments

Comments
 (0)