이 번역은 참고용입니다. 정식 기준은 영어판이며, 내용이 다를 경우 영어판이 우선합니다. 영어판 읽기

검증

Label 309가 정의하는 세 가지 검증자 역할, 판정 상태, 확정 깊이, 그리고 타입이 부여된 오류 카탈로그 — 누구나 공개 인프라만으로 동일한 결론에 도달하는 방법.

Label 309는 검증되는 것이지, 결코 단순히 주장되는 것이 아닙니다. 게시자는 콘텐츠 해시를 label 309 아래 Cardano에 기록합니다. 그 시점부터 주장은 그 바이트만으로 자립하며, 트랜잭션 참조를 가진 사람은 누구나 이를 확인할 수 있습니다. 이 페이지는 그 확인이 어떻게 진행되는지를 정의합니다. 세 가지 검증자 역할, 각 역할이 무엇에 접근하고 무엇에 접근하지 않는지, 그것들이 내놓는 판정 상태, 판정이 잠정적으로 머무는 확인 깊이의 하한, 그리고 동일한 입력에 대해 두 개의 독립적인 구현이 동일한 실패로 일치하도록 만드는 타입이 부여된 오류 카탈로그입니다.

결정적인 속성은 서비스 독립성입니다. 적합한 검증자는 공개 체인, 검증자가 직접 선택한 Cardano 익스플로러 또는 게이트웨이, 그리고 — 콘텐츠 주장과 봉인된 주장에 대해서는 — 마찬가지로 검증자가 직접 선택한 콘텐츠 주소 지정 스토리지 게이트웨이만을 사용하여 판정에 도달합니다. 검증자는 게시자에게 결코 연락하지 않습니다. 표준은 특정 제공자를 지명하지 않습니다. 게이트웨이는 운영자가 공급하는 입력입니다.

세 가지 역할, 각각이 엄격한 확장

검증은 계층을 이룹니다. 각 역할은 그 상위 역할이 하는 모든 일을 수행하고, 거기에 한 가지 능력을 더합니다. 하위 역할은 그 자체로 완전하고 유용한 검증자입니다 — 단지 증명하는 범위가 더 좁을 뿐입니다.

역할추가하는 것접근하는 대상
구조 검증기레코드 바이트에 대한 스키마 + 도메인 적합성 확인없음 — 순수 함수
공개 검증자체인 해석, 온체인 포함, 서명 검사Cardano 익스플로러 + 콘텐츠 게이트웨이
수신자 검증자봉인된 페이로드의 시험 복호화, 평문 해시 재계산검증자 자신의 개인 키

구조 검증기 — 바이트에 대한 순수 함수

구조 검증기는 바이트 문자열을 결과로 사상하는 단일 함수입니다. 이 검증기는 I/O도, 암호 서명 검사도, 복호화도 일절 수행하지 않습니다. 네트워크, 트랜잭션, 키를 결코 보지 않습니다. 동일한 입력을 주면 언제든, 어디서든 동일한 출력을 반환합니다 — 바로 이 점 덕분에 서버를 일절 거치지 않고도 게시자의 도구 안에서 제출 전 검사로, 서드파티 인덱서 안에서, 또는 장기적인 정형성을 확인하는 아카이브 도구 안에서 실행될 수 있습니다.

그 파이프라인은 고정되어 있습니다.

1. resource bounds   — a local, non-normative guard on input size; never a
                       Label 309 conformance error.
2. canonical decode  — decode with a canonical-CBOR decoder (RFC 8949 §4.2.1):
                       definite lengths, sorted map keys, no duplicate keys,
                       valid UTF-8. Any malformed or non-canonical input →
                       a single MALFORMED_CBOR.
3. schema parse      — type, length, and the chunk/length bounds; a strict
                       object mode that rejects unknown fields.
4. domain rules      — cross-field constraints the schema cannot express:
                       registry membership, the items-or-merkle rule, COSE
                       structural shape, URI reconstruction, envelope shape.
5. result            — { valid, record } with optional warnings/info, or a
                       sorted list of typed issues.

검증기는 프로파일에 비의존적입니다. 하류의 검증자가 어떤 부분집합에 대해 동작하려 하든 상관없이 전체 v1 스키마를 파싱합니다. 오류는 레코드를 불합격시킵니다. 경고와 정보 항목은 표면화되지만 레코드는 여전히 유효한 상태로 남습니다. 결정적으로, 검증기는 COSE*Sign1의 *형태_ — 4요소 배열, 분리된(null) 페이로드, 정형화된 보호 헤더 — 를 확인하지만, 서명 자체는 결코 검증하지 않으며, 서명 알고리즘이 인식하지 못하는 것이라는 이유만으로 레코드를 거부하는 일도 결코 없습니다(그 경우는 SIGNATURE_UNSUPPORTED, severity는 info로 태그되며 레코드는 유효한 상태로 남습니다). 서명을 검증하는 것은 공개 검증자의 일입니다. 이 단계가 강제하는 스키마에 대해서는 레코드를 참조하십시오.

공개 검증자 — 체인, 포함, 그리고 서명

공개 검증자는 구조 검증기 위에 체인을 얹습니다. Cardano 트랜잭션 참조가 주어지면 다음을 수행합니다.

  1. 검증자가 선택한 익스플로러를 해석합니다. 익스플로러 체인은 입력입니다. 검증자는 그것들을 순서대로 시도하며 온체인 원본 트랜잭션 CBOR를 가져옵니다 — 익스플로러의 메타데이터 JSON 투영은 결코 가져오지 않습니다. JSON 뷰는 CBOR 주요 타입을 JSON 유니온으로 접어 넣고 맵 키 순서, 확정 길이 프레이밍, 바이트 대 텍스트 판별자를 버리기 때문에, 그것에서 재인코딩한 검증자는 바이트 단위로 정확한 서명 입력을 재현할 수 없으며 적합한 레코드의 모든 서명이 실패하게 됩니다. 단일 제공자의 부정적 응답은 체인에 대해 권위가 없습니다 — 트랜잭션은 체인 위에 있고 단지 그 제공자가 모를 뿐일 수 있기 때문입니다 — 그래서 검증자는 TX_NOT_FOUND를 내놓기 전에 남은 모든 제공자에게 문의합니다. 모든 제공자가 도달 불가능하면 PROVIDER_UNAVAILABLE을 내놓습니다.
  2. 가져온 바이트를 트랜잭션 참조에 결속합니다. 가져온 트랜잭션에서 무엇이든 읽어내기 전에, 검증자는 가져온 트랜잭션 본문 바이트에 대해 blake2b-256을 재계산하고 — 원장의 정의에 따라 이것이 트랜잭션 id입니다 — 요청한 해시와 일치하지 않는 응답을 거부합니다. 그런 다음 가져온 auxiliary-data 바이트에 대해 blake2b-256을 재계산하고, 이제 검증된 본문의 auxiliary_data_hash와 일치하지 않는 것을 거부합니다. 두 다이제스트 모두 재인코딩이 아니라 가져온 그대로의 바이트에 대해 계산됩니다. 어느 한 검사라도 실패한 응답은 명백히 잘못된 바이트를 운반하므로 폐기됩니다. 어떤 제공자도 결속을 견뎌내는 응답을 내놓지 못하면 보고서는 TX_INTEGRITY_MISMATCH를 운반합니다. 이 단계 이후 레코드와 주변 트랜잭션의 모든 바이트는 호출자가 제공한 해시에 암호학적으로 커밋됩니다 — 어떤 익스플로러도 blake2b-256 제2 역상을 만들어내지 않고서는 레코드를 대체하거나 변경하거나 잘라낼 수 없습니다.
  3. auxiliary data를 풀어냅니다. Conway 시대의 원장은 세 가지 auxiliary-data 인코딩을 허용하며, 모두 유효합니다. 태그가 없는 맨 메타데이터 맵, 두 요소짜리 [ metadata, scripts ] 배열, 그리고 메타데이터를 키 0 아래에 둔 tag-259 맵입니다. 검증자는 이 세 가지를 모두 받아들여야 하며(MUST), 오직 최상위 CBOR 타입과 태그에 의거하여 분기해야 합니다 — tag-259 값은 키 부여 맵 형태이고, 태그가 없는 배열은 두 요소 형태이며, 태그가 없는 맵은 항상 메타데이터 맵 그 자체입니다. 검증자는 맵의 키를 살펴 형태를 추측해서는 안 됩니다(MUST NOT). 그 밖의 어떤 최상위 형태, 또는 259가 아닌 어떤 태그도 MALFORMED_CBOR입니다. 결속된 트랜잭션이 label-309 메타데이터를 운반하지 않으면 검증자는 METADATA_NOT_FOUND를 내놓습니다 — 이는 레코드에 귀속되는 결과입니다. 결속된 트랜잭션 자체가 그 부재를 증명하기 때문입니다. 어떤 제공자도 결속을 실패시키지 않고는 메타데이터를 벗겨낼 수 없었을 것입니다.
  4. 본문 전체의 청크 배열을 재조립합니다. label-309 값은 정준 CBOR 레코드 본문을 ≤ 64바이트 바이트 문자열의 배열로 분할한 것입니다. 검증자는 요소들을 순서대로 바이트 연결합니다 — 재인코딩 단계를 끼워 넣지 않고 원본 바이트를 반환하므로 — 정준 CBOR 검사가 비적합한 온체인 인코딩을 여전히 포착할 수 있습니다.
  5. 재조립된 본문을 구조적으로 검증합니다(상위 역할). 검증기가 거부하면 보고서는 판정 failed로 단락됩니다.
  6. 확인 깊이를 검사합니다(아래 참조). 임계값을 밑도는 트랜잭션은 여기서 판정 pending으로 멈춥니다.
  7. 엄격한 Ed25519 아래에서 모든 레코드 수준 서명을 검증합니다. 복호화는 하지 않습니다. 서명 해석, 도메인 분리된 페이로드, 엄격한 검증 규칙은 서명에 규정되어 있습니다.
  8. 콘텐츠를 가져와 해시 대조합니다. 레코드에 책임을 묻는 것은 URI 자신의 콘텐츠 주소에 귀속될 수 있는 바이트에 한정됩니다(아래 참조). 복호화는 하지 않습니다.

JSON이 아니라 원본 CBOR인 이유

서명은 레코드 본문의 바이트 단위로 정준한 CBOR에 대해 계산됩니다. 메타데이터의 JSON 투영은 구조상 손실이 있어 그 바이트로 왕복하여 되돌아갈 수 없습니다. JSON에서 재인코딩하면 적합한 레코드의 모든 서명이 깨집니다. 원본 트랜잭션 CBOR는 모든 암호 검사에 대해 유일하게 권위 있는 입력입니다. JSON 뷰는 검증이 이미 통과한 뒤, 사람에게 보여주기 위한 표시용입니다.

수신자 검증자 — 복호화하고 재계산하기

수신자 검증자는 추가로 개인 키를 보유한 공개 검증자입니다. 자신에게 주소 지정된 봉인된 항목에 대해, 온체인 키 슬롯을 자신의 키로 시험 복호화하고, 성공하면 콘텐츠 키를 복구하여 암호문을 복호화한 다음, 온체인 커밋먼트에 대해 평문 해시를 재계산합니다 — 이로써 암호화된 바이트와 콘텐츠 존재 주장 사이의 고리를 닫습니다. 봉인된 항목은 모두 적어도 하나의 콘텐츠 해시 항목을 운반하므로, 이 재계산에는 비교할 구체적인 대상이 언제나 존재합니다. 봉인 봉투, 키 슬롯, 그리고 언래핑 구성은 봉인된 PoE에 규정되어 있습니다.

수신자가 어떤 KEM이나 AEAD 프리미티브에 손대기 전에, 구조 검증기가 이미 실행한 것과 동일한 봉투 형태 및 리소스 검사를 다시 적용하여, 구조적으로 무효이거나 과도하게 큰 봉투를 먼저 거부합니다. 이 프리미티브 이전 가드 중 둘은 와이어 필드가 아니라 배포 시 고정되는 리소스 상한입니다. 참조 상한은 MAX_SLOTS = 1024 슬롯과 디코딩된 enc 봉투에 대한 65536 바이트이며, 둘 다 정직한 레코드를 제한하는 약 16 KiB의 Cardano 트랜잭션 메타데이터 천장을 훨씬 웃돕니다. 따라서 어느 한쪽이라도 초과하는 레코드는 정형이 아니며, 단 하나의 프리미티브가 실행되기 전에 ENC_SLOTS_TOO_MANY 또는 ENC_ENVELOPE_TOO_LARGE로 거부됩니다. 배포 측에서 더 빡빡하게 조여도 됩니다(MAY).

형태 검사 중 하나는 보안상의 핵심입니다. 캡슐화 재료는 하나의 slots[] 안에서 서로 달라야 합니다 (MUST) — x25519에서는 모든 epk 값이, mlkem768x25519에서는 모든 kem_ct 값이 달라야 합니다. 레코드 내부의 중복은 어떤 KEM이나 AEAD 프리미티브가 실행되기 전에 ENC_SLOTS_DUPLICATE_KEM_MATERIAL로 거부됩니다. 반복된 epk/kem_ct는 영 논스 래핑이 의존하는 슬롯별 키 유일성을 깨뜨리기 때문입니다. 레코드를 넘나드는, 또는 키를 넘나드는 키 재사용은 생산자의 의무이며 검증자가 탐지할 수 없습니다. 검증자가 탐지할 수 있는 것은 레코드 내부의 중복뿐입니다. 이 세 가지 검사는 모두 구조적인 것으로, 검증기가 모든 레코드에 대해 강제합니다(중복 재료, 슬롯 수, 디코딩된 봉투에 관한 각 코드는 Part-A 코드입니다). 수신자는 다층 방어로서 그것들을 단지 한 번 더 실행할 뿐입니다.

언래핑 자체는 수신자가 봉투로부터 — 어떤 곁가지 입력으로부터도 결코 아닌 — 재현하는 두 개의 암호 단계입니다. 먼저 슬롯 트랜스크립트 해시 slots_hash를 루프 전에 한 번 재계산하고, 모든 슬롯에 걸쳐 일정하게 유지합니다. slots_hash = SHA-256("cardano-poe-slots-transcript-v1" || canonicalEncode(SLOTS_TRANSCRIPT)) 이며, 여기서 트랜스크립트는 헤더 필드, 와이어상의 슬롯 집합(각 슬롯 필드는 단일 바이트 문자열로, 정규화할 필드별 청크화는 없습니다), 그리고 hashes_hash를 통한 항목의 해시 주장을 고정합니다. hashes_hash를 결속하는 것이야말로 수신자가, 어떤 암호문 가져오기보다도 먼저, 온체인 바이트만으로 봉투가 바로 이 정확한 해시 주장을 위해 봉인되었음을 확인할 수 있게 해 주는 것입니다 — 다른 hashes 맵을 가진 항목에 이어 붙여진 봉투는 MAC가 실패합니다. 검증자는 조기 이탈 없이 모든 슬롯을 순회하며, 슬롯은 그것이 산출하는 콘텐츠 키가 그 일정한 slots_hash에 대해 와이어상의 slots_mac도 재현할 때에 한해서만 받아들여집니다. 받아들임에는 또한 비밀에 의존하지 않는 유효성 비트가 섞여 듭니다. 고전적 경로에서는, X25519 공유 비밀을 전부 영 값으로 몰아가도록 조작된 슬롯이 그 비트를 거짓으로 만들고(0^32와의 상수 시간 비교), KEK는 0^32에서 파생된 더미로 상수 시간에 선택되어 루프가 동일한 작업을 수행하며, 그 비트가 슬롯의 받아들임을 통제합니다 — 무효 ECDH 슬롯은 그 래핑이나 MAC과 무관하게 결코 열리지 않습니다. 래핑 열기와 이후의 콘텐츠 열기는 모두 원자적입니다. AEAD 태그 실패 시 평문을 반환하지 않으며, 되돌려주는 후보(래핑된 키, 또는 콘텐츠 평문)는 실패한 암호문과 무관한 고정값 또는 유사난수 더미입니다 — 검증되지 않은 평문은 결코 외부로 풀려나지 않습니다.

수신자 키는 둘 이상의 슬롯에 정당하게 일치할 수 있습니다(MAY). 생산자는 동일한 콘텐츠 키를 동일한 수신자에게, 각각 새로운 슬롯별 KEM 재료를 곁들여 여러 슬롯에 봉인함으로써 수신자 수를 부풀릴 수 있습니다 — 이는 정당한 프라이버시 기법이며, 동일한 epk/kem_ct일 때에만 발동하는 중복 캡슐화 재료 거부와는 별개입니다. 검증자는 첫 번째 일치의 키를 선택하며, 여러 슬롯이 일치했다는 이유만으로 거부해서는 안 됩니다(MUST NOT). 검증자가 반드시(MUST) 거부해야 하는 유일한 이상은 서로 다른 콘텐츠 키를 복구하는 두 일치 슬롯입니다(상수 시간으로 비교). 루프는 cek_conflict 비트를 지니며, 이후의 어떤 일치가 선택된 키와 다른 키를 산출하면 그 단일한 일반적 실패를 표면화합니다. 이는 다층 방어입니다 — 슬롯 집합 커밋먼트 아래에서는 서로 다른 키를 산출하는 일치가 이미 실현 불가능하므로, 이 검사는 단지 안전하게 닫히며 실패할 뿐입니다.

그런 다음, 복구한 콘텐츠 암호화 키 위에서 콘텐츠 키(그 키의 HKDF 리프로, enc.nonce를 솔트로 함)를 파생하고, 세그먼트화된 STREAM 암호문을 청크 단위로 열어, 각 청크의 평문을 풀어주기 전에 그 청크의 태그를 검증합니다. 청크별 AAD는 비어 있습니다. 모든 헤더 컨텍스트는 slots_mac을 통해 이행적으로 이미 키에 결속되어 있으므로, 어떤 헤더 필드를 뒤집으면 수신자가 파생하는 것이 바뀌고 스트림이 열리지 않게 됩니다. 잘림은 마지막 플래그로 포착됩니다 — 누락된 마지막 청크, 그 뒤에 오는 데이터, 마지막이 아닌 청크에 붙은 마지막 플래그, 또는 짧은 비마지막 청크는 모두 TAMPERED_CIPHERTEXT로 실패합니다. 세그먼트화된 포맷은 암호학적 페이로드 천장을 부과하지 않습니다(88비트 청크 카운터는 2^88개의 청크를 허용합니다). 현실적인 최댓값은 스트림이 읽히는 동안 증분적으로 강제되는 배포 측의 서비스 거부 정책입니다. 패스프레이즈 경로에서는, 수신자가 어떤 청크를 열기 전에 먼저 선두의 32바이트 커밋먼트 헤더를 읽어 상수 시간으로 비교합니다.

수신자 경로야말로 오류 카탈로그가 그 정밀함을 발휘하는 곳입니다. 여기서는 어떤 슬롯도 이 키를 받아들이지 않은 경우(잘못된 수신자)와, 어떤 슬롯은 키를 받아들였으나 슬롯 집합 또는 암호문 이 변조된 경우를 구별합니다. 이것들은 서로 다른 보안 주장이며 서로 다른 코드를 지닙니다(아래 참조). 그러나 신뢰할 수 없는 호출자는 원인과 무관하게, 즉 어떤 슬롯도 열리지 않았든, 슬롯 집합이 변조되었든, 콘텐츠 태그가 실패했든, 정확히 하나의 일반적 실패 형태를 받습니다 — 그리고 응답은 어느 경우가 일어났는지도, 어느 슬롯이 일치했는지도 결코 드러내지 않습니다. 타입이 부여된 코드는 오로지 신뢰할 수 있는 로컬 호출자를 위한 내부 진단입니다.

타이밍은 하나의 명시적 모델을 따릅니다. 검증자는 무일치 검사 시점에서 — 콘텐츠 복호화 이전에 — 반환해도 됩니다(MAY). 그래서 비수신자와 수신자는 측정 가능한 만큼 다른 시간을 소요합니다. 그 차이가 드러내는 것은 오직 수신자인지 아닌지뿐이며, 어느 슬롯이 일치했는지도, 어떤 키 재료도 결코 드러내지 않습니다. 비수신자와, 암호문을 여는 데 실패하는 수신자 사이에 타이밍을 균일하게 하는 것은 요구되지 않으며(NOT), 더미 콘텐츠 열기를 의무화해서는 안 됩니다(MUST NOT) — 그것은 지나가는 모든 이에게 콘텐츠 복호화 비용을 부과하게 되기 때문입니다. 확실히 성립하는 상수 시간 보장은 슬롯 횡단 보장입니다. 단일 개인 키의 한 순회 안에서 루프는 상수 시간 비교로 모든 슬롯을 돌기 때문에, 그 키가 어느 슬롯을 언래핑하는지(있다면)는 아무것도 새어 나가지 않습니다.

확정성: 확인 깊이

Cardano의 결제는 확률적입니다. 한 블록 깊이의 트랜잭션은 짧은 리오그로 여전히 고아가 될 수 있습니다. 여러 블록 깊이에 도달한 트랜잭션은 압도적인 가능성으로 결제되었습니다. 한 블록 깊이의 레코드를 valid라 부르는 검증자는, 공격자가 경쟁 포크 위에 모순되는 레코드를 다시 기록하고 양쪽 모두에 「valid」 판정을 거두는 것을 허용하게 됩니다 — 이는 증명 전체가 의존하는 추가 전용 전제를 은밀히 깨뜨립니다.

그래서 검증자는, 레코드가 확인 깊이 임계값을 밑도는 동안 그것을 **failed가 아니라 pending**으로 보고합니다. 권고되는(RECOMMENDED) 범용 임계값은 15 블록 이상(대략 5분)입니다. 이 임계값은 와이어 상수가 아니라 검증자 정책입니다. 고가치 또는 증거 효력이 있는 레코드를 다루는 배포는 그것을 강한 확정성을 향해 올려야 하며(SHOULD), 검증자는 사용한 임계값을 표면화해야 합니다(MUST). 그래야 소비자가 그 위에 더 엄격한 정책을 얹을 수 있습니다. pending 레코드는 정형이며 온체인에 있습니다. 단지 아직 충분히 깊이 결제되지 않았을 뿐이며, 이후의 재시도에서 valid로 해소될 수 있습니다.

확인 깊이, 블록 높이, 블록 시각은 검증자가 결코 날조하지 않는, 익스플로러가 주장하는 사실입니다. 트랜잭션 참조 결속은 트랜잭션 내용 을 신뢰 불필요하게 만들지만, 그것에 관한 체인 사실은 바이트로부터 도출되지 않습니다 — 트랜잭션 CBOR는 타임스탬프도 높이도 운반하지 않기 때문입니다. 깊이는 (해석한 익스플로러의 팁 높이) − (포함 블록의 높이) + 1로 계산되므로, 팁 블록에 있는 트랜잭션의 깊이는 정확히 1입니다. 블록 시각은 포함 블록의 슬롯에 대한 POSIX 타임스탬프(정수 초, UTC)이며, 익스플로러의 시각 필드에서 취해지고 검증자가 재계산하는 일은 결코 없습니다. 이 사실들은 익스플로러의 말에 의존하므로, 검증자는 적어도 두 개의 독립적인 익스플로러로부터 그것들을 해석하고 어떤 불일치든 표면화해야 합니다 (SHOULD). 블록 시각이 핵심이 되는 배포 — 법적 공증, 마감 분쟁 — 는 그것을 교차 검사해야 합니다 (MUST). 보고서는 비교에 사용한 임계값과 함께 해석한 깊이를 운반하고, block_time(가능할 때는 block_slot도)을 운반합니다.

판정 상태

검증자는 네 가지 기계 판정 중 하나로 결론을 내리며, 각각이 하나의 프로세스 종료 코드와 일대일로 짝지어집니다. 그래서 호출자 — CI 게이트, 모니터, 스크립트 — 는 구조화된 보고서를 파싱하지 않고도 레코드에 귀속되는 실패를 일시적인 운영상의 실패와 구별할 수 있습니다. 판정을 관통하는 원칙은 귀속입니다. 레코드를 단죄하려면 레코드 자신의 바이트 — 또는 그 참조에 귀속 가능하게 결속된 바이트 — 가 실제로 제공하는 증거가 필요합니다. 어떤 제공자의 부정행위도 failed를 만들어낼 수 없습니다.

판정종료 코드의미
valid0검증자가 실행한 모든 검사가 ok를 반환했음. error 심각도의 문제는 하나도 없음.
failed1레코드에 귀속되는 실패. 구조 검증기가 바이트를 거부했거나, 서명이 검증되지 않았거나, 귀속 가능한 해시가 불일치했거나, 결속된 트랜잭션이 label-309 메타데이터를 운반하지 않거나(METADATA_NOT_FOUND), deny-host 규칙이 발동함.
unverifiable2레코드에 귀속되는 오류는 없으나, 필수 검사를 실행할 수 없었거나 귀속할 수 없었음 — 트랜잭션이 해석되지 않았거나, 결속을 견뎌낸 제공자 응답이 없었거나(TX_INTEGRITY_MISMATCH), 커밋된 콘텐츠/암호문을 가져오거나 귀속할 수 없었음. 동일한 레코드가 재시도에서, 또는 다른 게이트웨이 아래에서 valid로 검증될 수 있음.
pending3구조적으로 정형이고 온체인에 있으나, 확인 깊이 임계값을 밑돎(INSUFFICIENT_CONFIRMATIONS). 결제될 수 있음. pending 레코드에서 나온 어떤 결과도 최종으로 제시되어서는 안 됨.

레코드에 귀속되지 않는 검증자 호스트 런타임 실패는 종료 코드 4 이상을 사용하며, 어떤 판정에도 대응하지 않습니다.

error 심각도의 문제가 하나라도 존재할 때 valid 판정을 보고해서는 안 됩니다(MUST NOT). 비어 있지 않은 warnings 및/또는 info 리스트를 동반하더라도 레코드는 valid일 수 있습니다(MAY). 어느 층도 레코드를 통과시키기 위해 오류를 경고로 「연화」해서는 안 됩니다. 각 판정은 그 자신의 경우를 위해 예약되어 있습니다. pendingvalidfailed를 대신하는 일은 결코 없으며, 제공자에 귀속되는 실패는 failed가 아니라 unverifiable입니다.

커밋먼트 하한이 가용성을 다스립니다. 콘텐츠 검사가 실행되었으나 가용성 실패로 인해 실제로 검증된 레코드의 콘텐츠 커밋먼트가 하나도 남지 않았을 때, 판정은 valid가 아니라 unverifiable입니다 — valid 판정은 적어도 하나의 콘텐츠 커밋먼트가 검사되었음을 의미합니다. 무결성 결과는 이 하한에 좌우되지 않습니다. 귀속 가능한 바이트가 커밋먼트에 실패하면, 그 밖에 무엇이 가용했든 failed를 산출합니다.

콘텐츠 주소 결속과 귀속

8단계(그리고 수신자 검증자에 대해서는 복호화)는 검증자가 선택한 콘텐츠 주소 지정 스토리지 게이트웨이로부터 바이트를 가져옵니다 — 그리고 게이트웨이는 신뢰되지 않습니다. 위의 판정 기준선은 검증자가 가져온 모든 바이트 스트림에 대해 답할 수 있어야 하는 단 하나의 물음에 달려 있습니다. 이 바이트를 URI 자체에 귀속할 수 있는가? 두 스킴 모두 콘텐츠 주소 지정이므로, 귀속할 수 있습니다.

  • ipfs:// — 가져온 콘텐츠에 대해 CID를 재계산합니다(raw 코덱 CID라면 multihash를 직접, DAG 형태의 CID라면 해석된 경로를 따라 블록 단위로 다이제스트).
  • ar:// — 서명된 Arweave 트랜잭션을 검증하고 그 data_root에 대해 청크 Merkle 트리를 재계산합니다. ANS-104 데이터 항목이라면 deep-hash를 재계산하고, 소유자 서명을 검증하며, 그 서명의 SHA-256이 URI 안의 id와 같은지 검사합니다.

레코드 자신의 다이제스트를 충족하는 바이트는 결속 검사를 필요로 하지 않습니다 — 레코드의 커밋먼트는 스토리지 계층의 그것에 적어도 못지않게 강하기 때문입니다. 결속이 적용되는 곳에서는, 불일치가 무엇을 의미하는지를 귀속이 결정합니다.

  • 귀속 가능한 바이트 — 결속이 검증되었거나, 호출자가 대역 외로 공급한 것 — 는 커밋먼트에 실패하면 레코드에 책임이 돌아갑니다. 항목 콘텐츠에는 URI_INTEGRITY_MISMATCH(어떤 형제 URI가 무엇을 보유하든 하드한 무결성 실패), 리프 리스트에는 MERKLE_* 패밀리, 귀속 가능한 암호문 블롭에는 TAMPERED_CIPHERTEXT입니다. 판정은 failed.
  • 귀속 불가능한 바이트 — 결속이 검증되지 않았거나, 검증되었으나 실패한 것 — 는 레코드가 아니라 서비스를 제공한 제공자를 지목합니다. URI_PROVIDER_INTEGRITY_MISMATCH(경고)입니다. 검증자는 남은 URI와 게이트웨이로 계속합니다. 귀속 가능한 바이트가 하나도 남지 않은 주장은 가용성 결과 (CONTENT_UNAVAILABLE, MERKLE_LEAVES_UNAVAILABLE, CIPHERTEXT_UNAVAILABLE)로 끝나며, 판정은 unverifiable입니다 — 마치 아무것도 가져오지 않은 것과 정확히 같습니다.

이것은 트랜잭션 참조 결속의 스토리지 측 대응물입니다. 부정행위를 하는 게이트웨이는 가용성만을 저하시킬 수 있을 뿐, 판정은 결코 저하시킬 수 없습니다. URI_INTEGRITY_MISMATCHURI_PROVIDER_INTEGRITY_MISMATCH는 따라서 서로 다른 코드입니다 — 전자는 레코드를 단죄하고, 후자는 제공자를 단죄합니다 — 그리고 둘을 혼동하는 검증자는 적대적 게이트웨이가 failed를 위조하도록 허용하게 됩니다. 복호화 이후의 평문 해시 재확인은 귀속 한정자를 필요로 하지 않습니다. 인증된 봉투 아래에서 열리는 암호문은 AEAD 자신에 의해 귀속되므로, 거기서의 평문 해시 불일치(URI_INTEGRITY_MISMATCH)는 항상 레코드에 귀속되며 failed를 강제합니다.

타입이 부여된 오류 카탈로그

모든 실패 모드는 단일한 닫힌 카탈로그의 코드로 해소됩니다. 코드는 SCREAMING_SNAKE_CASE이며, 적합한 구현은 바로 그 문자열들을 정확히 내놓아야 합니다(MUST) — 파서 내부의 소문자 코드도, 자유 형식의 메시지도 결코 아닙니다. 두 언어로 된 두 구현이 동일한 입력에 도달하면 동일한 코드를 내놓습니다. 완전한 규범 목록은 적합성 테스트 스위트에 의해 바이트 단위로 고정되며, 카탈로그는 잠겨 있습니다(코드는 개정에 의해서만 추가됩니다).

심각도 모델

모든 문제는 세 가지 심각도 중 하나를 지니며, 이 구별은 핵심적인 역할을 합니다.

  • error — 판정을 무효화합니다. valid 결과는 어떤 error와도 공존할 수 없습니다.
  • warning — 비치명적인 런타임 이상(단일 게이트웨이가 실패함, 리프 리스트가 일부만 가용함)으로, valid를 막지 않습니다.
  • info — 의도적인 비검사. 검증자가 평가하지 않기로 선택한 측면입니다(프로파일 밖의 필드, 인식되지 않는 선택적 알고리즘). info 항목은 연화된 오류가 아니며, 결코 그런 식으로 사용되지 않습니다.

코드 하나는 별격입니다. INSUFFICIENT_CONFIRMATIONS는 심각도가 아니라 pending 판정에 대응하는데, 레코드가 정형이고 단지 결제를 기다리고 있을 뿐이기 때문입니다.

오류 패밀리

카탈로그는 패밀리별로 묶입니다. 망라적이지 않은, 대표적인 코드의 집합입니다.

패밀리심각도대표적인 코드
정형이 아닌 / 비정준 CBORerrorMALFORMED_CBOR, CHUNK_TOO_LARGE
스키마errorSCHEMA_TYPE_MISMATCH, SCHEMA_MISSING_REQUIRED, SCHEMA_UNKNOWN_FIELD, SCHEMA_EMPTY_RECORD
미지원 알고리즘errorUNSUPPORTED_HASH_ALG, UNSUPPORTED_AEAD_ALG, UNSUPPORTED_KEM_ALG, UNSUPPORTED_MERKLE_COMMIT_ALG
익스플로러 / 메타데이터error / pendingTX_NOT_FOUND, PROVIDER_UNAVAILABLE, TX_INTEGRITY_MISMATCH(→ unverifiable), METADATA_NOT_FOUND(→ failed); INSUFFICIENT_CONFIRMATIONS(→ pending)
서명error / infoMALFORMED_SIG_COSE_SIGN1, SIGNER_KEY_UNRESOLVED, SIGNATURE_INVALID, WALLET_ADDRESS_MISMATCH; SIGNATURE_UNSUPPORTED(info)
암호화 / KEM / 래핑errorKEM_EPK_LENGTH_MISMATCH, KEM_CT_LENGTH_MISMATCH, WRAP_LENGTH_MISMATCH, ENC_SLOTS_DUPLICATE_KEM_MATERIAL, ENC_SLOTS_TOO_MANY, ENC_ENVELOPE_TOO_LARGE, ENC_REQUIRES_CONTENT_HASH
복호화 결과errorWRONG_DECRYPTION_INPUT_SHAPE, WRONG_RECIPIENT_KEY, TAMPERED_HEADER, TAMPERED_CIPHERTEXT, KDF_DERIVATION_FAILED
URI / 콘텐츠error / warningINVALID_URI, URI_TARGET_FORBIDDEN, URI_INTEGRITY_MISMATCH, CONTENT_UNAVAILABLE(→ unverifiable), CIPHERTEXT_UNAVAILABLE(→ unverifiable); URI_PROVIDER_INTEGRITY_MISMATCH, URI_FETCH_FAILED(경고)
Merkle 리스트 커밋먼트error / warning / infoMERKLE_ROOT_MISMATCH, SCHEMA_MERKLE_LEAF_COUNT_MISMATCH; MERKLE_LEAVES_UNAVAILABLE(이중); MERKLE_UNSUPPORTED(이중)
서비스 독립성errorSERVICE_INDEPENDENCE_VIOLATION

네트워크/정책 코드(TX_NOT_FOUND, PROVIDER_UNAVAILABLE, CONTENT_UNAVAILABLE, CIPHERTEXT_UNAVAILABLE)와, 그 불일치가 레코드가 아니라 제공자에 대해 증명되는 TX_INTEGRITY_MISMATCH는 문제 리스트에서 error 심각도이지만(이들은 valid 판정을 막습니다) 레코드에 귀속되지 않으므로, failed가 아니라 unverifiable에 대응합니다. URI_PROVIDER_INTEGRITY_MISMATCH는 같은 원칙 아래의 가져오기별 경고입니다. 몇몇 코드는 이중 심각도를 지닙니다 — ENC_UNSUPPORTED, MERKLE_UNSUPPORTED, OUT_OF_PROFILE_SKIPPED, MERKLE_LEAVES_UNAVAILABLE — 기본적으로는 info/warning으로 읽히고, 엄격한 컨텍스트(수신자 역할, merkle 전용 에스컬레이션, 엄격한 종단 간 모드, 또는 커밋먼트 하한)에서는 error로 승격됩니다.

수신자 경로는 진단상 가장 정밀한 패밀리입니다. 신뢰할 수 있는 로컬 호출자에 대해서는, 실패한 복호화가 세 가지 내부 코드 중 하나로 해소됩니다. WRONG_RECIPIENT_KEY는 어떤 슬롯도 제공된 키를 받아들이지 않았음을 의미합니다(콘텐츠 키가 한 번도 복구되지 않음). TAMPERED_HEADER는 키는 복구되었으나 후보 콘텐츠 키가 slots_hash에 대해 slots_mac을 재현하지 못했음을 의미합니다(슬롯, 헤더 필드, 또는 slots_mac 자체가 변경됨). TAMPERED_CIPHERTEXT는 슬롯 집합은 온전했으나 키가 복구된 후에 콘텐츠 AEAD 태그가 실패했음을 의미합니다. 이 셋은 그 로컬 호출자에게는 구조적으로 구별 가능하며, 그 사이의 경계는 어떤 키 재료도 누설하지 않습니다. 신뢰할 수 없는 외부 호출자에 대해서는 이 셋 모두가 위에서 기술한 단일한 일반적 실패로 접혀 들어가, 응답 형태로는 구별할 수 없습니다. 타이밍 모델 아래에서, 허용되는 조기 무일치 반환이 WRONG_RECIPIENT_KEY(비수신자)를 두 개의 수신자 측 변조 결과로부터 분리하지만, 그 둘은 서로에 대해 더 이상의 구별을 지니지 않습니다 — 그래서 진단상의 정밀함이 슬롯별 또는 키별로 선택적인 오라클이 되는 일은 결코 없습니다.

코드가 네 가지 판정에 어떻게 대응하는가

구조 검증기가 내놓는 코드는 레코드 바이트가 적합하지 않음을 의미합니다. 공개 검증자는 어떤 후속 체인 작업이나 암호 작업도 실행하지 않고, 판정 failed — 종료 코드 1 — 와 검증기의 문제 리스트로 보고서를 단락합니다. 구조 검증이 통과한 이후에만 나오는 코드 — 검증되지 않은 서명, 일치하지 않은 귀속 가능한 해시, 결속된 트랜잭션 상의 METADATA_NOT_FOUND — 도 마찬가지로 failed입니다. 각각은 레코드에 귀속되며, 레코드 자신의 바이트가 제공하는 증거이기 때문입니다.

일시적인, 또는 제공자에 귀속되는 실패는 다른 판정입니다. 가져오거나 귀속할 수 없었던 콘텐츠, 도달 불가능했던 익스플로러, 또는 트랜잭션 참조 결속에 실패하는 바이트를 제공한 제공자는 모두 unverifiable — 종료 코드 2 — 에 대응합니다. 어느 것도 레코드의 과실이 아니며, 동일한 레코드가 재시도에서 valid로 검증될 수 있기 때문입니다. 따라서 종료 코드가 보존하는 구별은 실패 상태들 사이에서 세 갈래입니다. 레코드에 귀속되는 failed(1), 운영상 또는 제공자에 귀속되는 unverifiable(2), 그리고 임계값을 밑도는 pending(3)입니다.

몇몇 코드는 컨텍스트에 따른 심각도를 지닙니다. 선언한 프로파일보다 풍부한 레코드를 읽는 검증자(예를 들어 해시 전용 검증자가 봉인된 항목을 마주친 경우)는, 표시 컨텍스트에서는 그 여분의 필드를 info로 보고하고 여전히 해시 주장을 검증하거나, 엄격한 종단 간 감사 컨텍스트에서는 error로 보고합니다. 마찬가지로 인식되지 않는 선택적 서명 알고리즘은 info입니다 — 콘텐츠 존재 주장은 그것에 의존하지 않기 때문에 — 그래서 권고적 서명이 검증 불가능하더라도 공개된 해시 전용 증명은 여전히 valid로 남습니다.

서비스 독립성은 선택이 아니다

검증은 결코 게시자에게 되짚어 닿지 않습니다. 적합한 검증자가 발신하는 모든 아웃바운드 호출은 운영자가 선택한 인프라로 향합니다 — 트랜잭션을 위한 Cardano 익스플로러, 그리고 어떤 ar://ipfs:// 바이트를 위한 콘텐츠 주소 지정 스토리지 게이트웨이입니다. 이것은 코드 주석 상의 약속이 아니라 구조적으로 강제됩니다.

  • 모든 네트워크 호출은 단일한 이그레스 래퍼를 경유하며, 이 래퍼는 성공, 실패, 재시도를 막론하고 모든 호출에 대해 url, method, status, 바이트 수, 목적을 보고서 상의 필수 감사 추적에 기록합니다. 그 추적을 만들어낼 수 없는 검증자는 자신의 독립성을 증명할 수 없습니다.
  • 그 래퍼는 배포 시 공급되는 deny-host 리스트를 받아, 일치하는 호스트로의 어떤 호출이든 SERVICE_INDEPENDENCE_VIOLATION으로 하드 실패시킵니다. 이 리스트는 운영자 입력이며 — 적합성 스위트는 구현자 자신의 도메인으로 그것을 채웁니다 — Label 309의 와이어 상수가 아닙니다.
  • 적합성 하니스는 운영자 자신의 도메인이 어디로도 해석되지 않는 네트워크에서 동결된 픽스처 트랜잭션에 대해 검증자를 실행하고, 검증자가 여전히 valid를 반환함을 단언합니다. 이 단언은 소스를 grep하는 것이 아니라 OS 네트워크 계층에서 이루어집니다 — 하드코딩된 IP로 금지된 호스트에 닿는 검증자는 소스 스캔은 통과하겠지만 이 테스트에는 실패합니다.

검증자는 도달 가능한 Cardano 익스플로러가 필요하며, 구현자 고유의 어떤 것도 필요로 하지 않습니다. 콘텐츠 게이트웨이, 수신자 개인 키, 그리고 오프체인 리프 리스트는 각각 콘텐츠, 수신자, Merkle 검사를 여는 선택적 입력입니다. 그중 어느 것도 표준이 지명하는 서비스가 아니며, 그중 어느 것도 게시자가 아닙니다.

관련 페이지

  • 레코드 — 구조 검증기가 대조 검사하는 와이어 포맷: label 309, 맵 형태, 청크 재조립, 그리고 CDDL 스키마.
  • 서명 — 레코드 수준 COSE_Sign1 구성, 도메인 분리된 페이로드, 그리고 엄격한 Ed25519 검증 규칙.
  • 봉인된 PoE — 암호화 봉투, 수신자 키 슬롯, 그리고 수신자 검증자가 수행하는 언래핑.