NAC_Blockchain/protocol/nac-nvm/src/gas.rs

177 lines
4.2 KiB
Rust

//! Gas计量系统
use crate::bytecode::Opcode;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum GasError {
#[error("Gas不足")]
OutOfGas,
#[error("Gas溢出")]
GasOverflow,
}
/// Gas成本定义
pub struct GasCost;
impl GasCost {
/// 获取操作码的Gas成本
pub fn get(opcode: Opcode) -> u64 {
match opcode {
// 栈操作 - 低成本
Opcode::Push => 3,
Opcode::Pop => 2,
Opcode::Dup => 3,
Opcode::Swap => 3,
// 算术运算 - 中等成本
Opcode::Add => 5,
Opcode::Sub => 5,
Opcode::Mul => 8,
Opcode::Div => 10,
Opcode::Mod => 10,
// 比较运算 - 低成本
Opcode::Eq => 3,
Opcode::Ne => 3,
Opcode::Lt => 3,
Opcode::Le => 3,
Opcode::Gt => 3,
Opcode::Ge => 3,
// 逻辑运算 - 低成本
Opcode::And => 3,
Opcode::Or => 3,
Opcode::Not => 3,
// 内存操作 - 中等成本
Opcode::Load => 50,
Opcode::Store => 100,
// 控制流 - 中等成本
Opcode::Jump => 8,
Opcode::JumpIf => 10,
Opcode::Call => 100,
Opcode::Return => 5,
Opcode::Halt => 0,
// 区块链操作 - 高成本
Opcode::GetBalance => 400,
Opcode::Transfer => 9000,
Opcode::GetCaller => 2,
Opcode::GetTimestamp => 2,
Opcode::GetBlockNumber => 2,
// 加密操作 - 高成本
Opcode::Sha3 => 30,
Opcode::VerifySignature => 3000,
}
}
}
/// Gas计量器
#[derive(Debug, Clone)]
pub struct GasMetering {
gas_limit: u64,
gas_used: u64,
}
impl GasMetering {
pub fn new(gas_limit: u64) -> Self {
GasMetering {
gas_limit,
gas_used: 0,
}
}
/// 消耗Gas
pub fn consume(&mut self, amount: u64) -> Result<(), GasError> {
let new_used = self.gas_used.checked_add(amount)
.ok_or(GasError::GasOverflow)?;
if new_used > self.gas_limit {
return Err(GasError::OutOfGas);
}
self.gas_used = new_used;
Ok(())
}
/// 消耗操作码的Gas
pub fn consume_opcode(&mut self, opcode: Opcode) -> Result<(), GasError> {
let cost = GasCost::get(opcode);
self.consume(cost)
}
/// 获取剩余Gas
pub fn remaining(&self) -> u64 {
self.gas_limit.saturating_sub(self.gas_used)
}
/// 获取已使用Gas
pub fn used(&self) -> u64 {
self.gas_used
}
/// 获取Gas限制
pub fn limit(&self) -> u64 {
self.gas_limit
}
/// 重置Gas计量器
pub fn reset(&mut self) {
self.gas_used = 0;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gas_consumption() {
let mut gas = GasMetering::new(1000);
assert!(gas.consume(100).is_ok());
assert_eq!(gas.used(), 100);
assert_eq!(gas.remaining(), 900);
}
#[test]
fn test_gas_out_of_gas() {
let mut gas = GasMetering::new(100);
assert!(gas.consume(50).is_ok());
assert!(gas.consume(60).is_err());
}
#[test]
fn test_gas_opcode_cost() {
let mut gas = GasMetering::new(1000);
assert!(gas.consume_opcode(Opcode::Push).is_ok());
assert_eq!(gas.used(), 3);
assert!(gas.consume_opcode(Opcode::Add).is_ok());
assert_eq!(gas.used(), 8);
}
#[test]
fn test_gas_reset() {
let mut gas = GasMetering::new(1000);
gas.consume(500).expect("FIX-006: unexpected None/Err");
assert_eq!(gas.used(), 500);
gas.reset();
assert_eq!(gas.used(), 0);
}
#[test]
fn test_gas_costs() {
assert_eq!(GasCost::get(Opcode::Push), 3);
assert_eq!(GasCost::get(Opcode::Transfer), 9000);
assert_eq!(GasCost::get(Opcode::Sha3), 30);
}
}