Skip to content

Commit 82dda15

Browse files
fix: Change stored_chunks return type (#512)
1 parent 6330d29 commit 82dda15

File tree

6 files changed

+143
-8
lines changed

6 files changed

+143
-8
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88

99
## Unreleased
1010

11+
* Changed the return type of `stored_chunks` to a struct.
1112
* Added a prime256v1-based `Identity` impl to complement the ed25519 and secp256k1 `Identity` impls.
1213

1314
## [0.32.0] - 2024-01-18

Cargo.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ hex = "0.4.3"
3434
leb128 = "0.2.5"
3535
ring = "0.16.20"
3636
serde = "1.0.162"
37-
serde_bytes = "0.11.9"
37+
serde_bytes = "0.11.13"
3838
serde_cbor = "0.11.2"
3939
serde_json = "1.0.96"
4040
serde_repr = "0.1.12"

ic-utils/src/interfaces/management_canister.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55
use crate::{call::AsyncCall, Canister};
66
use candid::{CandidType, Deserialize, Nat};
77
use ic_agent::{export::Principal, Agent};
8-
use std::{convert::AsRef, fmt::Debug, ops::Deref};
8+
use std::{convert::AsRef, ops::Deref};
99
use strum_macros::{AsRefStr, EnumString};
1010

1111
pub mod attributes;
1212
pub mod builders;
13+
mod serde_impls;
1314
#[doc(inline)]
1415
pub use builders::{
1516
CreateCanisterBuilder, InstallBuilder, InstallChunkedCodeBuilder, InstallCodeBuilder,
@@ -139,6 +140,14 @@ pub struct DefiniteCanisterSettings {
139140
#[derive(Clone, Debug, Deserialize, CandidType)]
140141
pub struct UploadChunkResult {
141142
/// The hash of the uploaded chunk.
143+
#[serde(with = "serde_bytes")]
144+
pub hash: ChunkHash,
145+
}
146+
147+
/// The result of a [`ManagementCanister::stored_chunks`] call.
148+
#[derive(Clone, Debug)]
149+
pub struct ChunkInfo {
150+
/// The hash of the stored chunk.
142151
pub hash: ChunkHash,
143152
}
144153

@@ -367,7 +376,7 @@ impl<'agent> ManagementCanister<'agent> {
367376
pub fn stored_chunks(
368377
&self,
369378
canister_id: &Principal,
370-
) -> impl 'agent + AsyncCall<(Vec<ChunkHash>,)> {
379+
) -> impl 'agent + AsyncCall<(Vec<ChunkInfo>,)> {
371380
#[derive(CandidType)]
372381
struct Argument<'a> {
373382
canister_id: &'a Principal,

ic-utils/src/interfaces/management_canister/builders.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -741,7 +741,7 @@ impl<'agent: 'canister, 'canister: 'builder, 'builder> InstallBuilder<'agent, 'c
741741
)
742742
} else {
743743
let (existing_chunks,) = self.canister.stored_chunks(&self.canister_id).call_and_wait().await?;
744-
let existing_chunks = existing_chunks.into_iter().collect::<BTreeSet<_>>();
744+
let existing_chunks = existing_chunks.into_iter().map(|c| c.hash).collect::<BTreeSet<_>>();
745745
let to_upload_chunks_ordered = self.wasm.chunks(1024 * 1024).map(|x| (<[u8; 32]>::from(Sha256::digest(x)), x)).collect::<Vec<_>>();
746746
let to_upload_chunks = to_upload_chunks_ordered.iter().map(|&(k, v)| (k, v)).collect::<BTreeMap<_, _>>();
747747
let (new_chunks, setup) = if existing_chunks.iter().all(|hash| to_upload_chunks.contains_key(hash)) {
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
use std::fmt::Formatter;
2+
3+
use super::ChunkInfo;
4+
use candid::types::{CandidType, Type, TypeInner};
5+
use serde::de::{Deserialize, Deserializer, Error, IgnoredAny, MapAccess, SeqAccess, Visitor};
6+
use serde_bytes::ByteArray;
7+
// ChunkInfo can be deserialized from both `blob` and `record { hash: blob }`.
8+
// This impl can be removed when both mainnet and dfx no longer return `blob`.
9+
impl CandidType for ChunkInfo {
10+
fn _ty() -> Type {
11+
Type(<_>::from(TypeInner::Unknown))
12+
}
13+
fn idl_serialize<S>(&self, _serializer: S) -> Result<(), S::Error>
14+
where
15+
S: candid::types::Serializer,
16+
{
17+
unimplemented!()
18+
}
19+
}
20+
impl<'de> Deserialize<'de> for ChunkInfo {
21+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
22+
where
23+
D: Deserializer<'de>,
24+
{
25+
deserializer.deserialize_any(ChunkInfoVisitor)
26+
}
27+
}
28+
struct ChunkInfoVisitor;
29+
impl<'de> Visitor<'de> for ChunkInfoVisitor {
30+
type Value = ChunkInfo;
31+
fn expecting(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
32+
formatter.write_str("blob or record {hash: blob}")
33+
}
34+
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
35+
where
36+
E: Error,
37+
{
38+
// deserialize_any combined with visit_bytes produces an extra 6 byte for difficult reasons
39+
let v = if v.len() == 33 && v[0] == 6 {
40+
&v[1..]
41+
} else {
42+
v
43+
};
44+
Ok(ChunkInfo {
45+
hash: v
46+
.try_into()
47+
.map_err(|_| E::invalid_length(v.len(), &"32 bytes"))?,
48+
})
49+
}
50+
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
51+
where
52+
A: SeqAccess<'de>,
53+
{
54+
let mut hash = [0; 32];
55+
for (i, n) in hash.iter_mut().enumerate() {
56+
*n = seq
57+
.next_element()?
58+
.ok_or_else(|| A::Error::invalid_length(i, &"32 bytes"))?;
59+
}
60+
if seq.next_element::<IgnoredAny>()?.is_some() {
61+
Err(A::Error::invalid_length(
62+
seq.size_hint().unwrap_or(33),
63+
&"32 bytes",
64+
))
65+
} else {
66+
Ok(ChunkInfo { hash })
67+
}
68+
}
69+
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
70+
where
71+
A: MapAccess<'de>,
72+
{
73+
while let Some(k) = map.next_key::<Field>()? {
74+
eprintln!("here");
75+
if matches!(k, Field::Hash) {
76+
return Ok(ChunkInfo {
77+
hash: map.next_value::<ByteArray<32>>()?.into_array(),
78+
});
79+
} else {
80+
map.next_value::<IgnoredAny>()?;
81+
}
82+
}
83+
Err(A::Error::missing_field("hash"))
84+
}
85+
}
86+
// Needed because candid cannot infer field names without specifying them in _ty()
87+
enum Field {
88+
Hash,
89+
Other,
90+
}
91+
impl<'de> Deserialize<'de> for Field {
92+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
93+
where
94+
D: Deserializer<'de>,
95+
{
96+
deserializer.deserialize_identifier(FieldVisitor)
97+
}
98+
}
99+
struct FieldVisitor;
100+
impl<'de> Visitor<'de> for FieldVisitor {
101+
type Value = Field;
102+
fn expecting(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
103+
formatter.write_str("a field name")
104+
}
105+
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
106+
where
107+
E: Error,
108+
{
109+
if v == "hash" {
110+
Ok(Field::Hash)
111+
} else {
112+
Ok(Field::Other)
113+
}
114+
}
115+
fn visit_u32<E>(self, v: u32) -> Result<Self::Value, E>
116+
where
117+
E: Error,
118+
{
119+
if v == 1158164430 {
120+
Ok(Field::Hash)
121+
} else {
122+
Ok(Field::Other)
123+
}
124+
}
125+
}

0 commit comments

Comments
 (0)