BioErrorLog Tech Blog

試行錯誤の記録

dfx deploy と dfx canister install の違いをソースコードから理解する | Dfinity, Internet Computer

Internet ComputerのSDKコマンドdfx deploydfx canister installの違いを整理し、ソースコードからこれらを読み解きます。

はじめに

こんにちは、@bioerrorlogです。

dfx deploydfx canister installは共に、Internet Computerのcanisterをデプロイするコマンドです。

dfx deployはいろいろと勝手にやってくれるコマンド、dfx canister installdfx buildとかいろいろ事前にやっておかないといけないやつ、くらいの認識はあっても、ふとした瞬間に両者を混同しがちです。

--helpフラッグのコマンド解説に互いへの言及が無いのも、分かりにくいポイントでしょう。

そこで今回は、dfx deploydfx canister installの違いを整理し、ソースコードからもこれらを読み解いていきます。

dfx deploy と dfx canister install の違い

dfx deploydfx canister installの違いは、ドキュメントで説明されています。

整理すると、dfx deploy下記の3つのコマンドをまとめて実行するコマンドと言えます。

dfx canister create --all
dfx build
dfx canister install --all

dfx deployはデフォルトで全てのcanister(上記における--all)を対象としますが、この辺りはパラメータから変更可能です。

これらのコマンドの使い分け方針としては、

  • 特にこだわりがない場合はdfx deployが簡単
  • 各ステップ(create/build/install)の実行タイミングを制御したいときは個別コマンドが良い
  • 各ステップ(create/build/install)でオプションを細かく設定したいときは個別コマンドが見やすい

といった感じでしょうか。

上手いこと使い分けていきたいところですね。

ソースコードから読み解く

dfx deploydfx canister installの違いは理解できましたが、せっかくなのでソースコードからも読み解いていきます。

github.com

dfx deployの実装

まずは↓を出発点としましょうか。

pub fn exec(env: &dyn Environment, cmd: Command) -> DfxResult {
    match cmd {
        Command::Bootstrap(v) => bootstrap::exec(env, v),
        Command::Build(v) => build::exec(env, v),
        Command::Cache(v) => cache::exec(env, v),
        Command::Canister(v) => canister::exec(env, v),
        Command::Config(v) => config::exec(env, v),
        Command::Deploy(v) => deploy::exec(env, v),
        Command::Generate(v) => generate::exec(env, v),
        Command::Identity(v) => identity::exec(env, v),
        Command::LanguageServices(v) => language_service::exec(env, v),
        Command::Ledger(v) => ledger::exec(env, v),
        Command::New(v) => new::exec(env, v),
        Command::Ping(v) => ping::exec(env, v),
        Command::Remote(v) => remote::exec(env, v),
        Command::Replica(v) => replica::exec(env, v),
        Command::Start(v) => start::exec(env, v),
        Command::Stop(v) => stop::exec(env, v),
        Command::Toolchain(v) => toolchain::exec(env, v),
        Command::Upgrade(v) => upgrade::exec(env, v),
        Command::Wallet(v) => wallet::exec(env, v),
    }
}

sdk/mod.rs at 3e3127acb87bd6782a537d96190f0f897655cd5e · dfinity/sdk · GitHub

この部分から、dfxに与えられるサブコマンド毎に実行されるコードを辿ることができます。 Command::Deploy(v)からはdeploy::exec(env, v)が実行されているようです。

deploy::execの実装を見に行きしょう。

pub fn exec(env: &dyn Environment, opts: DeployOpts) -> DfxResult {
    let env = create_agent_environment(env, opts.network)?;

    let timeout = expiry_duration();
    let canister_name = opts.canister_name.as_deref();
    let argument = opts.argument.as_deref();
    let argument_type = opts.argument_type.as_deref();
    let mode = opts
        .mode
        .as_deref()
        .map(InstallMode::from_str)
        .transpose()
        .map_err(|err| anyhow!(err))?;

    let with_cycles = opts.with_cycles.as_deref();

    let force_reinstall = match (mode, canister_name) {
        (None, _) => false,
        (Some(InstallMode::Reinstall), Some(_canister_name)) => true,
        (Some(InstallMode::Reinstall), None) => {
            bail!("The --mode=reinstall is only valid when deploying a single canister, because reinstallation destroys all data in the canister.");
        }
        (Some(_), _) => {
            unreachable!("The only valid option for --mode is --mode=reinstall");
        }
    };

    let runtime = Runtime::new().expect("Unable to create a runtime");

    let call_sender = runtime.block_on(call_sender(&env, &opts.wallet))?;
    let proxy_sender;
    let create_call_sender = if !opts.no_wallet && !matches!(call_sender, CallSender::Wallet(_)) {
        let wallet = runtime.block_on(Identity::get_or_create_wallet_canister(
            &env,
            env.get_network_descriptor()
                .expect("Couldn't get the network descriptor"),
            env.get_selected_identity().expect("No selected identity"),
            true,
        ))?;
        proxy_sender = CallSender::Wallet(*wallet.canister_id_());
        &proxy_sender
    } else {
        &call_sender
    };
    runtime.block_on(fetch_root_key_if_needed(&env))?;

    runtime.block_on(deploy_canisters(
        &env,
        canister_name,
        argument,
        argument_type,
        force_reinstall,
        timeout,
        with_cycles,
        &call_sender,
        create_call_sender,
    ))
}

sdk/deploy.rs at 3e3127acb87bd6782a537d96190f0f897655cd5e · dfinity/sdk · GitHub

ちょっと長いですね。

でも落ち着いて読んでみると、最終的に実行されているのは以下の部分のようです。

   runtime.block_on(deploy_canisters(
        &env,
        canister_name,
        argument,
        argument_type,
        force_reinstall,
        timeout,
        with_cycles,
        &call_sender,
        create_call_sender,
    ))
}

deploy_canistersというそれっぽい名前の関数が実行されていますね。

この関数は、

use crate::lib::operations::canister::deploy_canisters;

インポートされています

これを頼りにdeploy_canistersの実装元を見に行きましょう。

pub async fn deploy_canisters(
    env: &dyn Environment,
    some_canister: Option<&str>,
    argument: Option<&str>,
    argument_type: Option<&str>,
    force_reinstall: bool,
    upgrade_unchanged: bool,
    timeout: Duration,
    with_cycles: Option<&str>,
    call_sender: &CallSender,
    create_call_sender: &CallSender,
) -> DfxResult {
    let log = env.get_logger();

    let config = env
        .get_config()
        .ok_or_else(|| anyhow!("Cannot find dfx configuration file in the current working directory. Did you forget to create one?"))?;
    let initial_canister_id_store = CanisterIdStore::for_env(env)?;

    let network = env.get_network_descriptor().unwrap();

    let canisters_to_build = canister_with_dependencies(&config, some_canister)?;

    let canisters_to_deploy = if force_reinstall {
        // don't force-reinstall the dependencies too.
        match some_canister {
            Some(canister_name) => {
                if config.get_config().is_remote_canister(canister_name, &network.name)? {
                    bail!("The '{}' canister is remote for network '{}' and cannot be force-reinstalled from here",
                    canister_name, &network.name);
                }
                vec!(String::from(canister_name))
            },
            None => bail!("The --mode=reinstall is only valid when deploying a single canister, because reinstallation destroys all data in the canister."),
        }
    } else {
        canisters_to_build.clone()
    };
    let canisters_to_deploy: Vec<String> = canisters_to_deploy
        .into_iter()
        .filter(|canister_name| {
            !matches!(
                config
                    .get_config()
                    .get_remote_canister_id(canister_name, &network.name),
                Ok(Some(_))
            )
        })
        .collect();

    if some_canister.is_some() {
        info!(log, "Deploying: {}", canisters_to_deploy.join(" "));
    } else {
        info!(log, "Deploying all canisters.");
    }

    register_canisters(
        env,
        &canisters_to_build,
        &initial_canister_id_store,
        timeout,
        with_cycles,
        create_call_sender,
        &config,
    )
    .await?;

    build_canisters(env, &canisters_to_build, &config)?;

    install_canisters(
        env,
        &canisters_to_deploy,
        &initial_canister_id_store,
        &config,
        argument,
        argument_type,
        force_reinstall,
        upgrade_unchanged,
        timeout,
        call_sender,
    )
    .await?;

    info!(log, "Deployed canisters.");

    Ok(())
}

sdk/deploy_canisters.rs at 1625fefc0e0dc028875b704031dd8f9a3f83cb19 · dfinity/sdk · GitHub

これまた長いですね。

落ち着いて読んでみると、最終的に以下が実行されていることが分かります。

    register_canisters(
        env,
        &canisters_to_build,
        &initial_canister_id_store,
        timeout,
        with_cycles,
        create_call_sender,
        &config,
    )
    .await?;

    build_canisters(env, &canisters_to_build, &config)?;

    install_canisters(
        env,
        &canisters_to_deploy,
        &initial_canister_id_store,
        &config,
        argument,
        argument_type,
        force_reinstall,
        upgrade_unchanged,
        timeout,
        call_sender,
    )
    .await?;

register_canisters/build_canisters/install_canisters三段階に分かれていますね。

先ほど、dfx deployは下記の3つのコマンドをまとめて実行するコマンド、とお伝えしました。

dfx canister create --all
dfx build
dfx canister install --all

この各コマンドと上記の三段階 (register_canisters/build_canisters/install_canisters) の処理にそれぞれ対応していれば、納得感があります。

それぞれ見ていきましょう。

register_canistersとdfx canister create

register_canistersdfx canister createが同じ処理をしているのかを見ていきます。

まずはregister_canistersの実装をもう少し追いかけてみましょう。

register_canistersは、deploy_canistersと同じmoduleに実装されています。

async fn register_canisters(
    env: &dyn Environment,
    canister_names: &[String],
    canister_id_store: &CanisterIdStore,
    timeout: Duration,
    with_cycles: Option<&str>,
    call_sender: &CallSender,
    config: &Config,
) -> DfxResult {
    let canisters_to_create = canister_names
        .iter()
        .filter(|n| canister_id_store.find(n).is_none())
        .cloned()
        .collect::<Vec<String>>();
    if canisters_to_create.is_empty() {
        info!(env.get_logger(), "All canisters have already been created.");
    } else {
        info!(env.get_logger(), "Creating canisters...");
        for canister_name in &canisters_to_create {
            let config_interface = config.get_config();
            let compute_allocation =
                config_interface
                    .get_compute_allocation(canister_name)?
                    .map(|arg| {
                        ComputeAllocation::try_from(arg.parse::<u64>().unwrap())
                            .expect("Compute Allocation must be a percentage.")
                    });
            let memory_allocation =
                config_interface
                    .get_memory_allocation(canister_name)?
                    .map(|arg| {
                        MemoryAllocation::try_from(
                        u64::try_from(arg.parse::<Bytes>().unwrap().size()).unwrap(),
                    )
                    .expect(
                        "Memory allocation must be between 0 and 2^48 (i.e 256TB), inclusively.",
                    )
                    });
            let freezing_threshold =
                config_interface
                    .get_freezing_threshold(canister_name)?
                    .map(|arg| {
                        FreezingThreshold::try_from(
                            u128::try_from(arg.parse::<Bytes>().unwrap().size()).unwrap(),
                        )
                        .expect("Freezing threshold must be between 0 and 2^64-1, inclusively.")
                    });
            let controllers = None;
            create_canister(
                env,
                canister_name,
                timeout,
                with_cycles,
                call_sender,
                CanisterSettings {
                    controllers,
                    compute_allocation,
                    memory_allocation,
                    freezing_threshold,
                },
            )
            .await?;
        }
    }
    Ok(())
}

sdk/deploy_canisters.rs at 1625fefc0e0dc028875b704031dd8f9a3f83cb19 · dfinity/sdk · GitHub

ざっくり、create_canisterがcanisterの数だけ実行されていることが分かります。

useによるインポートを確認すると、このcreate_canistercrate::lib::operations::canisterに宣言されています。

use crate::lib::operations::canister::{create_canister, install_canister};

sdk/deploy_canisters.rs at 1625fefc0e0dc028875b704031dd8f9a3f83cb19 · dfinity/sdk · GitHub

よってregister_canistersは、crate::lib::operations::canistercreate_canisterが、canisterの数だけ実行される、と読み取れます。


では次に、dfx canister createも同じ処理を処理をしているのかを見にいきましょう。

dfx deployのときと同じ手順でdfx canister createの実装を辿っていくと、下記のコードに行き当たります。

pub async fn exec(
    env: &dyn Environment,
    opts: CanisterCreateOpts,
    mut call_sender: &CallSender,
) -> DfxResult {
    let config = env.get_config_or_anyhow()?;
    let timeout = expiry_duration();

    fetch_root_key_if_needed(env).await?;

    let with_cycles = opts.with_cycles.as_deref();

    let config_interface = config.get_config();
    let network = env.get_network_descriptor().unwrap();

    let proxy_sender;
    if !opts.no_wallet && !matches!(call_sender, CallSender::Wallet(_)) {
        let wallet = Identity::get_or_create_wallet_canister(
            env,
            env.get_network_descriptor()
                .expect("Couldn't get the network descriptor"),
            env.get_selected_identity().expect("No selected identity"),
            false,
        )
        .await?;
        proxy_sender = CallSender::Wallet(*wallet.canister_id_());
        call_sender = &proxy_sender;
    }

    let controllers: Option<Vec<_>> = opts
        .controller
        .clone()
        .map(|controllers| {
            controllers
                .iter()
                .map(
                    |controller| match CanisterId::from_text(controller.clone()) {
                        Ok(principal) => Ok(principal),
                        Err(_) => {
                            let current_id = env.get_selected_identity().unwrap();
                            if current_id == controller {
                                Ok(env.get_selected_identity_principal().unwrap())
                            } else {
                                let identity_name = controller;
                                IdentityManager::new(env)?
                                    .instantiate_identity_from_name(identity_name)
                                    .and_then(|identity| {
                                        identity.sender().map_err(|err| anyhow!(err))
                                    })
                            }
                        }
                    },
                )
                .collect::<DfxResult<Vec<_>>>()
        })
        .transpose()?;

    if let Some(canister_name) = opts.canister_name.as_deref() {
        if config
            .get_config()
            .is_remote_canister(canister_name, &network.name)?
        {
            bail!("Canister '{}' is a remote canister on network '{}', and cannot be created from here.", canister_name, &network.name)
        }
        let compute_allocation = get_compute_allocation(
            opts.compute_allocation.clone(),
            config_interface,
            Some(canister_name),
        )?;
        let memory_allocation = get_memory_allocation(
            opts.memory_allocation.clone(),
            config_interface,
            Some(canister_name),
        )?;
        let freezing_threshold = get_freezing_threshold(
            opts.freezing_threshold.clone(),
            config_interface,
            Some(canister_name),
        )?;
        create_canister(
            env,
            canister_name,
            timeout,
            with_cycles,
            call_sender,
            CanisterSettings {
                controllers,
                compute_allocation,
                memory_allocation,
                freezing_threshold,
            },
        )
        .await?;
        Ok(())
    } else if opts.all {
        // Create all canisters.
        if let Some(canisters) = &config.get_config().canisters {
            for canister_name in canisters.keys() {
                if config
                    .get_config()
                    .is_remote_canister(canister_name, &network.name)?
                {
                    info!(
                        env.get_logger(),
                        "Skipping canister '{}' because it is remote for network '{}'",
                        canister_name,
                        &network.name,
                    );

                    continue;
                }
                let compute_allocation = get_compute_allocation(
                    opts.compute_allocation.clone(),
                    config_interface,
                    Some(canister_name),
                )?;
                let memory_allocation = get_memory_allocation(
                    opts.memory_allocation.clone(),
                    config_interface,
                    Some(canister_name),
                )?;
                let freezing_threshold = get_freezing_threshold(
                    opts.freezing_threshold.clone(),
                    config_interface,
                    Some(canister_name),
                )?;
                create_canister(
                    env,
                    canister_name,
                    timeout,
                    with_cycles,
                    call_sender,
                    CanisterSettings {
                        controllers: controllers.clone(),
                        compute_allocation,
                        memory_allocation,
                        freezing_threshold,
                    },
                )
                .await?;
            }
        }
        Ok(())
    } else {
        unreachable!()
    }
}

sdk/create.rs at 633ce44308755a34acd994d682af1627d201c275 · dfinity/sdk · GitHub

少し長いですが落ち着いて読んでみましょう。

いくつか分岐がありますが、いずれもcreate_canisterが必要分呼ばれていることが読み取れます。 register_canistersと同じですね。


以上より、dfx deployの第一段階register_canistersは、dfx canister createと大体同じ処理をしていることが分かりました。

build_canistersとdfx build

つぎはbuild_canistersdfx buildが同じ処理をしているのかを見ていきます。

build_canistersの実装はこちらです。

fn build_canisters(env: &dyn Environment, canister_names: &[String], config: &Config) -> DfxResult {
    info!(env.get_logger(), "Building canisters...");
    let build_mode_check = false;
    let canister_pool = CanisterPool::load(env, build_mode_check, canister_names)?;

    canister_pool.build_or_fail(&BuildConfig::from_config(config)?)
}

sdk/deploy_canisters.rs at 1625fefc0e0dc028875b704031dd8f9a3f83cb19 · dfinity/sdk · GitHub

canister_pool.build_or_fail(&BuildConfig::from_config(config)?)が実行されていることが分かります。


一方dfx buildの実装を辿っていくと、下記のコードにたどり着きます。

pub fn exec(env: &dyn Environment, opts: CanisterBuildOpts) -> DfxResult {
    let env = create_agent_environment(env, opts.network)?;

    let logger = env.get_logger();

    // Read the config.
    let config = env.get_config_or_anyhow()?;

    // Check the cache. This will only install the cache if there isn't one installed
    // already.
    env.get_cache().install()?;

    let build_mode_check = opts.check;
    let _all = opts.all;

    // Option can be None in which case --all was specified
    let canister_names = config
        .get_config()
        .get_canister_names_with_dependencies(opts.canister_name.as_deref())?;

    // Get pool of canisters to build
    let canister_pool = CanisterPool::load(&env, build_mode_check, &canister_names)?;

    // Create canisters on the replica and associate canister ids locally.
    if build_mode_check {
        slog::warn!(
            env.get_logger(),
            "Building canisters to check they build ok. Canister IDs might be hard coded."
        );
    } else {
        // CanisterIds would have been set in CanisterPool::load, if available.
        // This is just to display an error if trying to build before creating the canister.
        let store = CanisterIdStore::for_env(&env)?;
        for canister in canister_pool.get_canister_list() {
            store.get(canister.get_name())?;
        }
    }

    slog::info!(logger, "Building canisters...");

    canister_pool.build_or_fail(
        &BuildConfig::from_config(&config)?.with_build_mode_check(build_mode_check),
    )?;

    Ok(())
}

sdk/build.rs at 3e3127acb87bd6782a537d96190f0f897655cd5e · dfinity/sdk · GitHub

build_canistersよりだいぶ長いコードに見えますが、最終的には同じcanister_pool.build_or_failが実行されています。


以上より、build_canistersdfx buildも似たような処理をしていることが分かりました。

install_canistersとdfx canister install

最後にinstall_canistersdfx canister installも同じ処理をしているのかを見ていきます。

install_canistersの実装はこちらです。

async fn install_canisters(
    env: &dyn Environment,
    canister_names: &[String],
    initial_canister_id_store: &CanisterIdStore,
    config: &Config,
    argument: Option<&str>,
    argument_type: Option<&str>,
    force_reinstall: bool,
    upgrade_unchanged: bool,
    timeout: Duration,
    call_sender: &CallSender,
) -> DfxResult {
    info!(env.get_logger(), "Installing canisters...");

    let agent = env
        .get_agent()
        .ok_or_else(|| anyhow!("Cannot find dfx configuration file in the current working directory. Did you forget to create one?"))?;

    let canister_id_store = CanisterIdStore::for_env(env)?;

    for canister_name in canister_names {
        let (install_mode, installed_module_hash) = if force_reinstall {
            (InstallMode::Reinstall, None)
        } else {
            match initial_canister_id_store.find(canister_name) {
                Some(canister_id) => {
                    match agent
                        .read_state_canister_info(canister_id, "module_hash", false)
                        .await
                    {
                        Ok(installed_module_hash) => {
                            (InstallMode::Upgrade, Some(installed_module_hash))
                        }
                        // If the canister is empty, this path does not exist.
                        // The replica doesn't support negative lookups, therefore if the canister
                        // is empty, the replica will return lookup_path([], Pruned _) = Unknown
                        Err(AgentError::LookupPathUnknown(_))
                        | Err(AgentError::LookupPathAbsent(_)) => (InstallMode::Install, None),
                        Err(x) => bail!(x),
                    }
                }
                None => (InstallMode::Install, None),
            }
        };

        let canister_id = canister_id_store.get(canister_name)?;
        let canister_info = CanisterInfo::load(config, canister_name, Some(canister_id))?;

        let maybe_path = canister_info.get_output_idl_path();
        let init_type = maybe_path.and_then(|path| get_candid_init_type(&path));
        let install_args = blob_from_arguments(argument, None, argument_type, &init_type)?;

        install_canister(
            env,
            agent,
            &canister_info,
            &install_args,
            install_mode,
            timeout,
            call_sender,
            installed_module_hash,
            upgrade_unchanged,
        )
        .await?;
    }

    Ok(())
}

sdk/deploy_canisters.rs at 1625fefc0e0dc028875b704031dd8f9a3f83cb19 · dfinity/sdk · GitHub

Canisterをfor loopで回してinstall_canisterを実行しています。 ちょうどregister_canistersと似たような実装ですね。


一方dfx canister installの方はというと、

pub async fn exec(
    env: &dyn Environment,
    opts: CanisterInstallOpts,
    call_sender: &CallSender,
) -> DfxResult {
    let config = env.get_config_or_anyhow()?;
    let agent = env
        .get_agent()
        .ok_or_else(|| anyhow!("Cannot get HTTP client from environment."))?;
    let timeout = expiry_duration();

    fetch_root_key_if_needed(env).await?;

    let mode = InstallMode::from_str(opts.mode.as_str()).map_err(|err| anyhow!(err))?;
    let canister_id_store = CanisterIdStore::for_env(env)?;
    let network = env.get_network_descriptor().unwrap();

    if mode == InstallMode::Reinstall && (opts.canister.is_none() || opts.all) {
        bail!("The --mode=reinstall is only valid when specifying a single canister, because reinstallation destroys all data in the canister.");
    }

    if let Some(canister) = opts.canister.as_deref() {
        if config
            .get_config()
            .is_remote_canister(canister, &network.name)?
        {
            bail!("Canister '{}' is a remote canister on network '{}', and cannot be installed from here.", canister, &network.name)
        }

        let canister_id =
            Principal::from_text(canister).or_else(|_| canister_id_store.get(canister))?;
        let canister_info = CanisterInfo::load(&config, canister, Some(canister_id))?;

        let maybe_path = canister_info.get_output_idl_path();
        let init_type = maybe_path.and_then(|path| get_candid_init_type(&path));
        let arguments = opts.argument.as_deref();
        let arg_type = opts.argument_type.as_deref();
        let install_args = blob_from_arguments(arguments, None, arg_type, &init_type)?;
        let installed_module_hash =
            read_module_hash(agent, &canister_id_store, &canister_info).await?;

        install_canister(
            env,
            agent,
            &canister_info,
            &install_args,
            mode,
            timeout,
            call_sender,
            installed_module_hash,
        )
        .await
    } else if opts.all {
        // Install all canisters.

        if let Some(canisters) = &config.get_config().canisters {
            for canister in canisters.keys() {
                if config
                    .get_config()
                    .is_remote_canister(canister, &network.name)?
                {
                    info!(
                        env.get_logger(),
                        "Skipping canister '{}' because it is remote for network '{}'",
                        canister,
                        &network.name,
                    );

                    continue;
                }
                let canister_id =
                    Principal::from_text(canister).or_else(|_| canister_id_store.get(canister))?;
                let canister_info = CanisterInfo::load(&config, canister, Some(canister_id))?;
                let installed_module_hash =
                    read_module_hash(agent, &canister_id_store, &canister_info).await?;

                let install_args = [];

                install_canister(
                    env,
                    agent,
                    &canister_info,
                    &install_args,
                    mode,
                    timeout,
                    call_sender,
                    installed_module_hash,
                )
                .await?;
            }
        }
        Ok(())
    } else {
        unreachable!()
    }
}

sdk/install.rs at 3e3127acb87bd6782a537d96190f0f897655cd5e · dfinity/sdk · GitHub

こちらもcanisterごとにinstall_canisterを実行しています。

install_canistersと同じですね。

まとめ

以上より、

  • dfx deployregister_canisters / build_canisters / install_canisters の三段階の処理で構成される
  • register_canistersdfx canister createコマンドと似たような処理をやっている
  • build_canistersdfx buildコマンドと似たような処理をやっている
  • install_canistersdfx canister installコマンドと似たような処理をやっている
  • よってdfx deployコマンドはdfx canister create / dfx build / dfx canister install の三つのコマンドをまとめたようなものである

ということが理解できました。

おわりに

今回は、dfx deploydfx canister installの違いを整理したうえで、ソースコードを読み込んでみました。

最近Rustに入門したことでDfintiy / Internet Computer関連のソースコードも抵抗なく読めるようになりました。 楽しみがまたひとつ増えたのは嬉しいことです。

今後も都度ソースコードをあたりながら、この界隈を深く楽しんでいければと思っています。

[関連記事]

www.bioerrorlog.work

www.bioerrorlog.work

www.bioerrorlog.work

参考

dfx deploy :: Internet Computer

GitHub - dfinity/sdk: The DFINITY Canister Software Development Kit (SDK)