5个版本
0.2.2 | 2020年7月13日 |
---|---|
0.2.1 | 2020年7月13日 |
0.2.0 | 2020年2月26日 |
0.1.1 | 2020年1月20日 |
0.1.0 | 2020年1月8日 |
#40 in #singleton
每月 101 次下载
在 3 个 包中使用
48KB
901 行
██╗░░░██╗███╗░░██╗███╗░░░███╗░█████╗░██╗███╗░░██╗████████╗░█████╗░██╗███╗░░██╗███████╗██████╗░
██║░░░██║████╗░██║████╗░████║██╔══██╗██║████╗░██║╚══██╔══╝██╔══██╗██║████╗░██║██╔════╝██╔══██╗
██║░░░██║██╔██╗██║██╔████╔██║███████║██║██╔██╗██║░░░██║░░░███████║██║██╔██╗██║█████╗░░██║░░██║
██║░░░██║██║╚████║██║╚██╔╝██║██╔══██║██║██║╚████║░░░██║░░░██╔══██║██║██║╚████║██╔══╝░░██║░░██║
╚██████╔╝██║░╚███║██║░╚═╝░██║██║░░██║██║██║░╚███║░░░██║░░░██║░░██║██║██║░╚███║███████╗██████╔╝
░╚═════╝░╚═╝░░╚══╝╚═╝░░░░░╚═╝╚═╝░░╚═╝╚═╝╚═╝░░╚══╝░░░╚═╝░░░╚═╝░░╚═╝╚═╝╚═╝░░╚══╝╚══════╝╚═════╝░
This repository and the cw-storage crate are unmaintained. All features were moved to the
cosmwasm-storage package in https://github.com/CosmWasm/cosmwasm.
cw-storage
这是一个用于存储模式的CosmWasm库,其中包含有用的辅助函数。这个库不在核心库中,因此您可以根据自己的合约需求自由地进行分叉、修改或扩展。对于添加新功能或改进功能的Pull Requests,我们总是欢迎的。
需要Rust v1.38+(用于生成序列化错误消息的 std::any::type_name
)
与CosmWasm v0.7.x兼容
内容
前缀存储
在智能合约中,尤其是存储多种类型的数据时,创建具有唯一前缀的单独子存储是一个常见的技巧。因此,我们不是直接处理存储,而是将其封装,并将所有 Foo
放入键为 "foo" + id
的存储中,所有 Bar
放入键为 "bar" + id
的存储中。这样,我们可以在不过度增加认知负担的情况下添加多种类型的对象。类似于Mongo集合或SQL表那样的分离。
由于我们为 Storage
和 ReadonlyStorage
使用了不同的类型,因此我们使用了两个不同的构造函数
use cw_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
对象,而不是长时间挂在那里。
类型存储
当我们把存储空间分成不同的子空间或“桶”时,我们会很快注意到每个“桶”都使用一个独特的类型。这导致了很多重复的序列化和反序列化模板代码,可以移除。我们通过封装一个类型感知的 TypedStorage
结构体来这样做,该结构体为我们提供了对数据的更高级访问。
注意,TypedStorage
本身并没有实现 Storage
接口,所以在与 PrefixStorage
结合时,请确保先封装前缀。
use cosmwasm::mock::MockStorage;
use cw_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));
除了基本的 save
、load
和 may_load
,还有一个更高层次的 API 被暴露,即 update
。 Update
将加载数据,应用操作并再次保存(如果操作成功)。它还会返回发生的任何错误,或者在操作成功时写入的最终状态。
let 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", &birthday).unwrap();
let expected = Data {
name: "Maria".to_string(),
age: 43,
};
assert_eq!(output, expected);
Bucket
由于上述惯用语(用于一类项目的子空间)非常常见且有用,而且没有简单的方法从函数中返回它(桶包含对空间的引用,不能比局部变量活得久),因此经常将这两个合并成一个 Bucket
。Bucket 的工作方式与上面的例子一样,只不过创建可以在另一个函数中进行。
use cosmwasm::mock::MockStorage;
use cw_storage::{bucket, Bucket};
fn people<'a, S: Storage>(storage: &'a mut S) -> Bucket<'a, S, Data> {
bucket(b"people", storage)
}
fn do_stuff() -> Result <()> {
let mut store = MockStorage::new();
people(&mut store).save(b"john", &Data{
name: "John",
age: 314,
})?;
OK(())
}
Singleton
单例是 TypedStorage
API 的另一个包装器。有些情况下,我们不需要整个子空间来存储任意类型的键值查找,而只需要一个单一实例。最简单的例子是某个合同的 配置 信息。例如,在 名称服务示例 中,有一个 Bucket
来查找名称到名称的数据,但我们还有一个 Singleton
来存储全局配置——即购买名称的价格。
use cosmwasm::mock::MockStorage;
use cosmwasm::types::{Coin, coin};
use cw_storage::{singleton};
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct Config {
pub purchase_price: Option<Coin>,
pub transfer_price: Option<Coin>,
}
fn initialize() -> Result<()> {
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
一样,只不过 save
、load
、update
方法不需要键,并且 update
需要对象已经存在,因此闭包的类型为 T
,而不是 Option<T>
。(使用 save
在第一次创建对象)。对于 Buckets
,我们通常不知道哪些键存在,但 Singletons
应在合同实例化时初始化。
由于许多智能合约代码的核心只是对某些存储状态进行转换,我们可能只需编写状态转换,让 TypedStorage
API 处理所有模板代码。
依赖关系
~3.5MB
~77K SLoC