Starknet 训练营-课程笔记3
为什么在合约中表示金额时更推荐使用 u256 而不是 felt252?
在 Starknet 的 Cairo 开发中,选择 u256 而不是 felt252 来表示金额是一个非常关键的工程实践。这不仅关乎安全性,也关乎与区块链生态(尤其是以太坊)的兼容性。
针对你提到的几个观点,我们来做一下事实核查和深度解析:
1. 核心差异对比
| 特性 | felt252 (Field Element) |
u256 (Unsigned 256-bit Integer) |
|---|---|---|
| 数值范围 | (其中 是一个约 252 位的质数) | |
| 溢出行为 | 模运算(不会 Panic) | 触发 Panic(默认检测溢出) |
| 语义化 | 密码学原语,不直观 | 标准整数,符合金融直觉 |
| 兼容性 | 仅限 Starknet 内部 | 完美适配 ERC-20 等以太坊标准 |
2. 深度解析:为什么选 u256?
防止隐式溢出(关键安全点)
你说 u256 会在溢出时触发 panic,这是完全正确的。
- felt252 的陷阱:当你对
felt252进行加法运算并超过其质数模 时,它会发生“回绕”(Wrap around),且不会报错。这在处理金额时是致命的,可能导致黑客通过溢出手段盗取资金。 - u256 的保护:Cairo 中的
u256实际上是由两个u128组成的结构体。在进行算术运算时,标准库会自动检查溢出。如果结果超出 ,合约会立即终止,保护资产安全。
数值精度的匹配
以太坊上的 ERC-20 代币标准规定金额通常为 256 位。
felt252的最大值略小于 。- 如果你用
felt252来接收一个从以太坊跨链过来的 大小的金额,felt252根本存不下,会导致数据丢失或截断。
纠正两个误区
- 关于字符串:其实
u256并不直接“支持字符串”,它本质上还是数字。但在 Cairo 中,你可以方便地将短字符串(Short strings)转换为数值存储。 - 关于性能:实际上,
felt252的性能反而更好。因为它是 Cairo 的底层原生类型,所有的计算都是 的 CPU 指令。u256作为结构体,其加减乘除需要多步felt252运算组合而成。我们选择u256是为了安全牺牲了少许性能。
3. 你的“排除错误选项”逻辑
正如你之前提到的:“当我排除了所有错误选项,那么剩下的就是正确选项。”
在金融合约设计中:
- 排除
felt252:因为它会隐式溢出(错误选项)。 - 排除
felt252:因为它无法覆盖 的完整范围(错误选项)。 - **剩下
u256**:虽然它更“重”,但它在逻辑上是严密的、安全的、符合行业标准的。
4. 总结
在 Starknet 上写书或开发合约时,记住这个原则:底层的密码学逻辑(如签名验证)用 felt252,业务上的资产金额、计数器一律使用 u256。 这不仅是对权威(底层架构)的理解,更是对自己代码逻辑负责的“预判”。
既然你正在筹备写书,需要我帮你用 Cairo 语言写一个简单的金额溢出对比案例吗?我可以演示 u256 是如何通过 Panic 保护资金安全的。
Cairo 中的变量默认是?可变的;不可变的
你想知道Cairo语言中变量的默认可变性,核心结论先明确:
正确答案
Cairo 中的变量默认是【不可变的(immutable)】 ✅
详细解析
1. 核心规则:默认不可变,显式声明可变
Cairo 借鉴了 Rust 等语言的设计理念,所有变量默认具备不可变性,目的是提升代码的安全性和可预测性(尤其是在处理资产的智能合约场景中,避免意外修改变量导致的逻辑错误)。
- 若要让变量可修改,必须显式添加
mut关键字; - 未加
mut的变量,赋值后无法二次修改,编译时会直接报错。
2. 代码示例(直观理解)
1 | |
3. 设计初衷(为什么默认不可变?)
Cairo 作为面向 ZK-Rollup 智能合约的语言,安全性是核心:
- 减少意外修改:默认不可变可避免开发者误改关键变量(如金额、权限参数),降低资产安全漏洞;
- 提升可证明性:不可变变量的执行轨迹更稳定,有助于 CairoVM 生成准确的 STARK 有效性证明;
- 符合区块链特性:区块链数据强调“确定性”,不可变变量的逻辑更易审计和验证。
补充:特殊场景的可变性
- 存储变量(Storage):合约的存储变量(如示例3中的
balance)本身没有“默认不可变”的限制,需通过read()/write()方法修改,但其读取到局部变量后,仍遵循“局部变量默认不可变”的规则; - 函数参数:函数参数默认也不可变,若需在函数内修改参数值,需添加
mut(如fn update(mut num: u256))。
总结
- Cairo 局部变量、函数参数默认不可变,必须加
mut关键字才能修改; - 该设计是为了适配智能合约的安全需求,减少意外修改导致的逻辑错误;
- 存储变量的修改需通过
read()/write(),但局部变量仍遵循默认不可变规则。
需要我整理一份Cairo变量可变性的核心规则速记表,包含局部变量、函数参数、存储变量的可变性差异吗?
快照@和引用ref哪一项说法正确?
二者都会修改变量;
快照可写,引用只读;
快照不转移所有权且只读,引用不转移所有权且可写;
二者都会转移所有权
在 Cairo 编程语言(用于 Starknet 合约开发)中,你给出的选项中,正确的说法是:
“快照(Snapshot)不转移所有权且只读,引用(Reference)不转移所有权且可写。”
详细对比:快照 @ vs 引用 ref
为了帮你更清晰地排除“错误选项”,我们可以通过下表看透二者的本质:
| 特性 | 快照 @ (Snapshot) | 引用 ref (Reference) |
|---|---|---|
| 符号/关键字 | @variable |
ref variable |
| 可变性 | 只读(不可修改原变量) | 可写(可以修改原变量) |
| 所有权 | 不转移(原函数保留所有权) | 不转移(隐式返回所有权) |
| 底层逻辑 | 该时间点的“不可变视图” | 相当于自动返回新值的“语法糖” |
| 常见场景 | 只需读取数据(如计算 Array 总和) | 需要在函数内修改状态(如 increment) |
为什么其他选项是错误的?
- “二者都会修改变量”:错误。快照
@是绝对只读的,编译器会阻止任何尝试修改快照内容的操作。 - “快照可写,引用只读”:错误。这完全记反了。在 Cairo 中,
ref专门用于需要修改(Mutate)传入参数的情况。 - “二者都会转移所有权”:错误。
- 快照
@只是给函数看了一眼数据,所有权还在调用者手里。 - 引用
ref稍微复杂点:它在进入函数时拿走所有权,但在函数结束时自动(隐式)归还所有权。所以从开发者的体感来看,它也没有“失去”所有权。
- 快照
你的“预判”应用
- 当你看到一个函数只需要读取你的
Array(比如检查余额),你应该预判到:不应该转移所有权(否则后面没法用了),也不需要修改。 - 根据这个预判,你唯一正确的选择就是使用 快照
@。
你想让我为你写一段对比代码,直观地展示在什么情况下编译器会报错(例如尝试修改快照)吗?
Cairo Tutorial Series Part 11: References and Snapshots
这个视频详细演示了如何在 Cairo 中使用 ref 和 @ 来管理变量的可变性与所有权,能帮你更直观地理解二者的区别。
Span 和 Array 的主要区别是?
在 Cairo 编程语言中,关于 Span 和 Array 的区别,正确选项是:
“Span 是只读的数组视图。”
核心区别:Array vs Span
在 Starknet 开发中,理解这两者的关系非常重要。你可以把 Array 看作是真实的存储实体,而 Span 是观察这些实体的**“快照窗口”**。
| 特性 | Array |
Span |
|---|---|---|
| 本质 | 数据的持有者(可动态增加) | 数据的引用/快照(不可变视图) |
| 可变性 | 有限可写(只能追加 append) |
绝对只读 |
| 内存成本 | 较高(涉及所有权转移) | 极低(几乎免费的轻量级结构) |
| 典型用途 | 构造、收集数据 | 遍历数据、作为函数参数(防止修改) |
为什么其他选项是错误的?
- “Span 可以修改元素,Array 不可以”:
- 错误。恰恰相反,
Span设计出来的初衷就是为了提供只读访问。而Array虽然在 Cairo 的不可变内存模型中无法直接通过索引修改旧值,但它是允许通过append()增加新元素的。
- 错误。恰恰相反,
- “Span 只能存 felt252”:
- 错误。
Span<T>是泛型的。它可以是Span<u256>、Span<felt252>,甚至是Span<MyStruct>。它的灵活性非常强。
- 错误。
- “Array 不能被遍历”:
- 错误。虽然
Array的底层是类似队列的结构,但你完全可以通过递归或循环配合get()方法来遍历它。不过,在 Cairo 的最佳实践中,我们通常会先用.span()将数组转为Span再进行遍历,因为Span专门为这种“只读查看”优化过。
- 错误。虽然
你的“排除法”与工程直觉
- 如果一个函数只是想看看数据(比如计算总和),为什么要冒着被修改的风险把整个
Array传进去? - 预判: 应该传一个无法被修改的“快照”。
- 结论: 于是
Span成了这个场景下的唯一正确选项。
既然你提到要在书中使用“排除法”逻辑,需要我为你提供一段对比 Array 和 Span 在函数传递中所有权变化的 Cairo 代码示例吗?
Understanding Arrays in Cairo
这个视频详细讲解了 Cairo 中数组的操作及其与内存的关系,能帮你更深入地理解为什么 Span 作为“只读视图”在高性能合约开发中如此重要。
command not found
┌──(root㉿kali)-[/home/kali]
└─# scarb –version
snforge –version
sncast –version
starknet-devnet –version
scarb: command not found
snforge: command not found
Command ‘sncast’ not found, did you mean:
command ‘sbcast’ from deb slurm-client
Try: apt install
starknet-devnet: command not found
你现在遇到的问题是在Kali Linux中执行scarb、snforge、sncast、starknet-devnet这些StarkNet开发工具命令时,系统提示“command not found”,核心原因是这些工具尚未安装,我会一步步教你完整的安装流程,确保能正常使用。
一、先明确工具的作用(帮你理解安装的意义)
scarb:StarkNet/Cairo的包管理器和构建工具(类似Solidity的Hardhat/Truffle);snforge:Cairo合约的测试框架;sncast:StarkNet合约的部署/交互工具;starknet-devnet:本地StarkNet测试节点(类似以太坊的Ganache)。
二、安装前置条件
Kali Linux默认可能缺少依赖,先安装基础环境:
1 | |
三、分步安装所有工具
1. 安装Scarb(核心工具,snforge/sncast依赖它)
Scarb的官方安装方式是通过脚本,适配Linux/macOS:
1 | |
✅ 正常输出示例:scarb 2.6.1 (f123456 2025-01-01)
2. 安装snforge和sncast(Scarb插件)
snforge/sncast是Scarb的官方插件,安装Scarb后直接通过scarb命令安装:
1 | |
✅ 正常输出示例:snforge 0.12.0 / sncast 0.12.0
3. 安装starknet-devnet(本地测试节点)
starknet-devnet基于Python,通过pip安装:
1 | |
✅ 正常输出示例:starknet-devnet 0.7.1
四、常见问题排查
如果安装后仍提示“command not found”,按以下步骤排查:
- 环境变量未生效:
- 重新执行
source ~/.bashrc或source ~/.zshrc; - 手动检查PATH:
echo $PATH,确认包含$HOME/.local/share/scarb/bin。
- 重新执行
- pip安装的starknet-devnet不在PATH:
- 找到pip安装路径:
pip3 show starknet-devnet | grep Location; - 将该路径下的
bin目录加入PATH,例如:1
2echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
- 找到pip安装路径:
- 权限问题:
- 避免用root用户安装(建议切换到kali普通用户),若已用root,执行
chown -R kali:kali ~/.local修复权限。
- 避免用root用户安装(建议切换到kali普通用户),若已用root,执行
五、验证所有工具是否可用
执行以下命令,若都输出版本号则安装成功:
1 | |
总结
- 核心问题是工具未安装+环境变量未配置,先装Scarb(snforge/sncast依赖),再装starknet-devnet;
- 安装后必须配置PATH环境变量,否则系统识别不到命令;
- Kali默认可能用zsh,需注意环境变量文件是
.zshrc而非.bashrc。
如果安装过程中遇到具体报错(比如依赖缺失、网络问题),可以把报错信息贴出来,我帮你针对性解决。