ERC-6956 자산 결속 NFT (Asset-bound Non-Fungible Tokens)

Review · NFT·멀티토큰 · 2023-04-29 작성

실물이나 디지털 자산에 고유 식별자인 앵커(anchor)를 붙여 NFT와 1:1로 묶고 오라클이 자산을 실제로 쥐고 있음을 증명하면 기존 소유자의 서명 없이도 토큰을 옮길 수 있게 합니다.

왜 필요한가

ERC-721로 실물 상품 같은 오프체인 자산을 표현할 때는 보통 메타데이터에 자산 식별자를 적어 두는데, 메타데이터는 대부분 체인 밖에 있고 바뀔 수 있어서 토큰과 자산의 연결이 약합니다. 해시를 체인에 저장해 고정하면 이번에는 특성이나 플레이 시간처럼 바뀌어야 하는 메타데이터를 갱신할 수 없고 tokenURI가 가리키는 외부 저장소가 사라지는 사고도 있었습니다. 방향이 더 큰 문제입니다. 실물의 주인이 바뀌어도 기존 NFT 소유자가 협조하지 않거나 계정 접근권을 잃으면 체인 위 소유권을 옮길 방법이 없습니다. 자산을 실제로 갖고 있다는 사실이 체인 위 소유로 이어지지 않아, 주인이 수천 번 바뀔 수 있는 자산의 생애주기를 토큰이 따라가지 못합니다.

구현해야 하는 것

심층 설명

앵커와 토큰의 1:1 결속

앵커는 오프체인 자산을 유일하게 식별하는 값이고, 컨트랙트는 anchorByToken(tokenId)과 tokenByAnchor(anchor)의 양방향 매핑을 반드시 갖춰야 하며 앵커당 토큰은 최대 1개입니다. tokenId=0과 anchor=0x0은 무효로 정했는데, 솔리디티 매핑에서 존재하지 않는 키가 0을 돌려주는 성질을 이용해 존재 검사용 상태 변수를 생략하고 가스를 아끼기 위해서입니다. 배치 크기가 1을 넘는 전송은 금지되고 모든 전송은 AnchorTransfer(from, to, anchor, tokenId) 이벤트를 함께 내야 합니다. tokenURI(tokenId)는 baseURI/anchor 형태의 앵커 기반 URI가 권장되는데, 앵커가 처음 사용되기 전에는 앵커와 tokenId의 대응을 알 수 없기 때문입니다. 대량 생산품이라도 자산마다 소유 이력이 다르므로 멀티토큰이 아닌 ERC-721 확장을 택했습니다.

어테스테이션의 형식과 검증

어테스테이션은 to 주소, 앵커, 서명 시각 attestationTime, 유효 시작 validStartTime, 유효 종료 validEndTime을 abi 인코딩하고 오라클이 attestationHash = keccak256([to, anchor, attestationTime, validStartTime, validEndTime])에 대해 만든 65바이트 ETH 서명을 붙인 것입니다. decodeAttestationIfValid(attestation, data)는 이를 해독해 to, anchor, attestationHash를 돌려주되, 신뢰 오라클의 서명이 아니거나, 유효기간 밖이거나, 같은 attestationHash가 이미 사용됐으면 반드시 실패해야 합니다. 이 함수는 상태를 바꾸지 않으므로 어테스테이션을 소모하지 않고 유효성만 확인하는 데도 쓸 수 있습니다. 상태를 바꾸는 작업은 같은 트랜잭션 안에서 어테스테이션을 사용 처리(사용한 attestationHash 저장 권장)하고 attestationsUsedByAnchor를 1 올리며 AttestationUse 이벤트를 내야 합니다.

무허가 전송·승인·소각

transferAnchor, approveAnchor, burnAnchor는 모두 무허가(permissionless) 함수입니다. 현재 소유자(from)도 수령인(to)도 서명할 필요가 없고 누구나 트랜잭션을 제출할 수 있으며, 권한은 오직 오라클이 서명한 어테스테이션에서 나옵니다. transferAnchor는 ERC-721.safeTransferFrom과 같은 동작과 이벤트를 따르되 tokenByAnchor(anchor)가 0이면 NFT를 새로 민트하고 attestation.to가 이미 소유자이면 실패합니다. 승인과 소각 권한은 Authorization 열거형으로 OWNER(토큰 소유자), ISSUER(발행 컨트랙트), ASSET(어테스테이션)의 조합 중 하나로 정하고 burnAuthorization(), approveAuthorization()으로 조회합니다. 개인 키를 모르는 임의 계정이나 ERC-721을 지원하지 않는 컨트랙트로 토큰이 넘어간 경우에도 자산 증명만으로 승인과 소각이 가능하도록 하기 위한 설계입니다.

세 가지 선택 확장

AttestationLimited 확장은 앵커별 어테스테이션 사용 횟수에 상한을 둡니다. attestationLimit(anchor)와 attestationUsagesLeft(anchor)를 구현하고 한도 갱신 방향을 IMMUTABLE, INCREASE_ONLY, DECREASE_ONLY, FLEXIBLE 중 하나의 정책으로 고정해 attestationLimitPolicy()로 공개합니다. Floatable 확장은 토큰을 일시적으로 띄울 수 있게 합니다. floating(anchor)가 true면 해당 토큰은 어테스테이션 없이 통상의 ERC-721 방식(isApprovedOrOwner 승인)으로 전송할 수 있고 띄우기 시작과 중지 권한을 floatStartAuthorization과 floatStopAuthorization으로 따로 둡니다. ValidAnchors 확장은 유효한 앵커 집합을 머클 트리 루트 해시로 온체인에 기록하고 anchorValid(anchor, proof)로 검증합니다. 루트 갱신은 관리자(maintainer)만 할 수 있어 오라클이 손상돼도 새 앵커를 끼워 넣지 못합니다.

기본은 잠김, ERC-721과의 호환

앵커는 기본적으로 릴리스되지 않은 상태여야 하며 릴리스되지 않은 앵커의 토큰은 일반 ERC-721 전송이 모두 실패해야 합니다. 즉 기본 상태에서는 어테스테이션으로만 토큰이 움직입니다. Floatable 확장을 더하면 잘 알려진 ERC-721 동작에 어테스테이션 권한 메커니즘이 추가된 형태가 되어 ERC-721과 완전히 호환됩니다. 자주 쓰이는 _beforeTokenTransfer() 훅은 어테스테이션 기반 전송을 포함한 모든 전송에서 호출되어야 하므로, ERC-5192나 ERC-6982 같은 잠금 확장을 이 훅에서 함께 검사하면 일반 전송과 어테스테이션 전송을 모두 막을 수 있습니다. 마켓플레이스를 위해 ERC-6454, ERC-5484처럼 전송 가능 여부를 알리는 인터페이스로 보강하는 것도 권장됩니다.

실물 자산에 붙는 추가 요건

자산이 실물일 때 오라클은 to 주소를 지정하는 입력 장치와 물리적 앵커 사이의 물리적 근접(통상 수 밀리미터에서 수 미터)을 증명해야 하고 그 방법은 앵커의 복제·재현 시도와 스푸핑(제시 공격)에 강해야 합니다. 또 to 주소를 지정하는 쪽이 실물에 접근한 적 없이 거짓 어테스테이션을 얻으려는 악의적 당사자라는 가정 아래 구현해야 합니다. 실물 자산 자체는 고유 식별자 역할의 앵커(일련번호는 평문보다 해시 권장)와 물리적 존재를 증명할 보안 장치나 마킹을 갖춰야 하며 복제 불가능한 보안 특성을 가진 앵커 기술이 권장됩니다. 바코드나 일반 NFC 칩처럼 물리적으로든 디지털로든 쉽게 복제되는 기술은 권장되지 않습니다.

아직 검토 단계인 제안

이 제안은 Review 상태로, 검토 과정에서 인터페이스나 요구 사항이 바뀔 수 있으므로 구현 전에 최신 원문을 확인하는 것이 좋습니다. 표준 인터페이스의 ERC-165 식별자는 0xa9cf7635이고 AttestationLimited는 0x75a2e933, Floatable은 0xf82773f7, ValidAnchors는 0x051c9bd8입니다. 원문에는 표준 인터페이스만 구현한 최소 구현과 세 확장을 모두 지원하는 전체 구현, 어테스테이션을 생성하는 타입스크립트 예제, 두 구성에 대한 테스트가 함께 제공됩니다.

쓸 때 주의할 점

ERC 원문 · ← ERCs