# Sentinel

> NeuroChain의 트랜잭션 이상 점수 산정 Sentinel — 내장 모델과 특징, 카테고리, 수신 단계 거부 임계값, 그리고 거부 대신 보류하거나 확인을 요청하는 선택적 정책 계층과 신뢰 목록, 세션 키 규칙.

Source: https://docs.nro.world/ko/ai/sentinel

Sentinel은 들어오는 모든 송금에 0(정상)부터 1(매우 이례적)까지의 **이상 점수**를 부여합니다. 설정에 따라 노드는 송금을 수락하거나, 표시하거나, 거부하거나, 정책 계층이 켜져 있으면 일정 시간 동안 또는 소유자가 확인할 때까지 보류합니다.

## 모델

노드에 내장된 작은 신경망입니다(입력 8 → 16 → 8 → 1, 파라미터 289개). 입력은 사람이 아니라 송금을 설명합니다.

| 특징 | 출처 |
|------|------|
| 금액 크기(로그 스케일) | `value` |
| 가스 가격 | `gas_price` |
| nonce 간격 | `nonce`와 마지막 확정 nonce의 비교 |
| 새 수신자 여부 | 해당 수신자에게 보낸 이전 송금 |
| 딱 떨어지는 금액 | `value` |
| 보낸 사람의 송금 빈도 | 최근 10개 블록에서 보낸 사람의 송금 |
| 이동하는 잔액 비율 | `value`와 잔액의 비교 |
| 자기 자신에게 송금 | `from == to` |

합의 과정에서 노드는 이 값들을 **커밋된 블록에서만** 계산하므로(송금 빈도는 mempool이 아니라 블록에서 셉니다), 모든 검증자가 같은 트랜잭션에 대해 같은 점수를 얻습니다.

| 점수 | 카테고리 | `anomalous` |
|------|----------|-------------|
| < 0.5 | `normal` | 아니요 |
| 0.5 – 0.6 | `suspicious` | 예 |
| 0.6 – 0.8 | `elevated` | 예 |
| ≥ 0.8 | `high_risk` | 예 |

아무것도 보내지 않고 시험해 보세요.

```bash
curl -s -X POST "$GATEWAY/api/v1/sentinel/score" -H 'Content-Type: application/json' \
  -d '{"value":45000000,"gas_price":2,"nonce":3,"last_nonce":2,"balance":48000000,
       "tx_count_last_10":1,"recipient_tx_count":0,"is_self_transfer":false}'
# {"anomalous":true,"category":"elevated","score":0.7119}
```

## 송금은 어떻게 처리되나

![Sentinel 결정 흐름: 점수가 0.85 이상이면 수신 단계에서 거부됩니다. 그렇지 않으면, 정책 계층이 꺼져 있을 때 송금이 적용됩니다(이상이면 표시됨). 켜져 있을 때는 신뢰하는 수신자에게는 정상 적용되고, 점수가 0.70 이상(세션 키는 0.60 이상)이면 소유자가 확인할 때까지 보류되며, 점수가 0.60 이상이면 보류된 뒤 50블록 후 자동으로 해제됩니다](/diagrams/ko/sentinel-policy.svg)

### 항상 켜짐 <Badge type="tip" text="작동 중" />

- `sentinel_enabled = 1`: 모든 송금은 노드에 도달할 때 점수가 매겨집니다.
- 점수 ≥ `sentinel_reject_threshold`(0.85) → HTTP 400으로 거부됩니다.
- 이상이지만 그보다 낮으면 → 수락됩니다. 응답에 `sentinel_score`, `sentinel_category`, `sentinel_warning`이 포함되고, gateway가 `tx_anomaly`를 브로드캐스트합니다.

### 정책 계층 <Badge type="warning" text="기본값 꺼짐" />

`sentinel_policy_enabled = 1`이면, 원래 적용되었을 송금이 **보류**될 수 있습니다. 보낸 사람에게서는 차감되지만, 받는 사람에게는 아직 입금되지 않습니다.

| 구간 | 조건 | 처리 |
|------|------|------|
| 신뢰 | 수신자가 보낸 사람의 활성 신뢰 목록에 있음 | 정상 적용 |
| 보류 | 점수 ≥ `sentinel_hold_threshold`(0.60) | `sentinel_hold_blocks`(50) 후 자동 해제; 그 전에 보낸 사람이 취소 가능 |
| 추가 확인 | 점수 ≥ `sentinel_stepup_threshold`(0.70), **또는** 세션 키로 서명되고 점수 ≥ `sentinel_agent_confirm_threshold`(0.60) | 소유자가 **마스터 키로 확인**할 때까지 대기; `sentinel_confirm_expiry_blocks`(1 000) 안에 확인되지 않으면 환불 |

보류된 송금의 상태는 `held`이며, `confirmed`(해제됨), `cancelled`, `refunded` 중 하나로 끝납니다. 취소되거나 환불되면 금액은 보낸 사람에게 돌아가고, 수수료는 돌려주지 않습니다.

| 작업 | 엔드포인트 |
|------|------------|
| 보류된 송금 목록 | `GET /api/v1/held/:address` |
| 확인(소유자, 마스터 키) | `POST /api/v1/transactions/:hash/confirm` |
| 취소(소유자) | `POST /api/v1/transactions/:hash/cancel` |

WebSocket 이벤트: `tx_held`, `tx_released`, `tx_cancelled`.

### 신뢰 목록

소유자는 수신자를 신뢰 대상으로 표시할 수 있으며, 이들에게 보내는 송금은 보류를 건너뜁니다. 새 항목은 `trust_activation_blocks`(300) 이후에만 활성화됩니다 — 따라서 탈취된 키로 공격자의 주소를 추가해 즉시 사용할 수 없습니다.

`POST /api/v1/trust/add`, `POST /api/v1/trust/remove`, `GET /api/v1/trust/:owner`.

### 세션 키는 스스로를 승인할 수 없습니다

`neuro:system.trust`를 대상으로 하는 모든 것 — 신뢰 목록 변경, 보류된 송금의 확인이나 취소 — 은 세션 키에 대해 거부됩니다. AI 에이전트는 위험한 송금을 시작할 수 있지만, 이를 통과시킬 수 있는 것은 소유자의 마스터 키뿐입니다. [계정 추상화](./account-abstraction)를 참고하세요.

## 보정 <Badge type="warning" text="실험적" />

모델은 합성 트랜잭션 패턴으로 학습되었습니다. 실제 송금은 합성 데이터가 예상한 것보다 높은 점수를 받습니다. 테스트 네트워크에서 새 수신자에게 보낸 평범한 1.5 NRO 송금은 약 **0.62**를 받았고, 측정된 점수는 약 **0.74**를 넘는 경우가 드뭅니다. 그 결과 두 가지가 나타납니다.

- 0.85 거부 임계값에는 사실상 결코 도달하지 않습니다.
- 기본 임계값으로 정책 계층을 켜면, 새 수신자에게 보내는 평범한 송금이 다수 보류됩니다.

임계값은 거버넌스 파라미터이며, 공개 네트워크에서 정책 계층을 활성화하기 전에 실제 트랜잭션 데이터로 임계값을 조정하고 모델을 재학습할 예정입니다.

**테스트 방법:** 정책 계층은 쿼럼이 3인 검증자 4개에서 7단계 테스트를 통과했습니다 — 자동 해제, 다른 노드를 통한 추가 확인, 만료 시 환불, 세 번째 노드를 통한 취소, 노드 재시작 후 유지, 신뢰 목록 활성화, 그리고 모든 노드에서 동일한 카운터와 상태 루트. 노드 단위 테스트가 모든 분기를 다룹니다.

## 메트릭

Prometheus 카운터이며, 체인 상태에 보관되므로 모든 노드가 같은 숫자를 보고합니다: `nc_sentinel_flagged_total{category}`, `nc_sentinel_held_total`, `nc_sentinel_released_total{how}`.
