Skip to content

Commit 58388e6

Browse files
committed
Add cpi event
1 parent fc74e71 commit 58388e6

File tree

448 files changed

+56011
-147
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

448 files changed

+56011
-147
lines changed

chains/solana/contracts/programs/ccip-common/src/lib.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,24 @@ pub enum CommonCcipError {
3737
InvalidTVMAddress,
3838
#[msg("Invalid Aptos address")]
3939
InvalidAptosAddress,
40+
#[msg("Invalid Sui address")]
41+
InvalidSuiAddress,
4042
}
4143

42-
// https://github.com/smartcontractkit/chainlink/blob/ff8a597fd9df653f8967427498eaa5a04b19febb/contracts/src/v0.8/ccip/libraries/Internal.sol#L276
44+
// bytes4(keccak256("CCIP ChainFamilySelector APTOS"));
45+
pub const CHAIN_FAMILY_SELECTOR_APTOS: u32 = 0xac77ffec;
46+
47+
// bytes4(keccak256("CCIP ChainFamilySelector EVM"));
4348
pub const CHAIN_FAMILY_SELECTOR_EVM: u32 = 0x2812d52c;
49+
50+
// bytes4(keccak256("CCIP ChainFamilySelector SUI"));
51+
pub const CHAIN_FAMILY_SELECTOR_SUI: u32 = 0xc4e05953;
52+
53+
// bytes4(keccak256("CCIP ChainFamilySelector SVM"));
4454
pub const CHAIN_FAMILY_SELECTOR_SVM: u32 = 0x1e10bdc4;
55+
56+
// byte4(keccak256("CCIP ChainFamilySelector TVM"));
4557
pub const CHAIN_FAMILY_SELECTOR_TVM: u32 = 0x647e2ba9;
46-
pub const CHAIN_FAMILY_SELECTOR_APTOS: u32 = 0xac77ffec;
4758

4859
// Duplicates the router ID to declare router accounts that must be visible from the common crate,
4960
// avoiding a circular dependency. This means this crate may only declare accounts that belong

chains/solana/contracts/programs/ccip-common/src/v1.rs

Lines changed: 248 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,26 @@ use crate::{
1111

1212
pub const MIN_TOKEN_POOL_ACCOUNTS: usize = 13; // see TokenAccounts struct for all required accounts
1313
const U160_MAX: U256 = U256::from_words(u32::MAX as u128, u128::MAX);
14-
const EVM_PRECOMPILE_SPACE: u32 = 1024;
1514
pub const V1_TOKEN_ADMIN_REGISTRY_SIZE: usize = 169; // for migration v1->v2 of the TokenAdminRegistry, which adds the `supports_auto_derivation` field.
1615

16+
/// 32-byte big-endian uint256 representation of 11 (0x0b)
17+
pub const APTOS_PRECOMPILE_SPACE: [u8; 32] = [
18+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
19+
0x0b,
20+
];
21+
22+
/// 32-byte big-endian uint256 representation of 1024 (0x0400)
23+
pub const EVM_PRECOMPILE_SPACE: [u8; 32] = [
24+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x04,
25+
0x00,
26+
];
27+
28+
/// 32-byte big-endian uint256 representation of 0xdee9
29+
pub const SUI_PRECOMPILE_SPACE: [u8; 32] = [
30+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xde,
31+
0xe9,
32+
];
33+
1734
pub struct TokenAccounts<'a> {
1835
pub user_token_account: &'a AccountInfo<'a>,
1936
pub token_billing_config: &'a AccountInfo<'a>,
@@ -387,23 +404,39 @@ pub mod token_admin_registry_writable {
387404
}
388405
}
389406

407+
/// Returns true iff `a >= b` when both are interpreted as big-endian uint256.
408+
fn ge_be_u256(a: &[u8; 32], b: &[u8; 32]) -> bool {
409+
// Big-endian uint256 compare == lexicographic compare
410+
for i in 0..32 {
411+
match a[i].cmp(&b[i]) {
412+
std::cmp::Ordering::Greater => return true,
413+
std::cmp::Ordering::Less => return false,
414+
std::cmp::Ordering::Equal => {} // continue
415+
}
416+
}
417+
true // equal
418+
}
419+
390420
// address validation helpers based on the chain family selector
391421
pub fn validate_evm_address(address: &[u8]) -> Result<()> {
392422
require_eq!(address.len(), 32, CommonCcipError::InvalidEVMAddress);
393423

424+
let addr_32: &[u8; 32] = address
425+
.try_into()
426+
.expect("slice length is guaranteed to be 32");
427+
428+
require!(
429+
ge_be_u256(addr_32, &EVM_PRECOMPILE_SPACE),
430+
CommonCcipError::InvalidEVMAddress
431+
);
432+
394433
let address: U256 = U256::from_be_bytes(
395434
address
396435
.try_into()
397436
.map_err(|_| CommonCcipError::InvalidEncoding)?,
398437
);
399438
require!(address <= U160_MAX, CommonCcipError::InvalidEVMAddress);
400-
if let Ok(small_address) = TryInto::<u32>::try_into(address) {
401-
require_gte!(
402-
small_address,
403-
EVM_PRECOMPILE_SPACE,
404-
CommonCcipError::InvalidEVMAddress
405-
)
406-
};
439+
407440
Ok(())
408441
}
409442

@@ -443,14 +476,32 @@ pub fn validate_tvm_address(address: &[u8]) -> Result<()> {
443476
pub fn validate_aptos_address(address: &[u8]) -> Result<()> {
444477
require_eq!(address.len(), 32, CommonCcipError::InvalidAptosAddress);
445478

479+
let addr_32: &[u8; 32] = address
480+
.try_into()
481+
.expect("slice length is guaranteed to be 32");
482+
446483
require!(
447-
address.iter().any(|b| *b != 0),
484+
ge_be_u256(addr_32, &APTOS_PRECOMPILE_SPACE),
448485
CommonCcipError::InvalidAptosAddress
449486
);
450487

451488
Ok(())
452489
}
453490

491+
pub fn validate_sui_address(address: &[u8]) -> Result<()> {
492+
require_eq!(address.len(), 32, CommonCcipError::InvalidSuiAddress);
493+
494+
let addr_32: &[u8; 32] = address
495+
.try_into()
496+
.expect("slice length is guaranteed to be 32");
497+
498+
require!(
499+
ge_be_u256(addr_32, &SUI_PRECOMPILE_SPACE),
500+
CommonCcipError::InvalidSuiAddress
501+
);
502+
503+
Ok(())
504+
}
454505
#[cfg(test)]
455506
mod tests {
456507
use super::*;
@@ -552,6 +603,24 @@ mod tests {
552603
assert!(result.is_err());
553604
}
554605

606+
// -----------------------------
607+
// Helpers for address tests
608+
// -----------------------------
609+
610+
fn u256_to_32_be(x: U256) -> [u8; 32] {
611+
x.to_be_bytes()
612+
}
613+
614+
fn be32_with_last_byte(v: u8) -> [u8; 32] {
615+
let mut a = [0u8; 32];
616+
a[31] = v;
617+
a
618+
}
619+
620+
// -----------------------------
621+
// TON / TVM address tests
622+
// -----------------------------
623+
555624
#[test]
556625
fn validate_tvm_address_accepts_len_36_nonzero_account_id() {
557626
// 36-byte TON address raw format:
@@ -584,9 +653,18 @@ mod tests {
584653
);
585654
}
586655

656+
// -----------------------------
657+
// Aptos address tests
658+
// -----------------------------
659+
587660
#[test]
588-
fn validate_aptos_address_accepts_len_32_nonzero() {
589-
let addr = [1u8; 32];
661+
fn validate_aptos_address_accepts_valid_ge_min() {
662+
// exactly min
663+
validate_aptos_address(&APTOS_PRECOMPILE_SPACE).unwrap();
664+
665+
// > min
666+
let mut addr = APTOS_PRECOMPILE_SPACE;
667+
addr[31] = 0x0c;
590668
validate_aptos_address(&addr).unwrap();
591669
}
592670

@@ -600,11 +678,169 @@ mod tests {
600678
}
601679

602680
#[test]
603-
fn validate_aptos_address_rejects_all_zero() {
681+
fn validate_aptos_address_rejects_zero_address() {
604682
let addr = [0u8; 32];
605683
assert_eq!(
606684
validate_aptos_address(&addr).unwrap_err(),
607685
CommonCcipError::InvalidAptosAddress.into()
608686
);
609687
}
688+
689+
#[test]
690+
fn validate_aptos_address_rejects_lower_than_min() {
691+
// 0x0a < 0x0b
692+
let addr = be32_with_last_byte(0x0a);
693+
assert_eq!(
694+
validate_aptos_address(&addr).unwrap_err(),
695+
CommonCcipError::InvalidAptosAddress.into()
696+
);
697+
}
698+
699+
// -----------------------------
700+
// Sui address tests
701+
// -----------------------------
702+
703+
#[test]
704+
fn validate_sui_address_accepts_valid_ge_min() {
705+
// exactly min
706+
validate_sui_address(&SUI_PRECOMPILE_SPACE).unwrap();
707+
708+
// > min (increase last byte, staying >=)
709+
let mut addr = SUI_PRECOMPILE_SPACE;
710+
addr[31] = 0xea;
711+
validate_sui_address(&addr).unwrap();
712+
}
713+
714+
#[test]
715+
fn validate_sui_address_rejects_wrong_len() {
716+
let addr = [1u8; 31];
717+
assert_eq!(
718+
validate_sui_address(&addr).unwrap_err(),
719+
CommonCcipError::InvalidSuiAddress.into()
720+
);
721+
}
722+
723+
#[test]
724+
fn validate_sui_address_rejects_zero_address() {
725+
let addr = [0u8; 32];
726+
assert_eq!(
727+
validate_sui_address(&addr).unwrap_err(),
728+
CommonCcipError::InvalidSuiAddress.into()
729+
);
730+
}
731+
732+
#[test]
733+
fn validate_sui_address_rejects_lower_than_min() {
734+
// Make something just below 0xdee9: 0xdee8
735+
let mut addr = SUI_PRECOMPILE_SPACE;
736+
addr[31] = 0xe8;
737+
assert_eq!(
738+
validate_sui_address(&addr).unwrap_err(),
739+
CommonCcipError::InvalidSuiAddress.into()
740+
);
741+
}
742+
743+
// -----------------------------
744+
// EVM address tests
745+
// -----------------------------
746+
747+
#[test]
748+
fn validate_evm_address_accepts_valid_ge_min_and_le_u160max() {
749+
// exactly min (1024)
750+
validate_evm_address(&EVM_PRECOMPILE_SPACE).unwrap();
751+
752+
// > min but still small
753+
let mut addr = EVM_PRECOMPILE_SPACE;
754+
addr[31] = 0x01; // 0x0401
755+
validate_evm_address(&addr).unwrap();
756+
757+
// a high-but-valid 160-bit address: U160_MAX itself
758+
let high = u256_to_32_be(U160_MAX);
759+
validate_evm_address(&high).unwrap();
760+
}
761+
762+
#[test]
763+
fn validate_evm_address_rejects_wrong_len() {
764+
let addr = [1u8; 31];
765+
assert_eq!(
766+
validate_evm_address(&addr).unwrap_err(),
767+
CommonCcipError::InvalidEVMAddress.into()
768+
);
769+
}
770+
771+
#[test]
772+
fn validate_evm_address_rejects_zero_address() {
773+
let addr = [0u8; 32];
774+
assert_eq!(
775+
validate_evm_address(&addr).unwrap_err(),
776+
CommonCcipError::InvalidEVMAddress.into()
777+
);
778+
}
779+
780+
#[test]
781+
fn validate_evm_address_rejects_lower_than_min() {
782+
// 1023 (0x03ff) is below 1024
783+
let addr = [
784+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
785+
0, 0x03, 0xff,
786+
];
787+
assert_eq!(
788+
validate_evm_address(&addr).unwrap_err(),
789+
CommonCcipError::InvalidEVMAddress.into()
790+
);
791+
792+
// also check something very small like 1
793+
let addr = be32_with_last_byte(1);
794+
assert_eq!(
795+
validate_evm_address(&addr).unwrap_err(),
796+
CommonCcipError::InvalidEVMAddress.into()
797+
);
798+
}
799+
800+
#[test]
801+
fn validate_evm_address_rejects_greater_than_u160max() {
802+
// U160_MAX + 1 should fail the <= U160_MAX check
803+
let too_big = u256_to_32_be(U160_MAX + U256::new(1));
804+
assert_eq!(
805+
validate_evm_address(&too_big).unwrap_err(),
806+
CommonCcipError::InvalidEVMAddress.into()
807+
);
808+
809+
// also fail for something with non-zero in the top 96 bits (definitely > u160 max)
810+
let mut definitely_too_big = [0u8; 32];
811+
definitely_too_big[0] = 1; // sets a high-order byte
812+
definitely_too_big[31] = 0x10; // keep non-zero
813+
assert_eq!(
814+
validate_evm_address(&definitely_too_big).unwrap_err(),
815+
CommonCcipError::InvalidEVMAddress.into()
816+
);
817+
}
818+
819+
// -----------------------------
820+
// SVM address tests (Solana)
821+
// -----------------------------
822+
823+
#[test]
824+
fn validate_svm_address_accepts_valid_pubkey_bytes() {
825+
let pk = Pubkey::new_unique();
826+
validate_svm_address(pk.as_ref(), true).unwrap();
827+
}
828+
829+
#[test]
830+
fn validate_svm_address_rejects_wrong_len() {
831+
let addr = [1u8; 31];
832+
assert_eq!(
833+
validate_svm_address(&addr, true).unwrap_err(),
834+
CommonCcipError::InvalidSVMAddress.into()
835+
);
836+
}
837+
838+
#[test]
839+
fn validate_svm_address_rejects_all_zero() {
840+
let addr = [0u8; 32];
841+
assert_eq!(
842+
validate_svm_address(&addr, true).unwrap_err(),
843+
CommonCcipError::InvalidSVMAddress.into()
844+
);
845+
}
610846
}

chains/solana/contracts/programs/ccip-router/src/context.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ pub struct CcipSend<'info> {
285285
bump,
286286
constraint = valid_version(dest_chain_state.version, MAX_CHAINSTATE_V) @ CcipRouterError::InvalidVersion,
287287
)]
288-
pub dest_chain_state: Box<Account<'info, DestChain>>,
288+
pub dest_chain_state: Account<'info, DestChain>,
289289

290290
/// CHECK this represents the PDA where the message counters are stored. As it may be initialized or not,
291291
/// and it may be in it's v1 or v2 form, it is an UncheckedAccount and the code handles all cases manually.

0 commit comments

Comments
 (0)