合约运行时
Savitri 合约运行时在沙箱环境中执行智能合约,具备 Gas 计量、存储隔离和事件发射功能。
执行流程
Contract Call TX arrives
│
▼
BaseContract validation
│ check paused, owner, reserved slots
▼
Gas Meter initialization
│ gas_limit from TX fee
▼
ContractStorage setup
│ load contract state, init cache
▼
Function dispatch
│ match function_selector → handler
▼
Execution (with gas tracking)
│ SLOAD, SSTORE, events, calls
▼
Result
├── Success → commit storage, emit events
└── Failure → revert all changes
BaseContract
所有合约均继承 BaseContract,槽 0-99 为保留槽:
| 槽位 | 字段 | 大小 | 描述 |
|---|---|---|---|
| 0 | owner | 32 字节 | 合约所有者地址 |
| 1 | version | u64 | 合约版本 |
| 2 | governance_hook | bool | 治理集成是否启用 |
| 3 | fee_hook | bool | 自定义费用逻辑是否启用 |
| 4 | paused | bool | 合约暂停状态 |
| 5-99 | 保留 | - | 供将来使用 |
BaseContract 函数
| 函数 | 访问权限 | 描述 |
|---|---|---|
owner() | 公开 | 获取合约所有者 |
transfer_ownership(new_owner) | 仅所有者 | 转移所有权 |
version() | 公开 | 获取合约版本 |
upgrade(new_version) | 仅所有者 | 升级合约 |
pause() | 仅所有者 | 暂停所有操作 |
unpause() | 仅所有者 | 恢复操作 |
Gas 计量
Gas 费用
| 操作 | Gas | 描述 |
|---|---|---|
SLOAD | 100 | 存储读取 |
SSTORE(新建) | 20,000 | 存储写入(空槽) |
SSTORE(更新) | 5,000 | 存储写入(已有槽) |
CALL | 2,300 | 跨合约调用 |
CREATE | 32,000 | 合约部署 |
TRANSFER | 300 | 代币转账 |
LOG | 375 | 事件发射(基础) |
LOG_TOPIC | 375 | 每个额外主题 |
LOG_DATA | 8 | 每字节事件数据 |
CALLDATA | 16 | 每字节调用数据 |
批量计费
Gas 计量器采用批量计费(每批 100 次操作)以减少开销。Gas 按批次提交,而非逐操作提交。
Gas 上限
Gas 上限由交易费用派生:
gas_limit = tx.fee / gas_price
若执行过程中 Gas 耗尽,整个交易将回滚。
合约存储
基于槽的模型
每个合约拥有独立的键值存储,键为 u64 槽编号,值为 32 字节数组。
槽派生
对于映射,使用 Keccak256 派生槽以防止冲突:
// Simple value
slot = FIXED_SLOT_NUMBER
// Mapping (address → value)
slot = keccak256(address || base_slot)[0..8] as u64
// Nested mapping (address → address → value)
inner = keccak256(outer_key || base_slot)
slot = keccak256(inner_key || inner)[0..8] as u64
Merkle 树
合约存储维护一棵 Merkle 树用于生成状态证明。
事件系统
合约向外部消费者发出事件:
pub struct CustomEvent {
pub event_type: String, // e.g., "Transfer", "Approval"
pub topics: Vec<Vec<u8>>, // indexed fields
pub data: Vec<u8>, // non-indexed data
}
标准事件(来自 BaseContract):
OwnershipTransferred(old_owner, new_owner)ContractUpgraded(old_version, new_version)Paused(by)Unpaused(by)
合约部署
1. Create DeployTransaction (to = None, data = bytecode)
2. Assign contract address (derived from deployer + nonce)
3. Initialize BaseContract (set owner, version)
4. Run contract constructor (initialize function)
5. Store ContractInfo in CF_CONTRACTS
6. Emit ContractDeployed event
合约调用
1. Parse function_selector from TX data
2. Load contract from CF_CONTRACTS
3. Check BaseContract state (not paused, etc.)
4. Initialize GasMeter with gas_limit
5. Execute function with ContractStorage
6. If success: commit storage changes, deduct gas
7. If failure: revert all changes
EVM 解释器
evm_interpreter 模块提供基本 EVM 字节码执行,以兼容以太坊合约。这使得可以将 Solidity 编译的合约部署到 Savitri 上。
内存监控
memory_monitor 跟踪运行时内存使用情况以防止拒绝服务攻击:
- 每个合约的内存限制
- 分配跟踪
- 超出限制时自动终止
并行执行
parallel 模块为独立交易启用并行合约执行:
- 分析交易之间的依赖关系
- 检测存储槽冲突
- 并行执行无冲突的交易
- 有冲突的交易回退为串行执行
合约追踪
tracing 模块提供用于调试的执行追踪:
- 逐步执行日志
- 存储读写追踪
- 每次操作的 Gas 消耗
- 事件发射日志