difference between original and nova
https://hackmd.io/@ak36/tornado_cash_nova
commitement 구조 변경 :
core : 두 개의 랜덤 숫자 해싱 -> commitment
nova : amount, pubkey, blind(이전 버전의 시크릿 대신 사용되는 랜덤 값) 해싱 -> commitment
commitment = hash(amount, pubKey, blinding)
nullifier = hash(commitment, merklePath, sign(privKey, commitment, merklePath))
nulifier 계산 방식 :
commitment, merkle path, 두 값에 대한 개인 키 서명의 해시로 계산
입금 과정 :
사용자는 ETH 입금 시에도 snark proof 생성
proof의 public input에는 root, public amount, extDataHash(메타데이터)가 포함되어 추가적인 보안 제공 -> extDataHash가 있으면 왜 더 좋지?
UTXO 프라이버시 :
입출력 트랜잭션의 UTXO 내용 비공개 유지
머클 트리의 노드 경로를 지정하기 위해 경로 인덱스, 요소가 입력 요소로 포함
Universal Joint Split Transaction scheme :
이전의 고정된 입력과 출력 방식을 일반화한 새로운 방식
tx 처리과정 :
입력 UTXO의 commitment hash 계산 및 머클 트리내 존재 확인
입력 금액이 0인 경우(예: 사용자 입금 시) 포함 확인 불필요
UTXO의 공개키에 해당하는 개인키로 올바른 서명 확인
널리파이어 해시 계산 및 사용된 널리파이어 목록에 포함
출력 커밋먼트 해시 계산 및 공개 출력으로 발행
출력 금액이 248비트에 맞는지 확인하여 오버플로우 방지
중복 널리파이어 확인
트랜잭션 실행을 위한 확인 사항:

extDataHash : "외부 데이터의 해시"
트랜잭션의 추가 데이터의 해시를 저장하여, 데이터 위변조를 막음
새부내용을 미리 공개하지 않고도 검증 가능하기 때문에, 프론트 러닝 공격(공격자가 트랜잭션 내용을 보고 선행 거래를 실행) 어려워짐
트랜잭션을 대신 제출해서 사용자의 IP와 같은 메타데이터를 숨길 수 있음
사용자가 직접적인 블록체인과의 상호작용을 하지 않아서 추적할 수 없도록 함
IVerifier public immutable verifier;
uint256 public immutable denomination;
mapping(bytes32 => bool) public nullifierHashes;
// we store all commitments just to prevent accidental deposits with the same commitment
mapping(bytes32 => bool) public commitments;
event Deposit(bytes32 indexed commitment, uint32 leafIndex, uint256 timestamp);
event Withdrawal(address to, bytes32 nullifierHash, address indexed relayer, uint256 fee);
constructor(
IVerifier _verifier,
IHasher _hasher,
uint256 _denomination,
uint32 _merkleTreeHeight ) MerkleTreeWithHistory(_merkleTreeHeight, _hasher)
{ require(_denomination > 0, "denomination should be greater than 0"); verifier = _verifier; denomination = _denomination; }필요한 파라미터(검증자, 해시 함수, 금액, 머클 트리 높이) 초기화function deposit(bytes32 _commitment) external payable nonReentrant
{ require(!commitments[_commitment], "The commitment has been submitted");
uint32 insertedIndex = _insert(_commitment); commitments[_commitment] = true;
_processDeposit();
emit Deposit(_commitment, insertedIndex, block.timestamp); }
function withdraw(
bytes calldata _proof,
bytes32 _root,
bytes32 _nullifierHash,
address payable _recipient,
address payable _relayer,
uint256 _fee,
uint256 _refund ) external payable nonReentrant {
require(_fee <= denomination, "Fee exceeds transfer value");
require(!nullifierHashes[_nullifierHash], "The note has been already spent");
require(isKnownRoot(_root), "Cannot find your merkle root"); // Make sure to use a recent one
require( verifier.verifyProof( _proof, [uint256(_root), uint256(_nullifierHash), uint256(uint160(_recipient)), uint256(uint160(_relayer)), _fee, _refund] ), "Invalid withdraw proof" );
nullifierHashes[_nullifierHash] = true; _processWithdraw(_recipient, _relayer, _fee, _refund);
emit Withdrawal(_recipient, _nullifierHash, _relayer, _fee); }