Skip to main content

solana_program_runtime/
deploy.rs

1//! Program deployment functionality.
2
3#[cfg(feature = "metrics")]
4use {crate::program_metrics::LoadProgramMetrics, solana_svm_measure::measure::Measure};
5use {
6    crate::{
7        invoke_context::InvokeContext,
8        loaded_programs::{ProgramCacheForTxBatch, ProgramRuntimeEnvironment},
9        program_cache_entry::{ProgramCacheEntry, ProgramCacheEntryOwner},
10    },
11    solana_clock::Slot,
12    solana_instruction_error::InstructionError,
13    solana_pubkey::Pubkey,
14    solana_sbpf::{
15        elf::{ElfError, Executable},
16        program::{BuiltinProgram, SBPFVersion},
17        verifier::RequisiteVerifier,
18    },
19    solana_svm_log_collector::{LogCollector, ic_logger_msg},
20    solana_svm_type_overrides::sync::Arc,
21    std::{cell::RefCell, rc::Rc},
22};
23
24/// Derives the stricter environment that programs are verified against at
25/// deployment.
26pub fn morph_into_deployment_environment(
27    from: ProgramRuntimeEnvironment,
28    disable_sbpf_v0_v1_v2_deployment: bool,
29) -> Result<BuiltinProgram<InvokeContext<'static, 'static>>, ElfError> {
30    let mut config = (*from).get_config().clone();
31    config.reject_broken_elfs = true;
32    if disable_sbpf_v0_v1_v2_deployment {
33        config.enabled_sbpf_versions = SBPFVersion::V3..=*config.enabled_sbpf_versions.end();
34    }
35
36    let mut result = BuiltinProgram::new_loader(config);
37
38    for (_key, (name, value)) in (*from).get_function_registry().iter() {
39        // Deployment of programs with sol_alloc_free is disabled. So do not register the syscall.
40        if name != *b"sol_alloc_free_" {
41            result.register_function(unsafe { std::str::from_utf8_unchecked(name) }, value)?;
42        }
43    }
44
45    Ok(result)
46}
47
48/// Directly deploy a program using a provided invoke context.
49/// This function should only be invoked from the runtime, since it does not
50/// provide any account loads or checks.
51#[allow(clippy::too_many_arguments)]
52pub fn deploy_program(
53    log_collector: Option<Rc<RefCell<LogCollector>>>,
54    #[cfg(feature = "metrics")] load_program_metrics: &mut LoadProgramMetrics,
55    program_cache_for_tx_batch: &mut ProgramCacheForTxBatch,
56    program_runtime_environment: ProgramRuntimeEnvironment,
57    disable_sbpf_v0_v1_v2_deployment: bool,
58    program_id: &Pubkey,
59    loader_key: &Pubkey,
60    programdata: &[u8],
61    deployment_slot: Slot,
62) -> Result<(), InstructionError> {
63    #[cfg(feature = "metrics")]
64    let mut register_syscalls_time = Measure::start("register_syscalls_time");
65    let deployment_program_runtime_environment = morph_into_deployment_environment(
66        ProgramRuntimeEnvironment::clone(&program_runtime_environment),
67        disable_sbpf_v0_v1_v2_deployment,
68    )
69    .map_err(|e| {
70        ic_logger_msg!(log_collector, "Failed to register syscalls: {}", e);
71        InstructionError::ProgramEnvironmentSetupFailure
72    })?;
73    #[cfg(feature = "metrics")]
74    {
75        register_syscalls_time.stop();
76        load_program_metrics.register_syscalls_us = register_syscalls_time.as_us();
77    }
78    // Verify using stricter deployment_program_runtime_environment
79    #[cfg(feature = "metrics")]
80    let mut load_elf_time = Measure::start("load_elf_time");
81    let executable = Executable::<InvokeContext>::load(
82        programdata,
83        Arc::new(deployment_program_runtime_environment),
84    )
85    .map_err(|err| {
86        ic_logger_msg!(log_collector, "{}", err);
87        InstructionError::InvalidAccountData
88    })?;
89    #[cfg(feature = "metrics")]
90    {
91        load_elf_time.stop();
92        load_program_metrics.load_elf_us = load_elf_time.as_us();
93    }
94    #[cfg(feature = "metrics")]
95    let mut verify_code_time = Measure::start("verify_code_time");
96    executable.verify::<RequisiteVerifier>().map_err(|err| {
97        ic_logger_msg!(log_collector, "{}", err);
98        InstructionError::InvalidAccountData
99    })?;
100    #[cfg(feature = "metrics")]
101    {
102        verify_code_time.stop();
103        load_program_metrics.verify_code_us = verify_code_time.as_us();
104    }
105    // Insert but with program_runtime_environment
106    let program_cache_entry = ProgramCacheEntry::new_unloaded(
107        deployment_slot,
108        ProgramCacheEntryOwner::try_from(loader_key)
109            .map_err(|_| InstructionError::InvalidAccountData)?,
110        program_runtime_environment,
111    );
112    if let Some(old_entry) = program_cache_for_tx_batch.find(program_id) {
113        program_cache_entry.stats.merge_from(&old_entry.stats);
114    }
115    #[cfg(feature = "metrics")]
116    {
117        load_program_metrics.program_id = program_id.to_string();
118    }
119    program_cache_for_tx_batch.store_modified_entry(*program_id, Arc::new(program_cache_entry));
120    Ok(())
121}
122
123#[macro_export]
124macro_rules! deploy_program {
125    ($invoke_context:expr,
126     $program_id:expr,
127     $loader_key:expr,
128     $programdata:expr,
129     $deployment_slot:expr,
130     $disable_sbpf_v0_v1_v2_deployment:expr $(,)?) => {
131        assert_eq!(
132            $deployment_slot,
133            $invoke_context.program_cache_for_tx_batch.slot()
134        );
135        #[cfg(feature = "metrics")]
136        let mut load_program_metrics = $crate::program_metrics::LoadProgramMetrics::default();
137        $crate::deploy::deploy_program(
138            $invoke_context.get_log_collector(),
139            #[cfg(feature = "metrics")]
140            &mut load_program_metrics,
141            $invoke_context.program_cache_for_tx_batch,
142            $invoke_context
143                .get_program_runtime_environment_for_deployment()
144                .clone(),
145            $disable_sbpf_v0_v1_v2_deployment,
146            $program_id,
147            $loader_key,
148            $programdata,
149            $deployment_slot,
150        )?;
151        #[cfg(feature = "metrics")]
152        load_program_metrics.submit_datapoint(&mut $invoke_context.timings);
153    };
154}