#block-hash #steel #ethereum #state #zero #risc #query

risc0-steel

在 RISC Zero zkVM 中查询以太坊状态,或任何其他基于 EVM 的区块链状态。

2 个不稳定版本

0.11.1 2024年6月25日
0.10.0 2024年4月25日

#2148 in 魔法豆

29 个月下载量

Apache-2.0

1MB
2K SLoC

Steel banner

Steel - 为 EVM dapps 提供加固的链下执行

警告 该库正在积极开发中,预计会有破坏性更改。我们不建议在此阶段将 Steel 库用于生产环境。

在以太坊和智能合约领域,在不改变区块链状态的情况下直接从区块链获取数据——称为“查看调用”——是一种基本操作。传统上,这些操作,尤其是在证明和验证链下计算时,涉及一定程度的复杂性:要么是通过需要详细了解槽索引的存储证明机制,要么是通过为特定查询开发电路。

相比之下,该库抽象化了这些复杂性,允许开发者只需定义他们希望调用的 Solidity 方法即可查询以太坊的状态。为了演示使用查看调用库的简单示例,让我们考虑最常见的一个查看调用:查询特定地址的 ERC-20 代币余额。您可以在这里找到完整的示例。

访客代码

以下是从访客的代码片段。

/// Specify the function to call using the [`sol!`] macro.
/// This parses the Solidity syntax to generate a struct that implements the [SolCall] trait.
sol! {
    /// ERC-20 balance function signature.
    interface IERC20 {
        function balanceOf(address account) external view returns (uint);
    }
}

/// Function to call, implements the [SolCall] trait.
const CALL: IERC20::balanceOfCall = IERC20::balanceOfCall {
    account: address!("9737100D2F42a196DE56ED0d1f6fF598a250E7E4"),
};

/// Address of the deployed contract to call the function on (USDT contract on Sepolia).
const CONTRACT: Address = address!("aA8E23Fb1079EA71e0a56F48a2aA51851D8433D0");
/// Address of the caller. If not provided, the caller will be the [CONTRACT].
const CALLER: Address = address!("f08A50178dfcDe18524640EA6618a1f965821715");

fn main() {
    // Read the input from the guest environment.
    let input: EthEvmInput = env::read();

    // Converts the input into a `ViewCallEnv` for execution. The `with_chain_spec` method is used
    // to specify the chain configuration.
    let view_call_env = input.into_env().with_chain_spec(&ETH_SEPOLIA_CHAIN_SPEC);
    // Commit the block hash and number used when deriving `view_call_env` to the journal.
    env::commit_slice(&view_call_env.block_commitment().abi_encode());

    // Execute the view call; it returns the result in the type generated by the `sol!` macro.
    let contract = Contract::new(CONTRACT, &view_call_env);
    let returns = contract.call_builder(&CALL).from(CALLER).call();
    println!("View call result: {}", returns._0);
}

主机代码

以下是从主机的代码片段,它需要与访客相同的参数。

// Create a view call environment from an RPC endpoint and a block number. If no block number is
// provided, the latest block is used.
let mut env = EthViewCallEnv::from_rpc(&args.rpc_url, None)?;
//  The `with_chain_spec` method is used to specify the chain configuration.
env = env.with_chain_spec(&ETH_SEPOLIA_CHAIN_SPEC);

// Preflight the call to construct the input that is required to execute the function in
// the guest. It also returns the result of the call.
let mut contract = Contract::preflight(CONTRACT, &mut env);
let returns = contract.call_builder(&CALL).from(CALLER).call()?;
let input = env.into_input()?;

以太坊集成

Steel 可以与Bonsai Foundry 模板集成。验证 Groth16 证明的以太坊合同也必须验证 ViewCallEnv 承诺。

以下是一个使用Solidity Steel库实现验证的示例。日志包含承诺以及附加数据

struct Journal {
    Steel.Commitment commitment;
    address tokenAddress;
}

function validate(bytes calldata journalData, bytes calldata seal) external {
    Journal memory journal = abi.decode(journalData, (Journal));
    require(Steel.validateCommitment(journal.commitment), "Invalid commitment");
    verifier.verify(seal, imageId, sha256(journalData));
}

创建日志的客户端代码如下

use risc0_steel::SolCommitment;

sol! {
    struct Journal {
        SolCommitment commitment;
        address tokenAddress;
    }
}

...

let journal = Journal {
    commitment: view_call_env.block_commitment(),
    tokenAddress,
};
env::commit_slice(&journal.abi_encode());

我们还提供了一个示例,erc20-counter,展示了这种集成。

区块哈希验证

由于内部使用blockhash操作码进行验证,承诺不能超过256个区块。考虑到12秒的区块时间,这允许有超过50分钟的时间来创建证明并确保验证交易被包含在区块中。在许多情况下,这将非常有效:即使是非常大的计算,如证明整个Ethereum区块,只要有足够的资源,也可以在50分钟内完成。

对于需要验证超过256个区块的旧区块哈希的场景

  • 如果提前知道,将所需的区块哈希保存到合约状态(例如,在启动治理提案时)。
  • 使用RISC Zero证明从查询的区块到最近256个区块内的区块哈希链。

依赖项

~25–42MB
~794K SLoC