// file: crates/ksp-raw-transaction-lib/unit_tests/canonical.rs // version: 2 fn network() -> std::option::Option { return match ksp_store_api::RawNetworkId::new("devnet") { std::result::Result::Ok(value) => std::option::Option::Some(value), std::result::Result::Err(_) => std::option::Option::None, }; } fn signature() -> std::option::Option { let result = crate::parse_raw_transaction_signature("1".repeat(64).as_str()); return match result { std::result::Result::Ok(value) => std::option::Option::Some(value), std::result::Result::Err(_) => std::option::Option::None, }; } fn material( block_time: std::option::Option, transaction_data: std::string::String, meta: crate::RawTransactionWireField, version: crate::RawTransactionWireField, transaction_index: crate::RawTransactionWireField, ) -> std::option::Option { let network = match network() { std::option::Option::Some(value) => value, std::option::Option::None => return std::option::Option::None, }; let signature = match signature() { std::option::Option::Some(value) => value, std::option::Option::None => return std::option::Option::None, }; return std::option::Option::Some(crate::RawTransactionMaterial::binary_base64( network, signature, 123, block_time, transaction_data, meta, version, transaction_index, )); } #[test] fn pre_002_canonical_raw_v1_golden_bytes_and_hash_are_exact() { let material = match material( std::option::Option::Some(1_700_000_000), "AQID".to_owned(), crate::RawTransactionWireField::Value(serde_json::json!({"z": 1, "a": {"y": true, "x": null}})), crate::RawTransactionWireField::Value(crate::RawTransactionVersion::Legacy), crate::RawTransactionWireField::Value(7_u32), ) { std::option::Option::Some(value) => value, std::option::Option::None => return, }; let transaction = crate::canonicalize_raw_transaction(material); assert!(transaction.is_ok()); let transaction = match transaction { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; let expected = b"{\"transaction\":[\"AQID\",\"base64\"],\"meta\":{\"a\":{\"x\":null,\"y\":true},\"z\":1},\"version\":\"legacy\",\"transactionIndex\":7}"; assert_eq!(transaction.payload().bytes(), expected); assert_eq!(transaction.payload().byte_len(), 112); assert_eq!(transaction.payload().format_id().as_str(), crate::RAW_TRANSACTION_FORMAT_ID); assert_eq!(transaction.payload().format_version(), crate::RAW_TRANSACTION_FORMAT_VERSION); assert_eq!( transaction.payload().content_hash().as_bytes(), &[ 34, 7, 146, 210, 177, 93, 38, 47, 218, 36, 44, 178, 32, 119, 78, 233, 221, 239, 254, 191, 4, 220, 250, 218, 188, 248, 239, 118, 169, 177, 167, 195 ] ); assert_eq!(transaction.slot(), 123); assert_eq!(transaction.block_time().map(|value| return value.unix_millis()), std::option::Option::Some(1_700_000_000_000)); assert_eq!(transaction.reference().network().as_str(), "devnet"); assert_eq!(transaction.reference().signature().as_bytes(), &[0_u8; 64]); return; } #[test] fn pre_002_wire_omitted_null_and_value_states_remain_distinct() { let omitted = match material( std::option::Option::None, "AQID".to_owned(), crate::RawTransactionWireField::Omitted, crate::RawTransactionWireField::Omitted, crate::RawTransactionWireField::Omitted, ) { std::option::Option::Some(value) => value, std::option::Option::None => return, }; let omitted = super::canonical_payload_bytes(&omitted); assert!(omitted.is_ok()); if let std::result::Result::Ok(omitted) = omitted { assert_eq!(omitted, b"{\"transaction\":[\"AQID\",\"base64\"]}"); } let nulls = match material( std::option::Option::None, "AQID".to_owned(), crate::RawTransactionWireField::Null, crate::RawTransactionWireField::Null, crate::RawTransactionWireField::Null, ) { std::option::Option::Some(value) => value, std::option::Option::None => return, }; let nulls = super::canonical_payload_bytes(&nulls); assert!(nulls.is_ok()); if let std::result::Result::Ok(nulls) = nulls { assert_eq!(nulls, b"{\"transaction\":[\"AQID\",\"base64\"],\"meta\":null,\"version\":null,\"transactionIndex\":null}"); } return; } #[test] fn pre_002_canonical_json_sorts_objects_preserves_arrays_and_escapes_strings() { let meta = serde_json::json!({ "z": [3, 2, 1], "quote": "a\"b\\c\n", "a": {"n": -12.5, "b": false, "a": true} }); let material = match material( std::option::Option::None, "A\"B".to_owned(), crate::RawTransactionWireField::Value(meta), crate::RawTransactionWireField::Value(crate::RawTransactionVersion::Number(0)), crate::RawTransactionWireField::Value(u32::MAX), ) { std::option::Option::Some(value) => value, std::option::Option::None => return, }; let bytes = super::canonical_payload_bytes(&material); assert!(bytes.is_ok()); if let std::result::Result::Ok(bytes) = bytes { let expected = b"{\"transaction\":[\"A\\\"B\",\"base64\"],\"meta\":{\"a\":{\"a\":true,\"b\":false,\"n\":-12.5},\"quote\":\"a\\\"b\\\\c\\n\",\"z\":[3,2,1]},\"version\":0,\"transactionIndex\":4294967295}"; assert_eq!(bytes, expected); } return; } #[test] fn pre_002_block_time_rejects_negative_overflow_and_store_maximum_overrun() { for block_time in [-1_i64, i64::MAX, 253_402_300_800_i64] { let material = match material( std::option::Option::Some(block_time), "AQID".to_owned(), crate::RawTransactionWireField::Omitted, crate::RawTransactionWireField::Omitted, crate::RawTransactionWireField::Omitted, ) { std::option::Option::Some(value) => value, std::option::Option::None => return, }; let result = crate::canonicalize_raw_transaction(material); assert!(result.is_err()); if let std::result::Result::Err(error) = result { assert_eq!(error.code(), crate::ERROR_CODE_RAW_TRANSACTION_MATERIAL_INVALID); } } let maximum = match material( std::option::Option::Some(253_402_300_799_i64), "AQID".to_owned(), crate::RawTransactionWireField::Omitted, crate::RawTransactionWireField::Omitted, crate::RawTransactionWireField::Omitted, ) { std::option::Option::Some(value) => value, std::option::Option::None => return, }; let maximum = crate::canonicalize_raw_transaction(maximum); assert!(maximum.is_ok()); if let std::result::Result::Ok(maximum) = maximum { assert_eq!(maximum.block_time().map(|value| return value.unix_millis()), std::option::Option::Some(253_402_300_799_000)); } return; } #[test] fn pre_002_payload_bound_is_enforced_by_common_before_store_construction() { let material = match material( std::option::Option::None, "A".repeat(ksp_store_api::MAX_RAW_PAYLOAD_BYTES), crate::RawTransactionWireField::Omitted, crate::RawTransactionWireField::Omitted, crate::RawTransactionWireField::Omitted, ) { std::option::Option::Some(value) => value, std::option::Option::None => return, }; let result = crate::canonicalize_raw_transaction(material); assert!(result.is_err()); if let std::result::Result::Err(error) = result { assert_eq!(error.code(), crate::ERROR_CODE_RAW_TRANSACTION_CANONICALIZATION_INVALID); } return; } #[test] fn pre_002_material_and_wire_debug_do_not_render_transaction_or_meta_material() { let secret_transaction = "SECRET_TRANSACTION_BODY"; let secret_meta = "SECRET_META_VALUE"; let material = match material( std::option::Option::None, secret_transaction.to_owned(), crate::RawTransactionWireField::Value(serde_json::json!({"secret": secret_meta})), crate::RawTransactionWireField::Value(crate::RawTransactionVersion::Legacy), crate::RawTransactionWireField::Value(1_u32), ) { std::option::Option::Some(value) => value, std::option::Option::None => return, }; let debug = std::format!("{material:?}"); assert!(!debug.contains(secret_transaction)); assert!(!debug.contains(secret_meta)); assert_eq!(std::format!("{:?}", crate::RawTransactionWireField::Value(secret_meta)), "Value(..)"); return; } #[test] fn pre_004_embedded_signature_material_preserves_block_fields_and_canonicalizes() { let network = match ksp_store_api::RawNetworkId::new("devnet") { std::result::Result::Ok(network) => network, std::result::Result::Err(_) => return, }; let material = crate::RawTransactionMaterial::binary_base64_with_embedded_signature( network, 430_000_123, std::option::Option::Some(1_787_072_400), "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", crate::RawTransactionWireField::Value(serde_json::json!({"err":null,"fee":5000})), crate::RawTransactionWireField::Value(crate::RawTransactionVersion::Legacy), crate::RawTransactionWireField::Value(0_u32), ); assert!(material.is_ok()); let transaction = match material.and_then(crate::canonicalize_raw_transaction) { std::result::Result::Ok(transaction) => transaction, std::result::Result::Err(_) => return, }; assert_eq!(transaction.reference().signature().as_bytes(), &[0_u8; 64]); assert_eq!(transaction.slot(), 430_000_123); assert_eq!(transaction.block_time().map(|value| return value.unix_millis()), std::option::Option::Some(1_787_072_400_000)); assert!(transaction.payload().bytes().starts_with(b"{\"transaction\":[\"AQAAAA")); assert!(transaction.payload().bytes().ends_with(b"\"version\":\"legacy\",\"transactionIndex\":0}")); return; }