Skip to content

Commit 1cb425e

Browse files
committed
test: add functional test for sha metadata hashing
1 parent 9feedf2 commit 1cb425e

File tree

13 files changed

+246
-73
lines changed

13 files changed

+246
-73
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

program-libs/ctoken-types/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ light-macros = { workspace = true }
2525
solana-sysvar = { workspace = true, optional = true }
2626
spl-pod = { workspace = true }
2727
spl-token-2022 = { workspace = true }
28+
solana-msg = { workspace = true }
2829

2930
[dev-dependencies]
3031
rand = { workspace = true }

program-libs/ctoken-types/src/instructions/extensions/mod.rs

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1-
use light_hasher::Hasher;
1+
use light_hasher::{Hasher, Poseidon, Sha256};
22
pub mod compressible;
33
//pub mod metadata_pointer;
44
pub mod token_metadata;
5-
5+
use pinocchio::log::sol_log_compute_units;
6+
use solana_msg::msg;
67
//pub use metadata_pointer::{InitMetadataPointer, ZInitMetadataPointer};
78
pub use token_metadata::{TokenMetadataInstructionData, ZTokenMetadataInstructionData};
89

9-
use crate::{context::TokenContext, AnchorDeserialize, AnchorSerialize, CTokenError};
10+
use crate::{
11+
context::TokenContext, state::Version, AnchorDeserialize, AnchorSerialize, CTokenError,
12+
};
1013

1114
#[derive(Debug, Clone, PartialEq, Eq, AnchorSerialize, AnchorDeserialize)]
1215
pub enum ExtensionInstructionData {
@@ -85,7 +88,37 @@ impl ZExtensionInstructionData<'_> {
8588
metadata_pointer.hash_metadata_pointer::<H>(context)
8689
}*/
8790
ZExtensionInstructionData::TokenMetadata(token_metadata) => {
88-
token_metadata.hash_token_metadata::<H>(hashed_mint, context)
91+
match Version::try_from(token_metadata.version)? {
92+
Version::Poseidon => {
93+
// TODO: cleanup other hashing code
94+
msg!("poseidon");
95+
sol_log_compute_units();
96+
let hash =
97+
token_metadata.hash_token_metadata::<Poseidon>(hashed_mint, context);
98+
sol_log_compute_units();
99+
hash
100+
}
101+
Version::Sha256 => {
102+
msg!("sha256");
103+
sol_log_compute_units();
104+
let mut hash =
105+
token_metadata.hash_token_metadata::<Sha256>(hashed_mint, context)?;
106+
sol_log_compute_units();
107+
hash[0] = 0;
108+
Ok(hash)
109+
}
110+
_ => {
111+
msg!(
112+
"TokenMetadata hash version not supported {} (0 Poseidon, 1 Sha256 are supported).",
113+
token_metadata.version
114+
);
115+
unimplemented!(
116+
"TokenMetadata hash version not supported {}",
117+
token_metadata.version
118+
)
119+
} // Version::Keccak256 => <Self as DataHasher>::hash::<Keccak>(self),
120+
// Version::Sha256Flat => self.sha_flat(),
121+
}
89122
}
90123
_ => Err(CTokenError::UnsupportedExtension),
91124
}

program-libs/ctoken-types/src/state/extensions/token_metadata.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use light_hasher::{
44
Poseidon, Sha256,
55
};
66
use light_zero_copy::{ZeroCopy, ZeroCopyMut};
7+
use pinocchio::msg;
78

89
use crate::{AnchorDeserialize, AnchorSerialize};
910

@@ -55,8 +56,14 @@ pub struct TokenMetadata {
5556
impl TokenMetadata {
5657
pub fn hash(&self) -> Result<[u8; 32], HasherError> {
5758
match Version::try_from(self.version)? {
58-
Version::Poseidon => <Self as DataHasher>::hash::<Poseidon>(self),
59-
Version::Sha256 => <Self as DataHasher>::hash::<Sha256>(self),
59+
Version::Poseidon => {
60+
msg!("poseidon");
61+
<Self as DataHasher>::hash::<Poseidon>(self)
62+
}
63+
Version::Sha256 => {
64+
msg!("sha256");
65+
<Self as DataHasher>::hash::<Sha256>(self)
66+
}
6067
_ => unimplemented!("TokenMetadata hash version not supported {}", self.version),
6168
// Version::Keccak256 => <Self as DataHasher>::hash::<Keccak>(self),
6269
// Version::Sha256Flat => self.sha_flat(),

program-tests/compressed-token-test/tests/mint.rs

Lines changed: 184 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,6 @@ async fn test_create_compressed_mint() {
299299

300300
// 6. Compress SPL tokens to compressed tokens
301301
// Test compressing tokens to a new account
302-
println!("Testing compression of SPL tokens to compressed tokens...");
303302

304303
let compress_recipient = Keypair::new();
305304
let compress_amount = 100u64; // Compress 100 tokens
@@ -325,7 +324,7 @@ async fn test_create_compressed_mint() {
325324
)
326325
.await
327326
.unwrap();
328-
327+
println!("Compress 0 in 1 out");
329328
// Execute compression
330329
rpc.create_and_send_transaction(
331330
&[compress_instruction],
@@ -369,7 +368,7 @@ async fn test_create_compressed_mint() {
369368
)
370369
.await
371370
.unwrap();
372-
371+
println!("Compress 0 in 1 out");
373372
rpc.create_and_send_transaction(
374373
&[transfer_compress_instruction],
375374
&payer.pubkey(),
@@ -405,7 +404,7 @@ async fn test_create_compressed_mint() {
405404
)
406405
.await
407406
.unwrap();
408-
407+
println!("Compress 0 in 1 out");
409408
rpc.create_and_send_transaction(
410409
&[compress_for_multi_instruction],
411410
&payer.pubkey(),
@@ -440,6 +439,7 @@ async fn test_create_compressed_mint() {
440439
spl_mint_pda,
441440
)
442441
.unwrap();
442+
443443
rpc.create_and_send_transaction(
444444
&[create_decompress_ata_instruction],
445445
&payer.pubkey(),
@@ -501,6 +501,11 @@ async fn test_create_compressed_mint() {
501501
.unwrap();
502502

503503
// Execute the combined instruction with multiple signers
504+
println!(
505+
"Transfer {} in 2 out, compress 0 in 1 out, decompress {} in 1 out",
506+
remaining_compressed_tokens.len(),
507+
compressed_tokens_for_compress.len()
508+
);
504509
rpc.create_and_send_transaction(
505510
&[transfer2_instruction],
506511
&payer.pubkey(),
@@ -529,9 +534,7 @@ async fn test_create_compressed_mint() {
529534
/// 3. mint tokens with compressed mint
530535
#[tokio::test]
531536
#[serial]
532-
async fn test_create_compressed_mint_with_token_metadata() {
533-
use light_compressed_account::Pubkey as LightPubkey;
534-
537+
async fn test_create_compressed_mint_with_token_metadata_poseidon() {
535538
let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2(false, None))
536539
.await
537540
.unwrap();
@@ -565,7 +568,7 @@ async fn test_create_compressed_mint_with_token_metadata() {
565568
];
566569

567570
let token_metadata = TokenMetadataInstructionData {
568-
update_authority: Some(LightPubkey::from(mint_authority.to_bytes())),
571+
update_authority: None,
569572
metadata: Metadata {
570573
name: b"Test Token".to_vec(),
571574
symbol: b"TEST".to_vec(),
@@ -635,9 +638,179 @@ async fn test_create_compressed_mint_with_token_metadata() {
635638
// 3. Mint to compressed
636639
{
637640
// Get pre-token pool account state for decompressed mint
638-
let (token_pool_pda, _) = light_compressed_token::instructions::create_token_pool::find_token_pool_pda_with_index(&spl_mint_pda, 0);
641+
let (token_pool_pda, _) =
642+
light_compressed_token::instructions::create_token_pool::find_token_pool_pda_with_index(
643+
&spl_mint_pda,
644+
0,
645+
);
646+
let pre_pool_data = rpc.get_account(token_pool_pda).await.unwrap().unwrap();
647+
let pre_token_pool_account =
648+
spl_token_2022::state::Account::unpack(&pre_pool_data.data).unwrap();
649+
650+
let mint_amount = 100_000u64; // Mint 100,000 tokens
651+
let recipient_keypair = Keypair::new();
652+
let recipient = recipient_keypair.pubkey();
653+
654+
// Use our mint_to_compressed action helper (automatically handles decompressed mint config)
655+
mint_to_compressed(
656+
&mut rpc,
657+
spl_mint_pda,
658+
vec![Recipient {
659+
recipient: recipient.into(),
660+
amount: mint_amount,
661+
}],
662+
&mint_authority_keypair,
663+
&payer,
664+
None, // No lamports
665+
)
666+
.await
667+
.unwrap();
668+
669+
// Get pre-compressed mint and pre-spl mint for assertion
670+
let pre_compressed_mint_account = rpc
671+
.indexer()
672+
.unwrap()
673+
.get_compressed_account(compressed_mint_address, None)
674+
.await
675+
.unwrap()
676+
.value;
677+
let pre_compressed_mint: CompressedMint = BorshDeserialize::deserialize(
678+
&mut pre_compressed_mint_account.data.unwrap().data.as_slice(),
679+
)
680+
.unwrap();
681+
682+
let pre_spl_mint_data = rpc.get_account(spl_mint_pda).await.unwrap().unwrap();
683+
let pre_spl_mint = spl_token_2022::state::Mint::unpack(&pre_spl_mint_data.data).unwrap();
684+
685+
// Verify minted tokens using our assertion helper
686+
assert_mint_to_compressed_one(
687+
&mut rpc,
688+
spl_mint_pda,
689+
recipient,
690+
mint_amount,
691+
mint_amount, // Expected total supply after minting
692+
Some(pre_token_pool_account), // Pass pre-token pool account for decompressed mint validation
693+
pre_compressed_mint,
694+
Some(pre_spl_mint),
695+
)
696+
.await;
697+
}
698+
}
699+
700+
#[tokio::test]
701+
#[serial]
702+
async fn test_create_compressed_mint_with_token_metadata_sha() {
703+
let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2(false, None))
704+
.await
705+
.unwrap();
706+
let payer = rpc.get_payer().insecure_clone();
707+
708+
// Test parameters
709+
let decimals = 6u8;
710+
let mint_authority_keypair = Keypair::new();
711+
let mint_authority = mint_authority_keypair.pubkey();
712+
let freeze_authority = Pubkey::new_unique();
713+
let mint_seed = Keypair::new();
714+
715+
// Get address tree for creating compressed mint address
716+
let address_tree_pubkey = rpc.get_address_tree_v2().tree;
717+
// 1. Create compressed mint with metadata
718+
719+
// Create token metadata extension with additional metadata
720+
let additional_metadata = vec![
721+
AdditionalMetadata {
722+
key: b"website".to_vec(),
723+
value: b"https://mytoken.com".to_vec(),
724+
},
725+
AdditionalMetadata {
726+
key: b"category".to_vec(),
727+
value: b"DeFi".to_vec(),
728+
},
729+
AdditionalMetadata {
730+
key: b"creator".to_vec(),
731+
value: b"TokenMaker Inc.".to_vec(),
732+
},
733+
];
734+
735+
let token_metadata = TokenMetadataInstructionData {
736+
update_authority: None,
737+
metadata: Metadata {
738+
name: b"Test Token".to_vec(),
739+
symbol: b"TEST".to_vec(),
740+
uri: b"https://example.com/token.json".to_vec(),
741+
},
742+
additional_metadata: Some(additional_metadata.clone()),
743+
version: 1, // Sha hash version
744+
};
745+
light_token_client::actions::create_mint(
746+
&mut rpc,
747+
&mint_seed,
748+
decimals,
749+
mint_authority,
750+
Some(freeze_authority),
751+
Some(token_metadata.clone()),
752+
&payer,
753+
)
754+
.await
755+
.unwrap();
756+
let (spl_mint_pda, _) = Pubkey::find_program_address(
757+
&[COMPRESSED_MINT_SEED, mint_seed.pubkey().as_ref()],
758+
&light_compressed_token::ID,
759+
);
760+
let compressed_mint_address = light_compressed_token_sdk::instructions::create_compressed_mint::derive_compressed_mint_address(&mint_seed.pubkey(), &address_tree_pubkey);
761+
762+
// Verify the compressed mint was created
763+
let compressed_mint_account = rpc
764+
.indexer()
765+
.unwrap()
766+
.get_compressed_account(compressed_mint_address, None)
767+
.await
768+
.unwrap()
769+
.value;
770+
771+
assert_compressed_mint_account(
772+
&compressed_mint_account,
773+
compressed_mint_address,
774+
spl_mint_pda,
775+
decimals,
776+
mint_authority,
777+
freeze_authority,
778+
Some(token_metadata.clone()),
779+
);
780+
781+
// 2. Create SPL mint
782+
{
783+
// Get compressed mint data before creating SPL mint
784+
let pre_compressed_mint: CompressedMint = BorshDeserialize::deserialize(
785+
&mut compressed_mint_account.data.unwrap().data.as_slice(),
786+
)
787+
.unwrap();
788+
789+
// Use our create_spl_mint action helper (automatically handles proofs, PDAs, and transaction)
790+
create_spl_mint(
791+
&mut rpc,
792+
compressed_mint_address,
793+
&mint_seed,
794+
&mint_authority_keypair,
795+
&payer,
796+
)
797+
.await
798+
.unwrap();
799+
800+
// Verify SPL mint was created using our assertion helper
801+
assert_spl_mint(&mut rpc, mint_seed.pubkey(), &pre_compressed_mint).await;
802+
}
803+
// 3. Mint to compressed
804+
{
805+
// Get pre-token pool account state for decompressed mint
806+
let (token_pool_pda, _) =
807+
light_compressed_token::instructions::create_token_pool::find_token_pool_pda_with_index(
808+
&spl_mint_pda,
809+
0,
810+
);
639811
let pre_pool_data = rpc.get_account(token_pool_pda).await.unwrap().unwrap();
640-
let pre_token_pool_account = spl_token_2022::state::Account::unpack(&pre_pool_data.data).unwrap();
812+
let pre_token_pool_account =
813+
spl_token_2022::state::Account::unpack(&pre_pool_data.data).unwrap();
641814

642815
let mint_amount = 100_000u64; // Mint 100,000 tokens
643816
let recipient_keypair = Keypair::new();
@@ -680,7 +853,7 @@ async fn test_create_compressed_mint_with_token_metadata() {
680853
spl_mint_pda,
681854
recipient,
682855
mint_amount,
683-
mint_amount, // Expected total supply after minting
856+
mint_amount, // Expected total supply after minting
684857
Some(pre_token_pool_account), // Pass pre-token pool account for decompressed mint validation
685858
pre_compressed_mint,
686859
Some(pre_spl_mint),

program-tests/utils/src/mint_assert.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use light_ctoken_types::{
33
instructions::extensions::TokenMetadataInstructionData,
44
state::{CompressedMint, ExtensionStruct},
55
};
6+
use light_hasher::Poseidon;
67
use solana_sdk::pubkey::Pubkey;
78

89
#[track_caller]
@@ -56,9 +57,15 @@ pub fn assert_compressed_mint_account(
5657
);
5758

5859
// Deserialize and verify the CompressedMint struct matches expected
59-
let actual_compressed_mint: CompressedMint =
60+
let compressed_mint: CompressedMint =
6061
BorshDeserialize::deserialize(&mut compressed_account_data.data.as_slice()).unwrap();
61-
62-
assert_eq!(actual_compressed_mint, expected_compressed_mint);
62+
println!("Compressed Mint: {:?}", compressed_mint);
63+
assert_eq!(compressed_mint, expected_compressed_mint);
64+
if let Some(extensions) = compressed_mint.extensions {
65+
println!(
66+
"Compressed Mint extension hash: {:?}",
67+
extensions[0].hash::<Poseidon>()
68+
);
69+
}
6370
expected_compressed_mint
6471
}

0 commit comments

Comments
 (0)