# 노드 실행

> NeuroChain 노드 실행 — 소스에서 빌드(컨트랙트 먼저), 단일 테스트 노드 또는 다중 검증자 네트워크 시작, Docker Compose, 데이터 구조, 키, 스냅샷, Prometheus와 Grafana를 이용한 모니터링, 백업과 복원을 다룹니다.

Source: https://docs.nro.world/ko/operate/node

## 요구 사항

- `wasm32-unknown-unknown` 타깃이 포함된 Rust(stable)
- C++ 툴체인과 `clang`(RocksDB는 소스에서 빌드됩니다)
- gateway용 Node.js 18+
- 빌드를 위한 수 GB의 디스크 공간

## 빌드

노드에는 두 개의 시스템 컨트랙트가 내장되어 있으므로 이것을 **먼저** 빌드하십시오:

```bash
cd neurochain
rustup target add wasm32-unknown-unknown

for c in nrc20 ncert; do
  (cd contracts/$c && RUSTFLAGS="-C link-arg=--allow-undefined" \
     cargo build --release --target wasm32-unknown-unknown)
done

cargo build -p neurochain-node --bin neurochain        # → target/debug/neurochain
```

컨트랙트 단계를 건너뛰면 노드 빌드가 `couldn't read …/nrc20.wasm` 오류로 실패합니다. `--allow-undefined` 플래그는 컨트랙트의 호스트 임포트가 현재 Rust와 링크되도록 해 줍니다. 새 컨트랙트는 대신 `#[link(wasm_import_module = "env")]`를 선언할 수 있습니다([NeuroWASM](/ko/neurowasm/#a-minimal-contract) 참고).

첫 빌드는 몇 분이 걸리고(RocksDB), 이후 빌드는 몇 초면 끝납니다. 로컬 테스트 이외의 용도에는 `--release`를 사용하십시오.

## 단일 로컬 노드

```bash
CHAIN_ENV=testnet \
NEUROCHAIN_DATA_DIR=./data \
NEUROCHAIN_MODELS_DIR=./models \
NEUROCHAIN_P2P_PORT=30333 \
NEUROCHAIN_RPC_PORT=9933 \
NEUROCHAIN_NODE_ADDR=localhost:30333 \
NEUROCHAIN_MIN_VALIDATORS=1 \
RUST_LOG=neurochain=info \
./target/debug/neurochain
```

- `CHAIN_ENV=testnet`은 faucet을 켜고 개발을 위해 서명 검사를 완화합니다. 실제 용도에서는 설정하지 마십시오(mainnet 규칙).
- `NEUROCHAIN_GENESIS_VALIDATORS`가 없으면 노드는 자신을 유일한 검증자(`neuro:node-1.node`)로 만들고 즉시 블록 생성을 시작합니다. mainnet 규칙에서는 소유자가 필요합니다: `NEUROCHAIN_VALIDATOR_OWNER`를 노드를 운영하는 `.human` 지갑으로 설정하지 않으면 노드가 시작되지 않습니다.
- `NEUROCHAIN_MIN_VALIDATORS=1`은 검증자가 7개 미만이라는 경고를 끕니다.

확인합니다:

```bash
curl -s http://localhost:9933/health
# {"status":"ok","height":6,"validator_count":1,"bft_quorum":1,"bft_ready":false,…}
```

그런 다음 [gateway](./gateway)를 시작하십시오.

## 다중 검증자 네트워크 {#a-multi-validator-network}

모든 검증자는 **같은 제네시스**에서 시작해야 하며, 제네시스에는 모든 검증자와 그 합의 공개 키가 나열됩니다. 로컬 네트워크의 경우 저장소가 이 과정을 자동화합니다:

```bash
cd neurochain
scripts/net-test/net-test.sh keys 4    # 검증자 키 4개를 만들고 공유 제네시스 집합을 기록
scripts/net-test/net-test.sh up 4      # 시작(노드 1이 먼저, 나머지는 노드 1에서 부트스트랩)
scripts/net-test/net-test.sh check     # 높이 일치, 블록 생성, 스폰서 가스 일관성 확인
scripts/net-test/net-test.sh down
```

이 스크립트는 9941번 이상의 RPC 포트, 30341번 이상의 P2P 포트, `.net-test/` 아래의 데이터를 사용하므로 일반적인 `./data` 노드에는 영향을 주지 않습니다.

수동으로 하려면:

1. **각 검증자의 키를 만듭니다.** 각 노드를 고유한 `NEUROCHAIN_DATA_DIR`, `NEUROCHAIN_NODE_ADDR`, 포트로 한 번 시작합니다. 노드는 키를 `<data dir>/shard0/bft_key.bin`에 기록하고 다음을 로그에 남깁니다:
   ```
   Consensus validator … — ML-DSA-65 pubkey (put in NEUROCHAIN_GENESIS_VALIDATORS): 6e80bab8…
   ```
   노드를 중지하고 `<data dir>/shard0/state_rdb`와 `<data dir>/shard0/wal`을 삭제합니다. **`bft_key.bin`은 보관하십시오.**
2. **제네시스 집합을 작성합니다** — 모든 노드에 같은 값을 사용합니다:
   ```
   NEUROCHAIN_GENESIS_VALIDATORS='neuro:val1.node|host1:30333|1000000|0.9|<pubkey1>|neuro:<owner1>.human,neuro:val2.node|host2:30333|1000000|0.9|<pubkey2>|neuro:<owner2>.human,…'
   ```
   필드: 검증자 주소, 그 P2P 주소(해당 노드의 `NEUROCHAIN_NODE_ADDR`와 같아야 함), NRO 단위 스테이크, 초기 신뢰도(보고용으로만 사용), ML-DSA-65 공개 키, 그리고 **소유자** — 검증자를 운영하는 `.human` 지갑(프로필, unjail, 스테이크, 커미션)입니다. `bft_key.bin`의 합의 키는 블록에 서명만 하며 검증자의 계정을 제어하지 않습니다. 공개 키가 없는 항목은 합의에서 제외되며, mainnet 규칙에서 소유자가 없는 항목이 있으면 노드가 시작 시 중지됩니다.
3. 첫 번째 노드를 **시작**한 다음, 나머지 노드를 `NEUROCHAIN_BOOTSTRAP=host1:30333`으로 시작합니다. 검증자 수가 7개 미만이면 `NEUROCHAIN_MIN_VALIDATORS`를 검증자 수로 설정하십시오.

합의 메시지는 같은 P2P 포트로 전달되며, 별도의 합의 포트는 없습니다. 노드는 `NEUROCHAIN_NODE_ADDR`를 제네시스 집합의 P2P 주소와 대조하여 자신이 어느 검증자인지 인식합니다.

::: tip 제네시스 집합은 변경되지 않습니다
나중에 합류하는 검증자는 `.node`를 등록하고 스테이킹합니다([스테이킹](/ko/learn/staking) 참고). `NEUROCHAIN_GENESIS_VALIDATORS`를 수정하지 않습니다. 다른 제네시스 집합이나 잔액으로 시작한 노드는 다른 노드의 블록을 거부하고 `STATE DIVERGENCE`를 로그에 남깁니다.
:::

## Docker Compose

저장소 루트에서:

```bash
cp .env.example .env        # 필요에 따라 편집
docker compose up -d        # 노드, gateway, 웹 앱
docker compose --profile monitoring up -d   # Prometheus와 Grafana 추가
```

이미지는 컨트랙트와 노드를 단계별로 빌드합니다. 노드 데이터는 `node1-data` 볼륨에 저장되며, AI 모델 파일은 `neurochain/models`에서 읽기 전용으로 마운트됩니다.

## 데이터 디렉터리

| 경로 | 내용 |
|------|------|
| `<data dir>/shard<N>/bft_key.bin` | 노드의 합의 및 서명 키 — **백업하고, 절대 공유하지 마십시오** |
| `<data dir>/shard<N>/state_rdb/` | 샤드 N의 RocksDB 체인 상태 |
| `<data dir>/shard<N>/wal/` | 합의 write-ahead log로, 재시작 후 재실행됩니다 |

## 스냅샷에서 시작하기

새 노드는 실행 중인 노드에서 서명된 상태 스냅샷을 불러와 기록 재실행을 건너뛸 수 있습니다:

```bash
NEUROCHAIN_CHECKPOINT_URL=http://trusted-node:9933 ./target/debug/neurochain
```

데이터 디렉터리가 비어 있으면 노드는 `/api/v1/state/snapshot`을 가져와 적용하고, 그 높이부터 따라잡습니다. 스냅샷은 모든 잔액과 스테이크를 드러내므로 `NEUROCHAIN_SNAPSHOT_TOKEN`으로 엔드포인트를 보호하십시오(그러면 클라이언트는 `Authorization: Bearer <token>`을 보냅니다).

## 노출과 TLS

- RPC는 mainnet 규칙에서는 `localhost`에서, `CHAIN_ENV=testnet`에서는 `0.0.0.0`에서 수신합니다. 직접 선택하려면 `NEUROCHAIN_RPC_HOST`를 설정하십시오.
- RPC를 HTTPS로 제공하려면 `NEUROCHAIN_TLS_CERT`와 `NEUROCHAIN_TLS_KEY`(PEM 경로)를 설정하십시오.
- 사용자 앞에는 노드가 아니라 gateway를 두십시오.
- `NEUROCHAIN_INTERNAL_TOKEN`은 `X-Internal-Token` 헤더로 내부 엔드포인트(oracle 로그 쓰기, AI 데이터 내보내기)를 보호합니다.

## 모니터링

`GET /metrics`는 Prometheus 메트릭을 제공합니다:

| 메트릭 | 의미 |
|--------|------|
| `nc_block_height`, `nc_bft_round` | 체인 진행 |
| `nc_bft_validators_active`, `nc_bft_quorum_size` | 검증자 집합 |
| `nc_peer_count`, `nc_mempool_size`, `nc_tx_total` | 네트워크와 부하 |
| `nc_total_staked`, `nc_base_fee`, `nc_fees_burned` | 경제(금액은 uNRO 단위) |
| `nc_deploy_rejected_total{rule_id}` | VMGuardian이 거부한 배포, 규칙별 |
| `nc_model_active_count`, `nc_model_voting_count`, `nc_model_version{…}`, `nc_model_confirms_needed`, `nc_model_voting_deadline_remaining_blocks` | AI 모델 레지스트리 |
| `nc_sentinel_flagged_total{category}`, `nc_sentinel_held_total`, `nc_sentinel_released_total{how}` | Sentinel |

`monitoring/`에는 Prometheus 스크레이프 설정과 미리 구성된 Grafana 대시보드가 있으며, 큰 mempool, 피어 2개 미만, 배포 거부 급증에 대한 알림이 포함되어 있습니다.

## 백업과 복원

```bash
./scripts/backup.sh [data_dir] [backup_dir]     # 실시간 복사, 노드는 계속 실행됨
./scripts/restore.sh <backup_dir> [data_dir]    # 먼저 노드를 중지
```

백업에는 `bft_key.bin`이 포함되므로 키 자체만큼 안전하게 보관하십시오.

## 알아 둘 만한 로그

| 로그 줄 | 의미 |
|---------|------|
| `consensus decided, committing height=…` | 블록이 완결됨 |
| `Below minimum: n/7 active validators` | 검증자가 `NEUROCHAIN_MIN_VALIDATORS`보다 적음 |
| `STATE DIVERGENCE` | 이 노드의 상태가 제안자의 상태와 다르며, 해결될 때까지 투표를 거부함 |
| `genesis validator(s) missing ML-DSA pubkey` | 제네시스 집합의 항목에 공개 키가 없어 제외됨 |

모든 변수: [환경 변수](./env).
