NeuroWASM 개요
NeuroWASM은 NeuroChain의 컨트랙트 런타임입니다. wasmi 인터프리터가 실행하는 WebAssembly로, fuel로 계량되며, host 함수는 env 모듈에서 import합니다. 의미론은 CosmWasm과 NEAR를 따르며, 여기에 네이티브 AI host 함수가 더해집니다.
wasmd나 nearcore와 바이너리 호환되지는 않습니다 — 같은 아이디어, 다른 인터페이스입니다.
실행 모델
- 동기식 호출. 다른 컨트랙트를 호출한 컨트랙트는 그 컨트랙트가 끝날 때까지 실행한 뒤에 계속 진행합니다(EVM과 같음).
- 트랜잭션당 하나의 저널. 전체 호출 트리의 모든 스토리지 쓰기, 잔액 변경, 송금은 함께 커밋되며, 어느 하나라도 trap되면 아무것도 커밋되지 않습니다.
- 자신의 쓰기 읽기(read-your-writes). 중첩 호출은 호출자의 커밋되지 않은 쓰기를 봅니다.
- 중첩 호출 깊이 제한은 8입니다(
MAX_CALL_DEPTH). - 결정론적. 모든 검증자가 같은 상태에서 같은 바이트코드를 실행해 같은 결과를 얻습니다. 시계, 난수, 네트워크 접근은 없습니다.
이후 계획: reply 진입점을 갖는 비동기 하위 메시지, 그다음 NEAR 방식의 promise와 IBC v2 패킷.
Host 함수
모든 import는 env에서 옵니다. 포인터와 길이는 컨트랙트가 export한 memory에 대한 i32 오프셋입니다.
| 영역 | 작동 중 | 계획됨 |
|---|---|---|
| 메모리 | allocate, deallocate, abort | — |
| 스토리지 | get_storage, set_storage | remove_storage, storage_has, storage_usage |
| 컨텍스트 | get_caller, get_block_height, get_balance, get_stake, get_stake_at | get_self_addr, get_signer, get_block_time, get_shard_id, get_attached_deposit, gas_remaining |
| 뱅크와 이벤트 | transfer, emit_event, set_return | log |
| 컨트랙트 간 호출 | call_contract | call_contract_with_value |
| 암호화 | — | sha3_256, verify_mldsa65, addr_validate |
| AI host | — | 모델, 인증서, Sentinel, 신뢰 목록, 계정 추상화 상태 읽기 |
컨트랙트가 export하는 진입점은 게스트 export에, 제한과 fuel 가격은 가스와 제한에 설명되어 있습니다.
최소한의 컨트랙트
no_std Rust로 작성한 카운터입니다. 호출을 읽고, 스토리지에 숫자를 보관하고, 그 숫자를 반환합니다.
rust
#![no_std]
#[link(wasm_import_module = "env")]
extern "C" {
fn get_storage(key_ptr: i32, key_len: i32, val_ptr: i32, val_max: i32) -> i32;
fn set_storage(key_ptr: i32, key_len: i32, val_ptr: i32, val_len: i32);
fn set_return(ptr: i32, len: i32);
}
#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! { core::arch::wasm32::unreachable() }
const KEY: &[u8] = b"count";
fn read_count() -> u64 {
let mut buf = [0u8; 20];
let n = unsafe { get_storage(KEY.as_ptr() as i32, KEY.len() as i32, buf.as_mut_ptr() as i32, 20) };
if n <= 0 { return 0; }
core::str::from_utf8(&buf[..n as usize]).ok().and_then(|s| s.parse().ok()).unwrap_or(0)
}
#[no_mangle]
pub extern "C" fn call(input_ptr: i32, input_len: i32) {
let input = unsafe { core::slice::from_raw_parts(input_ptr as *const u8, input_len as usize) };
let mut n = read_count();
if input.windows(11).any(|w| w == b"\"increment\"") {
n += 1;
let mut out = [0u8; 20];
let s = fmt_u64(n, &mut out);
unsafe { set_storage(KEY.as_ptr() as i32, KEY.len() as i32, s.as_ptr() as i32, s.len() as i32) };
}
let mut out = [0u8; 20];
let s = fmt_u64(n, &mut out);
unsafe { set_return(s.as_ptr() as i32, s.len() as i32) };
}
fn fmt_u64(mut v: u64, buf: &mut [u8; 20]) -> &[u8] {
let mut i = 20;
loop { i -= 1; buf[i] = b'0' + (v % 10) as u8; v /= 10; if v == 0 { break; } }
&buf[i..]
}{"function":"increment"}로 호출하면 1이 더해지고, 그 밖의 내용으로 호출하면 값을 읽습니다. 빌드와 배포는 컨트랙트 배포와 같습니다. neurochain/contracts/nrc20의 NRC-20 토큰이 더 크고 완전한 예제입니다.
이 인터페이스의 설계 문서는 저장소의 docs/neuro-wasm-spec.md입니다.