diff --git a/Cargo.lock b/Cargo.lock index 3eef462e0d1..e0abc9b4a29 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7378,7 +7378,7 @@ dependencies = [ [[package]] name = "iota-rust-sdk" version = "0.0.0" -source = "git+https://github.com/iotaledger/iota-rust-sdk.git?rev=cd97687c9316045351d7db8b389ea381a5c335e1#cd97687c9316045351d7db8b389ea381a5c335e1" +source = "git+https://github.com/iotaledger/iota-rust-sdk.git?rev=93580286f3714248398d17e4481daf608508856a#93580286f3714248398d17e4481daf608508856a" dependencies = [ "base64ct", "bcs", diff --git a/Cargo.toml b/Cargo.toml index 8399292e2ac..933b0565e5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -435,7 +435,7 @@ iota-rosetta = { path = "crates/iota-rosetta" } iota-rpc-loadgen = { path = "crates/iota-rpc-loadgen" } iota-sdk = { path = "crates/iota-sdk" } # core-types with json format for REST API -iota-sdk2 = { package = "iota-rust-sdk", git = "https://github.com/iotaledger/iota-rust-sdk.git", rev = "cd97687c9316045351d7db8b389ea381a5c335e1", features = ["hash", "serde", "schemars"] } +iota-sdk2 = { package = "iota-rust-sdk", git = "https://github.com/iotaledger/iota-rust-sdk.git", rev = "93580286f3714248398d17e4481daf608508856a", features = ["hash", "serde", "schemars"] } iota-simulator = { path = "crates/iota-simulator" } iota-snapshot = { path = "crates/iota-snapshot" } iota-source-validation = { path = "crates/iota-source-validation" } diff --git a/crates/iota-core/benches/batch_verification_bench.rs b/crates/iota-core/benches/batch_verification_bench.rs index 402a050b851..e72c3d01a38 100644 --- a/crates/iota-core/benches/batch_verification_bench.rs +++ b/crates/iota-core/benches/batch_verification_bench.rs @@ -84,6 +84,7 @@ fn async_verifier_bench(c: &mut Criterion) { true, // accept_zklogin_in_multisig true, // accept_passkey_in_multisig Some(30), + true, )); b.iter(|| { diff --git a/crates/iota-core/src/authority/authority_per_epoch_store.rs b/crates/iota-core/src/authority/authority_per_epoch_store.rs index 4347f6bef44..855b620473f 100644 --- a/crates/iota-core/src/authority/authority_per_epoch_store.rs +++ b/crates/iota-core/src/authority/authority_per_epoch_store.rs @@ -937,6 +937,7 @@ impl AuthorityPerEpochStore { protocol_config.accept_zklogin_in_multisig(), protocol_config.accept_passkey_in_multisig(), protocol_config.zklogin_max_epoch_upper_bound_delta(), + protocol_config.additional_multisig_checks(), ); let authenticator_state_exists = epoch_start_configuration diff --git a/crates/iota-core/src/signature_verifier.rs b/crates/iota-core/src/signature_verifier.rs index 13c22b1e07f..7a38c646049 100644 --- a/crates/iota-core/src/signature_verifier.rs +++ b/crates/iota-core/src/signature_verifier.rs @@ -126,6 +126,8 @@ struct ZkLoginParams { pub accept_passkey_in_multisig: bool, /// Value that sets the upper bound for max_epoch in zkLogin signature. pub zklogin_max_epoch_upper_bound_delta: Option, + /// Flag to determine whether additional multisig checks are performed. + pub additional_multisig_checks: bool, } impl SignatureVerifier { @@ -137,6 +139,7 @@ impl SignatureVerifier { accept_zklogin_in_multisig: bool, accept_passkey_in_multisig: bool, zklogin_max_epoch_upper_bound_delta: Option, + additional_multisig_checks: bool, ) -> Self { Self { committee, @@ -163,6 +166,7 @@ impl SignatureVerifier { accept_zklogin_in_multisig, accept_passkey_in_multisig, zklogin_max_epoch_upper_bound_delta, + additional_multisig_checks, }, } } @@ -174,6 +178,7 @@ impl SignatureVerifier { accept_zklogin_in_multisig: bool, accept_passkey_in_multisig: bool, zklogin_max_epoch_upper_bound_delta: Option, + additional_multisig_checks: bool, ) -> Self { Self::new_with_batch_size( committee, @@ -183,6 +188,7 @@ impl SignatureVerifier { accept_zklogin_in_multisig, accept_passkey_in_multisig, zklogin_max_epoch_upper_bound_delta, + additional_multisig_checks, ) } @@ -390,6 +396,7 @@ impl SignatureVerifier { self.zk_login_params.accept_zklogin_in_multisig, self.zk_login_params.accept_passkey_in_multisig, self.zk_login_params.zklogin_max_epoch_upper_bound_delta, + self.zk_login_params.additional_multisig_checks, ); verify_sender_signed_data_message_signatures( signed_tx, diff --git a/crates/iota-core/src/unit_tests/batch_verification_tests.rs b/crates/iota-core/src/unit_tests/batch_verification_tests.rs index 12a0a04910a..58cdf52405e 100644 --- a/crates/iota-core/src/unit_tests/batch_verification_tests.rs +++ b/crates/iota-core/src/unit_tests/batch_verification_tests.rs @@ -150,6 +150,7 @@ async fn test_async_verifier() { true, // accept_zklogin_in_multisig true, // accept_passkey_in_multisig Some(30), + true, )); let tasks: Vec<_> = (0..32) diff --git a/crates/iota-core/src/unit_tests/move_package_tests.rs b/crates/iota-core/src/unit_tests/move_package_tests.rs index 7584e033fd6..9963c7bab07 100644 --- a/crates/iota-core/src/unit_tests/move_package_tests.rs +++ b/crates/iota-core/src/unit_tests/move_package_tests.rs @@ -14,7 +14,7 @@ use iota_types::{ move_package::{MovePackage, TypeOrigin, UpgradeInfo}, object::{Data, OBJECT_START_VERSION, Object}, }; -use move_binary_format::{file_format::CompiledModule, file_format_common::VERSION_MAX}; +use move_binary_format::file_format::CompiledModule; macro_rules! type_origin_table { {} => { Vec::new() }; @@ -47,17 +47,24 @@ macro_rules! linkage_table { #[test] fn test_new_initial() { let c_id1 = ObjectID::from_single_byte(0xc1); - let c_pkg = - MovePackage::new_initial(&build_test_modules("Cv1"), u64::MAX, VERSION_MAX, []).unwrap(); + let c_pkg = MovePackage::new_initial( + &build_test_modules("Cv1"), + &ProtocolConfig::get_for_max_version_UNSAFE(), + [], + ) + .unwrap(); let b_id1 = ObjectID::from_single_byte(0xb1); - let b_pkg = MovePackage::new_initial(&build_test_modules("B"), u64::MAX, VERSION_MAX, [&c_pkg]) - .unwrap(); + let b_pkg = MovePackage::new_initial( + &build_test_modules("B"), + &ProtocolConfig::get_for_max_version_UNSAFE(), + [&c_pkg], + ) + .unwrap(); let a_pkg = MovePackage::new_initial( &build_test_modules("A"), - u64::MAX, - VERSION_MAX, + &ProtocolConfig::get_for_max_version_UNSAFE(), [&b_pkg, &c_pkg], ) .unwrap(); @@ -106,8 +113,12 @@ fn test_new_initial() { #[test] fn test_upgraded() { let c_id1 = ObjectID::from_single_byte(0xc1); - let c_pkg = - MovePackage::new_initial(&build_test_modules("Cv1"), u64::MAX, VERSION_MAX, []).unwrap(); + let c_pkg = MovePackage::new_initial( + &build_test_modules("Cv1"), + &ProtocolConfig::get_for_max_version_UNSAFE(), + [], + ) + .unwrap(); let c_id2 = ObjectID::from_single_byte(0xc2); let c_new = c_pkg @@ -135,8 +146,12 @@ fn test_upgraded() { #[test] fn test_depending_on_upgrade() { let c_id1 = ObjectID::from_single_byte(0xc1); - let c_pkg = - MovePackage::new_initial(&build_test_modules("Cv1"), u64::MAX, VERSION_MAX, []).unwrap(); + let c_pkg = MovePackage::new_initial( + &build_test_modules("Cv1"), + &ProtocolConfig::get_for_max_version_UNSAFE(), + [], + ) + .unwrap(); let c_id2 = ObjectID::from_single_byte(0xc2); let c_new = c_pkg @@ -148,8 +163,12 @@ fn test_depending_on_upgrade() { ) .unwrap(); - let b_pkg = MovePackage::new_initial(&build_test_modules("B"), u64::MAX, VERSION_MAX, [&c_new]) - .unwrap(); + let b_pkg = MovePackage::new_initial( + &build_test_modules("B"), + &ProtocolConfig::get_for_max_version_UNSAFE(), + [&c_new], + ) + .unwrap(); assert_eq!( b_pkg.linkage_table(), @@ -162,8 +181,12 @@ fn test_depending_on_upgrade() { #[test] fn test_upgrade_upgrades_linkage() { let c_id1 = ObjectID::from_single_byte(0xc1); - let c_pkg = - MovePackage::new_initial(&build_test_modules("Cv1"), u64::MAX, VERSION_MAX, []).unwrap(); + let c_pkg = MovePackage::new_initial( + &build_test_modules("Cv1"), + &ProtocolConfig::get_for_max_version_UNSAFE(), + [], + ) + .unwrap(); let c_id2 = ObjectID::from_single_byte(0xc2); let c_new = c_pkg @@ -175,8 +198,12 @@ fn test_upgrade_upgrades_linkage() { ) .unwrap(); - let b_pkg = MovePackage::new_initial(&build_test_modules("B"), u64::MAX, VERSION_MAX, [&c_pkg]) - .unwrap(); + let b_pkg = MovePackage::new_initial( + &build_test_modules("B"), + &ProtocolConfig::get_for_max_version_UNSAFE(), + [&c_pkg], + ) + .unwrap(); let b_id2 = ObjectID::from_single_byte(0xb2); let b_new = b_pkg @@ -206,8 +233,12 @@ fn test_upgrade_upgrades_linkage() { #[test] fn test_upgrade_linkage_digest_to_new_dep() { let c_id1 = ObjectID::from_single_byte(0xc1); - let c_pkg = - MovePackage::new_initial(&build_test_modules("Cv1"), u64::MAX, VERSION_MAX, []).unwrap(); + let c_pkg = MovePackage::new_initial( + &build_test_modules("Cv1"), + &ProtocolConfig::get_for_max_version_UNSAFE(), + [], + ) + .unwrap(); let c_id2 = ObjectID::from_single_byte(0xc2); let c_new = c_pkg @@ -219,8 +250,12 @@ fn test_upgrade_linkage_digest_to_new_dep() { ) .unwrap(); - let b_pkg = MovePackage::new_initial(&build_test_modules("B"), u64::MAX, VERSION_MAX, [&c_pkg]) - .unwrap(); + let b_pkg = MovePackage::new_initial( + &build_test_modules("B"), + &ProtocolConfig::get_for_max_version_UNSAFE(), + [&c_pkg], + ) + .unwrap(); let b_id2 = ObjectID::from_single_byte(0xb2); let b_new = b_pkg @@ -262,8 +297,12 @@ fn test_upgrade_linkage_digest_to_new_dep() { #[test] fn test_upgrade_downngrades_linkage() { let c_id1 = ObjectID::from_single_byte(0xc1); - let c_pkg = - MovePackage::new_initial(&build_test_modules("Cv1"), u64::MAX, VERSION_MAX, []).unwrap(); + let c_pkg = MovePackage::new_initial( + &build_test_modules("Cv1"), + &ProtocolConfig::get_for_max_version_UNSAFE(), + [], + ) + .unwrap(); let c_id2 = ObjectID::from_single_byte(0xc2); let c_new = c_pkg @@ -275,8 +314,12 @@ fn test_upgrade_downngrades_linkage() { ) .unwrap(); - let b_pkg = MovePackage::new_initial(&build_test_modules("B"), u64::MAX, VERSION_MAX, [&c_new]) - .unwrap(); + let b_pkg = MovePackage::new_initial( + &build_test_modules("B"), + &ProtocolConfig::get_for_max_version_UNSAFE(), + [&c_new], + ) + .unwrap(); let b_id2 = ObjectID::from_single_byte(0xb2); let b_new = b_pkg @@ -306,8 +349,12 @@ fn test_upgrade_downngrades_linkage() { #[test] fn test_transitively_depending_on_upgrade() { let c_id1 = ObjectID::from_single_byte(0xc1); - let c_pkg = - MovePackage::new_initial(&build_test_modules("Cv1"), u64::MAX, VERSION_MAX, []).unwrap(); + let c_pkg = MovePackage::new_initial( + &build_test_modules("Cv1"), + &ProtocolConfig::get_for_max_version_UNSAFE(), + [], + ) + .unwrap(); let c_id2 = ObjectID::from_single_byte(0xc2); let c_new = c_pkg @@ -320,13 +367,16 @@ fn test_transitively_depending_on_upgrade() { .unwrap(); let b_id1 = ObjectID::from_single_byte(0xb1); - let b_pkg = MovePackage::new_initial(&build_test_modules("B"), u64::MAX, VERSION_MAX, [&c_pkg]) - .unwrap(); + let b_pkg = MovePackage::new_initial( + &build_test_modules("B"), + &ProtocolConfig::get_for_max_version_UNSAFE(), + [&c_pkg], + ) + .unwrap(); let a_pkg = MovePackage::new_initial( &build_test_modules("A"), - u64::MAX, - VERSION_MAX, + &ProtocolConfig::get_for_max_version_UNSAFE(), [&b_pkg, &c_new], ) .unwrap(); @@ -342,8 +392,12 @@ fn test_transitively_depending_on_upgrade() { #[test] fn package_digest_changes_with_dep_upgrades_and_in_sync_with_move_package_digest() { - let c_v1 = - MovePackage::new_initial(&build_test_modules("Cv1"), u64::MAX, VERSION_MAX, []).unwrap(); + let c_v1 = MovePackage::new_initial( + &build_test_modules("Cv1"), + &ProtocolConfig::get_for_max_version_UNSAFE(), + [], + ) + .unwrap(); let c_id2 = ObjectID::from_single_byte(0xc2); let c_v2 = c_v1 @@ -355,10 +409,18 @@ fn package_digest_changes_with_dep_upgrades_and_in_sync_with_move_package_digest ) .unwrap(); - let b_pkg = - MovePackage::new_initial(&build_test_modules("B"), u64::MAX, VERSION_MAX, [&c_v1]).unwrap(); - let b_v2 = MovePackage::new_initial(&build_test_modules("Bv2"), u64::MAX, VERSION_MAX, [&c_v2]) - .unwrap(); + let b_pkg = MovePackage::new_initial( + &build_test_modules("B"), + &ProtocolConfig::get_for_max_version_UNSAFE(), + [&c_v1], + ) + .unwrap(); + let b_v2 = MovePackage::new_initial( + &build_test_modules("Bv2"), + &ProtocolConfig::get_for_max_version_UNSAFE(), + [&c_v2], + ) + .unwrap(); let with_unpublished_deps = false; let local_v1 = build_test_package("B").get_package_digest(with_unpublished_deps); @@ -373,13 +435,17 @@ fn package_digest_changes_with_dep_upgrades_and_in_sync_with_move_package_digest #[test] #[should_panic] fn test_panic_on_empty_package() { - let _ = MovePackage::new_initial(&[], u64::MAX, VERSION_MAX, []); + let _ = MovePackage::new_initial(&[], &ProtocolConfig::get_for_max_version_UNSAFE(), []); } #[test] fn test_fail_on_missing_dep() { - let err = - MovePackage::new_initial(&build_test_modules("B"), u64::MAX, VERSION_MAX, []).unwrap_err(); + let err = MovePackage::new_initial( + &build_test_modules("B"), + &ProtocolConfig::get_for_max_version_UNSAFE(), + [], + ) + .unwrap_err(); assert_eq!( err.kind(), @@ -389,14 +455,26 @@ fn test_fail_on_missing_dep() { #[test] fn test_fail_on_missing_transitive_dep() { - let c_pkg = - MovePackage::new_initial(&build_test_modules("Cv1"), u64::MAX, VERSION_MAX, []).unwrap(); + let c_pkg = MovePackage::new_initial( + &build_test_modules("Cv1"), + &ProtocolConfig::get_for_max_version_UNSAFE(), + [], + ) + .unwrap(); - let b_pkg = MovePackage::new_initial(&build_test_modules("B"), u64::MAX, VERSION_MAX, [&c_pkg]) - .unwrap(); + let b_pkg = MovePackage::new_initial( + &build_test_modules("B"), + &ProtocolConfig::get_for_max_version_UNSAFE(), + [&c_pkg], + ) + .unwrap(); - let err = MovePackage::new_initial(&build_test_modules("A"), u64::MAX, VERSION_MAX, [&b_pkg]) - .unwrap_err(); + let err = MovePackage::new_initial( + &build_test_modules("A"), + &ProtocolConfig::get_for_max_version_UNSAFE(), + [&b_pkg], + ) + .unwrap_err(); assert_eq!( err.kind(), @@ -406,8 +484,12 @@ fn test_fail_on_missing_transitive_dep() { #[test] fn test_fail_on_transitive_dependency_downgrade() { - let c_pkg = - MovePackage::new_initial(&build_test_modules("Cv1"), u64::MAX, VERSION_MAX, []).unwrap(); + let c_pkg = MovePackage::new_initial( + &build_test_modules("Cv1"), + &ProtocolConfig::get_for_max_version_UNSAFE(), + [], + ) + .unwrap(); let c_id2 = ObjectID::from_single_byte(0xc2); let c_new = c_pkg @@ -419,13 +501,16 @@ fn test_fail_on_transitive_dependency_downgrade() { ) .unwrap(); - let b_pkg = MovePackage::new_initial(&build_test_modules("B"), u64::MAX, VERSION_MAX, [&c_new]) - .unwrap(); + let b_pkg = MovePackage::new_initial( + &build_test_modules("B"), + &ProtocolConfig::get_for_max_version_UNSAFE(), + [&c_new], + ) + .unwrap(); let err = MovePackage::new_initial( &build_test_modules("A"), - u64::MAX, - VERSION_MAX, + &ProtocolConfig::get_for_max_version_UNSAFE(), [&b_pkg, &c_pkg], ) .unwrap_err(); @@ -438,8 +523,12 @@ fn test_fail_on_transitive_dependency_downgrade() { #[test] fn test_fail_on_upgrade_missing_type() { - let c_pkg = - MovePackage::new_initial(&build_test_modules("Cv2"), u64::MAX, VERSION_MAX, []).unwrap(); + let c_pkg = MovePackage::new_initial( + &build_test_modules("Cv2"), + &ProtocolConfig::get_for_max_version_UNSAFE(), + [], + ) + .unwrap(); let c_id2 = ObjectID::from_single_byte(0xc2); let err = c_pkg diff --git a/crates/iota-core/src/unit_tests/unit_test_utils.rs b/crates/iota-core/src/unit_tests/unit_test_utils.rs index b0552dda4c4..d834140f9ab 100644 --- a/crates/iota-core/src/unit_tests/unit_test_utils.rs +++ b/crates/iota-core/src/unit_tests/unit_test_utils.rs @@ -41,8 +41,7 @@ async fn init_genesis( let pkg = Object::new_package( &modules, TransactionDigest::genesis_marker(), - config.max_move_package_size(), - config.move_binary_format_version(), + &config, &genesis_move_packages, ) .unwrap(); diff --git a/crates/iota-core/tests/staged/iota.yaml b/crates/iota-core/tests/staged/iota.yaml index 3c3a3dcbd92..489eb75e578 100644 --- a/crates/iota-core/tests/staged/iota.yaml +++ b/crates/iota-core/tests/staged/iota.yaml @@ -518,6 +518,8 @@ ExecutionFailureStatus: - congested_objects: TYPENAME: CongestedObjects - suggested_gas_price: U64 + 38: + InvalidLinkage: UNIT ExecutionStatus: ENUM: 0: diff --git a/crates/iota-e2e-tests/tests/multisig_tests.rs b/crates/iota-e2e-tests/tests/multisig_tests.rs index 07c11b4d761..8435ae368c3 100644 --- a/crates/iota-e2e-tests/tests/multisig_tests.rs +++ b/crates/iota-e2e-tests/tests/multisig_tests.rs @@ -368,9 +368,16 @@ async fn test_multisig_e2e() { .build_and_sign_multisig(multisig_pk.clone(), &[&keys[0], &keys[0]], 0b011); let res = context.execute_transaction_may_fail(tx6).await; assert!( - res.unwrap_err() + res.as_ref() + .unwrap_err() + .to_string() + .contains("Invalid sig for pk") + ); + assert!( + res.as_ref() + .unwrap_err() .to_string() - .contains("Invalid ed25519 pk bytes") + .contains("error=signature/pubkey type mismatch") ); // 7. mismatch pks in sig with multisig address fails to execute. @@ -702,9 +709,16 @@ async fn test_multisig_with_zklogin_scenarios() { let tx_11 = Transaction::from_generic_sig_data(tx_data.clone(), vec![multisig]); let res = context.execute_transaction_may_fail(tx_11).await; assert!( - res.unwrap_err() + res.as_ref() + .unwrap_err() + .to_string() + .contains("Invalid sig for pk") + ); + assert!( + res.as_ref() + .unwrap_err() .to_string() - .contains("Invalid ed25519 pk bytes") + .contains("error=signature/pubkey type mismatch") ); // 10. invalid bitmap b10000 when the max bitmap for 4 pks is b1111, fails to diff --git a/crates/iota-e2e-tests/tests/protocol_version_tests.rs b/crates/iota-e2e-tests/tests/protocol_version_tests.rs index d70fd78b3c9..274cc758854 100644 --- a/crates/iota-e2e-tests/tests/protocol_version_tests.rs +++ b/crates/iota-e2e-tests/tests/protocol_version_tests.rs @@ -65,6 +65,7 @@ mod sim_only_tests { use iota_json_rpc_types::{IotaTransactionBlockEffects, IotaTransactionBlockEffectsAPI}; use iota_macros::*; use iota_move_build::{BuildConfig, CompiledPackage}; + use iota_protocol_config::Chain; use iota_types::{ IOTA_AUTHENTICATOR_STATE_OBJECT_ID, IOTA_CLOCK_OBJECT_ID, IOTA_FRAMEWORK_PACKAGE_ID, IOTA_RANDOMNESS_STATE_OBJECT_ID, IOTA_SYSTEM_PACKAGE_ID, IOTA_SYSTEM_STATE_OBJECT_ID, @@ -86,7 +87,7 @@ mod sim_only_tests { TEST_ONLY_GAS_UNIT_FOR_GENERIC, TransactionData, TransactionKind, }, }; - use move_binary_format::{CompiledModule, file_format_common::VERSION_MAX}; + use move_binary_format::CompiledModule; use move_core_types::ident_str; use test_cluster::TestCluster; use tokio::time::{Duration, sleep}; @@ -994,8 +995,7 @@ mod sim_only_tests { Object::new_package( &iota_system_modules(fixture), TransactionDigest::genesis_marker(), - u64::MAX, - VERSION_MAX, + &ProtocolConfig::get_for_version(FINISH.into(), Chain::Unknown), &[ BuiltInFramework::get_package_by_id(&MOVE_STDLIB_PACKAGE_ID).genesis_move_package(), BuiltInFramework::get_package_by_id(&IOTA_FRAMEWORK_PACKAGE_ID) diff --git a/crates/iota-graphql-rpc/src/types/zklogin_verify_signature.rs b/crates/iota-graphql-rpc/src/types/zklogin_verify_signature.rs index e22bd7c8560..93c46fe1a89 100644 --- a/crates/iota-graphql-rpc/src/types/zklogin_verify_signature.rs +++ b/crates/iota-graphql-rpc/src/types/zklogin_verify_signature.rs @@ -121,8 +121,14 @@ pub(crate) async fn verify_zklogin_signature( } } } - let verify_params = - VerifyParams::new(oidc_provider_jwks, zklogin_env_native, true, true, Some(30)); + let verify_params = VerifyParams::new( + oidc_provider_jwks, + zklogin_env_native, + true, + true, + Some(30), + true, + ); let bytes = bytes.0; match intent_scope { diff --git a/crates/iota-open-rpc/spec/openrpc.json b/crates/iota-open-rpc/spec/openrpc.json index 3399aeadd21..ddeb63bbac6 100644 --- a/crates/iota-open-rpc/spec/openrpc.json +++ b/crates/iota-open-rpc/spec/openrpc.json @@ -1306,6 +1306,7 @@ "featureFlags": { "accept_passkey_in_multisig": false, "accept_zklogin_in_multisig": false, + "additional_multisig_checks": false, "congestion_control_gas_price_feedback_mechanism": false, "congestion_control_min_free_execution_slot": false, "consensus_batched_block_sync": false, @@ -1316,6 +1317,7 @@ "consensus_smart_ancestor_selection": false, "consensus_zstd_compression": false, "convert_type_argument_error": true, + "dependency_linkage_error": false, "disable_invariant_violation_check_in_swap_loc": true, "disallow_new_modules_in_deps_only_packages": true, "enable_group_ops_native_function_msm": true, diff --git a/crates/iota-protocol-config/src/lib.rs b/crates/iota-protocol-config/src/lib.rs index 68cabf3943e..cd61de33ddf 100644 --- a/crates/iota-protocol-config/src/lib.rs +++ b/crates/iota-protocol-config/src/lib.rs @@ -68,6 +68,8 @@ pub const MAX_PROTOCOL_VERSION: u64 = 10; // Enable the gas price feedback mechanism in devnet. // Enable Identifier input validation. // Removes unnecessary child object mutations +// Add additional signature checks +// Add additional linkage checks #[derive(Copy, Clone, Debug, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] pub struct ProtocolVersion(u64); @@ -305,6 +307,14 @@ struct FeatureFlags { // mutations #[serde(skip_serializing_if = "is_false")] minimize_child_object_mutations: bool, + + // If true enable additional linkage checks. + #[serde(skip_serializing_if = "is_false")] + dependency_linkage_error: bool, + + // If true enable additional multisig checks. + #[serde(skip_serializing_if = "is_false")] + additional_multisig_checks: bool, } fn is_true(b: &bool) -> bool { @@ -1296,6 +1306,14 @@ impl ProtocolConfig { pub fn minimize_child_object_mutations(&self) -> bool { self.feature_flags.minimize_child_object_mutations } + + pub fn dependency_linkage_error(&self) -> bool { + self.feature_flags.dependency_linkage_error + } + + pub fn additional_multisig_checks(&self) -> bool { + self.feature_flags.additional_multisig_checks + } } #[cfg(not(msim))] @@ -2092,6 +2110,8 @@ impl ProtocolConfig { } cfg.feature_flags.validate_identifier_inputs = true; + cfg.feature_flags.dependency_linkage_error = true; + cfg.feature_flags.additional_multisig_checks = true; } // Use this template when making changes: // diff --git a/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Mainnet_version_10.snap b/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Mainnet_version_10.snap index e642d83211b..7dfaaccbcd4 100644 --- a/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Mainnet_version_10.snap +++ b/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Mainnet_version_10.snap @@ -21,6 +21,8 @@ feature_flags: congestion_control_min_free_execution_slot: true validate_identifier_inputs: true minimize_child_object_mutations: true + dependency_linkage_error: true + additional_multisig_checks: true max_tx_size_bytes: 131072 max_input_objects: 2048 max_size_written_objects: 5000000 diff --git a/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Testnet_version_10.snap b/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Testnet_version_10.snap index f6f808e2643..36b2b15b850 100644 --- a/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Testnet_version_10.snap +++ b/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Testnet_version_10.snap @@ -22,6 +22,8 @@ feature_flags: consensus_batched_block_sync: true validate_identifier_inputs: true minimize_child_object_mutations: true + dependency_linkage_error: true + additional_multisig_checks: true max_tx_size_bytes: 131072 max_input_objects: 2048 max_size_written_objects: 5000000 diff --git a/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__version_10.snap b/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__version_10.snap index d2ddfea9cf1..c9bc31d3d78 100644 --- a/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__version_10.snap +++ b/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__version_10.snap @@ -29,6 +29,8 @@ feature_flags: congestion_control_gas_price_feedback_mechanism: true validate_identifier_inputs: true minimize_child_object_mutations: true + dependency_linkage_error: true + additional_multisig_checks: true max_tx_size_bytes: 131072 max_input_objects: 2048 max_size_written_objects: 5000000 diff --git a/crates/iota-rest-api/openapi/openapi.json b/crates/iota-rest-api/openapi/openapi.json index 1836e1a933b..69b30d9e57a 100644 --- a/crates/iota-rest-api/openapi/openapi.json +++ b/crates/iota-rest-api/openapi/openapi.json @@ -3152,6 +3152,21 @@ ] } } + }, + { + "description": "A valid linkage was unable to be determined for the transaction or one of its commands.", + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string", + "enum": [ + "invalid_linkage" + ] + } + } } ] }, diff --git a/crates/iota-types/src/execution_status.rs b/crates/iota-types/src/execution_status.rs index 05fe98c66a0..b4c456c312c 100644 --- a/crates/iota-types/src/execution_status.rs +++ b/crates/iota-types/src/execution_status.rs @@ -220,6 +220,9 @@ pub enum ExecutionFailureStatus { congested_objects: CongestedObjects, suggested_gas_price: u64, }, + + #[error("A valid linkage was unable to be determined for the transaction")] + InvalidLinkage, // NOTE: if you want to add a new enum, // please add it at the end for Rust SDK backward compatibility. } diff --git a/crates/iota-types/src/iota_sdk_types_conversions.rs b/crates/iota-types/src/iota_sdk_types_conversions.rs index c83ac032693..afca8ad6652 100644 --- a/crates/iota-types/src/iota_sdk_types_conversions.rs +++ b/crates/iota-types/src/iota_sdk_types_conversions.rs @@ -1243,6 +1243,7 @@ impl From for ExecutionError { congested_objects: congested_objects.0.into_iter().map(Into::into).collect(), suggested_gas_price, }, + ExecutionFailureStatus::InvalidLinkage => Self::InvalidLinkage, } } } @@ -1446,6 +1447,7 @@ impl From for crate::execution_status::ExecutionFailureStatus { ), suggested_gas_price, }, + ExecutionError::InvalidLinkage => Self::InvalidLinkage, } } } diff --git a/crates/iota-types/src/move_package.rs b/crates/iota-types/src/move_package.rs index 03f3ef23b31..d50d4f32c99 100644 --- a/crates/iota-types/src/move_package.rs +++ b/crates/iota-types/src/move_package.rs @@ -244,8 +244,7 @@ impl MovePackage { /// origin and linkage tables. pub fn new_initial<'p>( modules: &[CompiledModule], - max_move_package_size: u64, - move_binary_format_version: u32, + protocol_config: &ProtocolConfig, transitive_dependencies: impl IntoIterator, ) -> Result { let module = modules @@ -259,8 +258,7 @@ impl MovePackage { runtime_id, OBJECT_START_VERSION, modules, - max_move_package_size, - move_binary_format_version, + protocol_config, type_origin_table, transitive_dependencies, ) @@ -287,8 +285,7 @@ impl MovePackage { runtime_id, new_version, modules, - protocol_config.max_move_package_size(), - protocol_config.move_binary_format_version(), + protocol_config, type_origin_table, transitive_dependencies, ) @@ -350,8 +347,7 @@ impl MovePackage { self_id: ObjectID, version: SequenceNumber, modules: &[CompiledModule], - max_move_package_size: u64, - move_binary_format_version: u32, + protocol_config: &ProtocolConfig, type_origin_table: Vec, transitive_dependencies: impl IntoIterator, ) -> Result { @@ -369,7 +365,7 @@ impl MovePackage { ); let mut bytes = Vec::new(); - let version = if move_binary_format_version > VERSION_6 { + let version = if protocol_config.move_binary_format_version() > VERSION_6 { module.version } else { VERSION_6 @@ -379,12 +375,16 @@ impl MovePackage { } immediate_dependencies.remove(&self_id); - let linkage_table = build_linkage_table(immediate_dependencies, transitive_dependencies)?; + let linkage_table = build_linkage_table( + immediate_dependencies, + transitive_dependencies, + protocol_config, + )?; Self::new( storage_id, version, module_map, - max_move_package_size, + protocol_config.max_move_package_size(), type_origin_table, linkage_table, ) @@ -621,6 +621,7 @@ where fn build_linkage_table<'p>( mut immediate_dependencies: BTreeSet, transitive_dependencies: impl IntoIterator, + protocol_config: &ProtocolConfig, ) -> Result, ExecutionError> { let mut linkage_table = BTreeMap::new(); let mut dep_linkage_tables = vec![]; @@ -631,19 +632,36 @@ fn build_linkage_table<'p>( // Move binary version during deserialization is OK let original_id = transitive_dep.original_package_id(); - if immediate_dependencies.remove(&original_id) { - // Found an immediate dependency, mark it as seen, and stash a reference to its - // linkage table to check later. + let imm_dep = immediate_dependencies.remove(&original_id); + + if protocol_config.dependency_linkage_error() { dep_linkage_tables.push(&transitive_dep.linkage_table); - } - linkage_table.insert( - original_id, - UpgradeInfo { - upgraded_id: transitive_dep.id, - upgraded_version: transitive_dep.version, - }, - ); + let existing = linkage_table.insert( + original_id, + UpgradeInfo { + upgraded_id: transitive_dep.id, + upgraded_version: transitive_dep.version, + }, + ); + + if existing.is_some() { + return Err(ExecutionErrorKind::InvalidLinkage.into()); + } + } else { + if imm_dep { + // Found an immediate dependency, mark it as seen, and stash a reference to its + // linkage table to check later. + dep_linkage_tables.push(&transitive_dep.linkage_table); + } + linkage_table.insert( + original_id, + UpgradeInfo { + upgraded_id: transitive_dep.id, + upgraded_version: transitive_dep.version, + }, + ); + } } // (1) Every dependency is represented in the transitive dependencies if !immediate_dependencies.is_empty() { diff --git a/crates/iota-types/src/multisig.rs b/crates/iota-types/src/multisig.rs index 4f706642b90..f4820a9c014 100644 --- a/crates/iota-types/src/multisig.rs +++ b/crates/iota-types/src/multisig.rs @@ -147,6 +147,17 @@ impl AuthenticatorTrait for MultiSig { })?; let res = match sig { CompressedSignature::Ed25519(s) => { + if verify_params.additional_multisig_checks + && !matches!(subsig_pubkey.scheme(), SignatureScheme::ED25519) + { + return Err(IotaError::InvalidSignature { + error: format!( + "Invalid sig for pk={} address={:?} error=signature/pubkey type mismatch", + subsig_pubkey.encode_base64(), + IotaAddress::from(subsig_pubkey) + ), + }); + } let pk = Ed25519PublicKey::from_bytes(subsig_pubkey.as_ref()).map_err(|_| { IotaError::InvalidSignature { @@ -161,6 +172,17 @@ impl AuthenticatorTrait for MultiSig { ) } CompressedSignature::Secp256k1(s) => { + if verify_params.additional_multisig_checks + && !matches!(subsig_pubkey.scheme(), SignatureScheme::Secp256k1) + { + return Err(IotaError::InvalidSignature { + error: format!( + "Invalid sig for pk={} address={:?} error=signature/pubkey type mismatch", + subsig_pubkey.encode_base64(), + IotaAddress::from(subsig_pubkey) + ), + }); + } let pk = Secp256k1PublicKey::from_bytes(subsig_pubkey.as_ref()).map_err(|_| { IotaError::InvalidSignature { @@ -175,6 +197,17 @@ impl AuthenticatorTrait for MultiSig { ) } CompressedSignature::Secp256r1(s) => { + if verify_params.additional_multisig_checks + && !matches!(subsig_pubkey.scheme(), SignatureScheme::Secp256r1) + { + return Err(IotaError::InvalidSignature { + error: format!( + "Invalid sig for pk={} address={:?} error=signature/pubkey type mismatch", + subsig_pubkey.encode_base64(), + IotaAddress::from(subsig_pubkey) + ), + }); + } let pk = Secp256r1PublicKey::from_bytes(subsig_pubkey.as_ref()).map_err(|_| { IotaError::InvalidSignature { @@ -189,6 +222,20 @@ impl AuthenticatorTrait for MultiSig { ) } CompressedSignature::ZkLogin(z) => { + if verify_params.additional_multisig_checks + && !matches!( + subsig_pubkey.scheme(), + SignatureScheme::ZkLoginAuthenticator + ) + { + return Err(IotaError::InvalidSignature { + error: format!( + "Invalid sig for pk={} address={:?} error=signature/pubkey type mismatch", + subsig_pubkey.encode_base64(), + IotaAddress::from(subsig_pubkey) + ), + }); + } let authenticator = ZkLoginAuthenticator::from_bytes(&z.0).map_err(|_| { IotaError::InvalidSignature { error: "Invalid zklogin authenticator bytes".to_string(), diff --git a/crates/iota-types/src/object.rs b/crates/iota-types/src/object.rs index 5b59cf6f0e9..105f0be8b28 100644 --- a/crates/iota-types/src/object.rs +++ b/crates/iota-types/src/object.rs @@ -625,15 +625,13 @@ impl Object { pub fn new_package<'p>( modules: &[CompiledModule], previous_transaction: TransactionDigest, - max_move_package_size: u64, - move_binary_format_version: u32, + protocol_config: &ProtocolConfig, dependencies: impl IntoIterator, ) -> Result { Ok(Self::new_package_from_data( Data::Package(MovePackage::new_initial( modules, - max_move_package_size, - move_binary_format_version, + protocol_config, dependencies, )?), previous_transaction, @@ -666,13 +664,7 @@ impl Object { ) -> Result { let dependencies: Vec<_> = dependencies.into_iter().collect(); let config = ProtocolConfig::get_for_max_version_UNSAFE(); - Self::new_package( - modules, - previous_transaction, - config.max_move_package_size(), - config.move_binary_format_version(), - &dependencies, - ) + Self::new_package(modules, previous_transaction, &config, &dependencies) } /// Create a system package which is not subject to size limits. Panics if diff --git a/crates/iota-types/src/signature.rs b/crates/iota-types/src/signature.rs index 981c4245544..da1bdae08b7 100644 --- a/crates/iota-types/src/signature.rs +++ b/crates/iota-types/src/signature.rs @@ -43,6 +43,7 @@ pub struct VerifyParams { pub accept_zklogin_in_multisig: bool, pub accept_passkey_in_multisig: bool, pub zklogin_max_epoch_upper_bound_delta: Option, + pub additional_multisig_checks: bool, } impl VerifyParams { @@ -52,6 +53,7 @@ impl VerifyParams { accept_zklogin_in_multisig: bool, accept_passkey_in_multisig: bool, zklogin_max_epoch_upper_bound_delta: Option, + additional_multisig_checks: bool, ) -> Self { Self { oidc_provider_jwks, @@ -59,6 +61,7 @@ impl VerifyParams { accept_zklogin_in_multisig, accept_passkey_in_multisig, zklogin_max_epoch_upper_bound_delta, + additional_multisig_checks, } } } diff --git a/crates/iota-types/src/unit_tests/base_types_tests.rs b/crates/iota-types/src/unit_tests/base_types_tests.rs index 54f19df33e1..52c8134dee4 100644 --- a/crates/iota-types/src/unit_tests/base_types_tests.rs +++ b/crates/iota-types/src/unit_tests/base_types_tests.rs @@ -370,8 +370,7 @@ fn test_move_package_size_for_gas_metering() { let package = Object::new_package( &[module], TransactionDigest::genesis_marker(), - config.max_move_package_size(), - config.move_binary_format_version(), + &config, &[], // empty dependencies for empty package (no modules) ) .unwrap(); diff --git a/crates/iota-types/src/unit_tests/multisig_tests.rs b/crates/iota-types/src/unit_tests/multisig_tests.rs index de8b0ccba53..804808eb65e 100644 --- a/crates/iota-types/src/unit_tests/multisig_tests.rs +++ b/crates/iota-types/src/unit_tests/multisig_tests.rs @@ -434,7 +434,7 @@ fn test_zklogin_in_multisig_works_with_both_addresses() { .into_iter() .collect(); - let aux_verify_data = VerifyParams::new(parsed, ZkLoginEnv::Test, true, true, Some(30)); + let aux_verify_data = VerifyParams::new(parsed, ZkLoginEnv::Test, true, true, Some(30), true); let res = multisig.verify_claims( intent_msg, multisig_address, diff --git a/crates/iota-types/src/unit_tests/zk_login_authenticator_test.rs b/crates/iota-types/src/unit_tests/zk_login_authenticator_test.rs index d6177ced58c..dffd043efa8 100644 --- a/crates/iota-types/src/unit_tests/zk_login_authenticator_test.rs +++ b/crates/iota-types/src/unit_tests/zk_login_authenticator_test.rs @@ -111,7 +111,7 @@ fn test_zklogin_sign_personal_message() { // Construct the required info to verify a zk login authenticator, jwks, // supported providers list and env (prod/test). - let aux_verify_data = VerifyParams::new(parsed, ZkLoginEnv::Test, true, true, Some(30)); + let aux_verify_data = VerifyParams::new(parsed, ZkLoginEnv::Test, true, true, Some(30), true); let res = authenticator.verify_authenticator( &intent_msg, user_address, diff --git a/crates/iota-types/tests/staged/exec_failure_status.yaml b/crates/iota-types/tests/staged/exec_failure_status.yaml index 8d1a713694e..20366f2676e 100644 --- a/crates/iota-types/tests/staged/exec_failure_status.yaml +++ b/crates/iota-types/tests/staged/exec_failure_status.yaml @@ -37,3 +37,4 @@ 35: CoinTypeGlobalPause 36: ExecutionCancelledDueToRandomnessUnavailable 37: ExecutionCancelledDueToSharedObjectCongestionV2 +38: InvalidLinkage diff --git a/crates/iota/src/keytool.rs b/crates/iota/src/keytool.rs index c28ba837959..57f7eaa1a02 100644 --- a/crates/iota/src/keytool.rs +++ b/crates/iota/src/keytool.rs @@ -1173,7 +1173,7 @@ impl KeyToolCommand { * ZkLoginEnv::Prod, _ => bail!("Invalid * network"), }; * let verify_params = - * VerifyParams::new(parsed, vec![], env, true, true, Some(2)); */ + * VerifyParams::new(parsed, vec![], env, true, true, Some(2), true); */ /* let (serialized, res) = match IntentScope::try_from(intent_scope) * .map_err(|_| anyhow!("Invalid scope"))? diff --git a/iota-execution/latest/iota-adapter/src/programmable_transactions/context.rs b/iota-execution/latest/iota-adapter/src/programmable_transactions/context.rs index 57e02825f3d..eb0a0cf3d16 100644 --- a/iota-execution/latest/iota-adapter/src/programmable_transactions/context.rs +++ b/iota-execution/latest/iota-adapter/src/programmable_transactions/context.rs @@ -539,12 +539,7 @@ mod checked { modules: &[CompiledModule], dependencies: impl IntoIterator, ) -> Result { - MovePackage::new_initial( - modules, - self.protocol_config.max_move_package_size(), - self.protocol_config.move_binary_format_version(), - dependencies, - ) + MovePackage::new_initial(modules, self.protocol_config, dependencies) } /// Create a package upgrade from `previous_package` with `new_modules`