5.2 合约存储(Contract Storage)

When developing a smart contract, you will typically need some sort of persistent storage. In this case, persistent storage, often just called storage in this context, is a place where you can store values that are persisted inside the contract itself. This is in contrast to a regular value in memory, which disappears after the contract exits.

在开发智能合约时,你通常需要某种持久性存储。在这种情况下,持久性存储,在这里通常被称为 storage,是一个你可以在合约本身中持久存储值的地方。这与 memory 中的普通值不同,后者在合约退出后就会消失。

Put in conventional programming terms, contract storage is like saving data to a hard drive. That data is saved even after the program which saved it exits. That data is persistent. Using memory is like declaring a variable in a program: it exists for the duration of the program and is non-persistent.

用传统的编程术语来说,合约存储就像把数据保存在硬盘上。即使在保存数据的程序退出后,这些数据也会被保存。该数据是持久的。使用(传统)存储就像在程序中声明一个变量:它存在于程序的持续时间内,是不持久的。

Some basic use cases of storage include declaring an owner address for a contract and saving balances in a wallet.

存储的一些基本用例,包括为一个合约声明一个所有者地址,以及在钱包中保存余额。

通过storage关键字进行存储访问 (Storage Accesses Via the storage Keyword)

Declaring variables in storage requires a storage declaration that contains a list of all your variables, their types, and their initial values as follows:

在存储中声明变量需要一个storage声明,其中包含所有变量的列表,它们的类型,以及它们的初始值,如下所示:

struct Type1 {
    x: u64,
    y: u64,
}

struct Type2 {
    w: b256,
    z: bool,
}

storage {
    var1: Type1 = Type1 { x: 0, y: 0 },
    var2: Type2 = Type2 {
        w: 0x0000000000000000000000000000000000000000000000000000000000000000,
        z: false,
    },
}

To write into a storage variable, you need to use the storage keyword as follows:

要写进一个存储变量,你需要使用storage关键字,如下所示:

    #[storage(write)]
    fn store_something() {
        storage.var1.x.write(42);
        storage.var1.y.write(77);
        storage.var2.w.write(0x1111111111111111111111111111111111111111111111111111111111111111);
        storage.var2.z.write(true);
    }

To read a storage variable, you also need to use the storage keyword as follows:

要读取一个存储变量,你还需要使用storage关键字,如下所示:

    #[storage(read)]
    fn get_something() -> (u64, u64, b256, bool) {
        (
            storage.var1.x.read(),
            storage.var1.y.read(),
            storage.var2.w.read(),
            storage.var2.z.read(),
        )
    }

存储映射 (Storage Maps)

Generic storage maps are available in the standard library as StorageMap<K, V> which have to be defined inside a storage block and allow you to call insert() and get() to insert values at specific keys and get those values respectively. Refer to Storage Maps for more information about StorageMap<K, V>.

通用的存储映射在标准库中以 StorageMap<K, V>的形式存在,必须在 storage 区块中定义,并允许你调用 insert()get()来分别在特定的键上插入值和获取这些值。关于 StorageMap<K, V>的更多信息,请参考Storage Maps

手动存储管理 (Manual Storage Management)

It is possible to leverage FuelVM storage operations directly using the std::storage::storage_api::write and std::storage::storage_api::read functions provided in the standard library. With this approach you will have to manually assign the internal key used for storage. An example is as follows:

可以直接使用标准库中提供的std::storage::storage_api::writestd::storage_api::read函数来利用FuelVM的存储操作。使用这种方法,你将不得不手动分配用于存储的内部密钥。一个例子是这样的:

contract;

use std::storage::storage_api::{read, write};

abi StorageExample {
    #[storage(write)]
    fn store_something(amount: u64);

    #[storage(read)]
    fn get_something() -> u64;
}

const STORAGE_KEY: b256 = 0x0000000000000000000000000000000000000000000000000000000000000000;

impl StorageExample for Contract {
    #[storage(write)]
    fn store_something(amount: u64) {
        write(STORAGE_KEY, 0, amount);
    }

    #[storage(read)]
    fn get_something() -> u64 {
        let value: Option<u64> = read::<u64>(STORAGE_KEY, 0);
        value.unwrap_or(0)
    }
}

Note: Though these functions can be used for any data type, they should mostly be used for arrays because arrays are not yet supported in storage blocks. Note, however, that all data types can be used as types for keys and/or values in StorageMap<K, V> without any restrictions.

注意: 虽然这些函数可以用于任何数据类型,但它们应该主要用于数组,因为数组在storage 区块中还不被支持。但是请注意,_所有的数据类型都可以在StorageMap<K, V>中作为键和/或值的类型,没有任何限制。

Last updated