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}