安装

依赖:ubuntu、python3.7+

sudo apt install -y libgmp3-dev python3-dev
python3 -m venv ~/cairo_venv
source ~/cairo_venv/bin/activate

确保 venv 已激活 - 您应该(cairo_venv)在命令行提示符中看到。

pip3 install ecdsa fastecdsa sympy cairo-lang

编译和运行 Cairo 程序

创建一个名为test.cairo的文件

其中包含以下几行:

func main():
    [ap] = 1000; ap++
    [ap] = 2000; ap++
    [ap] = [ap - 2] + [ap - 1]; ap++
    ret
end

编译

cairo-compile test.cairo --output test_compiled.json

运行

cairo-run \
  --program=test_compiled.json --print_output \
  --print_info --relocate_prints

调试

您可以通过提供–tracer标志来打开开罗跟踪器cairo-run。然后在 http://localhost:8100/ 打开它。

编写第一份合约

StarkNet 是一个无需许可的去中心化 ZK-Rollup,作为以太坊上的 L2 网络运行,任何 dApp 都可以在其中实现无限规模的计算,而不会影响以太坊的可组合性和安全性。

主网上的 StarkNet Alpha
StarkNet 核心合约:0xc662c410C0ECf747543f5bA90660f6ABeBD9C8c4

定序器网址:https://alpha-mainnet.starknet.io

代码参考:https://github.com/xJonathanLEI/starknet-l1-contracts

Goerli 上的 StarkNet Alpha 版本 4
StarkNet 核心合约:0xde29d060D45901Fb19ED6C6e959EB22d8626708e

音序器网址:https://alpha4.starknet.io

让我们从下面的 StarkNet 合约开始:

创建一个名为的文件并将contract.cairo合约代码复制到其中。

# Declare this file as a StarkNet contract and set the required
# builtins.
%lang starknet
%builtins pedersen range_check

from starkware.cairo.common.cairo_builtins import HashBuiltin

# Define a storage variable.
@storage_var
func balance() -> (res : felt):
end

# Increases the balance by the given amount.
@external
func increase_balance{
        syscall_ptr : felt*, pedersen_ptr : HashBuiltin*,
        range_check_ptr}(amount : felt):
    let (res) = balance.read()
    balance.write(res + amount)
    return ()
end

# Returns the current balance.
@view
func get_balance{
        syscall_ptr : felt*, pedersen_ptr : HashBuiltin*,
        range_check_ptr}() -> (res : felt):
    let (res) = balance.read()
    return (res)
end

%lang starknet是声明使用的编译器。因此使用cairo-compile将编译失败。应使用starknet-compile代替。

编译

starknet-compile contract.cairo \
    --output contract_compiled.json \
    --abi contract_abi.json

生成了abi和编译后的json。
abi里显示有increase_balanceget_balance两个接口。

在 StarkNet 测试网上部署合约

您应该--network=alpha-goerli在每次使用时传递,或者STARKNET_NETWORK按如下方式设置环境变量:(下文均使用了此环境变量)

export STARKNET_NETWORK=alpha-goerli

部署

starknet deploy --contract contract_compiled.json

得到以下结果:

Deploy transaction was sent.
Contract address: 0x03d2c69f4b89395b062da026c9fabb396b21883f7eb9c2b5cde4fcbcd8120c3f
Transaction hash: 0x7638d01c1736f6329b13cdfcf298e45e0e1cfd460d1e701a040f3cda3233275

Contract address: 部署的合约地址
Transaction hash: 交易hash

与合约交互

运行以下命令来调用该increase_balance()函数。--address填对应上文部署的合约地址。

starknet invoke \
    --address 0x03d2c69f4b89395b062da026c9fabb396b21883f7eb9c2b5cde4fcbcd8120c3f \
    --abi contract_abi.json \
    --function increase_balance \
    --inputs 1234

结果应如下所示:

Invoke transaction was sent.
Contract address: 0x03d2c69f4b89395b062da026c9fabb396b21883f7eb9c2b5cde4fcbcd8120c3f
Transaction hash: 0x159b6df5a1a375c2e08244b0276678ceb4abbb9e18546592ae7865362c85441

查询状态

starknet tx_status --hash 0x159b6df5a1a375c2e08244b0276678ceb4abbb9e18546592ae7865362c85441

状态可能会经过PENDING最终到达ACCEPTED:

{
    "block_hash": "0x34cb1372fa583d5d08eaeba9ba6a9b8fa791df2184b143307b07a08b952db9a",
    "tx_status": "ACCEPTED_ON_L2"
}

可能的状态是:

  • NOT_RECEIVED:尚未收到交易(即未写入存储)。
  • RECEIVED:事务已被定序器接收。
  • PENDING:交易通过验证,进入待处理区块。
  • REJECTED:交易验证失败,因此被跳过。
  • ACCEPTED_ON_L2:交易通过验证并进入实际创建的区块。
  • ACCEPTED_ON_L1:交易在链上被接受。

查询余额

使用以下命令查询当前余额:

starknet call \
    --address 0x03d2c69f4b89395b062da026c9fabb396b21883f7eb9c2b5cde4fcbcd8120c3f \
    --abi contract_abi.json \
    --function get_balance

结果应该是:

1234

请注意,要查看最新余额,您应该等到increase_balance 交易状态至少为ACCEPTED_ON_L2(即ACCEPTED_ON_L2或ACCEPTED_ON_L1)。否则,您将看到increase_balance交易执行前的余额(即 0)。

获取交易

starknet get_transaction --hash 0x159b6df5a1a375c2e08244b0276678ceb4abbb9e18546592ae7865362c85441
starknet get_transaction_receipt --hash 0x159b6df5a1a375c2e08244b0276678ceb4abbb9e18546592ae7865362c85441

获取代码

starknet get_code --contract_address 0x03d2c69f4b89395b062da026c9fabb396b21883f7eb9c2b5cde4fcbcd8120c3f

获取块

starknet get_block --number 41301

通过key来查询state存储的值

编写以下python代码查询key值。
此处对应@storage_varbalance

from starkware.starknet.public.abi import get_storage_var_address

balance_key = get_storage_var_address('balance')
print(f'Balance key: {balance_key}')

你应该得到:

Balance key: 916907772491729262376534102982219947830828984996257231353398618781993312401

现在,您可以使用以下方法查询余额:

starknet get_storage_at \
    --contract_address 0x03d2c69f4b89395b062da026c9fabb396b21883f7eb9c2b5cde4fcbcd8120c3f \
    --key 916907772491729262376534102982219947830828984996257231353398618781993312401

使用我们目前使用的同一个合约,你应该得到:

0x4d2

请注意,这与调用get_balance获得的结果相同。

也可以添加–block_hash指定特定块高查询的结果。

starknet get_storage_at \
    --contract_address 0x03d2c69f4b89395b062da026c9fabb396b21883f7eb9c2b5cde4fcbcd8120c3f \
    --key 916907772491729262376534102982219947830828984996257231353398618781993312401 \
    --block_hash 0x109decabe47793bead0be4ba2f06835e1b4ede7575cb60b6bd7d29762b3d34e

结果:

0x0

修改合约使每个人有独立的balance

存储映射

使用kvmap存数据

# A map from user (public key) to a balance.
@storage_var
func balance(user : felt) -> (res : felt):
end

验证签名

我们现在必须修改increase_balance以执行以下操作:

  • 不同用户写入相应的balance条目。
  • 验证用户是否对此次更改操作签名(ACL)。

我们将需要ecdsa内置程序来验证签名,因此我们将%builtins 行更改为:

%builtins pedersen range_check ecdsa

并添加以下导入语句:

from starkware.cairo.common.cairo_builtins import (
    HashBuiltin, SignatureBuiltin)
from starkware.cairo.common.hash import hash2
from starkware.cairo.common.signature import (
    verify_ecdsa_signature)
from starkware.starknet.common.syscalls import get_tx_signature

接下来,我们将代码更改increase_balance()为:

Increases the balance of the given user by the given amount.

@external
func increase_balance{
        syscall_ptr : felt*, pedersen_ptr : HashBuiltin*,
        range_check_ptr, ecdsa_ptr : SignatureBuiltin*}(
        user : felt, amount : felt):
    # Fetch the signature.
    let (sig_len : felt, sig : felt*) = get_tx_signature()

    # Verify the signature length.
    assert sig_len = 2

    # Compute the hash of the message.
    # The hash of (x, 0) is equivalent to the hash of (x).
    let (amount_hash) = hash2{hash_ptr=pedersen_ptr}(amount, 0)

    # Verify the user's signature.
    verify_ecdsa_signature(
        message=amount_hash,
        public_key=user,
        signature_r=sig[0],
        signature_s=sig[1])

    let (res) = balance.read(user=user)
    balance.write(user, res + amount)
    return ()
end

verify_ecdsa_signature行为类似于断言——如果签名无效,该函数将恢复整个交易。

请注意,我们在这里没有设置重放保护——一旦用户签署交易,有人可能会多次调用它。防止重放攻击的一种方法是向increase_balance中添加一个nonce参数,将签名消息更改为随机数和数量的Pedersen哈希,并定义另一个从签名消息到标志(0 或 1)的存储映射,指示是否交易由系统执行。StarkNet的未来版本将处理用户身份验证并防止重放攻击。

同样,更改get_balance(). 这里我们不需要验证签名(因为 StarkNet 的存储无论如何都不是私有的),所以更改更简单:

更改get_balance()

@view
func get_balance{
        syscall_ptr : felt*, pedersen_ptr : HashBuiltin*,
        range_check_ptr}(user : felt) -> (res : felt):
    let (res) = balance.read(user=user)
    return (res)
end

编译和部署

将新合同文件另存为user_auth.cairo

starknet-compile user_auth.cairo \
    --output user_auth_compiled.json \
    --abi user_auth_abi.json

starknet deploy --contract user_auth_compiled.json

得到:

Deploy transaction was sent.
Contract address: 0x00a22e04b15bac3e899d500ed13ef411ac7b8ceab2711a286abcc1c332f1562a
Transaction hash: 0x6f6d8f253dfaef85398cac92bbd784f43c5f349908cd63cc24f8bcc6056b8

与合约交互

需要一对公私钥进行签名,使用python生成:

from starkware.crypto.signature.signature import (
    pedersen_hash, private_to_stark_key, sign)
private_key = 123456 #实际中私钥应该尽量复杂
message_hash = pedersen_hash(4321) # 4321是调用传入的amount
public_key = private_to_stark_key(private_key)
signature = sign(
    msg_hash=message_hash, priv_key=private_key)
print(f'Public key: {public_key}')
print(f'Signature: {signature}')

得到:

Public key: 3512654880572580671014088124487384125967296770469815068887364768195237224797
Signature: (108302296121512525602232466769839751031019513985679519327892998945991838582, 2367704193695023621669265858663986904236455946141267548614905838978060564822)
更新余额
starknet invoke \
    --address 0x00a22e04b15bac3e899d500ed13ef411ac7b8ceab2711a286abcc1c332f1562a \
    --abi user_auth_abi.json \
    --function increase_balance \
    --inputs \
        3512654880572580671014088124487384125967296770469815068887364768195237224797 \
        4321 \
    --signature \
        108302296121512525602232466769839751031019513985679519327892998945991838582 \
        2367704193695023621669265858663986904236455946141267548614905838978060564822

得到:

Invoke transaction was sent.
Contract address: 0x00a22e04b15bac3e899d500ed13ef411ac7b8ceab2711a286abcc1c332f1562a
Transaction hash: 0x2fe59707d58ce8cd3ac10372979a80453a0ab78dbe00646738ffa55137f5ece
查询状态和结果
starknet tx_status --hash 0x2fe59707d58ce8cd3ac10372979a80453a0ab78dbe00646738ffa55137f5ece
starknet call \                                                   
    --address 0x00a22e04b15bac3e899d500ed13ef411ac7b8ceab2711a286abcc1c332f1562a \
    --abi user_auth_abi.json \
    --function get_balance \
    --inputs 3512654880572580671014088124487384125967296770469815068887364768195237224797

最终tx_status改为ACCEPTEDget_balance改为4321

通过get_storage_var_address需要传入用户的pubkey

from starkware.starknet.public.abi import get_storage_var_address

user = 3512654880572580671014088124487384125967296770469815068887364768195237224797
user_balance_key = get_storage_var_address('balance', user)
print(f'Storage key for user {user}:\n{user_balance_key}')
如果使用无效签名

tx_status将会REJECTED
使用--contract--error_message回溯错误

starknet tx_status \
    --hash TX_HASH \
    --contract user_auth_compiled.json \
    --error_message

构造函数

使用@constructor 声明

@constructor
func constructor{
        syscall_ptr : felt*, pedersen_ptr : HashBuiltin*,
        range_check_ptr}(owner_address : felt):
    owner.write(value=owner_address)
    return ()
end

在deploy时加--inputs

starknet-compile ownable.cairo \
    --output ownable_compiled.json \
    --abi ownable_abi.json
starknet deploy --contract ownable_compiled.json --inputs 123

更多功能

  1. 多个值的存储变量
# A mapping from user to a pair (min, max).
@storage_var
func range(user : felt) -> (res : (felt, felt)):
end

# 读取和写入
@external
func extend_range{
        syscall_ptr : felt*, pedersen_ptr : HashBuiltin*,
        range_check_ptr}(user : felt):
    let (min_max) = range.read(user)
    range.write(user, (min_max[0] - 1, min_max[1] + 1))
    return ()
end
  1. 结构参数的存储变量
# 存储变量的参数也可以是结构体或元组,只要它们不包含指针
struct User:
    member first_name : felt
    member last_name : felt
end

# A mapping from a user to 1 if they voted and 0 otherwise.
@storage_var
func user_voted(user : User) -> (res : felt):
end

@external
func vote{
        syscall_ptr : felt*, pedersen_ptr : HashBuiltin*,
        range_check_ptr}(user : User):
    user_voted.write(user, 1)
    return ()
end
  1. 外部函数使用数组作为参数

在外部函数中使用数组参数的StarkNet合约必须内置range_check,用于验证数组的长度是否为非负。

@external
func compare_arrays(
        a_len : felt, a : felt*, b_len : felt, b : felt*):
    assert a_len = b_len
    if a_len == 0:
        return ()
    end
    assert a[0] = b[0]
    return compare_arrays(
        a_len=a_len - 1, a=a + 1, b_len=b_len - 1, b=b + 1)
end
  1. 传入参数包含数组长度和元素
starknet invoke \
    --address CONTRACT_ADDRESS \
    --abi contract_abi.json \
    --function compare_arrays \
    --inputs 4 10 20 30 40 2 50 60
  1. 在 calldata 中传递元组和结构
    Calldata 参数和返回值可以是不包含指针的任何类型。例如,带有毛毡成员的结构、毛毡元组和毛毡元组元组。例如:
struct Point:
    member x : felt
    member y : felt
end

@view
func sum_points(points : (Point, Point)) -> (res : Point):
    return (
        res=Point(
        x=points[0].x + points[1].x,
        y=points[0].y + points[1].y))
end

为了sum_points使用点调用,您应该将以下输入传递给:(1, 2), (10, 20)starknet call

starknet call \
    --address CONTRACT_ADDRESS \
    --abi contract_abi.json \
    --function sum_points \
    --inputs 1 2 10 20

合约相互调用

编写合约test_proxy.cario调用之前部署的合约:

%lang starknet
%builtins pedersen range_check

from starkware.cairo.common.cairo_builtins import HashBuiltin

@contract_interface
namespace IBalanceContract:
    func increase_balance(amount : felt):
    end

    func get_balance() -> (res : felt):
    end
end

@external
func call_increase_balance{syscall_ptr : felt*, range_check_ptr}(
        contract_address : felt, amount : felt):
    IBalanceContract.increase_balance(
        contract_address=contract_address, amount=amount)
    return ()
end

@view
func call_get_balance{syscall_ptr : felt*, range_check_ptr}(
        contract_address : felt) -> (res : felt):
    let (res) = IBalanceContract.get_balance(
        contract_address=contract_address)
    return (res=res)
end

编译部署

# 编译
starknet-compile test_proxy.cairo \
    --output test_proxy_compiled.json \
    --abi test_proxy_abi.json

# 部署
starknet deploy --contract test_proxy_compiled.json 

# 合约地址及hash
Deploy transaction was sent.
Contract address: 0x02a828cb2ee25629d597625df71ac41779fc9166624654ec52bcc51e77ae75a6
Transaction hash: 0x63e09a708a40d9a6c2e6f33f19d451eb0c774fc2c37db1fa8fe2245c5117c0c

调用

starknet invoke \
    --address 0x02a828cb2ee25629d597625df71ac41779fc9166624654ec52bcc51e77ae75a6 \
    --abi test_proxy_abi.json \     
    --function call_increase_balance \
    --inputs "0x03d2c69f4b89395b062da026c9fabb396b21883f7eb9c2b5cde4fcbcd8120c3f" 10000

Invoke transaction was sent.
Contract address: 0x02a828cb2ee25629d597625df71ac41779fc9166624654ec52bcc51e77ae75a6
Transaction hash: 0x6fa1501929fdf6971dd35935eb3bdfda2e64668a16d77a665d3555d9992518c

查询值变更

starknet call \                                                                            
    --address 0x03d2c69f4b89395b062da026c9fabb396b21883f7eb9c2b5cde4fcbcd8120c3f \
    --abi contract_abi.json \
    --function get_balance
11234

获取调用者地址

get_caller_address()您可以使用库函数检索调用您的函数的合约的地址

from starkware.starknet.common.syscalls import get_caller_address

# ...

let (caller_address) = get_caller_address()

注: 如果调用者是用户则返回0,如果是合约则返回合约地址。

获取当前合约的地址

您可以使用get_contract_address()库函数获取当前合约的地址。

from starkware.starknet.common.syscalls import (
    get_contract_address)

# ...

let (contract_address) = get_contract_address()

以上与Solidity中的address(this)类似。

未完,待续。。。

StarkNet

StarkNet是基于STARK零知识证明方案的L2的ZK-Rollup.

RoadMap

分四个步骤构建StarkNet:

  • 阶段0——地基(Foundations)(已完成*)
  • 阶段I——行星(Planets):单一应用汇总(Rollup)
  • 阶段II——星座(Constellations):多应用汇总
  • 阶段III——宇宙(Universe):去中心化汇总

StarkNet基于图灵完备的Cairo语言,支持以太坊上通用的计算。

在StarkNet上,开发者可以构建应用并部署;用户可以发起交易并在StarkNet上执行; 节点可以通过激励保证网络良好运转。

StarkNet的所有交易周期性地批量打包到一个proof中,在以太坊上验证。

所有构建StarkNet的数据在链上(on-chain)发布。

TPS

在主网上使用单个证明处理 30 万笔交易,在 Rollup 吞吐量上创下了高达 3000 tps 的世界记录。在此过程中,我们同样在 Rollup 的 gas 效率上创下每笔交易 315 gas 的世界纪录,比以太坊 L1 上的交易便宜几个数量级。

阶段

行星:只支持单一 App 的 Rollup

在该阶段,开发者可以在 StarkNet 上构建并部署他们自己的可扩展应用。

每个 StarkNet 实例都能运行一个应用。不同的实例可能会运行不同的应用。

StarkNet 架构包含以下部分:

  • 为任意 Cairo 逻辑生成 STARK 证明、然后提交证明并在以太坊上验证所需的机制
  • 与 L1 以太坊交互:L1 代币的存取、链上数据的发布、保护用户免受恶意 StarkNet 运营者攻击的逃跑机制
  • L2 用户余额以及应用存储项的管理

开发者只需专注于根据自己的商业逻辑构建应用,然后正式上线:在 StarkNet 上部署并大规模运行。

我们之所以能构建通用计算可扩展 ZK-Rollup,主要依赖于以下两点:

  • 通用型图灵完备的编程语言 Cairo
  • 我们强大的 STARK 技术(证明器和验证器),可以将大量计算捆绑到一个证明中

星群:支持多 App 的 Rollup

该阶段将支持多个应用在同一个 StarkNet 实例上运行,并访问同一个的全局 L2 状态。这样一来,不同应用之间将具有互操作性,而且规模经济也有助于降低 gas 成本。

强大的 STARK 栈 Cairo 和 GPS 增强了 StarkNet 在支持多 App Rollup 方面的竞争优势。

在这一阶段,StarkNet 将成为一个功能完备的框架,可以在以太坊的基础上运行多个商业逻辑不同的应用,每个实例都由一个单独的运营者来运行。

运营者可以运行 StarkNet 节点,应用开发者可以在上面部署自己的合约。从用户的角度来说,StarkNet 感觉上跟以太坊差不多,但是在可扩展性上强于以太坊。

宇宙:去中心化 Rollup

StarkNet 发展的最后一步是实现去中心化运营。

关于该阶段,我们现在正在解决一些有趣的研发问题,包括:(i)使用 ZK-Rollups 来改进共识机制,以及 (ii)设计密码学经济机制来激励去中心化 StarkNet 贡献者和运营者(交易排序者、证明者等)高效、公平且安全地运行。

STARK

什么是 STARK

STARK 是一个证明系统。

复杂度:

  • 证明者时空复杂度:O(nlogn)O(n\log{}n) 准线性
  • 见证长度和验证时间:O(log2n)O(log_{2}n) 亚线性

在没有Cairo之前我们需要对每个问题都去创建特殊的代数中间件(arithmetic intermediate representation, AIR)。Cairo相当于建立了一个通用的AIR,类似ASIC->CPU的转换。

生产级图灵完备 STARK

概念: 一个链下证明者,它处理大量计算(如大批量交易),并产生指数级较小的有效性证明,然后在链上进行验证。

什么是 Cairo

Cairo 是图灵完备的高级编程语言和框架,可以为通用计算生成 STARK 证明。应用开发者可以使用 Cairo 定义任何商业逻辑,在链下生成证明,并在链上进行验证,而无需自己编写复杂的“电路”或 AIR。Cairo 已在主网上线,向所有开发者开放。

我们将在以太坊公共测试网上发布 Cairo 的通用证明服务(Generic Proof Service ,GPS)的 alpha 版本。通过该服务,开发者可以使用 Cairo 构建自己的应用,实现他们想要的一切商业逻辑。他们会将自己的 Cairo 代码发送至 GPS 来生成证明,再在链上进行验证。

GPS 可以使用单个证明来证明多个独立应用的执行完整性,让这些应用可以分摊证明验证的 gas 成本。

Cairo 和 GPS 都是 StarkNet 的基础。我们决定将二者开放给外部开发者,让他们早些接触 StarkNet 技术。这样一来,开发者不仅可以开始在 StarkNet 的基础上进行构建,还能影响 StarkNet 的发展。

我们会基于开发者社区的需求和反馈继续开发 Cairo。我们会引入新的功能、语法和能够提高其可用性的内部插件来强化该语言。我们会继续开发并改进 Cairo 工具:编译器、跟踪器/调试器以及与通用 IDE(集成开发环境)的集成。

StarkNet 也会在底层运行 Cairo。

Cairo 与区块链结合

今天,大多数区块链 dApp 基本上是一个实现某种逻辑的 Solidity 合约,可能是一些用于良好用户体验的前端,也可能是后端。这些 dApp 成功后,不可避免地面临可扩展性问题。

我们越来越多地看到 dApp 通过转向基于证明的 L2 可扩展性解决方案(如带有 StarkEx 的 DeversiFi)来解决其可扩展性问题。链下组件接管了业务逻辑的一些更复杂的部分,并与链上智能合约进行通信,而不会放弃安全性,因为系统状态的所有更改都经过证明。规模得到改善,因为验证链上证明比完全在链上执行业务逻辑要便宜得多。

在开罗之前,创建一个涵盖特定业务逻辑的证明系统很困难(就像通过在硅晶片上放置与非门来为该业务逻辑构建芯片一样)。在开罗,使用证明来实现可扩展性的障碍要低得多。你在开罗编写你的复杂逻辑,在链下证明它(我们将在一分钟内解释如何),一旦证明在链上得到验证,你的智能合约应用程序就可以无信任地使用结果——就好像它执行了那样链上的复杂逻辑,因为这就是证明所断言的。

如何工作

以太坊上的三件事是昂贵的:计算、传输和存储。开罗解决了所有三个问题。要了解它是如何做到的,我们需要引入一个新概念——共享证明器(或 SHARP)。

SHARP 是您的开罗代码和 Solidity 智能合约之间的连接链接。它具有三个主要组成部分——证明者(链下)、验证者智能合约(链上)和事实登记合约(链上)。

证明者获取您的开罗程序的执行跟踪,证明它是有效的,并将此证明发送给验证者。检验证明后,上链验证需要一个重要的额外的步骤:将其写入一个事实,证明在事实登记证明的有效性。这个事实就像一个无需信任的批准印章,证明开罗计划的计算是正确的。现在 dApp 的智能合约剩下的就是检查这个事实是否存在,以便依赖链下执行的计算。

让我们看一个玩具示例——一个基于区块链的数独游戏,提出正确解决方案的人将获得奖励。

今天,您需要实现验证难题的解决方案在 Solidity 中是否正确的整个业务逻辑——这是在链上执行的昂贵计算。相反,您可以在开罗编写此逻辑,然后在链外执行。Cairo 程序将检查解决方案,然后触发 SHARP 生成证明,在链上验证它并写下一个事实——一个批准该解决方案是正确解决方案的印章。数独的智能合约检查该事实是否存在,并向获胜者支付奖金。我们节省了解决方案验证的昂贵计算和链上数独游戏的解决方案的传输

dApp 智能合约的角色发生了变化——从负责执行昂贵的业务逻辑的组件变成了负责处理该业务逻辑的廉价后果的组件(在我们的示例中,将奖品发送给获胜者或更新一些state),同时依赖于业务逻辑被正确执行的事实。

我们在这篇博文中讨论了计算和传输——我们将在以后的博文中讨论开罗如何解决以太坊上的存储问题。

STARK的性能对比

STARK生成过程


通常 zk-STARK 证明需要以下过程生成证明。验证者的证明过程分为两步:

  1. 第一步为计算完整性声明经过算术后生成代数中间代码表示
  2. 第二步为经过测试后生成 FRI (StarkWare Co-Founder 2017年所著论文中对证明的优化方法)。

之后经过加密算法后输出可拓展的公开透明知识论据 (也就是 STARK)。简而言之就是通过安全可信的环境以及优化算法生成一个可信并且高性能的证明。

参考资料

https://www.youtube.com/watch?v=khVPrv69Zd4&t=108s
https://medium.com/nethermind-eth/solidity-on-starknet-terminal-velocity-e8df5f63e010
https://www.cairo-lang.org/cairo-for-blockchain-developers/
https://eprint.iacr.org/2021/1063.pdf
https://www.jianshu.com/p/6ce113baa464
https://ethfans.org/posts/on-the-road-to-starknet-a-permissionless-stark-powered-l2-zk-rollup
https://www.ise.tu-berlin.de/fileadmin/fg308/publications/2018/Off-chaining_Models_and_Approaches_to_Off-chain_Computations.pdf

介绍

Immutable X是一个基于StarkWare的layer2网络使用Cairo编写的NFT跨链和交易应用。用户可以在此网络内进行快速低成本的交易。并且由于StarkNet底层使用了ZK-Rollup的方案,相比传统sidechain拥有更高的去中心化安全性、即时交易确认、高tps、抗DDos等优点。

原理

Immutable X使用ZK-Rollup,将layer2上的一定量的交易打包,批量生成有效的证明,提交到layer1上的智能合约进行验证。通过验证后即可完成“跨链”的存取操作。

Immutable X的使用

在Immutable X集成ERC721

先决条件:

  1. ERC721的部署:在layer1上部署继承了Immutable X的Mintable合约的ERC721。
  2. 合约注册:上文所述的合约要在Immutable X登记。
  3. 用户注册:用户使用前也需要先登记。目的是为了将layer1私钥与layer2私钥进行绑定。

ERC721的部署

在layer1上部署继承了Immutable X的Mintable合约的ERC721。确保IMX拥有铸币权。

Mintable.sol
此合约内部提供了一个mintFor方法确保IMX拥有铸币权。token特性相关的代码需要实现_mintFor方法。

modifier onlyIMX() {
        require(msg.sender == imx, "Function can only be called by IMX");
        _;
    }
​
    function mintFor(
        address user,
        uint256 quantity,
        bytes calldata mintingBlob
    ) external override onlyIMX {
        require(quantity == 1, "Mintable: invalid quantity");
        (uint256 id, bytes memory blueprint) = Minting.split(mintingBlob);
        _mintFor(user, id, blueprint);
        blueprints[id] = blueprint;
        emit AssetMinted(user, id, blueprint);
    }
​
    function _mintFor(
        address to,
        uint256 id,
        bytes memory blueprint
    ) internal virtual;

合约注册

部署合约后,需要向 Immutable X 注册。
目前只支持手动验证注册,后续项目方支持自动验证注册。
将以下详细信息,在此处提交给项目方后收到回执邮件。
然后在https://api.x.immutable.com/v1/collections可以查询到我们的合约。

{
  "name": "Collection Name (usually Partner Name)",
  "description": "Some Description of this collection",
  "owner_public_key": "<CHANGE-ME>",
  "contract_address": "<CHANGE-ME>",
  "metadata_api_url": "https://<CHANGE-ME>",
  "icon_url": "https://<CHANGE-ME>",
  "collection_image_url": "https://<CHANGE-ME>"
}

用户注册

将按照EIP-2645对用户的layer1和layer2的私钥进行绑定。

在Immutable X使用

铸币

  1. 用户在layer2用对应的私钥发起铸币请求。
  2. Immutable X Engine对签名、请求、状态等验证同步提交到layer2。
  3. layer2最后根据状态转移进行零知识证明生成proof。
  4. 将证明结果提交给layer1。

资产存款

用户在layer1上发起存款请求,layer2接收到事件后,在layer2上进行铸造。

  1. 用户首先向API请求存款数据。
  2. 用户拿到存款数据后,将token存入layer1的存款合约。
  3. 存款合约发出事件后,Immutable X engine收到事件后在layer2上处理存款请求。
  4. layer2生成proof提交到layer1。

资产提取

用户在layer2上发起提取请求。当确认后,用户可以在layer1上发起请求进行提款。

  1. 用户首先向API请求提款数据。
  2. 用户拿到提款数据后发起提款。
  3. 当layer2协商一致后并且proof成功上链后,layer1用户会收到提款事件。
  4. 用户layer1发起提款,并收到资产。

资产交易

用户交易本身发生在layer2,无须与layer1交互。

  1. 用户首先向API请求卖单数据。
  2. 用户根据数据提交卖单。(此部分没有进行实质上的状态数据变化,所有无须与其他节点同步)
  3. 某用户确认认购后,Immutable Engine将卖单买单数据同步给其他节点。
  4. 生成proof提交到layer1。

参考资料

Cairo合约开发文档 https://www.cairo-lang.org/
immutable x 文档 https://docs.x.immutable.com/docs/
starknet https://starkware.co/starknet/

介绍

Axie Infinity是一个卡片对战类型的gamefi游戏玩家可以通过PVE、PVP对战、获取SLP、购买NFT精灵、繁殖、升级等养成类游戏。
部分代码开源:
public-smart-contracts
ronin-smart-contracts

玩法分析

  1. 用户通过跨链将资产转移到ronin chain。需购买或其他方式得到3个NFT小精灵,进行游戏对战。
  2. 用私钥对邮箱地址签名进行账号绑定。
  3. 玩家进入游戏可以进行PVE、PVP获得SLP。用户等待锁定期后通过链上claim获取SLP。SLP的释放速率受控于项目方。
  4. NFT可以有性繁殖,中间消耗SLP和AXS。
  5. NFT交易市场可以自由买卖、拍卖NFT。
  6. 玩家获取的SLP和AXS可以进行Defi挖矿(Lp挖矿和单币挖矿)。
  7. 对于PVP Rank topN,赛季后会释放奖金激励。

涉及的合约

ronin侧

Ronin Gateway Contract

Ronin和Ethereum的跨链桥

合约地址

ronin:e35d62ebe18413d96ca2a2f7cf215bb21a406b4b

作用

Ronin和Ethereum的跨链桥

核心方法

functionId 调用权限 备注
acknowledWithdrawalOnMainchain(uint256 _withdrawalId) onlyValidator 需要多个validator对取款到ethereum的确认,如果阈值达到设置,则从pending中移除。
submitWithdrawalSignatures(uint256 _withdrawalId,bool _shouldReplace,bytes memory _sig) onlyValidator validator提供取款操作签名给ethereum,当ethereum收集足够的签名时可以被批准取出token
withdrawERC20(address _token, uint256 _amount) user 用户调用取token到ethereum,创建withdrawEntry到pending
depositERCTokenFor(uint256 _depositId, address _owner, address _token, uint32 _standard, uint256 _tokenNumber) onlyValidator validator调用,当满足阈值时可以在ronin取出token

提款到MainChain

sequenceDiagram
participant Validitor
participant User
participant SideChain as SideChain Gateway
participant MainChain as MainChain Gateway

User->>+SideChain:withdrawERC20 (transferFrom token)
Validitor->>SideChain:N x acknowledWithdrawalOnMainchain
Validitor->>SideChain:N x submitWithdrawalSignatures
SideChain-->>-User: N x Signatures
User->>+MainChain:withdrawERC20 (with Signatures)
MainChain-->>-User:token transfer

存款到SideChain

sequenceDiagram
participant Validitor
participant User
participant SideChain as SideChain Gateway
participant MainChain as MainChain Gateway

User->>+MainChain:depositERC20
MainChain-->>-Validitor:emit TokenDeposited
Validitor->>+SideChain:N x depositERCTokenFor
SideChain-->>-User:token transfer

注:涉及weth取出存入与token操作相同此处不再赘述
注:涉及batch操作与原子操作类似此处不再赘述

Ronin Registry Contract

合约地址

ronin:3a860626b0467809d50c58bef89b8ac6247fd62a

作用

维护例如GATEWAY、WETH_TOKEN、VALIDATOR、ACKNOWLEDGEMENT合约名称到地址的注册合约。

Ronin Validator Contract

合约地址

ronin:0000000000000000000000000000000000000011

作用

validator白名单合约,用于添加、删除validator和更改阈值投票。维护Ronin的POA共识。

Smooth Love Potion Contract

合约地址

ronin:a8754b9fa15fc18bb59458815510e40a12cd2014

作用

游戏内的ERC20:SLP,用户进行游戏后服务器发放给玩家的“经验”,用于其他玩法消耗。

核心方法

除常规IERC20外,多了一个checkpoint方法用于“claim”。

function 调用权限 备注
checkpoint(address _owner,uint256 _amount,uint256 _createdAt,bytes _signature) user 用户取出slp时需要服务器给出相对应的签名

Axie Infinity Shard Contract

合约地址

ronin:97a9107c1793bc407d6f527b77e7fff4d812bece

作用

ERC20合约:axs

Axie Contract

合约地址

ronin:32950db2a7164ae833121501c797d79e7b79d74c

作用

ERC721合约:axie。除了正常的IERC721以外,里面包含了一些管理员操作例如:growAxieggToAdult、batchMintAxieggs、mintAxie、和一些改变genes的设置。此部分代码未开源。

Katana Router Contract

合约地址

ronin:7d0556d55ca1a92708681e2e231733ebd922597d

Lp对

slp-weth lp ronin:306a28279d04a47468ed83d55088d0dcd1369294
axs-weth lp ronin:c6344bc1604fcab1a5aad712d766796e2b7a70b9
usdc-weth lp ronin:a7964991f339668107e2b6a6f6b8e8b74aa9d017

AXS Staking Pool Contract

合约地址

ronin:05b0bb3c1c320b280501b86706c3551995bc8571

作用

质押axs挖矿(单币挖矿)

核心方法

function 备注
stake(uint256) 质押axs
restakeRewards() 复投
claimPendingRewards() 获取收益
unstake(uint256) 取出质押

Staking Pool Contracts

合约地址

SLP-WETH LP Staking Pool ronin:d4640c26c1a31cd632d8ae1a96fe5ac135d1eb52
AXS-WETH LP Staking Pool ronin:487671acdea3745b6dac3ae8d1757b44a04bfe8a

作用

质押LP挖矿。

Marketplace Contract

NFT交易二级市场

合约地址

ronin:213073989821f738a7ba3520c3d31a1f9ad31bbd

核心方法

function 备注
settleAuction(address,address,uint256,uint256,uint256) 买NFT
createAuction(uint8[],address[],uint256[],uint256[],uint256[],address[],uint256[]) 创建卖单
cancelAuction(uint256) 撤销卖单
revalidateRelatedAuctions(uint256) 未知,似乎是管理员的操作
sequenceDiagram
participant Consumer
participant Seller
participant Marketplace

Seller->>+Marketplace:createAuction 创建卖单
alt
    Seller->>Marketplace:cancelAuction 撤单
else
    Consumer->>Marketplace:settleAuction 购买
    Marketplace-->>-Consumer:transfer NFT
end

Ethereum侧

Ethereum侧包含gateway、ERC20、ERC721合约与ronin侧大致相同。不同点在于Ethereum上的gateway使用了EIP712方式取款减少交互节约gas。

引言

现如今的智能合约虚拟机是一种对称式的虚拟机。即:Ф(s)->s'。其中s是原状态,Ф是状态转移函数,s'是经过计算后的新状态。由上公式可以得出,全节点验证状态转移的合法性过程需要执行一次相同的计算Ф(s)->s'验证s'是否与链上结果一致。Ф相当于合约代码。如果Ф逻辑较为复杂,这无疑增加的了全节点的性能负担,导致出块时间不平衡的等系统性风险。以太坊通过gas limitblock gas limits限制计算量保证全节点都可以在规定的时间范围内计算出数据结果。但这样同时也限制了Ф的复杂度,合约不能执行复杂的逻辑代码。

MetaProofs

为此根据论文“METAPROOFS”。一种新的理论证明方式引起广泛的讨论。

MetaProofs基于零知识证明,大致原理会生成一个证明(Prover)结果µP证明s通过Ф函数计算出s'。验证(Verifier)的时候只需要将这四个参数传入验证函数中,得到结果为true则可以表示状态转移是合法的。详细步骤如下图所示。

MetaProofs是一个证明Prover复杂度高,但Verifier快的函数。并且验证与Ф函数本身的复杂度无关。
因此将MetaProofs引入区块链,链下Prover生成,链上Verifier。既可以使智能合约处理更复杂的逻辑代码,又可以降低全节点的计算负载。不仅如此MetaProofs也可以生成单个链的证明,从而对分片和layer2有更好性能提升。

注: 此处因作者水平有限,如有理解偏差。欢迎讨论告知。

相关的语言

Lurk

Lurk 是一种用于图灵完备递归 SNARK 的开发中的编程语言:

yatima

yatima 是一个去中心化网络的编程语言

参考文献

https://eprint.iacr.org/2012/495.pdf
https://www.youtube.com/watch?v=iQkedSqQH34

cosmwasm 特点

  • 合约在cosmwasm被称之为actor
  • 异步栈式调用,将每个调用压栈,触发调用弹栈。避免重入攻击风险,并且有跨链优势

actor 调用接口

交互actor的接口封装在WasmMsg结构中,共有5种类型

调用接口 说明
Instantiate 初始化合约入口
Execute 执行合约交易
Migrate 迁移合约至新的代码
UpdateAdmin 升级管理员
ClearAdmin 清除管理员

说明:

  1. 每个actor有个内置的Admin充当管理员的角色。
  2. 只有Admin拥有UpdateAdminClearAdminMigrate调用权限。
  3. Admin成功调用ClearAdmin后,actor将失去管理员角色。
  4. 成功调用Migrate后,只会改变actorcode,地址和状态数据不会改变。
  5. InstantiateExecuteMigrate接口,wasm有对应的事件处理函数。

wasm 调用事件入口

instantiate

处理初始化事件代码逻辑入口

#[entry_point]
pub fn instantiate(
  deps: DepsMut,
  env: Env,
  info: MessageInfo,
  msg: InstantiateMsg,
) -> Result<Response, ContractError> {}

execute

处理交互合约事件代码逻辑入口

#[entry_point]
pub fn execute(
  deps: DepsMut,
  env: Env,
  info: MessageInfo,
  msg: ExecuteMsg,
) -> Result<Response, ContractError> {}

migrate

处理迁移事件代码逻辑入口。
此部分调用逻辑是在更新的代码中触发。

#[entry_point]
pub fn migrate(deps: DepsMut, env: Env, msg: MigrateMsg) -> Result<Response, ContractError> {}

actor 查询接口

查询接口 说明
Smart 查询公共API的接口
Raw 查询kv-store类型的接口

wasm 查询事件入口

对于Smart查询wasm的query接口

#[entry_point]
pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result<Binary, ContractError> {}

使用cw-template模板初始化

环境要求

  • Rust 1.55+

安装cargo-generate

cargo install cargo-generate --features vendored-openssl
cargo install cargo-run-script

创建新的合约

cargo generate --git https://github.com/CosmWasm/cw-template.git --name PROJECT_NAME

使用旧版本需要加上版本号作为参数

cargo generate --git https://github.com/CosmWasm/cw-template.git --branch <version> --name PROJECT_NAME

Example:

cargo generate --git https://github.com/CosmWasm/cw-template.git --branch 0.16 --name PROJECT_NAME

观前提示

以下时间均为北京时间

参与者

角色 地址 备注
攻击者 Exploiter 0x48c94305bddfd80c6f4076963866d968cac27d79 BXH攻击者外部账户
攻击合约 Attacker 0x887716168FC8c1A2933F97ea3858a94c9E229201 BXH攻击者创建的攻击合约(通过此合约攻击BXH策略)创建于 2021-10-27 21:47:34
攻击者收款合约 0x13b81fa9c0873a74c49a85bd8149c1c20bf9d18c 创建于 2021-10-30 11:15:45
BXH策略地址 0x6AcECA12de5a15f11ca51b654433259533B0B802 BXH策略资金地址
BXH管理员 0x56146b129017940d06d8e235c02285a3d05d6b7c 总管理员
BXH访问权限控制合约 0x83dae57c46985c3a2346e8a3c6991ca908f8b06b 用于控制各个合约访问权限

攻击时序图

sequenceDiagram
participant Exploiter as 攻击者 Exploiter
participant Attacker as 攻击合约 Attacker
participant AttackContract as 攻击者收款合约 AttackContract
participant ACL as BXH访问权限控制合约 ACL
participant BXH as BXH策略合约
participant master as BXH管理员
rect rgba(0, 0, 255, .1)
Exploiter->>Attacker: 2021-10-27 21:47:34 创建攻击合约
master->>+ACL: 2021-10-29 16:19:00 给攻击合约提权
ACL-->>-Attacker: 攻击合约获得权限
%% Note over Exploiter,master: 省略:期间尝试七次攻击,均失败
end
Exploiter->>AttackContract: 2021-10-30 11:15:45 创建攻击者收款合约
Exploiter->>+Attacker: 2021-10-30 11:27:47 给攻击者收款合约提权
Attacker-->>+ACL: 访问权限控制合约0xf047601d函数,给攻击者收款合约尝试提权
ACL-->>-AttackContract: ACL内部调用收款合约的0x1f1fcd51。最后给攻击者收款合约添加INTERFACE_ROLE角色
deactivate Attacker
Exploiter->>+Attacker:2021-10-30 11:32:53 第八次发起攻击
Attacker-->>+ACL: 调用ACL的0x8b6b802函数,此函数反编译失败。但猜测内部将资产从策略合约转移给攻击者
ACL-->>+BXH:请求资产转移
BXH-->>-AttackContract:资产转移至攻击者收款合约
deactivate ACL
AttackContract-->>Exploiter:ETH转移至攻击者
deactivate Attacker

注: 攻击者在最后一次成功攻击前,有7次尝试更改逻辑代码的失败攻击。

从攻击者角度去分析

  1. 2021-10-29 16:19:00 未知原因拥有BXH最高权限的管理员给Attacker开启GOVERNANCE_ROLE权限。

  2. 2021-10-30 11:27:47 Exploiter给AttackContract添加INTERFACE_ROLE权限。

  3. 2021-10-30 11:32:53 Exploiter通过GOVERNANCE_ROLE权限对ACL合约发起攻击成功获取资产。

ACL内部角色对应的bytes32的值

ACL内角色 bytes32
keccak256(‘INTERFACE_ROLE’) 0x886f5b5d23a7f8b3645010a8eb98414557d4607145067846682ff187e4950fe3
keccak256(‘WITHDRAW_ROLE’) 0x5d8e12c39142ff96d79d04d15d1ba1269e4fe57bb9d26f43523628b34ba108ec
keccak256(‘GOVERNANCE_ROLE’) 0x71840dc4906352362b0cdaf79870196c8e42acafade72d5d5a6d59291253ceb1
阅读全文 »

rust安装省略

升级rust并安装wasm32 target

rust版本要求1.51.0+

rustup default stable
cargo version
# If this is lower than 1.51.0+, update
rustup update stable

rustup target list --installed
rustup target add wasm32-unknown-unknown # 安装wasm32的target

安装wasmd

git clone https://github.com/CosmWasm/wasmd.git
cd wasmd
# replace the v0.20.0 with the most stable version on https://github.com/CosmWasm/wasmd/releases
git checkout v0.20.0
make install

# verify the installation
wasmd version

建立环境

pebblenet 测试网

Block Explorer: https://block-explorer.pebblenet.cosmwasm.com

设置pebblenet的参数到本地临时环境变量

source <(curl -sSL https://raw.githubusercontent.com/CosmWasm/testnets/master/pebblenet-1/defaults.env)

添加wallet

注意备份助记词

# add wallets for testing
wasmd keys add wallet
# output
>
{
  "name": "wallet",
  "type": "local",
  "address": "wasm13nt9rxj7v2ly096hm8qsyfjzg5pr7vn5saqd50",
  "pubkey": "wasmpub1addwnpepqf4n9afaefugnfztg7udk50duwr4n8p7pwcjlm9tuumtlux5vud6qvfgp9g",
  "mnemonic": "hobby bunker rotate piano satoshi planet network verify else market spring toward pledge turkey tip slim word jaguar congress thumb flag project chalk inspire"
}

wasmd keys add wallet2

Error: No such interface “org.freedesktop.DBus.Properties” on object at path /
:如果你使用的是headless mode的linux这里可能会报错。可以使用以下命令代替:

wasmd keys add wallet --keyring-backend file
# 此命令会在--keyring-dir中写入文件

查看wallet

wasmd keys list #or
wasmd keys list --keyring-backend file

水龙头申请代币

JSON=$(jq -n --arg addr $(wasmd keys show -a wallet) '{"denom":"upebble","address":$addr}') && curl -X POST --header "Content-Type: application/json" --data "$JSON" https://faucet.pebblenet.cosmwasm.com/credit
# 如果使用file的keyring-backend,则使用下列命令
JSON=$(jq -n --arg addr $(wasmd keys show -a wallet --keyring-backend file) '{"denom":"upebble","address":$addr}') && curl -X POST --header "Content-Type: application/json" --data "$JSON" https://faucet.pebblenet.cosmwasm.com/credit

查询余额

区块浏览器查询钱包地址: https://block-explorer.pebblenet.cosmwasm.com/

有用的环境变量

# bash
export NODE="--node $RPC"
export TXFLAG="${NODE} --chain-id ${CHAIN_ID} --gas-prices 0.001upebble --gas auto --gas-adjustment 1.3"

# 如果你用的是zsh则添加以下环境变量
# zsh
export NODE=(--node $RPC)
export TXFLAG=($NODE --chain-id $CHAIN_ID --gas-prices 0.001upebble --gas auto --gas-adjustment 1.3)

下载合约示例代码和编译

git clone https://github.com/InterWasm/cw-contracts
cd cw-contracts
git fetch --tags
git checkout nameservice-0.11.0
cd contracts/nameservice

cargo wasm # 此处若依赖下载报错,Cargo则需要换源
RUSTFLAGS='-C link-arg=-s' cargo wasm #以此命令生成最下wasm

上传和交互

上传

将代码上传到链上

RES=$(wasmd tx wasm store target/wasm32-unknown-unknown/release/cw_nameservice.wasm --from wallet $TXFLAG -y --keyring-backend file)
CODE_ID=$(echo $RES | jq -r '.logs[0].events[-1].attributes[0].value')

# 查找当前codeId生成的contract,如果不存在则返回空
wasmd query wasm list-contract-by-code $CODE_ID $NODE --output json

# 你也可以从链上下载这个wasm并且和本地检查一下是否一致
# you can also download the wasm from the chain and check that the diff between them is empty
wasmd query wasm code $CODE_ID $NODE download.wasm
diff artifacts/cw_nameservice.wasm download.wasm

实例化

# instantiate contract and verify
# 构造函数的参数
INIT='{"purchase_price":{"amount":"100","denom":"upebble"},"transfer_price":{"amount":"999","denom":"upebble"}}'
# 实例化
wasmd tx wasm instantiate $CODE_ID "$INIT" \
    --from wallet --label "awesome name service" $TXFLAG -y --keyring-backend file

# check the contract state (and account balance)
# 再次查询是否有实例化的合约
wasmd query wasm list-contract-by-code $CODE_ID $NODE --output json
CONTRACT=$(wasmd query wasm list-contract-by-code $CODE_ID $NODE --output json | jq -r '.contracts[-1]')
echo $CONTRACT

wasmd query wasm contract $CONTRACT $NODE

# you can dump entire contract state
wasmd query wasm contract-state all $CONTRACT $NODE

# Note that keys are hex encoded, and val is base64 encoded.
# To view the returned data (assuming it is ascii), try something like:
# (Note that in many cases the binary data returned is non in ascii format, thus the encoding)
wasmd query wasm contract-state all $CONTRACT $NODE --output "json" | jq -r '.models[0].key' | xxd -r -ps
wasmd query wasm contract-state all $CONTRACT $NODE --output "json" | jq -r '.models[0].value' | base64 -d

# or try a "smart query", executing against the contract
wasmd query wasm contract-state smart $CONTRACT '{}' $NODE
# (since we didn't implement any valid QueryMsg, we just get a parse error back)

注册和转移name

# execute fails if wrong person
REGISTER='{"register":{"name":"fred"}}'
wasmd tx wasm execute $CONTRACT "$REGISTER" \
    --amount 100upebble \
    --from wallet $TXFLAG -y --keyring-backend file

# query name record
NAME_QUERY='{"resolve_record": {"name": "fred"}}'
wasmd query wasm contract-state smart $CONTRACT "$NAME_QUERY" $NODE --output json
# {"data":{"address":"wasm1av9uhya7ltnusvunyqay3xcv9x0nyc872cheu5"}}

# 转移
# buy and transfer name record to wallet2
TRANSFER='{"transfer":{"name":"fred","to":"wasm1um2e88neq8sxzuuem5ztt9d0em033rpr5ps9tv"}}'
wasmd tx wasm execute $CONTRACT "$TRANSFER" \
    --amount 999upebble \
    --from wallet $TXFLAG -y --keyring-backend file