크라우니 스테이킹 슬래싱 집행 (2차)
개요
1차 스테이킹 서비스(
/Users/ef/crowny-staking/, :9975, staking.crowny.org 라이브)의 슬래싱은 규칙 정의만 있고 집행은 미구현(
"enforced":0)이었다. 2차에서
하트비트 기반 다운타임 슬래싱 집행을 붙였다. 순수 한선씨. 라이브 파일은 백업 후 1회 재기동, 검증 후 라이브 데이터 원상복구.
무엇을 했는지
하트비트 기록 (밸리데이터하트비트(주소, 시각)): 밸리데이터 생존 신호를 append-only 로그(같은 stakes.dat)에 기록. evt="heartbeat", ts=하트비트 시각. 시각<=0 이면 서버시각. 마지막하트비트(주소)가 최신값 조회(없으면 -1).
다운타임 슬래싱 집행 (슬래싱집행(현재시각)): 밸리데이터 순회 →
기준선 = 마지막하트비트(없으면 최초 등록시각)
에폭수 = floor((현재시각 − 기준선)/86400)
목표BP = min(에폭수 × 10bp, 500bp 상한)
새BP = 목표BP − 누적슬래시BP → 새BP만 신규 차감(멱등 핵심)
차감액 = floor(활성스테이크 × 새BP / 10000), 몫곱(1e8×500 무붕괴), 원금 상한·0 하한 클램프
slash 이벤트 append: evt="slash", amt=차감액, bp, epochs, reason="downtime"
집행 커서(evt="enforce", cursor=기준시각) append — 감사·멱등 기준
멱등: 같은 다운타임 구간 재집행 → 목표BP 불변 → 새BP=0 → 스킵. 상한 도달분도 목표BP=500 고정이라 자동 스킵.
활성스테이크 반영: 활성스테이크·총스테이크가 slash 이벤트 amt를 차감하도록 수정 → 차감이 즉시 잔여 스테이크에 반영.
API (스테이킹서버.한선):
POST /api/validator/heartbeat {"addr":"..","ts":선택} — ts 생략 시 서버시각
POST /api/slashing/enforce {"now":선택} — now 생략 시 서버시각, 전 밸리데이터 집행
GET /api/slashing/log?addr= — 슬래시 이벤트 이력(addr 생략=전체)
GET /api/slashing/status — 집행 요약(slash_events·total_deducted·last_cursor·enforced:1)
상태JSON의 slashing 블록 enforced:0 → 1관련 파일
/Users/ef/crowny-staking/libs/스테이킹엔진.한선 — 엔진(하트비트·집행 함수 추가)
/Users/ef/crowny-staking/libs/스테이킹엔진.rpn.한선 — RPN 부본(high2rpn.sh 재생성)
/Users/ef/crowny-staking/스테이킹서버.한선 — 4개 라우트 추가
/tmp/staking-test/테스트_슬래싱.한선 — 격리 데이터파일 5시나리오 검증(22/22)
- 백업:
스테이킹서버.한선.bak-slashing, libs/스테이킹엔진.한선.bak-slashing, crowny-data/staking/stakes.dat.bak-preslash
검증 결과 (엔진 격리테스트 22/22 통과 + 라이브 스모크)
- ① 정상(하트비트 최신, <1에폭): slashed=0, 활성 15000 유지, 누적BP 0
- ② 2에폭 다운타임: 20bp, 차감 30맘(15000×20/10000=30 손계산 일치), 활성 14970, total_deducted:30
- ④ 재집행 멱등(동일 다운타임): slashed=0, 누적BP 여전히 20, 활성 14970 불변
- ③ 60에폭: 500bp 캡, 차감 750맘(15000×500/10000=750), 활성 14250; 캡후 재집행 slashed=0(누적BP 500 고정)
- ⑤ 원금 초과 금지: 차감 750 < 원금 15000, 활성 > 0; 요약 enforced:1, 로그 bp:500/deducted:750
- 라이브 스모크(:9975): 밸리데이터 stake 20000 + 3에폭 전 하트비트 → enforce → 30bp/60맘 차감, 활성 20000→19940, log·status·staking 블록 정상. 스모크분은 라이브 stakes.dat 원상복구.
VM 함정 준수
- 맵있나() 부재 시 -1 →
!=1로만 비교(중복 밸리데이터 방지)
- 배열 append-only, 인라인 누적 금지(임시변수 t차감)
- 몫곱 패턴으로 1e8×500 오버플로 회피, 내림나눗셈으로 자연반올림 보정
- 슬래시 amt 원금 상한·0 하한 클램프
잔여 이슈 (3차)
- 하트비트/slash/enforce 이벤트가 stakes.dat에 함께 누적 → 읽기() 64KB·줄목록 1023 상한 도달 전 스냅샷+압축 필요(1차 잔여와 동일, 이벤트 종류 늘어 도달 앞당겨짐)
- 자동 집행 스케줄러(현재 수동 POST /enforce 트리거만) — LaunchAgent 금지 정책상 외부 크론/집사 연동은 별도 승인 후
- 뱅크서버(:9733) 잔고 연동(차감분 실잔고 반영)
크라우니코드: lookup HIT 3(fn_합의POS_슬래싱·밸리데이터수익시뮬슬래싱·fn_서버코어_하트비트확인) / MISS 2(append-only 파일형 집행·멱등 커서) / learn 2(fn_스테이킹_슬래싱집행·fn_스테이킹_하트비트기록)