204 lines
8.3 KiB
Rust
204 lines
8.3 KiB
Rust
// nac-nvm-service — NAC NVM 虚拟机服务(L1层)端口:9550
|
||
use actix_web::{web, App, HttpServer, HttpResponse};
|
||
use serde::{Deserialize, Serialize};
|
||
use std::sync::{Arc, Mutex};
|
||
use std::collections::HashMap;
|
||
use chrono::Utc;
|
||
use uuid::Uuid;
|
||
use sha3::{Sha3_384, Digest};
|
||
use tracing::info;
|
||
|
||
const CHAIN_ID: u64 = 5132611;
|
||
const SERVICE_VERSION: &str = "1.0.0";
|
||
const NVM_VERSION: &str = "2.0";
|
||
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct ContractRecord {
|
||
pub contract_id: String,
|
||
pub address: String,
|
||
pub code_hash: String,
|
||
pub deployer: String,
|
||
pub language: String,
|
||
pub bytecode_size: usize,
|
||
pub deployed_at: i64,
|
||
pub execution_count: u64,
|
||
pub status: String,
|
||
}
|
||
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct ExecRecord {
|
||
pub execution_id: String,
|
||
pub contract_address: String,
|
||
pub caller: String,
|
||
pub function: String,
|
||
pub success: bool,
|
||
pub gas_used: u64,
|
||
pub timestamp: i64,
|
||
}
|
||
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct NvmState {
|
||
pub contracts: HashMap<String, ContractRecord>,
|
||
pub exec_records: Vec<ExecRecord>,
|
||
pub total_executions: u64,
|
||
pub total_contracts: u64,
|
||
pub started_at: i64,
|
||
}
|
||
|
||
type SharedState = Arc<Mutex<NvmState>>;
|
||
|
||
#[derive(Debug, Deserialize)]
|
||
pub struct DeployReq {
|
||
pub bytecode: String,
|
||
pub deployer: String,
|
||
pub language: Option<String>,
|
||
}
|
||
|
||
#[derive(Debug, Deserialize)]
|
||
pub struct ExecuteReq {
|
||
pub contract_address: String,
|
||
pub caller: String,
|
||
pub function: String,
|
||
pub args: Option<serde_json::Value>,
|
||
pub gas_limit: Option<u64>,
|
||
}
|
||
|
||
fn sha3_384_hex(data: &[u8]) -> String {
|
||
let mut h = Sha3_384::new(); h.update(data); hex::encode(h.finalize())
|
||
}
|
||
|
||
fn validate_address(addr: &str) -> bool {
|
||
addr.len() == 64 && addr.chars().all(|c| c.is_ascii_hexdigit())
|
||
}
|
||
|
||
async fn health(state: web::Data<SharedState>) -> HttpResponse {
|
||
let s = state.lock().expect("lock not poisoned");
|
||
HttpResponse::Ok().json(serde_json::json!({
|
||
"status": "healthy", "service": "nac-nvm-service",
|
||
"version": SERVICE_VERSION, "nvm_version": NVM_VERSION,
|
||
"chain_id": CHAIN_ID, "contracts": s.contracts.len(),
|
||
"total_executions": s.total_executions,
|
||
"type_system": {"address_bytes": 32, "hash_bytes": 48, "hash_algorithm": "SHA3-384"},
|
||
"timestamp": Utc::now().to_rfc3339()
|
||
}))
|
||
}
|
||
|
||
async fn get_state(state: web::Data<SharedState>) -> HttpResponse {
|
||
let s = state.lock().expect("lock not poisoned");
|
||
HttpResponse::Ok().json(serde_json::json!({
|
||
"chain_id": CHAIN_ID, "nvm_version": NVM_VERSION,
|
||
"contracts_deployed": s.contracts.len(),
|
||
"total_executions": s.total_executions,
|
||
"uptime_ms": Utc::now().timestamp_millis() - s.started_at,
|
||
"timestamp": Utc::now().to_rfc3339()
|
||
}))
|
||
}
|
||
|
||
async fn vm_info() -> HttpResponse {
|
||
HttpResponse::Ok().json(serde_json::json!({
|
||
"vm_name": "NVM", "full_name": "NewAssetChain Virtual Machine",
|
||
"version": NVM_VERSION, "chain_id": CHAIN_ID,
|
||
"features": {"jit_compilation": true, "cbpp_integration": true, "charter_language": true},
|
||
"supported_languages": ["charter"],
|
||
"type_system": {"address_bytes": 32, "hash_bytes": 48, "hash_algorithm": "SHA3-384"},
|
||
"timestamp": Utc::now().to_rfc3339()
|
||
}))
|
||
}
|
||
|
||
async fn deploy_contract(state: web::Data<SharedState>, req: web::Json<DeployReq>) -> HttpResponse {
|
||
if !validate_address(&req.deployer) {
|
||
return HttpResponse::BadRequest().json(serde_json::json!({
|
||
"success": false, "error": "deployer 必须为 NAC Address(32字节,64个十六进制字符)"
|
||
}));
|
||
}
|
||
let mut s = state.lock().expect("lock not poisoned");
|
||
let bytecode_bytes = hex::decode(&req.bytecode).unwrap_or_else(|_| req.bytecode.as_bytes().to_vec());
|
||
let code_hash = sha3_384_hex(&bytecode_bytes);
|
||
let addr_input = format!("{}:{}", req.deployer, s.total_contracts);
|
||
let contract_address = sha3_384_hex(addr_input.as_bytes())[..64].to_string();
|
||
let contract_id = format!("CONTRACT-{}", &Uuid::new_v4().to_string()[..8].to_uppercase());
|
||
s.contracts.insert(contract_address.clone(), ContractRecord {
|
||
contract_id: contract_id.clone(), address: contract_address.clone(),
|
||
code_hash: code_hash.clone(), deployer: req.deployer.clone(),
|
||
language: req.language.clone().unwrap_or_else(|| "charter".to_string()),
|
||
bytecode_size: bytecode_bytes.len(), deployed_at: Utc::now().timestamp_millis(),
|
||
execution_count: 0, status: "active".to_string(),
|
||
});
|
||
s.total_contracts += 1;
|
||
HttpResponse::Ok().json(serde_json::json!({
|
||
"success": true, "contract_id": contract_id,
|
||
"contract_address": contract_address,
|
||
"code_hash": {"hex": code_hash, "algorithm": "SHA3-384", "byte_length": 48},
|
||
"timestamp": Utc::now().to_rfc3339()
|
||
}))
|
||
}
|
||
|
||
async fn execute_contract(state: web::Data<SharedState>, req: web::Json<ExecuteReq>) -> HttpResponse {
|
||
if !validate_address(&req.caller) {
|
||
return HttpResponse::BadRequest().json(serde_json::json!({
|
||
"success": false, "error": "caller 必须为 NAC Address(32字节,64个十六进制字符)"
|
||
}));
|
||
}
|
||
let mut s = state.lock().expect("lock not poisoned");
|
||
if !s.contracts.contains_key(&req.contract_address) {
|
||
return HttpResponse::NotFound().json(serde_json::json!({
|
||
"success": false, "error": format!("合约 {} 不存在", req.contract_address)
|
||
}));
|
||
}
|
||
let gas_limit = req.gas_limit.unwrap_or(1_000_000);
|
||
let execution_id = format!("EXEC-{}", &Uuid::new_v4().to_string()[..12].to_uppercase());
|
||
let tx_input = format!("{}:{}:{}:{}", req.caller, req.contract_address, req.function, Utc::now().timestamp_millis());
|
||
let tx_hash = sha3_384_hex(tx_input.as_bytes());
|
||
if let Some(c) = s.contracts.get_mut(&req.contract_address) { c.execution_count += 1; }
|
||
s.exec_records.push(ExecRecord {
|
||
execution_id: execution_id.clone(), contract_address: req.contract_address.clone(),
|
||
caller: req.caller.clone(), function: req.function.clone(),
|
||
success: true, gas_used: gas_limit / 10, timestamp: Utc::now().timestamp_millis(),
|
||
});
|
||
s.total_executions += 1;
|
||
HttpResponse::Ok().json(serde_json::json!({
|
||
"success": true, "execution_id": execution_id,
|
||
"tx_hash": {"hex": tx_hash, "algorithm": "SHA3-384", "byte_length": 48},
|
||
"gas_used": gas_limit / 10, "timestamp": Utc::now().to_rfc3339()
|
||
}))
|
||
}
|
||
|
||
async fn list_contracts(state: web::Data<SharedState>) -> HttpResponse {
|
||
let s = state.lock().expect("lock not poisoned");
|
||
let contracts: Vec<&ContractRecord> = s.contracts.values().collect();
|
||
HttpResponse::Ok().json(serde_json::json!({"contracts": contracts, "total": contracts.len()}))
|
||
}
|
||
|
||
async fn get_stats(state: web::Data<SharedState>) -> HttpResponse {
|
||
let s = state.lock().expect("lock not poisoned");
|
||
HttpResponse::Ok().json(serde_json::json!({
|
||
"service": "nac-nvm-service", "layer": "L1-NVM",
|
||
"chain_id": CHAIN_ID, "nvm_version": NVM_VERSION,
|
||
"contracts": s.contracts.len(), "total_executions": s.total_executions,
|
||
"uptime_ms": Utc::now().timestamp_millis() - s.started_at,
|
||
"timestamp": Utc::now().to_rfc3339()
|
||
}))
|
||
}
|
||
|
||
#[actix_web::main]
|
||
async fn main() -> std::io::Result<()> {
|
||
tracing_subscriber::fmt().with_max_level(tracing::Level::INFO).init();
|
||
let port: u16 = std::env::var("NVM_PORT").unwrap_or_else(|_| "9550".to_string()).parse().unwrap_or(9550);
|
||
let state = web::Data::new(Arc::new(Mutex::new(NvmState {
|
||
contracts: HashMap::new(), exec_records: Vec::new(),
|
||
total_executions: 0, total_contracts: 0,
|
||
started_at: Utc::now().timestamp_millis(),
|
||
})));
|
||
info!("NAC NVM Service v{} 启动,端口 {}", SERVICE_VERSION, port);
|
||
HttpServer::new(move || {
|
||
App::new().app_data(state.clone())
|
||
.route("/health", web::get().to(health))
|
||
.route("/state", web::get().to(get_state))
|
||
.route("/stats", web::get().to(get_stats))
|
||
.route("/vm/info", web::get().to(vm_info))
|
||
.route("/contract/deploy", web::post().to(deploy_contract))
|
||
.route("/contract/execute", web::post().to(execute_contract))
|
||
.route("/contracts", web::get().to(list_contracts))
|
||
}).bind(format!("0.0.0.0:{}", port))?.run().await
|
||
}
|