diff --git a/CHANGELOG.md b/CHANGELOG.md index bacba16471..1ff3bfe35c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ # UNRELEASED +### fix(deps): content of wasm_hash_url can have extra fields than the hash + +It is natural to point `wasm_hash_url` to the `.sha256` file generated by `shasum` or `sha256sum` which consists of the hash and the file name. + +Now when `dfx deps pull`, such content will be accept properly. + # 0.16.1 ### feat: query stats support diff --git a/docs/concepts/pull-dependencies.md b/docs/concepts/pull-dependencies.md index 70a077c34f..76d50c4e40 100644 --- a/docs/concepts/pull-dependencies.md +++ b/docs/concepts/pull-dependencies.md @@ -57,6 +57,10 @@ In other cases, the wasm module at `wasm_url` is not the same as the on-chain wa A URL to get the SHA256 hash of the wasm module located at `wasm_url`. +The content of this URL can be the SHA256 hash only. + +It can also be the output of `shasum` or `sha256sum` which contains the hash and the file name. + This field is optional. Aside from specifying SHA256 hash of the wasm module directly using `wasm_hash`, providers can also specify the hash with this URL. If both are defined, the `wasm_hash_url` field will be ignored. diff --git a/e2e/tests-dfx/deps.bash b/e2e/tests-dfx/deps.bash index c56aa217be..c050e3dbc8 100644 --- a/e2e/tests-dfx/deps.bash +++ b/e2e/tests-dfx/deps.bash @@ -227,12 +227,11 @@ Failed to download from url: http://example.com/c.wasm." # A: set dfx:wasm_hash CUSTOM_HASH_A="$(sha256sum .dfx/local/canisters/a/a.wasm | cut -d " " -f 1)" jq '.canisters.a.pullable.wasm_hash="'"$CUSTOM_HASH_A"'"' dfx.json | sponge dfx.json - # B: set dfx:wasm_hash_url - echo -n "$(sha256sum .dfx/local/canisters/b/b.wasm.gz | cut -d " " -f 1)" > ../www/b.wasm.gz.sha256 + # B: set dfx:wasm_hash_url with output of sha256sum + echo -n "$(sha256sum .dfx/local/canisters/b/b.wasm.gz)" > ../www/b.wasm.gz.sha256 jq '.canisters.b.pullable.wasm_hash_url="'"http://localhost:$E2E_WEB_SERVER_PORT/b.wasm.gz.sha256"'"' dfx.json | sponge dfx.json - # C: set both dfx:wasm_hash and dfx:wasm_hash_url. This should be avoided by providers. + # C: set dfx:wasm_hash_url with the hash only CUSTOM_HASH_C="$(sha256sum .dfx/local/canisters/c/c.wasm | cut -d " " -f 1)" - jq '.canisters.c.pullable.wasm_hash="'"$CUSTOM_HASH_C"'"' dfx.json | sponge dfx.json echo -n "$CUSTOM_HASH_C" > ../www/c.wasm.sha256 jq '.canisters.c.pullable.wasm_hash_url="'"http://localhost:$E2E_WEB_SERVER_PORT/c.wasm.sha256"'"' dfx.json | sponge dfx.json @@ -249,11 +248,21 @@ Failed to download from url: http://example.com/c.wasm." assert_command dfx deps pull --network local -vvv assert_contains "Canister $CANISTER_ID_A specified a custom hash:" assert_contains "Canister $CANISTER_ID_B specified a custom hash via url:" + assert_contains "Canister $CANISTER_ID_C specified a custom hash via url:" + + # warning: specified both `wasm_hash` and `wasm_hash_url`. Providers should avoid this. + PULLED_DIR="$DFX_CACHE_ROOT/.cache/dfinity/pulled/" + rm -r "${PULLED_DIR:?}/" + cd ../onchain + jq '.canisters.c.pullable.wasm_hash="'"$CUSTOM_HASH_C"'"' dfx.json | sponge dfx.json + dfx build + dfx canister install c -m reinstall --argument 3 --yes + + cd ../app + assert_command dfx deps pull --network local -vvv assert_contains "WARN: Canister $CANISTER_ID_C specified both \`wasm_hash\` and \`wasm_hash_url\`. \`wasm_hash\` will be used." - assert_contains "Canister $CANISTER_ID_C specified a custom hash:" # warning: hash mismatch - PULLED_DIR="$DFX_CACHE_ROOT/.cache/dfinity/pulled/" rm -r "${PULLED_DIR:?}/" cd ../onchain cp .dfx/local/canisters/a/a.wasm ../www/a.wasm # now the webserver has the onchain version of canister_a which won't match wasm_hash diff --git a/src/dfx/src/commands/deps/pull.rs b/src/dfx/src/commands/deps/pull.rs index db92b3cb6f..e05c3526b7 100644 --- a/src/dfx/src/commands/deps/pull.rs +++ b/src/dfx/src/commands/deps/pull.rs @@ -305,9 +305,15 @@ async fn get_hash_on_chain( let wasm_hash_content = download_file(&wasm_hash_url) .await .with_context(|| format!("Failed to download wasm_hash from {wasm_hash_url}."))?; - let wasm_hash_encoded = String::from_utf8(wasm_hash_content) + let wasm_hash_str = String::from_utf8(wasm_hash_content) .with_context(|| format!("Content from {wasm_hash_url} is not valid text."))?; - Ok(hex::decode(&wasm_hash_encoded) + // The content might contain the file name (usually from tools like shasum or sha256sum). + // We only need the hash part. + let wasm_hash_encoded = wasm_hash_str + .split_whitespace() + .next() + .with_context(|| format!("Content from {wasm_hash_url} is empty."))?; + Ok(hex::decode(wasm_hash_encoded) .with_context(|| format!("Failed to decode {wasm_hash_encoded} as sha256 hash."))?) } else { match read_state_tree_canister_module_hash(agent, canister_id).await? {