PoCs

프로토콜에 흡수된 DVT

분산 밸리데이터를 미들웨어가 아니라 프로토콜이 직접 다루자는 제안 정독 노트 — 키를 쪼개는 대신 여러 키를 m-of-n으로 묶습니다. ⚠️ EIP 번호가 확정된 표준이 아니라 ethresear.ch 단계의 논의이므로, 구현 보고가 아니라 설계 분석입니다.

↓ 기술 노트 보기 (목적·동작 방식)

배경 — 잔고 유연성이라는 지반

Pectra의 EIP-7251이 유효 잔고 상한을 32에서 2048 ETH로 올리면서, 32·n ETH를 예치하면 n개의 슬롯이 선다는 산수가 프로토콜 안에서 성립하게 되었습니다. 여러 참여자를 하나의 밸리데이터로 묶는 이야기가 프로토콜 바깥의 요령이 아니라 프로토콜 안의 설계 선택지가 된 출발점입니다.

오늘의 DVT — 열쇠를 쪼갠다

Obol·SSV 같은 현행 DVT는 하나의 밸리데이터 키를 샤미르 분할이나 임계 BLS로 쪼개 노드들이 나눠 들고, 서명이 필요할 때마다 오프체인 합의 라운드(QBFT 계열)를 돌려 재조립합니다. 분산은 전부 프로토콜 밖 미들웨어에 삽니다 — 프로토콜은 여전히 밸리데이터 하나만 봅니다.

제안 — 금고를 바꾼다

키를 쪼개지 않습니다. 각 참여자가 처음부터 자기 키를 등록하고(n ≤ 16), 프로토콜이 그것을 m-of-n으로 묶습니다. BLS 집계와 참여 비트필드를 쓰는데, 이는 오늘날 attestation 집계와 같은 문법입니다 — m개 이상이 참여하면 인정. 사라지는 것 둘: ① 서명마다의 오프체인 합의 라운드 ② 키 세리머니와 DKG.

비유하자면: DVT는 열쇠를 조각내 바깥에서 조립해 금고를 열고, 이 제안은 열쇠 여러 개가 꽂히면 열리도록 금고 자체를 바꿉니다.

정독하며 남은 질문 넷

슬래싱 귀속

m 중 한 참여자가 슬래싱 대상 서명을 하면 누가 무는가? 부분 슬래싱은 오늘의 프로토콜이 하지 않는 새로운 회계입니다.

지연 예산

제 시간에 m명이 모이지 않으면 그룹이 의무를 놓칩니다. Liveness 페널티는 누군가에게 귀속돼야 하는데, 탓할 조율 라운드가 없으니 "누구 잘못인가"가 자명하지 않습니다.

m < n 의 트레이드오프

m이 낮으면 가용성이 높아지는 대신 공모가 쉬워지고, 높으면 저항성이 오르는 대신 멈추기 쉬워집니다. 파라미터로 위장한 제품 결정입니다.

n ≤ 16 의 근거

보안 논거라기보다 집계 비용 상한으로 보이지만, 가정하지 말고 원 스레드에서 확인할 항목입니다.

실무 함의

미들웨어의 운명 패턴

번들러·페이마스터가 ERC-4337로 미들웨어에 살다가 네이티브 AA 논의(EIP-7702, EIP-8141)로 프로토콜에 흡수되는 흐름과 구조적으로 같습니다. 프로토콜 바깥에서 잘 작동하는 것은 결국 안으로 들어오고, 미들웨어는 서비스 계층으로 후퇴합니다 — 이 사이트의 AA 페이지들이 다루는 것과 같은 이야기의 다른 사례입니다.

기관 스테이킹의 신뢰 가정이 한 층 내려간다

오늘 기관은 운영자를 믿고, 운영자는 DVT 미들웨어를 믿습니다. 프로토콜이 m-of-n을 직접 다루면 그 중간 한 층의 신뢰 가정이 사라집니다 — ETP·커스터디 배관을 실사하는 쪽에 직접적인 변화입니다.

참여 비트필드 = 새로운 지표군

누가 참여했는지가 온체인에 남으면 운영자별 가동률이 공개 데이터가 됩니다. 인덱서 관점에서는 없던 지표군이 생기는 일이고, 아래 PoC 후보 중 하나가 정확히 여기서 나옵니다.

PoC 후보 — 무엇을 지금 만들 수 있나

제안 자체는 프로토콜 변경이라 개인이 구현할 수 없습니다. 하지만 아이디어의 회계 구조 — 독립 키 n개, m개 일치로 성립, 참여 기록이 평판이 됨 — 는 한 층 위에서 오늘 그대로 만들 수 있습니다.

m-of-n 정산 리졸버 — 예측시장 오라클 어댑터지금 만들 수 있음

오늘 단일 운영자나 단일 분쟁 게임에 의존하는 정산 계층이라면 이 아이디어가 그대로 이식됩니다. n명의 리졸버가 각자 독립 생성한 키를 들고, m개의 일치하는 서명이 마켓을 정산하며, 비트필드가 실제로 누가 참여했는지 기록합니다. EIP를 기다릴 필요가 없어요 — 한 층 위에서 같은 모양입니다.

m-of-n 독립 체커 — 암호학 없이 같은 회계 구조지금 만들 수 있음

"리뷰어 1명 승인"을 "독립 체크 n개 중 m개 통과"로 바꾸는 것 — 링크 검사, 빌드, 맞춤법, 정책 린터. 수입할 규칙은 체커들이 서로의 결과를 보지 않는다는 점입니다: 조율 라운드가 없으면 검증 비용이 n²이 아니라 n에 비례합니다. 누적된 통과/실패 기록이 비트필드의 CI 버전이고, 쓸모없는 체커를 의견이 아니라 데이터로 퇴출할 수 있게 해줍니다.

밸리데이터 셋 레지스트리 — 비트필드 기반 운영자 신용평가제안 채택 이후

참여 여부가 공개 비트필드가 되면, 운영자별 가동률과 슬래싱 이력이 처음으로 인덱싱 가능한 온체인 데이터가 됩니다. 이를 점수화하면 운영자를 고르는 쪽 — 가장 분명하게는 기관 스테이킹 데스크 — 을 위한 실사 도구가 됩니다. 이건 정말로 제안 채택을 기다려야 해요. 데이터 자체가 아직 없습니다.

셋 큐레이션 — 세리머니가 사라진 뒤 남는 것제안 채택 이후

DKG를 없애는 것은 기술적 의례를 없앨 뿐 그 뒤의 질문을 없애지 않습니다: 누가 n에 들어갈 자격이 있는가, 누가 본드를 서는가, 누가 셋을 보험 처리하는가. 이건 서비스 계층이고, 프로토콜이 핵심 기능을 흡수했을 때 오늘의 미들웨어 운영자들이 착지할 자리입니다. 추정이지만 낯익은 패턴이에요.

첫 후보의 스펙 초안

정독하며 남은 질문 넷이 그대로 스펙 결정 넷으로 번역된다는 점이 이 아이디어의 매력입니다 — 슬래싱 귀속은 부분 슬래싱 회계로, 지연 예산은 폴백 규칙으로, m<n 트레이드오프는 등급별 m 테이블로, n 상한은 집계 가스 비용으로.

ResolverSetAdapter

  resolvers[n]   independently generated keys, n <= 16
  threshold m    set per market — a product tier, not a constant
  attest(marketId, outcome, sig)
                 each resolver submits alone, seeing no one else
  bitfield       who actually submitted — the reputation ledger
  slashing       only the wrong signers lose bond, not the set
  liveness       fewer than m by deadline + T  ->  fall back

그리고 비트필드가 제품이 됩니다 — "가동률 99%인 리졸버 7명 중 5명이 정산한 마켓" 같은 문장을 UI 배지로 보여줄 수 있어요. 신뢰 모델을 사용자에게 파는 방법입니다.

기술 노트

각 항목이 실제로 무엇을 보여주고 어떻게 동작하는지 — 위 카드보다 자세한 기술 설명입니다.

프로토콜에 흡수된 DVT완료

목적: 진행 중인 프로토콜 설계 논의를, 사람들이 흔히 뭉뚱그리는 세 가지를 분리할 만큼 자세히 읽는 작업입니다: 제안이 실제로 바꾸는 것, 미해결로 남긴 것, 그리고 제안을 기다리지 않고 한 층 위에서 지금 만들 수 있는 부분. 이 사이트가 이미 다른 곳에서 기록하고 있는 패턴의 두 번째 사례이기도 합니다 — 미들웨어가 어떤 일을 잘 해내다가 프로토콜에 흡수되는 흐름으로, ERC-4337 번들러가 네이티브 계정 추상화 앞에서 맞고 있는 상황과 같습니다.

동작 방식: 오늘의 DVT(Obol·SSV)는 하나의 밸리데이터 키를 샤미르 분할이나 임계 BLS로 쪼갠 뒤, 서명이 필요할 때마다 오프체인 합의 라운드로 재조립합니다 — 프로토콜은 여전히 밸리데이터 하나만 보고, 분산은 전부 미들웨어에 삽니다. 제안은 키를 쪼개지 않습니다: 각 참여자가 자기 키를 등록하고(n ≤ 16), 프로토콜이 m-of-n으로 묶으며, BLS 집계와 참여 비트필드(오늘날 attestation 집계와 같은 문법)로 충분한 인원이 참여했는지 판정합니다. 이로써 서명마다의 합의 라운드와 DKG 세리머니가 함께 사라지고, 이것이 가능한 이유는 EIP-7251이 유효 잔고 상한을 올려 32·n ETH가 n개 슬롯을 세운다는 산수를 프로토콜이 내부적으로 할 수 있게 되었기 때문입니다. 페이지는 이것이 EIP 번호가 없는 ethresear.ch 단계의 논의임을 명시하고, 미해결로 남은 질문 넷(슬래싱 귀속, 지연 예산, m<n 공모 트레이드오프, n ≤ 16의 근거)을 나열하며, PoC 후보를 애플리케이션 계층에서 지금 만들 수 있는 것과 실제로 채택을 기다려야 하는 것으로 구분합니다.

오늘 — 키 하나를 쪼개, 프로토콜 밖에서 재조립
제안 — 따로 있는 키들을 프로토콜이 m-of-n으로 묶음
관련 코드:
# DVT in the protocol — the proposal never splits one validator key (unlike today's
# Obol/SSV, which Shamir-splits a key and reassembles a signature via off-chain
# consensus). Instead each of n participants keeps their own key; the protocol groups
# them m-of-n and a participation bitfield (mocked BLS aggregation) decides if enough
# signed — the same grammar as today's attestation aggregation.

import hashlib
from dataclasses import dataclass


@dataclass
class Participant:
    operator_id: str
    private_key: str  # each operator's own key — never shared or split

    def sign(self, message: str) -> str:
        # Mock signature: a real BLS sig would be a curve point; a hash stands in.
        return hashlib.sha256((self.private_key + message).encode()).hexdigest()[:16]


def bls_aggregate(signatures: list[str]) -> str:
    """Mock aggregation: real BLS sums curve points into one constant-size signature."""
    combined = "".join(sorted(signatures))
    return hashlib.sha256(combined.encode()).hexdigest()[:16]


def protocol_check(n: int, m_threshold: int, signed_by: list[Participant], message: str) -> dict:
    """The protocol groups n registered operators m-of-n and checks the bitfield."""
    bitfield = [1 if p in signed_by else 0 for p in OPERATORS[:n]]
    aggregate_sig = bls_aggregate([p.sign(message) for p in signed_by])
    quorum_met = sum(bitfield) >= m_threshold
    return {
        "bitfield": bitfield,
        "participants_signed": sum(bitfield),
        "threshold": m_threshold,
        "quorum_met": quorum_met,
        "aggregate_signature": aggregate_sig if quorum_met else None,
    }


OPERATORS = [Participant(f"operator-{i}", private_key=f"sk-{i}") for i in range(1, 6)]  # n = 5

if __name__ == "__main__":
    print("DVT absorbed into the protocol — separate keys, m-of-n grouping, participation bitfield\n")

    n, m = 5, 3
    message = "attest(slot=1234)"

    print(f"n={n} registered operators, m={m} required to reach quorum\n")

    # Only 3 of 5 operators actually sign this round.
    signers = OPERATORS[:3]
    result = protocol_check(n, m, signers, message)
    print(f"Signers this round: {[p.operator_id for p in signers]}")
    print(f"Participation bitfield: {result['bitfield']}")
    print(f"Signed {result['participants_signed']}/{n}, threshold {result['threshold']} "
          f"-> quorum_met={result['quorum_met']}")
    print(f"Aggregate signature: {result['aggregate_signature']}\n")

    # Below threshold: only 2 sign.
    signers = OPERATORS[:2]
    result = protocol_check(n, m, signers, message)
    print(f"Signers this round: {[p.operator_id for p in signers]}")
    print(f"Participation bitfield: {result['bitfield']}")
    print(f"Signed {result['participants_signed']}/{n}, threshold {result['threshold']} "
          f"-> quorum_met={result['quorum_met']}")
    print("No aggregate signature: quorum not reached.")

docs/code/pocs/dvt.py