@@ -1111,6 +1111,7 @@ async fn test_two_move_authenticators_rejected_with_disabled_move_auth_for_spons
11111111 // Disable Move authentication for the sponsor.
11121112 let _guard = ProtocolConfig :: apply_overrides_for_testing ( |_, mut config| {
11131113 config. set_enable_move_authentication_for_sponsor_for_testing ( false ) ;
1114+ config. set_pre_consensus_sponsor_only_move_authentication_for_testing ( false ) ;
11141115 config
11151116 } ) ;
11161117
@@ -1172,6 +1173,7 @@ async fn test_sponsor_only_move_auth_rejected_with_disabled_move_auth_for_sponso
11721173 // Disable Move authentication for the sponsor.
11731174 let _guard = ProtocolConfig :: apply_overrides_for_testing ( |_, mut config| {
11741175 config. set_enable_move_authentication_for_sponsor_for_testing ( false ) ;
1176+ config. set_pre_consensus_sponsor_only_move_authentication_for_testing ( false ) ;
11751177 config
11761178 } ) ;
11771179
@@ -1347,6 +1349,199 @@ async fn test_aa_sender_and_aa_sponsor_rejected_when_sponsor_aa_fails_with_enabl
13471349 Ok ( ( ) )
13481350}
13491351
1352+ // ------------------------------------------------------------------
1353+ // --- pre_consensus_sponsor_only_move_authentication flag tests ---
1354+ // ------------------------------------------------------------------
1355+
1356+ /// With `pre_consensus_sponsor_only_move_authentication` enabled, only the
1357+ /// sponsor's MA runs pre-consensus for a sponsored TX. A sender MA signed over
1358+ /// the wrong digest is therefore accepted pre-consensus (the sponsor's
1359+ /// free-access MA passes) but fails post-consensus when the sender's MA is
1360+ /// finally executed and the signature doesn't match.
1361+ #[ sim_test]
1362+ async fn test_sponsored_tx_sender_aa_fails_post_consensus_when_only_sponsor_runs_pre_consensus ( )
1363+ -> Result < ( ) , anyhow:: Error > {
1364+ telemetry_subscribers:: init_for_testing ( ) ;
1365+ let client_ip = SocketAddr :: new ( [ 127 , 0 , 0 , 1 ] . into ( ) , 0 ) ;
1366+
1367+ // Sender is an AA with ED25519 authentication; sponsor is an AA with
1368+ // free-access authentication.
1369+ let mut test_env = TestEnvironment :: new ( ) . await ;
1370+ test_env
1371+ . setup_abstract_account ( AA_AUTHENTICATE_FN_NAME_ED25519 )
1372+ . await ?;
1373+ let sender_aa_ref = test_env. aa_ref . unwrap ( ) ;
1374+ let aa_sender: IotaAddress = sender_aa_ref. object_id . into ( ) ;
1375+
1376+ let sponsor_aa_ref = test_env
1377+ . create_extra_abstract_account_with ( AA_AUTHENTICATE_FN_NAME_FREE_ACCESS )
1378+ . await ?;
1379+ let sponsor_addr: IotaAddress = sponsor_aa_ref. object_id . into ( ) ;
1380+
1381+ let rgp = test_env. test_cluster . get_reference_gas_price ( ) . await ;
1382+ let sponsor_gas = test_env
1383+ . test_cluster
1384+ . fund_address_and_return_gas ( rgp, Some ( 20_000_000_000 ) , sponsor_addr)
1385+ . await ;
1386+
1387+ let pt = test_env. craft_aa_simple_ptb ( AA_MODULE_NAME ) ?;
1388+ let tx_data = test_env
1389+ . craft_tx_from_pt ( pt, sponsor_gas, aa_sender, Some ( sponsor_addr) )
1390+ . await ?;
1391+
1392+ // Sender's MA is signed over the wrong digest — it will fail post-consensus.
1393+ let wrong_digest = [ 0u8 ; 32 ] ;
1394+ let sender_aa_sig = test_env. create_move_authenticator_for_ed25519 ( & wrong_digest) ?;
1395+ let sponsor_aa_sig =
1396+ test_env. create_move_authenticator_for_free_access_for_ref ( sponsor_aa_ref) ?;
1397+ let tx = Transaction :: from_generic_sig_data ( tx_data, vec ! [ sender_aa_sig, sponsor_aa_sig] ) ;
1398+
1399+ // Pre-consensus: only the sponsor's MA is executed (free-access → passes).
1400+ // The validator must sign the TX, producing a certificate.
1401+ let cert = test_env
1402+ . test_cluster
1403+ . create_certificate ( tx, Some ( client_ip) )
1404+ . await ?;
1405+
1406+ // Post-consensus: both MAs are executed → sender's ED25519 MA fails.
1407+ let QuorumDriverResponse { effects_cert, .. } = test_env
1408+ . test_cluster
1409+ . authority_aggregator ( )
1410+ . process_certificate (
1411+ HandleCertificateRequestV1 :: new ( cert) . with_events ( ) ,
1412+ Some ( client_ip) ,
1413+ )
1414+ . await ?;
1415+
1416+ let summary = effects_cert. summary_for_debug ( ) ;
1417+ assert ! (
1418+ summary. status. is_failure( ) ,
1419+ "Expected TX to fail post-consensus due to the sender's MA failure"
1420+ ) ;
1421+ assert ! (
1422+ matches!(
1423+ summary. status. unwrap_err( ) . 0 ,
1424+ ExecutionFailureStatus :: MoveAbort { .. }
1425+ ) ,
1426+ "Expected a Move abort from the failed ED25519 authentication"
1427+ ) ;
1428+
1429+ // Even though the TX failed, the sponsor must have paid gas. Verify that
1430+ // computation was charged and that the correct gas object (the sponsor's coin)
1431+ // was debited.
1432+ assert ! (
1433+ summary. gas_used. computation_cost > 0 ,
1434+ "Expected computation cost > 0: the sponsor must pay gas even for a post-consensus failure"
1435+ ) ;
1436+ assert_eq ! (
1437+ effects_cert. data( ) . gas_object( ) . 0 . object_id,
1438+ sponsor_gas. object_id,
1439+ "Expected the sponsor's gas coin to be used for the failed TX"
1440+ ) ;
1441+
1442+ Ok ( ( ) )
1443+ }
1444+
1445+ /// With `pre_consensus_sponsor_only_move_authentication` disabled, both the
1446+ /// sender's and the sponsor's MAs run pre-consensus. A sender MA signed over
1447+ /// the wrong digest is therefore rejected immediately, before consensus.
1448+ #[ sim_test]
1449+ async fn test_sponsored_tx_sender_aa_rejected_pre_consensus_without_sponsor_only_flag ( )
1450+ -> Result < ( ) , anyhow:: Error > {
1451+ telemetry_subscribers:: init_for_testing ( ) ;
1452+
1453+ // Disable the flag so ALL MAs run pre-consensus.
1454+ let _guard = ProtocolConfig :: apply_overrides_for_testing ( |_, mut config| {
1455+ config. set_pre_consensus_sponsor_only_move_authentication_for_testing ( false ) ;
1456+ config
1457+ } ) ;
1458+
1459+ let mut test_env = TestEnvironment :: new ( ) . await ;
1460+ test_env
1461+ . setup_abstract_account ( AA_AUTHENTICATE_FN_NAME_ED25519 )
1462+ . await ?;
1463+ let sender_aa_ref = test_env. aa_ref . unwrap ( ) ;
1464+ let aa_sender: IotaAddress = sender_aa_ref. object_id . into ( ) ;
1465+
1466+ let sponsor_aa_ref = test_env
1467+ . create_extra_abstract_account_with ( AA_AUTHENTICATE_FN_NAME_FREE_ACCESS )
1468+ . await ?;
1469+ let sponsor_addr: IotaAddress = sponsor_aa_ref. object_id . into ( ) ;
1470+
1471+ let rgp = test_env. test_cluster . get_reference_gas_price ( ) . await ;
1472+ let sponsor_gas = test_env
1473+ . test_cluster
1474+ . fund_address_and_return_gas ( rgp, Some ( 20_000_000_000 ) , sponsor_addr)
1475+ . await ;
1476+
1477+ let pt = test_env. craft_aa_simple_ptb ( AA_MODULE_NAME ) ?;
1478+ let tx_data = test_env
1479+ . craft_tx_from_pt ( pt, sponsor_gas, aa_sender, Some ( sponsor_addr) )
1480+ . await ?;
1481+
1482+ let wrong_digest = [ 0u8 ; 32 ] ;
1483+ let sender_aa_sig = test_env. create_move_authenticator_for_ed25519 ( & wrong_digest) ?;
1484+ let sponsor_aa_sig =
1485+ test_env. create_move_authenticator_for_free_access_for_ref ( sponsor_aa_ref) ?;
1486+ let tx = Transaction :: from_generic_sig_data ( tx_data, vec ! [ sender_aa_sig, sponsor_aa_sig] ) ;
1487+
1488+ // Pre-consensus: both MAs are executed → sender's MA fails → rejected
1489+ // immediately.
1490+ let err = test_env. handle_tx ( tx) . await . unwrap_err ( ) ;
1491+
1492+ assert ! (
1493+ matches!( & err, IotaError :: MoveAuthenticatorExecutionFailure { .. } ) ,
1494+ "Expected MoveAuthenticatorExecutionFailure from the sender's MA, got: {err:?}"
1495+ ) ;
1496+
1497+ Ok ( ( ) )
1498+ }
1499+
1500+ /// With `pre_consensus_sponsor_only_move_authentication` enabled, the flag
1501+ /// only skips the sender's MA pre-consensus check for *sponsored* transactions.
1502+ /// For non-sponsored transactions, the sender's MA still runs pre-consensus, so
1503+ /// a bad sender MA is still rejected before consensus.
1504+ #[ sim_test]
1505+ async fn test_non_sponsored_tx_sender_aa_rejected_pre_consensus_with_sponsor_only_flag ( )
1506+ -> Result < ( ) , anyhow:: Error > {
1507+ telemetry_subscribers:: init_for_testing ( ) ;
1508+
1509+ let mut test_env = TestEnvironment :: new ( ) . await ;
1510+ test_env
1511+ . setup_abstract_account ( AA_AUTHENTICATE_FN_NAME_ED25519 )
1512+ . await ?;
1513+ let aa_ref = test_env. aa_ref . unwrap ( ) ;
1514+ let aa_sender: IotaAddress = aa_ref. object_id . into ( ) ;
1515+
1516+ let rgp = test_env. test_cluster . get_reference_gas_price ( ) . await ;
1517+ let aa_gas = test_env
1518+ . test_cluster
1519+ . fund_address_and_return_gas ( rgp, Some ( 20_000_000_000 ) , aa_sender)
1520+ . await ;
1521+
1522+ // Non-sponsored TX — sender pays its own gas.
1523+ let pt = test_env. craft_aa_simple_ptb ( AA_MODULE_NAME ) ?;
1524+ let tx_data = test_env
1525+ . craft_tx_from_pt ( pt, aa_gas, aa_sender, None )
1526+ . await ?;
1527+
1528+ // Sender's MA is signed over the wrong digest.
1529+ let wrong_digest = [ 0u8 ; 32 ] ;
1530+ let sender_aa_sig = test_env. create_move_authenticator_for_ed25519 ( & wrong_digest) ?;
1531+ let tx = Transaction :: from_generic_sig_data ( tx_data, vec ! [ sender_aa_sig] ) ;
1532+
1533+ // Pre-consensus: non-sponsored TX → sender's MA always runs pre-consensus even
1534+ // with the sponsor-only flag → sender's MA fails → rejected immediately.
1535+ let err = test_env. handle_tx ( tx) . await . unwrap_err ( ) ;
1536+
1537+ assert ! (
1538+ matches!( & err, IotaError :: MoveAuthenticatorExecutionFailure { .. } ) ,
1539+ "Expected MoveAuthenticatorExecutionFailure for non-sponsored TX, got: {err:?}"
1540+ ) ;
1541+
1542+ Ok ( ( ) )
1543+ }
1544+
13501545// ---------------------------------------------------
13511546// --- Test Environment for Abstract Account tests ---
13521547// ---------------------------------------------------
0 commit comments