#cosmwasm #pattern #helper #bucket #singleton #storage #smart-contracts

prov-cosmwasm-storage

CosmWasm库,包含用于存储模式的实用辅助函数

1个版本 (0个不稳定)

1.0.0-provbeta22022年2月23日

#31#singleton

Apache-2.0

58KB
1K SLoC

cosmwasm-storage

cosmwasm-storage on crates.io

CosmWasm库,包含用于存储模式的实用辅助函数。您可以在 cosmwasm-std 中使用 Storage 实现,或者依赖这些辅助函数以删除一些常见的样板代码。

内容

前缀存储

智能合约中的一种常见技术,特别是在存储多种类型的数据时,是创建具有独特前缀的单独子存储。因此,我们不是直接处理存储,而是将其封装,并将所有 Foo 放入具有键 "foo" + id 的存储中,并将所有 Bar 放入具有键 "bar" + id 的存储中。这使得我们能够添加多种类型的对象,而不需要太多的认知开销。类似于Mongo集合或SQL表那样的分离。

由于我们为 StorageReadonlyStorage 使用了不同的类型,我们使用了两个不同的构造函数

use cosmwasm_std::testing::MockStorage;
use cosmwasm_storage::{prefixed, prefixed_read};

let mut store = MockStorage::new();

let mut foos = prefixed(b"foo", &mut store);
foos.set(b"one", b"foo");

let mut bars = prefixed(b"bar", &mut store);
bars.set(b"one", b"bar");

let read_foo = prefixed_read(b"foo", &store);
assert_eq!(b"foo".to_vec(), read_foo.get(b"one").unwrap());

let read_bar = prefixed_read(b"bar", &store);
assert_eq!(b"bar".to_vec(), read_bar.get(b"one").unwrap());

请注意,在某一时刻只能有一个对底层存储的可变引用有效。编译器看到我们在构造 bars 之后从未使用过 foos,所以这个例子是有效的。然而,如果我们再次在底部使用 foos,它将正确地抱怨违反了唯一可变引用。

关键是要在需要时创建 PrefixedStorage 对象,并且不要长时间保留它们。

类型存储

随着我们将存储空间划分为不同的子空间或“桶”,我们很快就会注意到每个“桶”都在处理一个独特的类型。这导致大量重复的序列化和反序列化样板代码,可以去除。我们通过将 Storage 封装在具有类型感知的 TypedStorage 结构体中来实现这一点,该结构体为我们提供了对数据的更高级访问。

请注意,TypedStorage 本身没有实现 Storage 接口,因此在与 PrefixStorage 结合时,请确保首先包装前缀。

use cosmwasm_std::testing::MockStorage;
use cosmwasm_storage::{prefixed, typed};

let mut store = MockStorage::new();
let mut space = prefixed(b"data", &mut store);
let mut bucket = typed::<_, Data>(&mut space);

// save data
let data = Data {
    name: "Maria".to_string(),
    age: 42,
};
bucket.save(b"maria", &data).unwrap();

// load it properly
let loaded = bucket.load(b"maria").unwrap();
assert_eq!(data, loaded);

// loading empty can return Ok(None) or Err depending on the chosen method:
assert!(bucket.load(b"john").is_err());
assert_eq!(bucket.may_load(b"john"), Ok(None));

除了基本的 saveloadmay_load,还提供了一个更高级的 API,即 updateUpdate 将加载数据,执行操作并再次保存(如果操作成功)。它还会返回发生的任何错误,或者在成功的情况下写入的最终状态。

let on_birthday = |mut m: Option<Data>| match m {
    Some(mut d) => {
        d.age += 1;
        Ok(d)
    },
    None => NotFound { kind: "Data" }.fail(),
};
let output = bucket.update(b"maria", &on_birthday).unwrap();
let expected = Data {
    name: "Maria".to_string(),
    age: 43,
};
assert_eq!(output, expected);

Bucket

由于上述习语(一个物品类别的子空间)非常常见且有用,而且没有简单的方法可以从函数中返回它(bucket 包含对空间的引用,且不能比局部变量存活时间更长),因此通常将它们合并成一个 Bucket。Bucket 的工作方式与上面的例子类似,只是创建可以在另一个函数中完成。

use cosmwasm_std::StdResult;
use cosmwasm_std::testing::MockStorage;
use cosmwasm_storage::{bucket, Bucket};

fn people<'a, S: Storage>(storage: &'a mut S) -> Bucket<'a, S, Data> {
    bucket(b"people", storage)
}

fn do_stuff() -> StdResult<()> {
    let mut store = MockStorage::new();
    people(&mut store).save(b"john", &Data{
        name: "John",
        age: 314,
    })?;
    OK(())
}

Singleton

Singleton 是围绕 TypedStorage API 的另一个包装器。有些情况下,我们不需要整个子空间来存储任意类型的键值查找,而只需要一个存储键。最简单的例子是某个合约的 配置 信息。例如,在 名称服务示例 中,有一个 Bucket 用于查找名称到名称数据,但我们还有一个 Singleton 来存储全局配置——即购买名称的价格。

请注意,在此上下文中,“singleton” 一词并不指代 单例模式,而是一个包含单个元素的容器。

use cosmwasm_std::{Coin, coin, StdResult};
use cosmwasm_std::testing::MockStorage;

use cosmwasm_storage::{singleton};

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct Config {
    pub purchase_price: Option<Coin>,
    pub transfer_price: Option<Coin>,
}

fn initialize() -> StdResult<()> {
    let mut store = MockStorage::new();
    let config = singleton(&mut store, b"config");
    config.save(&Config{
        purchase_price: Some(coin("5", "FEE")),
        transfer_price: None,
    })?;
    config.update(|mut cfg| {
        cfg.transfer_price = Some(coin(2, "FEE"));
        Ok(cfg)
    })?;
    let loaded = config.load()?;
    OK(())
}

Singleton 的工作方式与 Bucket 类似,只是 saveloadupdate 方法不需要键,并且 update 要求对象已经存在,因此闭包的类型为 T,而不是 Option<T>。 (使用 save 首次创建对象)。对于 Buckets,我们通常不知道哪些键存在,但 Singleton 应在合约实例化时初始化。

由于许多智能合约代码的核心仅仅是某些存储状态的转换,我们可能只需编写状态转换,让 TypedStorage API 处理所有样板代码。

许可协议

此包是 cosmwasm 存储库的一部分,根据 Apache License 2.0 许可(请参阅 NOTICELICENSE)。

依赖关系

~4–5.5MB
~125K SLoC