2 个不稳定版本
0.11.1 | 2024年6月25日 |
---|---|
0.10.0 | 2024年4月25日 |
#2148 in 魔法豆
29 个月下载量
1MB
2K SLoC
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(Ð_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(Ð_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