axone_objectarium/msg.rs
1use cosmwasm_schema::{cw_serde, QueryResponses};
2use cosmwasm_std::{Binary, Uint128};
3use derive_builder::Builder;
4use enum_iterator::{all, Sequence};
5
6/// ObjectId is the type of identifier of an object in the bucket.
7pub type ObjectId = String;
8
9/// Cursor is the opaque type of cursor used for pagination.
10pub type Cursor = String;
11
12/// Instantiate messages
13#[cw_serde]
14pub struct InstantiateMsg {
15 /// The name of the bucket.
16 /// The name could not be empty or contains whitespaces.
17 /// If name contains whitespace, they will be removed.
18 pub bucket: String,
19 /// The configuration of the bucket.
20 #[serde(default)]
21 pub config: BucketConfig,
22 /// The limits of the bucket.
23 #[serde(default)]
24 pub limits: BucketLimits,
25 /// The configuration for paginated query.
26 #[serde(default)]
27 pub pagination: PaginationConfig,
28}
29
30/// Execute messages
31#[cw_serde]
32pub enum ExecuteMsg {
33 /// # StoreObject
34 /// StoreObject store an object to the bucket and make the sender the owner of the object.
35 /// The object is referenced by the hash of its content and this value is returned.
36 /// If the object is already stored, it is a no-op. It may be pinned though.
37 ///
38 /// The "pin" parameter specifies whether the object should be pinned for the sender. Pinning an
39 /// object ensures it is protected from being removed from storage, making it persistent and
40 /// guaranteeing its indefinite accessibility. It’s important to note that pinning is optional;
41 /// objects can be stored without pinning. However, be aware that non-pinned objects can be removed
42 /// from the storage by anyone at any time, making them no longer accessible.
43 ///
44 /// The "compression_algorithm" parameter specifies the algorithm for compressing the object before
45 /// storing it in the storage, which is optional. If no algorithm is specified, the algorithm used
46 /// is the first algorithm of the bucket configuration limits. Note that the chosen algorithm can
47 /// save storage space, but it will increase CPU usage. Depending on the chosen compression algorithm
48 /// and the achieved compression ratio, the gas cost of the operation will vary, either increasing or decreasing.
49 StoreObject {
50 /// The content of the object to store.
51 data: Binary,
52 /// Specifies whether the object should be pinned for the sender.
53 /// Pinning ensures the object remains persistent and cannot be removed from storage by anyone.
54 pin: bool,
55 /// Specifies the compression algorithm to use when storing the object.
56 /// If None, the first algorithm specified in the list of accepted compression algorithms of the bucket
57 /// is used (see [BucketLimits::accepted_compression_algorithms]).
58 compression_algorithm: Option<CompressionAlgorithm>,
59 },
60
61 /// # ForgetObject
62 /// ForgetObject first unpins the object from the bucket for the sender, then removes
63 /// it from storage if it is no longer pinned by anyone.
64 /// If the object is still pinned by other senders, it is not removed from storage and an error is returned.
65 /// If the object is not pinned for the sender, this operation is a no-op.
66 ForgetObject { id: ObjectId },
67
68 /// # PinObject
69 /// PinObject pins the object in the bucket for the sender. If the object is already pinned
70 /// for the sender, this operation is a no-op.
71 /// While an object is pinned, it cannot be removed from storage.
72 PinObject { id: ObjectId },
73
74 /// # UnpinObject
75 /// UnpinObject unpins the object in the bucket for the sender. If the object is not pinned
76 /// for the sender, this operation is a no-op.
77 /// The object can be removed from storage if it is no longer pinned by anyone.
78 UnpinObject { id: ObjectId },
79}
80
81/// Query messages
82#[cw_serde]
83#[derive(QueryResponses)]
84pub enum QueryMsg {
85 /// # Bucket
86 /// Bucket returns the bucket information.
87 #[returns(BucketResponse)]
88 Bucket {},
89
90 /// # Object
91 /// Object returns the object information with the given id.
92 #[returns(ObjectResponse)]
93 Object {
94 /// The id of the object to get.
95 id: ObjectId,
96 },
97
98 /// # Objects
99 /// Objects returns the list of objects in the bucket with support for pagination.
100 #[returns(ObjectsResponse)]
101 Objects {
102 /// The owner of the objects to get.
103 address: Option<String>,
104 /// The number of objects to return.
105 first: Option<u32>,
106 /// The point in the sequence to start returning objects.
107 after: Option<Cursor>,
108 },
109
110 /// # ObjectData
111 /// ObjectData returns the content of the object with the given id.
112 #[returns(Binary)]
113 ObjectData {
114 /// The id of the object to get.
115 id: ObjectId,
116 },
117
118 /// # ObjectPins
119 /// ObjectPins returns the list of addresses that pinned the object with the given id with
120 /// support for pagination.
121 #[returns(ObjectPinsResponse)]
122 ObjectPins {
123 /// The id of the object to get the pins for.
124 id: ObjectId,
125 /// The number of pins to return.
126 first: Option<u32>,
127 /// The point in the sequence to start returning pins.
128 after: Option<Cursor>,
129 },
130}
131
132/// # PageInfo
133/// PageInfo is the page information returned for paginated queries.
134#[cw_serde]
135pub struct PageInfo {
136 /// Tells if there is a next page.
137 pub has_next_page: bool,
138 /// The cursor to the next page.
139 pub cursor: Cursor,
140}
141
142/// # BucketResponse
143/// BucketResponse is the response of the Bucket query.
144#[cw_serde]
145pub struct BucketResponse {
146 /// The name of the bucket.
147 pub name: String,
148 /// The configuration of the bucket.
149 pub config: BucketConfig,
150 /// The limits of the bucket.
151 pub limits: BucketLimits,
152 /// The configuration for paginated query.
153 pub pagination: PaginationConfig,
154 /// The statistics of the bucket.
155 pub stat: BucketStat,
156}
157
158/// CompressionAlgorithm is an enumeration that defines the different compression algorithms
159/// supported for compressing the content of objects.
160/// The compression algorithm specified here are relevant algorithms for compressing data on-chain,
161/// which means that they are fast to compress and decompress, and have a low computational cost.
162///
163/// The order of the compression algorithms is based on their estimated computational cost (quite opinionated)
164/// during both compression and decompression, ranging from the lowest to the highest. This particular
165/// order is utilized to establish the default compression algorithm for storing an object.
166#[cw_serde]
167#[derive(Copy, Eq, PartialOrd, Sequence)]
168pub enum CompressionAlgorithm {
169 /// # Passthrough
170 /// Represents no compression algorithm.
171 /// The object is stored as is without any compression.
172 Passthrough,
173 /// # Snappy
174 /// Represents the Snappy algorithm.
175 /// Snappy is a compression/decompression algorithm that does not aim for maximum compression. Instead, it aims for very high speeds and reasonable
176 /// compression.
177 ///
178 /// See [the snappy web page](https://google.github.io/snappy/) for more information.
179 Snappy,
180 /// # Lzma
181 /// Represents the LZMA algorithm.
182 /// LZMA is a lossless data compression/decompression algorithm that features a high compression ratio and a variable compression-dictionary size up to 4 GB.
183 ///
184 /// See [the LZMA wiki page](https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Markov_chain_algorithm) for more information.
185 Lzma,
186}
187
188/// HashAlgorithm is an enumeration that defines the different hash algorithms
189/// supported for hashing the content of objects.
190#[cw_serde]
191#[derive(Copy)]
192pub enum HashAlgorithm {
193 /// # MD5
194 /// Represents the MD5 algorithm.
195 /// MD5 is a widely used cryptographic hash function that produces a 128-bit hash value.
196 /// The computational cost of MD5 is relatively low compared to other hash functions, but its short hash length
197 /// makes it easier to find hash collisions. It is now considered insecure for cryptographic purposes, but can still
198 /// used in non-security contexts.
199 ///
200 /// MD5 hashes are stored on-chain as 32 hexadecimal characters.
201 ///
202 /// See [the MD5 Wikipedia page](https://en.wikipedia.org/wiki/MD5) for more information.
203 MD5,
204
205 /// # SHA1
206 /// Represents the SHA-224 algorithm.
207 /// SHA-224 is a variant of the SHA-2 family of hash functions that produces a 224-bit hash value.
208 /// It is similar to SHA-256, but with a shorter output size.
209 /// The computational cost of SHA-224 is moderate, and its relatively short hash length makes it easier to store
210 /// and transmit.
211 ///
212 /// SHA-224 hashes are stored on-chain as 56 hexadecimal characters.
213 ///
214 /// See [the SHA-2 Wikipedia page](https://en.wikipedia.org/wiki/SHA-2) for more information.
215 Sha224,
216
217 /// # SHA256
218 /// Represents the SHA-256 algorithm.
219 /// SHA-256 is a member of the SHA-2 family of hash functions that produces a 256-bit hash value.
220 /// It is widely used in cryptography and other security-related applications.
221 /// The computational cost of SHA-256 is moderate, and its hash length strikes a good balance between security
222 /// and convenience.
223 ///
224 /// SHA-256 hashes are stored on-chain as 64 hexadecimal characters.
225 ///
226 /// See [the SHA-2 Wikipedia page](https://en.wikipedia.org/wiki/SHA-2) for more information.
227 Sha256,
228
229 /// # SHA384
230 /// Represents the SHA-384 algorithm.
231 /// SHA-384 is a variant of the SHA-2 family of hash functions that produces a 384-bit hash value.
232 /// It is similar to SHA-512, but with a shorter output size.
233 /// The computational cost of SHA-384 is relatively high, but its longer hash length provides better security
234 /// against hash collisions.
235 ///
236 /// SHA-384 hashes are stored on-chain as 96 hexadecimal characters.
237 ///
238 /// See [the SHA-2 Wikipedia page](https://en.wikipedia.org/wiki/SHA-2) for more information.
239 Sha384,
240
241 /// # SHA512
242 /// Represents the SHA-512 algorithm.
243 /// SHA-512 is a member of the SHA-2 family of hash functions that produces a 512-bit hash value.
244 /// It is widely used in cryptography and other security-related applications.
245 /// The computational cost of SHA-512 is relatively high, but its longer hash length provides better security
246 /// against hash collisions.
247 ///
248 /// SHA-512 hashes are stored on-chain as 128 hexadecimal characters.
249 ///
250 /// See [the SHA-2 Wikipedia page](https://en.wikipedia.org/wiki/SHA-2) for more information.
251 Sha512,
252}
253
254impl Default for HashAlgorithm {
255 fn default() -> Self {
256 Self::Sha256
257 }
258}
259
260/// BucketConfig is the type of the configuration of a bucket.
261///
262/// The configuration is set at the instantiation of the bucket, and is immutable and cannot be changed.
263/// The configuration is optional and if not set, the default configuration is used.
264#[cw_serde]
265#[derive(Builder)]
266#[builder(default, setter(into, strip_option))]
267pub struct BucketConfig {
268 /// The algorithm used to hash the content of the objects to generate the id of the objects.
269 /// The algorithm is optional and if not set, the default algorithm is used.
270 ///
271 /// The default algorithm is Sha256 if not set.
272 #[serde(default)]
273 pub hash_algorithm: HashAlgorithm,
274 /// The acceptable compression algorithms for the objects in the bucket.
275 /// If this parameter is not set, then all compression algorithms are accepted.
276 /// If this parameter is set, then only the compression algorithms in the array are accepted.
277 ///
278 /// When an object is stored in the bucket without a specified compression algorithm, the first
279 /// algorithm in the array is used. Therefore, the order of the algorithms in the array is significant.
280 /// Typically, the most efficient compression algorithm, such as the NoCompression algorithm, should
281 /// be placed first in the array.
282 ///
283 /// Any attempt to store an object using a different compression algorithm than the ones specified
284 /// here will fail.
285 #[serde(default = "CompressionAlgorithm::values")]
286 pub accepted_compression_algorithms: Vec<CompressionAlgorithm>,
287}
288
289impl Default for BucketConfig {
290 fn default() -> Self {
291 Self {
292 hash_algorithm: Default::default(),
293 accepted_compression_algorithms: CompressionAlgorithm::values(),
294 }
295 }
296}
297
298impl CompressionAlgorithm {
299 pub fn values() -> Vec<CompressionAlgorithm> {
300 all::<CompressionAlgorithm>().collect::<Vec<_>>()
301 }
302}
303
304/// BucketLimits is the type of the limits of a bucket.
305///
306/// The limits are optional and if not set, there is no limit.
307#[cw_serde]
308#[derive(Default, Builder)]
309#[builder(default, setter(into, strip_option))]
310pub struct BucketLimits {
311 /// The maximum total size of the objects in the bucket.
312 pub max_total_size: Option<Uint128>,
313 /// The maximum number of objects in the bucket.
314 pub max_objects: Option<Uint128>,
315 /// The maximum size of the objects in the bucket.
316 pub max_object_size: Option<Uint128>,
317 /// The maximum number of pins in the bucket for an object.
318 pub max_object_pins: Option<Uint128>,
319}
320
321/// PaginationConfig is the type carrying configuration for paginated queries.
322///
323/// The fields are optional and if not set, there is a default configuration.
324#[cw_serde]
325#[derive(Builder)]
326#[builder(default, setter(strip_option))]
327pub struct PaginationConfig {
328 /// The maximum elements a page can contain.
329 ///
330 /// Shall be less than `u32::MAX - 1`.
331 /// Default to '30' if not set.
332 #[serde(default = "PaginationConfig::default_page_max_size")]
333 pub max_page_size: u32,
334 /// The default number of elements in a page.
335 ///
336 /// Shall be less or equal than `max_page_size`.
337 /// Default to '10' if not set.
338 #[serde(default = "PaginationConfig::default_page_default_size")]
339 pub default_page_size: u32,
340}
341
342impl PaginationConfig {
343 const fn default_page_max_size() -> u32 {
344 30
345 }
346 const fn default_page_default_size() -> u32 {
347 10
348 }
349}
350
351impl Default for PaginationConfig {
352 fn default() -> Self {
353 PaginationConfig {
354 max_page_size: Self::default_page_max_size(),
355 default_page_size: Self::default_page_default_size(),
356 }
357 }
358}
359
360/// # BucketStat
361///
362/// BucketStat is the type of the statistics of a bucket.
363#[cw_serde]
364#[derive(Default, Builder)]
365pub struct BucketStat {
366 /// The total size of the objects contained in the bucket.
367 pub size: Uint128,
368 /// The total size of the objects contained in the bucket after compression.
369 pub compressed_size: Uint128,
370 /// The number of objects in the bucket.
371 pub object_count: Uint128,
372}
373
374/// # ObjectResponse
375/// ObjectResponse is the response of the Object query.
376#[cw_serde]
377pub struct ObjectResponse {
378 /// The id of the object.
379 pub id: ObjectId,
380 /// The owner of the object.
381 pub owner: String,
382 /// Tells if the object is pinned by at least one address.
383 pub is_pinned: bool,
384 /// The size of the object.
385 pub size: Uint128,
386 /// The size of the object when compressed. If the object is not compressed, the value is the
387 /// same as `size`.
388 pub compressed_size: Uint128,
389 /// The compression algorithm used to compress the content of the object.
390 pub compression_algorithm: CompressionAlgorithm,
391}
392
393/// # ObjectsResponse
394/// ObjectsResponse is the response of the Objects query.
395#[cw_serde]
396pub struct ObjectsResponse {
397 /// The list of objects in the bucket.
398 pub data: Vec<ObjectResponse>,
399 /// The page information.
400 pub page_info: PageInfo,
401}
402
403/// # ObjectPinsResponse
404/// ObjectPinsResponse is the response of the GetObjectPins query.
405#[cw_serde]
406pub struct ObjectPinsResponse {
407 /// The list of addresses that pinned the object.
408 pub data: Vec<String>,
409 /// The page information.
410 pub page_info: PageInfo,
411}
412
413#[cfg(test)]
414mod tests {
415 use crate::msg::CompressionAlgorithm::{Lzma, Passthrough, Snappy};
416 use crate::msg::HashAlgorithm::Sha256;
417 use crate::msg::{BucketConfig, BucketLimits, InstantiateMsg, PaginationConfig};
418 use schemars::_serde_json;
419
420 #[test]
421 fn pagination_config_default_deserialization() {
422 let json = r#"
423 {}
424 "#;
425
426 let page: PaginationConfig = _serde_json::from_str(json).unwrap();
427 assert_eq!(page.max_page_size, 30);
428 assert_eq!(page.default_page_size, 10);
429 }
430
431 #[test]
432 fn bucket_config_default_deserialization() {
433 let json = r#"
434 {}
435 "#;
436
437 let config: BucketConfig = _serde_json::from_str(json).unwrap();
438 assert_eq!(config.hash_algorithm, Sha256);
439 assert_eq!(
440 config.accepted_compression_algorithms,
441 vec![Passthrough, Snappy, Lzma]
442 );
443 }
444
445 #[test]
446 fn bucket_limit_default_deserialization() {
447 let json = r#"
448 {}
449 "#;
450
451 let limits: BucketLimits = _serde_json::from_str(json).unwrap();
452 assert_eq!(limits.max_object_pins, None);
453 assert_eq!(limits.max_objects, None);
454 assert_eq!(limits.max_object_size, None);
455 assert_eq!(limits.max_total_size, None);
456 }
457
458 #[test]
459 fn instantiate_default_deserialization() {
460 let json = r#"
461 {
462 "bucket": "foo"
463 }
464 "#;
465 let msg: InstantiateMsg = _serde_json::from_str(json).unwrap();
466
467 assert_eq!(msg.pagination.max_page_size, 30);
468 assert_eq!(msg.pagination.default_page_size, 10);
469 assert_eq!(msg.config.hash_algorithm, Sha256);
470 assert_eq!(
471 msg.config.accepted_compression_algorithms,
472 vec![Passthrough, Snappy, Lzma]
473 );
474 assert_eq!(msg.limits.max_object_pins, None);
475 assert_eq!(msg.limits.max_objects, None);
476 assert_eq!(msg.limits.max_object_size, None);
477 assert_eq!(msg.limits.max_total_size, None);
478 }
479}