《禁闭岛》与零知识证明:记忆上链与身份验证
2010年,马丁·斯科塞斯在《禁闭岛》中讲述了一个关于"记忆"与"身份"的故事:联邦执法官泰迪·丹尼尔斯(莱昂纳多·迪卡普里奥饰)前往禁闭岛精神病院调查一名失踪的女患者,却发现自己才是"真正的病人"——他深爱的妻子在杀死他们的三个孩子后被他枪杀,他的"调查"实际上是医院为他设计的"角色扮演治疗"。这部电影的核心主题是"记忆的不可靠性"——我们相信自己的记忆,但记忆可能被篡改、被压抑、被重构。十五年后的今天,当区块链技术试图将"身份"和"记忆"上链时,我们面临同样的问题:如何证明"我是谁"?如何证明"我的记忆是真实的"?如何在不泄露隐私的前提下验证"身份"?零知识证明(Zero-Knowledge Proof)给出了一个优雅的答案。
第一幕:禁闭岛上的"身份谜题"
第一场:泰迪的"身份危机"
《禁闭岛》最令人震撼的"反转"出现在电影的最后一幕:泰迪·丹尼尔斯发现,他根本不是"联邦执法官",而是"安德鲁·莱迪斯"——一个杀死了自己妻子的精神病患者。他所相信的"身份"——联邦执法官、调查员、正义的化身——全部是"虚假记忆",是医院为他设计的"角色扮演"的一部分。
这个"身份危机"在密码学中对应着"身份验证"(Identity Verification)问题。在现实世界中,我们通过"身份凭证"来证明"我是谁"——护照、身份证、驾驶证、社保卡。但在数字世界中,"身份凭证"可以轻易被伪造、被盗用、被篡改。区块链上的"身份"也一样——一个地址可以代表任何人,但如何证明"这个地址属于我"?
在《禁闭岛》中,泰迪的"身份"是由"记忆"定义的——他相信自己的"记忆",所以他认为自己是"联邦执法官"。但当他的"记忆"被证明是"虚假"的,他的"身份"也随之崩塌。在区块链中,一个地址的"身份"是由"私钥"定义的——只有拥有私钥的人才能控制该地址。但"私钥"本身也是一种"记忆"——如果你忘记了私钥,你的"身份"就丢失了。
第二场:记忆的"零知识"特性
在《禁闭岛》中,泰迪的"真实记忆"——他杀死妻子的事实——是被"压抑"的。他知道"有某个事情发生了",但他不知道"具体是什么"。这种"知道某件事发生了,但不知道具体内容"的状态,正是"零知识证明"的核心概念。
零知识证明(ZKP)是一种密码学协议,它允许"证明者"(Prover)向"验证者"(Verifier)证明"某个陈述为真",而不泄露"陈述的具体内容"。在《禁闭岛》中,泰迪可以"证明"他是"安德鲁·莱迪斯"(他的真实身份),而不需要泄露"他杀死了妻子"这个事实。他只需要向医生证明"我知道我是谁",而不需要证明"我做了什么"。
第三场:禁闭岛作为"零知识证明系统"
从区块链的视角来看,《禁闭岛》中的"阿什克里夫医院"(Ashcliffe Hospital)本身就是一个"零知识证明系统":
- 证明者(Prover):泰迪/安德鲁——他需要证明"他的真实身份"。
- 验证者(Verifier):考利医生和希恩医生——他们需要验证泰迪的"真实身份"。
- 见证(Witness):泰迪的"记忆密码"——即他妻子杀死孩子、他枪杀妻子的事实。
- 公共输入(Public Input):泰迪的精神病症状、他的"角色扮演"行为。
- 证明(Proof):泰迪最终"承认"他是安德鲁·莱迪斯——他提供了"零知识证明"。
从"证明者"的角度来看,泰迪经历了"从抵抗到接受"的过程——他最初"否认"自己的真实身份,就像"证明者"不配合"验证"过程。但在考利医生的"引导"下,他逐渐"接受"了真实身份,完成了"验证"。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
contract MemoryIdentity is AccessControl, ReentrancyGuard {
using ECDSA for bytes32;
bytes32 public constant IDENTITY_ISSUER_ROLE = keccak256("IDENTITY_ISSUER_ROLE");
bytes32 public constant VERIFIER_ROLE = keccak256("VERIFIER_ROLE");
bytes32 public constant RECOVERY_AGENT_ROLE = keccak256("RECOVERY_AGENT_ROLE");
enum IdentityStatus {
UNVERIFIED,
PENDING,
VERIFIED,
SUSPENDED,
REVOKED,
RECOVERING
}
enum MemoryType {
BIOMETRIC,
SOCIAL,
BEHAVIORAL,
KNOWLEDGE,
BIographic
}
enum VerificationMethod {
DIGITAL_SIGNATURE,
BIOMETRIC_HASH,
KNOWLEDGE_PROOF,
SOCIAL_RECOVERY,
HARDWARE_TOKEN
}
struct Identity {
address identityAddress;
bytes32 identityHash;
bytes32 commitmentHash; // Pedersen commitment to identity data
IdentityStatus status;
uint256 createdAt;
uint256 lastVerified;
uint256 recoveryCount;
bool isActive;
string metadataURI;
address[] guardians;
uint256 guardianThreshold;
}
struct MemoryRecord {
uint256 memoryId;
address identityAddress;
bytes32 memoryHash;
MemoryType memoryType;
bytes32 nullifier; // Prevent double-use
uint256 timestamp;
bool isEncrypted;
bool isVerified;
uint256 verificationCount;
}
struct ZKProof {
bytes32 proofId;
address identityAddress;
bytes32 publicSignals;
bytes proof;
VerificationMethod method;
uint256 timestamp;
bool isValid;
uint256 expiration;
}
struct RecoveryRequest {
uint256 requestId;
address identityAddress;
address[] approvers;
uint256 approvalsNeeded;
uint256 approvalsReceived;
uint256 expiryTime;
bool isExecuted;
bytes32 newCommitment;
}
mapping(address => Identity) public identities;
mapping(uint256 => MemoryRecord) public memories;
mapping(bytes32 => ZKProof) public proofs;
mapping(uint256 => RecoveryRequest) public recoveryRequests;
mapping(address => uint256[]) public identityMemories;
mapping(bytes32 => bool) public usedNullifiers;
uint256 private _memoryCounter;
uint256 private _proofCounter;
uint256 private _recoveryCounter;
uint256 public constant MAX_GUARDIANS = 10;
uint256 public constant PROOF_EXPIRY = 365 days;
uint256 public constant RECOVERY_DELAY = 3 days;
event IdentityCreated(
address indexed identityAddress,
bytes32 identityHash,
bytes32 commitmentHash
);
event MemoryRecorded(
uint256 indexed memoryId,
address indexed identityAddress,
MemoryType memoryType,
bytes32 memoryHash
);
event ProofGenerated(
bytes32 indexed proofId,
address indexed identityAddress,
VerificationMethod method
);
event ProofVerified(
bytes32 indexed proofId,
bool isValid
);
event RecoveryRequested(
uint256 indexed requestId,
address indexed identityAddress
);
event RecoveryExecuted(
address indexed identityAddress,
bytes32 newCommitment
);
modifier onlyIdentityOwner(address identityAddr) {
require(msg.sender == identityAddr, "Not identity owner");
_;
}
constructor() {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(IDENTITY_ISSUER_ROLE, msg.sender);
_grantRole(VERIFIER_ROLE, msg.sender);
_grantRole(RECOVERY_AGENT_ROLE, msg.sender);
}
function createIdentity(
bytes32 _identityHash,
bytes32 _commitmentHash,
string memory _metadataURI,
address[] memory _guardians,
uint256 _guardianThreshold
) external returns (address) {
require(identities[msg.sender].status == IdentityStatus.UNVERIFIED ||
identities[msg.sender].status == IdentityStatus.REVOKED, "Identity exists");
require(_guardians.length <= MAX_GUARDIANS, "Too many guardians");
require(_guardianThreshold > 0 && _guardianThreshold <= _guardians.length, "Invalid threshold");
identities[msg.sender] = Identity({
identityAddress: msg.sender,
identityHash: _identityHash,
commitmentHash: _commitmentHash,
status: IdentityStatus.PENDING,
createdAt: block.timestamp,
lastVerified: 0,
recoveryCount: 0,
isActive: true,
metadataURI: _metadataURI,
guardians: _guardians,
guardianThreshold: _guardianThreshold
});
emit IdentityCreated(msg.sender, _identityHash, _commitmentHash);
return msg.sender;
}
function verifyIdentity(
address _identityAddress,
bytes32 _publicSignals,
bytes calldata _proof,
VerificationMethod _method
) external onlyRole(VERIFIER_ROLE) returns (bytes32) {
Identity storage identity = identities[_identityAddress];
require(identity.status == IdentityStatus.PENDING ||
identity.status == IdentityStatus.VERIFIED, "Invalid status");
_proofCounter++;
bytes32 proofId = keccak256(abi.encodePacked(_proofCounter, _identityAddress, block.timestamp));
proofs[proofId] = ZKProof({
proofId: proofId,
identityAddress: _identityAddress,
publicSignals: _publicSignals,
proof: _proof,
method: _method,
timestamp: block.timestamp,
isValid: true,
expiration: block.timestamp + PROOF_EXPIRY
});
if (identity.status == IdentityStatus.PENDING) {
identity.status = IdentityStatus.VERIFIED;
identity.lastVerified = block.timestamp;
}
emit ProofGenerated(proofId, _identityAddress, _method);
return proofId;
}
function recordMemory(
bytes32 _memoryHash,
MemoryType _memoryType,
bool _isEncrypted
) external returns (uint256) {
Identity storage identity = identities[msg.sender];
require(identity.status == IdentityStatus.VERIFIED, "Identity not verified");
bytes32 nullifier = keccak256(abi.encodePacked(msg.sender, _memoryHash, block.timestamp));
require(!usedNullifiers[nullifier], "Nullifier already used");
_memoryCounter++;
uint256 memoryId = _memoryCounter;
memories[memoryId] = MemoryRecord({
memoryId: memoryId,
identityAddress: msg.sender,
memoryHash: _memoryHash,
memoryType: _memoryType,
nullifier: nullifier,
timestamp: block.timestamp,
isEncrypted: _isEncrypted,
isVerified: false,
verificationCount: 0
});
usedNullifiers[nullifier] = true;
identityMemories[msg.sender].push(memoryId);
emit MemoryRecorded(memoryId, msg.sender, _memoryType, _memoryHash);
return memoryId;
}
function verifyMemory(
uint256 _memoryId,
bytes32 _proof
) external onlyRole(VERIFIER_ROLE) {
MemoryRecord storage memory_ = memories[_memoryId];
require(!memory_.isVerified, "Already verified");
memory_.isVerified = true;
memory_.verificationCount++;
}
function requestRecovery(
bytes32 _newCommitment
) external returns (uint256) {
Identity storage identity = identities[msg.sender];
require(identity.status == IdentityStatus.VERIFIED ||
identity.status == IdentityStatus.SUSPENDED, "Invalid status");
require(identity.guardians.length > 0, "No guardians set");
identity.status = IdentityStatus.RECOVERING;
_recoveryCounter++;
uint256 requestId = _recoveryCounter;
recoveryRequests[requestId] = RecoveryRequest({
requestId: requestId,
identityAddress: msg.sender,
approvers: new address[](0),
approvalsNeeded: identity.guardianThreshold,
approvalsReceived: 0,
expiryTime: block.timestamp + RECOVERY_DELAY,
isExecuted: false,
newCommitment: _newCommitment
});
emit RecoveryRequested(requestId, msg.sender);
return requestId;
}
function approveRecovery(
uint256 _requestId
) external {
RecoveryRequest storage request = recoveryRequests[_requestId];
require(!request.isExecuted, "Already executed");
require(block.timestamp < request.expiryTime, "Request expired");
Identity storage identity = identities[request.identityAddress];
bool isGuardian = false;
for (uint256 i = 0; i < identity.guardians.length; i++) {
if (identity.guardians[i] == msg.sender) {
isGuardian = true;
break;
}
}
require(isGuardian, "Not a guardian");
request.approvers.push(msg.sender);
request.approvalsReceived++;
if (request.approvalsReceived >= request.approvalsNeeded) {
identity.commitmentHash = request.newCommitment;
identity.status = IdentityStatus.VERIFIED;
identity.recoveryCount++;
request.isExecuted = true;
emit RecoveryExecuted(request.identityAddress, request.newCommitment);
}
}
function suspendIdentity(
address _identityAddress
) external onlyRole(IDENTITY_ISSUER_ROLE) {
Identity storage identity = identities[_identityAddress];
require(identity.status == IdentityStatus.VERIFIED, "Not verified");
identity.status = IdentityStatus.SUSPENDED;
}
function revokeIdentity(
address _identityAddress
) external onlyRole(IDENTITY_ISSUER_ROLE) {
Identity storage identity = identities[_identityAddress];
require(identity.status != IdentityStatus.REVOKED, "Already revoked");
identity.status = IdentityStatus.REVOKED;
identity.isActive = false;
}
function getIdentityMemories(
address _identityAddress
) external view returns (uint256[] memory) {
return identityMemories[_identityAddress];
}
function getIdentityInfo(
address _identityAddress
) external view returns (Identity memory) {
require(identities[_identityAddress].status != IdentityStatus.UNVERIFIED, "Identity not found");
return identities[_identityAddress];
}
}
第二幕:零知识证明的"叙事结构"
第一场:证明者、验证者与见证者
零知识证明系统有三个核心角色:证明者(Prover)、验证者(Verifier)和见证者(Witness)。在《禁闭岛》中,这三个角色分别对应着:
- 证明者=泰迪/安德鲁:他需要向医生证明他的"真实身份"。
- 验证者=考利医生:他需要验证泰迪的"真实身份",但不应该知道泰迪的"全部记忆"。
- 见证者=泰迪的"潜意识":包含了"他杀死妻子"这个事实的"记忆"。
在零知识证明中,"见证者"是证明者知道的"秘密信息",但不需要告诉验证者。在《禁闭岛》中,泰迪的"见证者"是他"杀死妻子"的记忆——他不需要告诉考利医生"我杀了妻子",他只需要证明"我知道我是谁"。
第二场:零知识证明的"三幕结构"
零知识证明的过程可以分解为"三幕结构",就像电影的三幕叙事:
第一幕(设置):证明者和验证者就"证明的内容"达成一致。在《禁闭岛》中,考利医生告诉泰迪:"如果你能证明你是谁,你就可以离开这个岛。"——这就是"证明的公共输入"。
第二幕(挑战):验证者向证明者提出"挑战",证明者必须响应"挑战"而不泄露"秘密"。在《禁闭岛》中,考利医生让泰迪"回忆"他的过去,泰迪通过"回忆"来证明他的身份,但他不需要"回忆"全部事实——他只需要"回忆"足够的事实来证明"他是安德鲁·莱迪斯"。
第三幕(响应):证明者提供"证明",验证者确认"证明"的有效性。在《禁闭岛》中,泰迪最终"承认"了他的真实身份——他提供了"证明",考利医生"验证"了证明,确认泰迪确实"恢复了"——这意味着泰迪的"治疗"成功。
第三场:记忆的"可验证计算"
在《禁闭岛》中,泰迪的"记忆"是不可靠的——他的"虚假记忆"和"真实记忆"混合在一起,难以区分。在区块链中,"链上记忆"同样面临"可靠性"问题——上链的数据可能是错误的、被篡改的、或者被伪造的。
零知识证明通过"可验证计算"(Verifiable Computation)解决了这个问题。在"可验证计算"中,证明者可以"证明"某个计算是"正确执行"的,而不需要透露"计算的具体输入"。在《禁闭岛》中,如果泰迪的"记忆"被"上链",零知识证明可以"证明"泰迪的"身份"是"真实"的,而不需要透露"他杀死妻子"这个"敏感信息"。
import hashlib
import json
import time
import random
from dataclasses import dataclass
from typing import Dict, List, Optional, Tuple, Set
from enum import Enum
class IdentityStatus(Enum):
UNVERIFIED = "unverified"
PENDING = "pending"
VERIFIED = "verified"
SUSPENDED = "suspended"
REVOKED = "revoked"
RECOVERING = "recovering"
class MemoryType(Enum):
BIOMETRIC = "biometric"
SOCIAL = "social"
BEHAVIORAL = "behavioral"
KNOWLEDGE = "knowledge"
BIOGRAPHIC = "biographic"
class ProofStatus(Enum):
PENDING = "pending"
GENERATED = "generated"
VERIFIED = "verified"
EXPIRED = "expired"
REJECTED = "rejected"
@dataclass
class Identity:
address: str
identity_hash: str
commitment: str
status: IdentityStatus
created_at: int
last_verified: int
guardians: List[str]
guardian_threshold: int
metadata_uri: str
@dataclass
class Memory:
memory_id: int
identity_address: str
memory_hash: str
memory_type: MemoryType
nullifier: str
timestamp: int
is_encrypted: bool
is_verified: bool
@dataclass
class ZKProof:
proof_id: str
identity_address: str
public_signals: str
proof_data: str
method: str
timestamp: int
status: ProofStatus
expiration: int
@dataclass
class RecoveryRequest:
request_id: int
identity_address: str
new_commitment: str
approvals_needed: int
approvals_received: int
approvers: List[str]
expiry_time: int
is_executed: bool
class ZeroKnowledgeMemory:
"""
零知识记忆系统:模拟《禁闭岛》中的记忆验证与身份恢复
"""
def __init__(self):
self.identities: Dict[str, Identity] = {}
self.memories: Dict[int, Memory] = {}
self.proofs: Dict[str, ZKProof] = {}
self.recovery_requests: Dict[int, RecoveryRequest] = {}
self.identity_memories: Dict[str, List[int]] = {}
self.used_nullifiers: Set[str] = set()
self.memory_counter = 0
self.recovery_counter = 0
def _hash(self, *args) -> str:
data = ":".join(str(a) for a in args)
return hashlib.sha256(data.encode()).hexdigest()
def create_identity(
self,
address: str,
secret_data: str,
guardians: List[str] = None,
guardian_threshold: int = 2
) -> str:
if address in self.identities:
identity = self.identities[address]
if identity.status not in [IdentityStatus.UNVERIFIED, IdentityStatus.REVOKED]:
raise ValueError("身份已存在")
# 创建身份承诺(哈希)
identity_hash = self._hash(address, secret_data, "identity_v1")
commitment = self._hash(identity_hash, str(time.time()))
identity = Identity(
address=address,
identity_hash=identity_hash,
commitment=commitment,
status=IdentityStatus.PENDING,
created_at=int(time.time()),
last_verified=0,
guardians=guardians or [],
guardian_threshold=min(guardian_threshold, len(guardians or [])),
metadata_uri=f"ipfs://identity/{commitment[:16]}"
)
self.identities[address] = identity
print(f"[身份创建] {address[:8]}")
print(f" 身份哈希: {identity_hash[:16]}...")
print(f" 承诺: {commitment[:16]}...")
print(f" 守护者: {len(identity.guardians)} 人, 阈值: {identity.guardian_threshold}")
return address
def verify_identity_zk(
self,
address: str,
secret_data: str,
public_signal: str = "verify_identity"
) -> Tuple[str, bool]:
if address not in self.identities:
raise ValueError(f"身份 {address[:8]} 不存在")
identity = self.identities[address]
# 零知识验证:证明者知道secret_data,但不泄露它
# 在真实系统中,这里是zk-SNARKs/STARKs验证
# 在模拟中,我们使用哈希比较
expected_hash = self._hash(address, secret_data, "identity_v1")
if expected_hash == identity.identity_hash:
# 验证通过
proof_id = self._hash("proof", address, str(time.time()), "zk_verify")
proof = ZKProof(
proof_id=proof_id,
identity_address=address,
public_signals=public_signal,
proof_data="zk_proof_data_placeholder",
method="zk_snark",
timestamp=int(time.time()),
status=ProofStatus.VERIFIED,
expiration=int(time.time()) + 365 * 86400
)
self.proofs[proof_id] = proof
if identity.status == IdentityStatus.PENDING:
identity.status = IdentityStatus.VERIFIED
identity.last_verified = int(time.time())
print(f"[零知识验证] {address[:8]}: 验证通过")
print(f" 证明ID: {proof_id[:16]}...")
print(f" 公共信号: {public_signal}")
return proof_id, True
else:
print(f"[零知识验证] {address[:8]}: 验证失败")
return "", False
def record_memory(
self,
identity_address: str,
memory_content: str,
memory_type: MemoryType,
is_encrypted: bool = True
) -> int:
if identity_address not in self.identities:
raise ValueError(f"身份 {identity_address[:8]} 不存在")
identity = self.identities[identity_address]
if identity.status != IdentityStatus.VERIFIED:
raise ValueError(f"身份 {identity_address[:8]} 未验证")
# 生成nullifier防止重复使用
nullifier = self._hash(identity_address, memory_content, str(time.time()))
if nullifier in self.used_nullifiers:
raise ValueError("记忆已存在,防止重复记录")
self.memory_counter += 1
memory_id = self.memory_counter
memory_hash = self._hash(memory_content, str(memory_id))
memory = Memory(
memory_id=memory_id,
identity_address=identity_address,
memory_hash=memory_hash,
memory_type=memory_type,
nullifier=nullifier,
timestamp=int(time.time()),
is_encrypted=is_encrypted,
is_verified=False
)
self.memories[memory_id] = memory
self.used_nullifiers.add(nullifier)
if identity_address not in self.identity_memories:
self.identity_memories[identity_address] = []
self.identity_memories[identity_address].append(memory_id)
print(f"[记忆记录] #{memory_id}: {identity_address[:8]}")
print(f" 类型: {memory_type.value}, 加密: {is_encrypted}")
print(f" 哈希: {memory_hash[:16]}...")
return memory_id
def verify_memory_zk(
self,
memory_id: int,
memory_content: str,
public_signal: str = "verify_memory"
) -> Tuple[str, bool]:
if memory_id not in self.memories:
raise ValueError(f"记忆 #{memory_id} 不存在")
memory = self.memories[memory_id]
expected_hash = self._hash(memory_content, str(memory_id))
if expected_hash == memory.memory_hash:
memory.is_verified = True
proof_id = self._hash("memory_proof", str(memory_id), str(time.time()))
proof = ZKProof(
proof_id=proof_id,
identity_address=memory.identity_address,
public_signals=public_signal,
proof_data="memory_zk_proof",
method="zk_stark",
timestamp=int(time.time()),
status=ProofStatus.VERIFIED,
expiration=int(time.time()) + 365 * 86400
)
self.proofs[proof_id] = proof
print(f"[记忆验证] #{memory_id}: 验证通过")
return proof_id, True
else:
print(f"[记忆验证] #{memory_id}: 验证失败")
return "", False
def prove_identity_without_memory(
self,
address: str,
secret_data: str,
allowed_memories: List[str]
) -> Tuple[str, bool]:
"""
零知识证明:证明"我是谁"而不泄露"我的记忆"
对应《禁闭岛》中泰迪证明"他是安德鲁·莱迪斯"而不需要说出"他杀了妻子"
"""
if address not in self.identities:
raise ValueError(f"身份 {address[:8]} 不存在")
identity = self.identities[address]
expected_hash = self._hash(address, secret_data, "identity_v1")
if expected_hash != identity.identity_hash:
print(f"[身份证明] {address[:8]}: 身份验证失败")
return "", False
# 证明身份,但不泄露具体记忆内容
proof_id = self._hash("zk_proof", address, str(time.time()), "no_memory_leak")
# 创建零知识证明
proof = ZKProof(
proof_id=proof_id,
identity_address=address,
public_signals=json.dumps({
"claim": "I am who I claim to be",
"allowed_memories_count": len(allowed_memories),
"verified_without": "memory_content"
}),
proof_data="zk_proof_no_memory_leakage",
method="zk_snark_no_memory",
timestamp=int(time.time()),
status=ProofStatus.VERIFIED,
expiration=int(time.time()) + 365 * 86400
)
self.proofs[proof_id] = proof
print(f"[身份证明] {address[:8]}: 零知识证明成功")
print(f" 证明ID: {proof_id[:16]}...")
print(f" 不泄露记忆内容: {len(allowed_memories)} 条记忆被保护")
return proof_id, True
def request_recovery(
self,
address: str,
new_secret: str
) -> int:
if address not in self.identities:
raise ValueError(f"身份 {address[:8]} 不存在")
identity = self.identities[address]
if len(identity.guardians) == 0:
raise ValueError("没有设置守护者,无法恢复")
identity.status = IdentityStatus.RECOVERING
self.recovery_counter += 1
request_id = self.recovery_counter
new_commitment = self._hash(new_secret, str(time.time()))
request = RecoveryRequest(
request_id=request_id,
identity_address=address,
new_commitment=new_commitment,
approvals_needed=identity.guardian_threshold,
approvals_received=0,
approvers=[],
expiry_time=int(time.time()) + 3 * 86400,
is_executed=False
)
self.recovery_requests[request_id] = request
print(f"[恢复请求] #{request_id}: {address[:8]}")
print(f" 需要: {identity.guardian_threshold}/{len(identity.guardians)} 守护者批准")
print(f" 新承诺: {new_commitment[:16]}...")
return request_id
def approve_recovery(self, request_id: int, guardian: str) -> bool:
if request_id not in self.recovery_requests:
raise ValueError(f"恢复请求 #{request_id} 不存在")
request = self.recovery_requests[request_id]
if request.is_executed:
raise ValueError("恢复请求已执行")
if int(time.time()) >= request.expiry_time:
raise ValueError("恢复请求已过期")
identity = self.identities[request.identity_address]
if guardian not in identity.guardians:
raise ValueError(f"{guardian[:8]} 不是守护者")
if guardian in request.approvers:
print(f"[恢复批准] {guardian[:8]}: 已批准,无需重复")
return True
request.approvers.append(guardian)
request.approvals_received += 1
print(f"[恢复批准] {guardian[:8]}: 批准恢复 #{request_id}")
print(f" 进度: {request.approvals_received}/{request.approvals_needed}")
if request.approvals_received >= request.approvals_needed:
self._execute_recovery(request_id)
return True
def _execute_recovery(self, request_id: int) -> bool:
request = self.recovery_requests[request_id]
identity = self.identities[request.identity_address]
identity.commitment = request.new_commitment
identity.status = IdentityStatus.VERIFIED
identity.last_verified = int(time.time())
identity.recovery_count = getattr(identity, 'recovery_count', 0) + 1
request.is_executed = True
print(f"[恢复执行] #{request_id}: {identity.address[:8]}")
print(f" 新承诺已激活")
return True
def get_identity_info(self, address: str) -> Dict:
if address not in self.identities:
raise ValueError(f"身份 {address[:8]} 不存在")
identity = self.identities[address]
memory_ids = self.identity_memories.get(address, [])
return {
"address": identity.address,
"identity_hash": identity.identity_hash[:16] + "...",
"commitment": identity.commitment[:16] + "...",
"status": identity.status.value,
"created_at": identity.created_at,
"last_verified": identity.last_verified,
"guardians": len(identity.guardians),
"guardian_threshold": identity.guardian_threshold,
"memories": len(memory_ids),
"verified_memories": sum(
1 for mid in memory_ids
if mid in self.memories and self.memories[mid].is_verified
)
}
def simulate_shutter_island(self) -> Dict:
"""
模拟《禁闭岛》中的身份验证场景
"""
print(f"\n{'='*60}")
print(f" 模拟:《禁闭岛》中的零知识身份验证")
print(f"{'='*60}\n")
# 角色创建
print(">>> 角色创建\n")
teddy = self.create_identity(
address="Teddy_Daniels_FED",
secret_data="teddy_identity_secret_123",
guardians=["Dr_Cawley", "Dr_Sheehan", "Chuck_Aule"],
guardian_threshold=2
)
andrew = self.create_identity(
address="Andrew_Laeddis_PATIENT",
secret_data="andrew_laeddis_real_identity_456",
guardians=["Dr_Cawley", "Dr_Sheehan"],
guardian_threshold=2
)
# 模拟泰迪的"虚假记忆"
print(">>> 虚假记忆记录\n")
fake_memory_id = self.record_memory(
identity_address="Teddy_Daniels_FED",
memory_content="I am a federal marshal investigating a disappearance at Ashecliffe Hospital",
memory_type=MemoryType.BIOGRAPHIC,
is_encrypted=False
)
# 模拟安德鲁的"真实记忆"
print(">>> 真实记忆记录\n")
real_memory_1 = self.record_memory(
identity_address="Andrew_Laeddis_PATIENT",
memory_content="My wife Dolores drowned our three children in the lake",
memory_type=MemoryType.BIOGRAPHIC,
is_encrypted=True
)
real_memory_2 = self.record_memory(
identity_address="Andrew_Laeddis_PATIENT",
memory_content="I shot and killed my wife Dolores",
memory_type=MemoryType.BIOGRAPHIC,
is_encrypted=True
)
# 零知识身份验证:泰迪证明"他是安德鲁"而不泄露"他杀了妻子"
print(">>> 零知识身份验证\n")
print("场景: 泰迪需要证明"他是安德鲁·莱迪斯",")
print("但不需要泄露"他杀了妻子"这个事实\n")
# 第一次尝试:使用虚假身份
proof_id_1, result_1 = self.verify_identity_zk(
address="Teddy_Daniels_FED",
secret_data="teddy_identity_secret_123",
public_signal="verify_federal_marshal"
)
print(f" 虚假身份验证: {'通过' if result_1 else '失败'}\n")
# 第二次尝试:使用真实身份,零知识证明
# 先验证安德鲁的真实身份
self.verify_identity_zk(
address="Andrew_Laeddis_PATIENT",
secret_data="andrew_laeddis_real_identity_456",
public_signal="verify_patient_identity"
)
# 零知识证明:证明是安德鲁,但不泄露记忆内容
proof_id_zk, result_zk = self.prove_identity_without_memory(
address="Andrew_Laeddis_PATIENT",
secret_data="andrew_laeddis_real_identity_456",
allowed_memories=[
"My wife Dolores",
"Three children",
"The lake",
"Shot and killed"
]
)
print(f" 零知识证明: {'通过' if result_zk else '失败'}")
print(f" -> 证明'我是安德鲁·莱迪斯',而不泄露具体的记忆内容")
# 恢复身份
print("\n>>> 身份恢复\n")
print("场景: 泰迪通过守护者恢复身份\n")
recovery_id = self.request_recovery(
address="Andrew_Laeddis_PATIENT",
new_secret="andrew_healed_identity_789"
)
self.approve_recovery(recovery_id, "Dr_Cawley")
self.approve_recovery(recovery_id, "Dr_Sheehan")
# 最终状态
print("\n>>> 最终身份状态\n")
print(f"泰迪(虚假身份): {json.dumps(self.get_identity_info('Teddy_Daniels_FED'), indent=2, ensure_ascii=False)}")
print(f"\n安德鲁(真实身份): {json.dumps(self.get_identity_info('Andrew_Laeddis_PATIENT'), indent=2, ensure_ascii=False)}")
print(f"\n{'='*60}")
print(f" 模拟完成")
print(f"{'='*60}")
return {
"teddy_identity": self.get_identity_info("Teddy_Daniels_FED"),
"andrew_identity": self.get_identity_info("Andrew_Laeddis_PATIENT"),
"fake_memory": fake_memory_id,
"real_memories": [real_memory_1, real_memory_2],
"zk_proof_result": result_zk,
"recovery_completed": True
}
def main():
zk_memory = ZeroKnowledgeMemory()
result = zk_memory.simulate_shutter_island()
print(f"\n=== 模拟结果 ===")
print(f"零知识证明: {'成功' if result['zk_proof_result'] else '失败'}")
print(f"身份恢复: {'成功' if result['recovery_completed'] else '失败'}")
if __name__ == "__main__":
main()
第三幕:记忆上链与身份验证的技术实现
第一场:链上身份与Soulbound Token
2026年,区块链上的"身份"概念正在从"地址"向"Soulbound Token"(SBT)演变。SBT是一种不可转让的NFT,代表一个"灵魂"(Soul)的"身份属性"——包括教育背景、工作经历、社交关系、信用记录等。
在《禁闭岛》中,泰迪/安德鲁的"身份"是"不可转让"的——他不能把"联邦执法官"的身份转让给其他人,也不能把"精神病患者"的身份转让给其他人。在区块链中,SBT与泰迪的"身份"具有相同的特性:不可转让、不可伪造、不可销毁。
第二场:链上记忆的"隐私保护"
在《禁闭岛》中,泰迪的"真实记忆"——他杀死妻子的事实——是"隐私"信息。他不想让任何人知道这个"秘密",但他又需要证明"他是安德鲁·莱迪斯"来获得"治疗"。
在区块链中,"链上记忆"同样面临"隐私保护"的挑战。如果所有的"身份属性"都被公开上链,那么用户的"隐私"将完全暴露。零知识证明正是为了解决这个问题而设计的——它允许用户"证明"他们拥有某些"属性",而不需要"公开"这些"属性"的具体内容。
第三场:社交恢复与"守护者"系统
在《禁闭岛》中,泰迪的"身份恢复"依赖于"医生"的"帮助"——考利医生和希恩医生通过"角色扮演治疗"帮助泰迪"恢复"他的"真实身份"。在区块链中,"身份恢复"同样依赖于"社交恢复"(Social Recovery)机制——一组"守护者"(Guardian)可以在用户"丢失私钥"时帮助用户"恢复"身份。
Vitalik Buterin提出的"社交恢复钱包"(Social Recovery Wallet)方案就是基于这种逻辑:用户选择一组"守护者"(通常是朋友、家人或机构),当用户丢失私钥时,守护者可以通过"多签"来恢复用户的"身份控制权"。
第四幕:从禁闭岛到Web3的"身份重构"
第一场:禁闭岛作为"身份验证器"
从Web3的视角来看,《禁闭岛》中的"阿什克里夫医院"可以被理解为一个"身份验证器"——它通过"角色扮演治疗"来验证泰迪的"真实身份"。在区块链中,"身份验证器"是一个智能合约,它通过"零知识证明"来验证用户的"身份属性"。
"角色扮演治疗"与"零知识证明"在结构上是同构的:
- 角色=身份属性:"联邦执法官"和"精神病患者"是两种不同的"身份属性"。
- 扮演=证明:泰迪通过"扮演"联邦执法官来"证明"他是"正常人"。
- 治疗=验证:医生通过"治疗"来"验证"泰迪的"真实身份"。
第二场:记忆的"碎片化"与"聚合"
在《禁闭岛》中,泰迪的"记忆"是"碎片化"的——他"记得"自己是联邦执法官,但"不记得"自己杀了妻子。他的"身份"是由"碎片化记忆"构成的,只有当他"聚合"了所有"记忆碎片",他才能"认识"真正的自己。
在区块链中,"链上身份"同样是由"碎片化数据"构成的——你的教育背景存在于一个SBT中,你的工作经历存在于另一个SBT中,你的社交关系存在于第三个SBT中。这些"碎片化身份数据"需要被"聚合"起来,才能形成完整的"数字身份"。
第三场:零知识证明的"叙事反转"
《禁闭岛》最大的"叙事反转"是:泰迪不是"调查者",而是"被调查者"。这种"反转"在零知识证明的架构中同样存在——在传统的"身份验证"中,"验证者"是"权威"(如政府、银行),"证明者"是"用户"(如公民、客户)。但在零知识证明中,"验证者"不需要是"权威"——任何人都可以成为"验证者",而"用户"可以在不泄露"隐私"的前提下"证明"自己的"身份"。
这种"叙事反转"颠覆了传统的"身份验证"权力结构——从"中心化权威验证"到"去中心化自我验证"。"用户"不再是"被验证者",而是"自我身份的证明者"。
第五幕:镜头之外的思考
第一场:从"记忆陷阱"到"身份陷阱"
《禁闭岛》的终极问题是:我们如何知道"我是谁"?我们的"记忆"是否"真实"?我们的"身份"是否"稳定"?
在Web3时代,这些问题变得更加复杂。我们的"数字身份"是由"链上数据"定义的——钱包地址、交易记录、NFT集合、SBT属性。但"链上数据"是否"真实"?我们的"数字身份"是否"稳定"?
零知识证明提供了一个"元答案":我们不需要知道"记忆是否真实",我们只需要知道"证明是否有效"。在《禁闭岛》中,泰迪的"记忆"可能是"虚假"的,但他的"现实"——他是安德鲁·莱迪斯——是"真实"的。在区块链中,一个地址的"交易记录"可能是"伪造"的,但"零知识证明"可以验证"所有权"的"真实性"。
第二场:从"精神分析"到"密码学分析"
《禁闭岛》是马丁·斯科塞斯对"精神分析"(Psychoanalysis)的致敬——电影中充满了弗洛伊德式(Freudian)的符号和隐喻,如"梦境"、"幻觉"、"压抑"、"移情"等。
从密码学的角度来看,《禁闭岛》中的"精神分析"可以被理解为一种"密码学分析"(Cryptanalysis)——医生通过"分析"泰迪的"记忆碎片"来"解密"他的"真实身份"。而"零知识证明"则是"密码学分析"的反面——它允许用户"证明"身份而不需要"解密"隐私。
第三场:广播电视编导的"身份叙事"
从广播电视编导的专业视角来看,《禁闭岛》与零知识证明的类比,揭示了"身份"的本质——"身份"不是"客观事实",而是"叙事建构"。
泰迪/安德鲁的"身份"不是由"客观事实"定义的,而是由"叙事"定义的——"联邦执法官"是一个"叙事","精神病患者"是另一个"叙事"。在区块链中,"数字身份"同样不是由"客观数据"定义的,而是由"链上叙事"定义的——你的"钱包地址"是一个"叙事",你的"交易记录"是另一个"叙事"。
零知识证明为"身份叙事"提供了一个"验证工具"——它允许用户"证明"他们的"身份叙事"是"真实"的,而不需要"泄露""叙事"的"具体内容"。这就像《禁闭岛》中泰迪的"治疗"——他不需要"告诉"医生他"杀了妻子"的"细节",他只需要"证明"他"知道"这个"事实"。
在这个万物皆可Token化的时代,技术的迭代往往比镜头切换更快。作为北京城市学院2021级广播电视编导的毕业生,我始终在影像与区块链的交汇处寻找共鸣。感谢阅读,我是王森涛,让我们在视听与去中心化的世界里,继续探索。