| 
 | 1 | +// Copyright © Aptos Foundation  | 
 | 2 | + | 
 | 3 | +use crate::{  | 
 | 4 | +    aptos_vm::get_or_vm_startup_failure,  | 
 | 5 | +    errors::expect_only_successful_execution,  | 
 | 6 | +    move_vm_ext::{AptosMoveResolver, SessionId},  | 
 | 7 | +    system_module_names::{JWKS_MODULE, UPSERT_INTO_OBSERVED_JWKS},  | 
 | 8 | +    validator_txns::jwk::{  | 
 | 9 | +        ExecutionFailure::{Expected, Unexpected},  | 
 | 10 | +        ExpectedFailure::{  | 
 | 11 | +            IncorrectVersion, MissingResourceObservedJWKs, MissingResourceValidatorSet,  | 
 | 12 | +            MultiSigVerificationFailed, NotEnoughVotingPower,  | 
 | 13 | +        },  | 
 | 14 | +    },  | 
 | 15 | +    AptosVM,  | 
 | 16 | +};  | 
 | 17 | +use aptos_types::{  | 
 | 18 | +    fee_statement::FeeStatement,  | 
 | 19 | +    jwks,  | 
 | 20 | +    jwks::{Issuer, ObservedJWKs, ProviderJWKs, QuorumCertifiedUpdate},  | 
 | 21 | +    move_utils::as_move_value::AsMoveValue,  | 
 | 22 | +    on_chain_config::{OnChainConfig, ValidatorSet},  | 
 | 23 | +    transaction::{ExecutionStatus, TransactionStatus},  | 
 | 24 | +    validator_verifier::ValidatorVerifier,  | 
 | 25 | +};  | 
 | 26 | +use aptos_vm_logging::log_schema::AdapterLogSchema;  | 
 | 27 | +use aptos_vm_types::output::VMOutput;  | 
 | 28 | +use move_core_types::{  | 
 | 29 | +    account_address::AccountAddress,  | 
 | 30 | +    value::{serialize_values, MoveValue},  | 
 | 31 | +    vm_status::{AbortLocation, StatusCode, VMStatus},  | 
 | 32 | +};  | 
 | 33 | +use move_vm_types::gas::UnmeteredGasMeter;  | 
 | 34 | +use std::collections::HashMap;  | 
 | 35 | + | 
 | 36 | +enum ExpectedFailure {  | 
 | 37 | +    // Move equivalent: `errors::invalid_argument(*)`  | 
 | 38 | +    IncorrectVersion = 0x010103,  | 
 | 39 | +    MultiSigVerificationFailed = 0x010104,  | 
 | 40 | +    NotEnoughVotingPower = 0x010105,  | 
 | 41 | + | 
 | 42 | +    // Move equivalent: `errors::invalid_state(*)`  | 
 | 43 | +    MissingResourceValidatorSet = 0x30101,  | 
 | 44 | +    MissingResourceObservedJWKs = 0x30102,  | 
 | 45 | +}  | 
 | 46 | + | 
 | 47 | +enum ExecutionFailure {  | 
 | 48 | +    Expected(ExpectedFailure),  | 
 | 49 | +    Unexpected(VMStatus),  | 
 | 50 | +}  | 
 | 51 | + | 
 | 52 | +impl AptosVM {  | 
 | 53 | +    pub(crate) fn process_jwk_update(  | 
 | 54 | +        &self,  | 
 | 55 | +        resolver: &impl AptosMoveResolver,  | 
 | 56 | +        log_context: &AdapterLogSchema,  | 
 | 57 | +        session_id: SessionId,  | 
 | 58 | +        update: jwks::QuorumCertifiedUpdate,  | 
 | 59 | +    ) -> Result<(VMStatus, VMOutput), VMStatus> {  | 
 | 60 | +        match self.process_jwk_update_inner(resolver, log_context, session_id, update) {  | 
 | 61 | +            Ok((vm_status, vm_output)) => Ok((vm_status, vm_output)),  | 
 | 62 | +            Err(Expected(failure)) => {  | 
 | 63 | +                // Pretend we are inside Move, and expected failures are like Move aborts.  | 
 | 64 | +                Ok((  | 
 | 65 | +                    VMStatus::MoveAbort(AbortLocation::Script, failure as u64),  | 
 | 66 | +                    VMOutput::empty_with_status(TransactionStatus::Discard(StatusCode::ABORTED)),  | 
 | 67 | +                ))  | 
 | 68 | +            },  | 
 | 69 | +            Err(Unexpected(vm_status)) => Err(vm_status),  | 
 | 70 | +        }  | 
 | 71 | +    }  | 
 | 72 | + | 
 | 73 | +    fn process_jwk_update_inner(  | 
 | 74 | +        &self,  | 
 | 75 | +        resolver: &impl AptosMoveResolver,  | 
 | 76 | +        log_context: &AdapterLogSchema,  | 
 | 77 | +        session_id: SessionId,  | 
 | 78 | +        update: jwks::QuorumCertifiedUpdate,  | 
 | 79 | +    ) -> Result<(VMStatus, VMOutput), ExecutionFailure> {  | 
 | 80 | +        // Load resources.  | 
 | 81 | +        let validator_set = ValidatorSet::fetch_config(resolver)  | 
 | 82 | +            .ok_or_else(|| Expected(MissingResourceValidatorSet))?;  | 
 | 83 | +        let observed_jwks = ObservedJWKs::fetch_config(resolver)  | 
 | 84 | +            .ok_or_else(|| Expected(MissingResourceObservedJWKs))?;  | 
 | 85 | + | 
 | 86 | +        let mut jwks_by_issuer: HashMap<Issuer, ProviderJWKs> =  | 
 | 87 | +            observed_jwks.into_providers_jwks().into();  | 
 | 88 | +        let issuer = update.update.issuer.clone();  | 
 | 89 | +        let on_chain = jwks_by_issuer  | 
 | 90 | +            .entry(issuer.clone())  | 
 | 91 | +            .or_insert_with(|| ProviderJWKs::new(issuer));  | 
 | 92 | +        let verifier = ValidatorVerifier::from(&validator_set);  | 
 | 93 | + | 
 | 94 | +        let QuorumCertifiedUpdate {  | 
 | 95 | +            update: observed,  | 
 | 96 | +            multi_sig,  | 
 | 97 | +        } = update;  | 
 | 98 | + | 
 | 99 | +        // Check version.  | 
 | 100 | +        if on_chain.version + 1 != observed.version {  | 
 | 101 | +            return Err(Expected(IncorrectVersion));  | 
 | 102 | +        }  | 
 | 103 | + | 
 | 104 | +        let authors = multi_sig.get_signers_addresses(&verifier.get_ordered_account_addresses());  | 
 | 105 | + | 
 | 106 | +        // Check voting power.  | 
 | 107 | +        verifier  | 
 | 108 | +            .check_voting_power(authors.iter(), true)  | 
 | 109 | +            .map_err(|_| Expected(NotEnoughVotingPower))?;  | 
 | 110 | + | 
 | 111 | +        // Verify multi-sig.  | 
 | 112 | +        verifier  | 
 | 113 | +            .verify_multi_signatures(&observed, &multi_sig)  | 
 | 114 | +            .map_err(|_| Expected(MultiSigVerificationFailed))?;  | 
 | 115 | + | 
 | 116 | +        // All verification passed. Apply the `observed`.  | 
 | 117 | +        let mut gas_meter = UnmeteredGasMeter;  | 
 | 118 | +        let mut session = self.new_session(resolver, session_id);  | 
 | 119 | +        let args = vec![  | 
 | 120 | +            MoveValue::Signer(AccountAddress::ONE),  | 
 | 121 | +            vec![observed].as_move_value(),  | 
 | 122 | +        ];  | 
 | 123 | + | 
 | 124 | +        session  | 
 | 125 | +            .execute_function_bypass_visibility(  | 
 | 126 | +                &JWKS_MODULE,  | 
 | 127 | +                UPSERT_INTO_OBSERVED_JWKS,  | 
 | 128 | +                vec![],  | 
 | 129 | +                serialize_values(&args),  | 
 | 130 | +                &mut gas_meter,  | 
 | 131 | +            )  | 
 | 132 | +            .map_err(|e| {  | 
 | 133 | +                expect_only_successful_execution(e, UPSERT_INTO_OBSERVED_JWKS.as_str(), log_context)  | 
 | 134 | +            })  | 
 | 135 | +            .map_err(|r| Unexpected(r.unwrap_err()))?;  | 
 | 136 | + | 
 | 137 | +        let output = crate::aptos_vm::get_transaction_output(  | 
 | 138 | +            session,  | 
 | 139 | +            FeeStatement::zero(),  | 
 | 140 | +            ExecutionStatus::Success,  | 
 | 141 | +            &get_or_vm_startup_failure(&self.storage_gas_params, log_context)  | 
 | 142 | +                .map_err(Unexpected)?  | 
 | 143 | +                .change_set_configs,  | 
 | 144 | +        )  | 
 | 145 | +        .map_err(Unexpected)?;  | 
 | 146 | + | 
 | 147 | +        Ok((VMStatus::Executed, output))  | 
 | 148 | +    }  | 
 | 149 | +}  | 
0 commit comments