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::{DELAY_VISIBILITY_SLOT_OFFSET, ProgramCacheEntry},
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
24fn morph_into_deployment_environment(
25    from: ProgramRuntimeEnvironment,
26    disable_sbpf_v0_v1_v2_deployment: bool,
27) -> Result<BuiltinProgram<InvokeContext<'static, 'static>>, ElfError> {
28    let mut config = (*from).get_config().clone();
29    config.reject_broken_elfs = true;
30    if disable_sbpf_v0_v1_v2_deployment {
31        config.enabled_sbpf_versions = SBPFVersion::V3..=*config.enabled_sbpf_versions.end();
32    }
33
34    let mut result = BuiltinProgram::new_loader(config);
35
36    for (_key, (name, value)) in (*from).get_function_registry().iter() {
37        // Deployment of programs with sol_alloc_free is disabled. So do not register the syscall.
38        if name != *b"sol_alloc_free_" {
39            result.register_function(unsafe { std::str::from_utf8_unchecked(name) }, value)?;
40        }
41    }
42
43    Ok(result)
44}
45
46/// Directly deploy a program using a provided invoke context.
47/// This function should only be invoked from the runtime, since it does not
48/// provide any account loads or checks.
49#[allow(clippy::too_many_arguments)]
50pub fn deploy_program(
51    log_collector: Option<Rc<RefCell<LogCollector>>>,
52    #[cfg(feature = "metrics")] load_program_metrics: &mut LoadProgramMetrics,
53    program_cache_for_tx_batch: &mut ProgramCacheForTxBatch,
54    program_runtime_environment: ProgramRuntimeEnvironment,
55    disable_sbpf_v0_v1_v2_deployment: bool,
56    program_id: &Pubkey,
57    loader_key: &Pubkey,
58    account_size: usize,
59    programdata: &[u8],
60    deployment_slot: Slot,
61) -> Result<(), InstructionError> {
62    #[cfg(feature = "metrics")]
63    let mut register_syscalls_time = Measure::start("register_syscalls_time");
64    let deployment_program_runtime_environment = morph_into_deployment_environment(
65        ProgramRuntimeEnvironment::clone(&program_runtime_environment),
66        disable_sbpf_v0_v1_v2_deployment,
67    )
68    .map_err(|e| {
69        ic_logger_msg!(log_collector, "Failed to register syscalls: {}", e);
70        InstructionError::ProgramEnvironmentSetupFailure
71    })?;
72    #[cfg(feature = "metrics")]
73    {
74        register_syscalls_time.stop();
75        load_program_metrics.register_syscalls_us = register_syscalls_time.as_us();
76    }
77    // Verify using stricter deployment_program_runtime_environment
78    #[cfg(feature = "metrics")]
79    let mut load_elf_time = Measure::start("load_elf_time");
80    let executable = Executable::<InvokeContext>::load(
81        programdata,
82        Arc::new(deployment_program_runtime_environment),
83    )
84    .map_err(|err| {
85        ic_logger_msg!(log_collector, "{}", err);
86        InstructionError::InvalidAccountData
87    })?;
88    #[cfg(feature = "metrics")]
89    {
90        load_elf_time.stop();
91        load_program_metrics.load_elf_us = load_elf_time.as_us();
92    }
93    #[cfg(feature = "metrics")]
94    let mut verify_code_time = Measure::start("verify_code_time");
95    executable.verify::<RequisiteVerifier>().map_err(|err| {
96        ic_logger_msg!(log_collector, "{}", err);
97        InstructionError::InvalidAccountData
98    })?;
99    #[cfg(feature = "metrics")]
100    {
101        verify_code_time.stop();
102        load_program_metrics.verify_code_us = verify_code_time.as_us();
103    }
104    // Reload but with program_runtime_environment
105    let executor = unsafe {
106        // SAFETY: The executable has been verified just above.
107        ProgramCacheEntry::reload(
108            loader_key,
109            program_runtime_environment,
110            deployment_slot,
111            deployment_slot.saturating_add(DELAY_VISIBILITY_SLOT_OFFSET),
112            programdata,
113            account_size,
114            #[cfg(feature = "metrics")]
115            load_program_metrics,
116        )
117    }
118    .map_err(|err| {
119        ic_logger_msg!(log_collector, "{}", err);
120        InstructionError::InvalidAccountData
121    })?;
122    if let Some(old_entry) = program_cache_for_tx_batch.find(program_id) {
123        executor.stats.merge_from(&old_entry.stats);
124    }
125    #[cfg(feature = "metrics")]
126    {
127        load_program_metrics.program_id = program_id.to_string();
128    }
129    program_cache_for_tx_batch.store_modified_entry(*program_id, Arc::new(executor));
130    Ok(())
131}
132
133#[macro_export]
134macro_rules! deploy_program {
135    ($invoke_context:expr,
136     $program_id:expr,
137     $loader_key:expr,
138     $account_size:expr,
139     $programdata:expr,
140     $deployment_slot:expr,
141     $disable_sbpf_v0_v1_v2_deployment:expr $(,)?) => {
142        assert_eq!(
143            $deployment_slot,
144            $invoke_context.program_cache_for_tx_batch.slot()
145        );
146        #[cfg(feature = "metrics")]
147        let mut load_program_metrics = $crate::program_metrics::LoadProgramMetrics::default();
148        $crate::deploy::deploy_program(
149            $invoke_context.get_log_collector(),
150            #[cfg(feature = "metrics")]
151            &mut load_program_metrics,
152            $invoke_context.program_cache_for_tx_batch,
153            $invoke_context
154                .get_program_runtime_environment_for_deployment()
155                .clone(),
156            $disable_sbpf_v0_v1_v2_deployment,
157            $program_id,
158            $loader_key,
159            $account_size,
160            $programdata,
161            $deployment_slot,
162        )?;
163        #[cfg(feature = "metrics")]
164        load_program_metrics.submit_datapoint(&mut $invoke_context.timings);
165    };
166}