Rand Protocol
문서
핵심 개념

트랜잭션과 액션

네트워크상의 트랜잭션과 그 id, 원장이 인식하는 모든 Action, chain 20이 허용하는 Action, 그리고 노드가 제출된 트랜잭션을 검사하는 순서를 설명합니다.

이 페이지는 트랜잭션을 구성, 제출 또는 디코딩할 때 사용합니다. rand_sendTransaction이 받는 와이어 형식, id를 계산하는 방법, 번들 및 수수료 규칙을 포함한 전체 액션 목록, 그리고 어떤 오류가 먼저 반환되는지를 결정하는 승인 순서를 다룹니다.

트랜잭션

Transaction { chain_id: u64, bundle: Option<Bundle>, action: Action }

Bundle {                                        // the hidden-asset bundle (since chain 14)
  anchor: Word8, nullifiers: [Word8; 4], commitments: [Word8; 4],
  fee: u64, burn_a: u64, burn_r: u64, burn_asset: u32, time: u32,
  envelopes: [Envelope; 4], proof: Vec<u8>,     // postcard(Proof) of the hidden-asset guest
  auth_commit: Word8, auth_proof: Vec<u8>,      // since chain 17: split authorisation's c and its auth proof
}
Envelope { kem_ct: Vec<u8>, to_receiver: Vec<u8>, to_sender: Vec<u8>, body: Vec<u8> }

Word8은 리틀 엔디언 u32 워드 여덟 개이며, 소문자 16진수 64자로 표현합니다. 와이어 형식은 bincode 기본 설정(고정 폭 정수, 8바이트 길이 접두사, u32 열거형 태그)을 사용한 bincode(Transaction)이며, rand_sendTransaction은 이를 16진수로 받습니다. 트랜잭션에 대한 서명도, 발신자 필드도 없습니다. 번들은 자신의 증명(번들 증명과, chain 17부터는 그 옆의 auth 증명)으로 스스로를 승인하고, 서명되는 액션은 서명을 액션 내부에 담습니다. 재생 방지는 번들의 경우 nullifier 집합, 서명되는 스테이킹 액션의 경우 레지스터의 밸리데이터별 nonce, TokenMint와 SetAuthority의 경우 토큰별 mint_nonce, 브리지의 거버넌스 액션의 경우 브리지의 pause_nonce와 list_nonce가 담당합니다.

트랜잭션은 번들을 최대 하나 담으며, 번들이 없는 액션이 아닌 모든 액션은 번들을 정확히 하나 담습니다. 번들의 슬롯 0–1은 비공개 자산을, 슬롯 2–3은 RAND를 담으므로, 하나의 증명으로 토큰을 이동하고 그 RAND 수수료를 지불합니다. 이전 설계의 두 번들 브리지 소각은 사라졌으며, 소각은 번들 하나입니다(노트).

rand 지갑은 이 모든 과정을 randprotocol_client::wallet::{send, submit}을 통해 구성합니다. 입력을 선택하고, 앵커를 가져오고, 지갑이 보유한 트리 사본으로 witness를 계산하고, 출력을 만들고 네 개의 봉투를 모두 봉인한 다음, 트랜잭션의 바인딩을 구하고, 번들과 auth 증명을 증명하고, 번들 증명이 게시한 다이제스트를 확인한 뒤 제출합니다.

트랜잭션 바인딩

트랜잭션에는 서명이 없으며, 번들의 증명은 번들 자체의 다이제스트(앵커, nullifier, 커밋먼트, 수수료, 소각 필드, time)만 커밋합니다. chain 13까지는 증명된 번들을 함께 실린 액션이나 봉투에 묶어 두는 것이 없었으므로, 트랜잭션을 본 누구나 증명 바이트를 모두 유지한 채 나머지를 바꿀 수 있었습니다. BridgeBurn의 목적지, Bond의 밸리데이터, 봉투가 그 예입니다. chain 14부터 모든 번들 증명은 트랜잭션의 바인딩을 대상으로 생성되고 이에 대해 검증됩니다.

binding(tx) = blake3("rand-tx-bind-1" || bincode(chain_id, bundle', action'))            chains 14 to 19
binding(tx) = blake3("rand-tx-bind-2" || bincode(genesis_hash, chain_id, bundle', action'))  chain 20
                                                                                  as 8 LE u32 words

여기서 '는 모든 번들 증명 바이트열을 빈 벡터로 대체한다는 뜻이며, 대상은 bundle.proof, chain 17부터는 bundle.auth_proof, 그리고 Aggregate의 proof입니다. 그 밖의 모든 것은 안에 포함됩니다. auth_commit, 네 개의 봉투 전부, 모든 소각 필드, 목적지, 밸리데이터, 수신자, 서명, 가디언 증명서와 그 Dilithium2 공동 서명, 그리고 Call 자체의 증명이 해당합니다. 여덟 워드는 증명의 공개 입력 세그먼트에 실리며, 그 다이제스트 H_PUB는 회로가 이미 바인딩하고 있으므로 이 수정에는 guest나 회로의 변경이 필요하지 않았습니다. 노드는 Machine::verify_public(hc_bundle, binding, proof)를 실행하고, 다른 공개 높이를 선언하는 번들 증명을 거부합니다(public height not the transaction binding's). Transaction::binding은 지갑이 증명 전에, 원장이 검증 전에 호출하는 단일 함수입니다. chain 17부터 auth 증명도 같은 여덟 워드를 대상으로 생성되고 검증되므로, 다른 트랜잭션으로 옮길 수 없습니다.

Chain 20은 제네시스 해시를 바인딩합니다. chain 19까지는 바인딩과 번들이 없는 액션의 서명 메시지가 chain id만 포함했으므로, chain id를 공유하는 두 체인(재생성한 체인, 비공개 사본)은 서로의 증명과 서명을 받아들일 수 있었습니다. Chain 20의 제네시스는 binding_domain: 1을 설정합니다(v0.6.8, audit v6 BIND-1, crates/randprotocol-core/src/types/binding.rs). 이후 제네시스 해시가 모든 프리이미지의 맨 앞에 오며, 태그는 한 버전 올라갑니다. 두 바인딩에는 rand-tx-bind-2와 rand-call-bind-2가 쓰이고, faucet 민트(rand-mint-3), Unbond와 Withdraw(rand-unbond-2, rand-withdraw-2), RPL 민트 및 권한 메시지(rand-rpl-mint-2, rand-rpl-authority-2), 애그리게이터 액션, 브리지의 거버넌스 메시지에도 같은 방식이 적용됩니다. 이런 체인에서 밸리데이터 등록은 항상 v2 메시지(제네시스, 체인, 주소, 지급처)입니다. 한 체인을 위해 증명되거나 서명된 것은 chain id와 무관하게 다른 체인에서 검증되지 않습니다. 바인딩 워드는 공개 입력이므로 guest와 verifier 키는 바뀌지 않았습니다. v0.6.8보다 오래된 지갑은 여전히 chain id 형식에 대해 증명하므로 chain 20에서 거부됩니다. rand_getLimits와 rand_status는 binding_domain을 제공합니다(chain 20에서는 1, 필드가 없으면 0).

제네시스 hardening_v6(chain 16~20)에서는 Call 자체의 증명도, 번들의 증명뿐 아니라 호출 증명도 비운 채, 같은 구성을 별도의 도메인으로 적용한 두 번째 다이제스트에 바인딩됩니다.

call_binding(tx) = blake3("rand-call-bind-1" || bincode(chain_id, bundle', action''))   as 8 LE u32 words

Chain 20에서 태그는 rand-call-bind-2이며 위에서와 같이 제네시스 해시가 프리이미지의 맨 앞에 옵니다. Invoke의 증명도 같은 방식으로 바인딩되며, 선언된 전이(transition)가 프리이미지에 유지되므로 증명은 트랜잭션이 담은 상태 변경, 지급 및 수신자를 정확히 커밋합니다.

호출 증명은 이 워드들을 공개 입력 세그먼트에 싣습니다. 프로그램에 배포 시점의 공개 입력이 있으면 그 뒤에 싣습니다. 따라서 다른 수수료 번들에 붙인 사본은 검증되지 않습니다. 두 바인딩은 중첩됩니다. 지갑은 수수료 번들의 노트를 고르고 증명을 비운 채 트랜잭션을 구성하며, 호출 바인딩에 대해 호출을 증명하고, 그 증명을 넣은 다음에야 트랜잭션의 바인딩을 구해 번들을 증명합니다.

클라이언트에 대한 결론은 다음과 같습니다. 출력, 봉투, 액션 등 트랜잭션 전체를 증명하기 전에 구성하십시오. 포크 이전에 만들어진 지갑은 빈 공개 세그먼트에 대해 증명하므로 거부됩니다. 번들이 없는 액션은 바인딩할 증명이 없으며 자신의 서명에 의존합니다. 바인딩은 트랜잭션 id가 아니며, 트랜잭션 id는 바뀌지 않았습니다.

트랜잭션 id

txid = blake3("rand-txid-3" || bincode(view))

여기서 view는 번들의 proof 바이트를 blake3(proof)(proof_hash)로, auth_proof 바이트를 blake3(auth_proof)(auth_proof_hash, 그대로 들어가는 auth_commit 뒤에 위치)로 대체하고 다른 모든 필드는 그대로 둔 트랜잭션입니다. chain 16까지는 auth 필드 두 개가 없는 rand-txid-2 도메인이었으며, chain 17에서 모든 트랜잭션 id가 바뀌었습니다. 증명을 다이제스트로 해시하기 때문에 가지치기된 번들(블록 집계, chain 20에서는 활성화되지 않음)도 원래 형태와 같은 id를 유지할 수 있으며, 가지치기된 번들은 auth 증명을 유지합니다. 번들이 없는 트랜잭션은 바이트를 그대로 해시합니다. 같은 id가 멤풀, 영수증 및 모든 RPC 조회의 키가 됩니다.

모든 액션

Action은 bincode 열거형이며, 태그는 선언 순서를 따릅니다.

태그액션필드번들최소 수수료
0None—필수BUNDLE_BASE = 0.001 RAND
1Mintcm, pk, time, r, envelope, amount, minter: PublicKey, signature없어야 함0 (validator 서명, faucet 체인 전용, amount ≤ 100 RAND)
2Deploybase_pc: u32, words: Vec<u32>, public: Vec<u32>필수기본 수수료 + 프로그램 워드 및 public 워드당 100,000 단위
3Callprogram, proof, input_envelope: Option<CallEnvelope>필수chain 20에서는 기본 수수료 + gas_price × 증명이 선언한 GAS_LIMIT + 증명과 입력 envelope의 KiB당 byte_price; gas 섹션이 없는 체인에서는 기본 수수료 + call_fee(tier)
4Bondvalidator, amount, registration: Option<Registration>필수이며 burn_r == amount기본 수수료
5Unbondvalidator, amount, nonce, signature없어야 함0
6Withdrawvalidator, amount, nonce, time, r, envelope, signature없어야 함전송 시점에는 0; 기본 수수료는 amount에서 차감됨
7BridgeAttestattestation, recipient: ShieldedAddress, r, time, asset, envelope, pq_signatures: Vec<PqSignature>필수기본 수수료
8BridgeBurnasset, amount, relayer_fee, to_chain: u16, token: [u8; 32], to: [u8; 32]필수이며 burn_asset == asset, burn_a == amountBRIDGE_BURN_FEE = 0.01 RAND
9RegisterAggregatorregistration필수이며 burn_r = 본드기본 수수료
10UnbondAggregatoraggregator, nonce, signature없어야 함0
11WithdrawAggregatoraggregator, nonce, time, r, envelope, signature없어야 함0
12SlashAggregator서명된 두 개의 집계 헤더없어야 함0
13Aggregatecovers, proof, aggregator, nonce, time, r, envelope, signature없어야 함0
14RegisterTokenname, symbol, decimals: u8, authority: MintAuthority, initial: Option<InitialMint>, salt: [u8; 32], index: u32필수기본 수수료 + 레지스트리의 registration_fee (chain 20에서 1 RAND, 소각됨)
15TokenMintasset, amount, recipient: ShieldedAddress, r, time, envelope, nonce, signature필수기본 수수료
16SetAuthorityasset, new: Option<PublicKey>, nonce, signature필수기본 수수료
17TokenBurnasset, amount필수이며 burn_asset == asset, burn_a == amount기본 수수료
18PauseMintsnonce, signature없어야 함0
19UnpauseMintsnonce, pq_signatures없어야 함0
20RegisterBridgedTokenname, symbol, salt, chain: u16, token: [u8; 32], decimals: u8, nonce, pq_signatures필수기본 수수료 + registration_fee
21ListBackingtoken_index, chain: u16, token: [u8; 32], decimals: u8, nonce, pq_signatures필수기본 수수료
22RotatePqGuardiansnew_pq_guardians: Vec<PublicKey>, nonce, pq_signatures없어야 함0
23RotatePauseKeynew_pause_key: PublicKey, nonce, pq_signatures없어야 함0
24ClaimVestedentry: [u8; 32], amount, nonce, to: ShieldedAddress, time, r, envelope, signature없어야 함전송 시점에는 0; 기본 수수료는 amount에서 차감됨
25RevokeVestingentry: [u8; 32], unvested, nonce, to: ShieldedAddress, time, r, envelope, signatures (v0.6.8부터 해당 엔트리 revoker의 임계값 서명이며, 제네시스 엔트리가 지정한 treasury에 지급함; 이전에는 signature 하나)없어야 함전송 시점에는 0; 기본 수수료는 unvested에서 차감됨
26BondVestedentry: [u8; 32], validator, amount, registration: Option<Registration>, nonce, signature없어야 함0
27UnbondVestedentry: [u8; 32], amount, nonce, signature없어야 함0
28AdmitValidatorcandidate: PublicKey, signatures: Vec<(PublicKey, Signature)>없어야 함0
29SlashEquivocationfirst, second: 한 리더가 하나의 view에 대해 서명한 두 개의 블록 헤더없어야 함0
30RotatePqGuardiansV2new_pq_guardians: Vec<PublicKey>, possession: Vec<Vec<u8>>, nonce, pq_signatures없어야 함0
31RotatePauseKeyV2new_pause_key: PublicKey, possession: Vec<u8>, nonce, pq_signatures없어야 함0
32CancelRotationkind: u8, nonce, signature없어야 함0
33Invokeprogram, proof, input_envelope: Option<CallEnvelope>, transition: Transition필수; burn_r와 burn_asset의 burn_a가 프로그램으로 들어가는 값임Call의 최소 수수료 + 생성된 셀당 cell_fee
MintAuthority = None | Key(PublicKey) | Bridge { backings: Vec<Backing> } | Program(Hash)
InitialMint   { amount: u64, recipient: ShieldedAddress, r: Word8, time: u32, envelope: Envelope }
PqSignature   { index: u8, signature: Vec<u8> }   // one guardian's Dilithium2 co-signature, by set index

chain 20에서는 태그 08, 1421, 28, 3033을 사용할 수 있습니다. 제네시스에는 rules_v2가 있는 bridge 섹션, tokens 섹션, staking.admission_by_vote(태그 28), bridge.rotation(태그 3032), program_state(태그 33)가 있습니다. 태그 22와 23은 여기서 거부됩니다. bridge.rotation.needs_possession에 따라 교체는 새 키 각각의 자체 서명을 담은 …V2 형식이어야 하기 때문입니다. 이러한 섹션이 전혀 없던 chain 19에서는 태그 08과 1423을 사용할 수 있었습니다(rules_v2가 없던 chain 14는 22와 23을 거부했습니다). SlashEquivocation에는 staking.slashing 섹션이 필요하며, chain 20의 해당 섹션은 null입니다. 다섯 가지 집계 액션에는 aggregation 섹션이 필요한데, chain 20에는 이 섹션이 없습니다(집계를 다시 측정하기 전까지는 해당 섹션이 있는 제네시스로는 빌드가 시작을 거부합니다). 노드는 이 모든 액션을 다른 어떤 검사보다 먼저 게이트에서 거부하지만, 이들은 여전히 bincode 태그를 차지하므로 태그 번호를 하드코딩하는 인코더는 이를 반드시 포함해서 세어야 합니다. 집계는 Proofs에서 예정된 기능으로 설명되어 있습니다. 제네시스 베스팅 액션 네 가지(태그 2427, v0.5.11부터 빌드에 포함)에는 vesting 섹션이 필요한데, chain 20을 포함해 어떤 체인에도 이 섹션이 있었던 적이 없으며, 섹션이 없으면 수락 단계에서 거부됩니다. 태그 2833은 v0.6.8에서 새로 추가되어 맨 끝에 덧붙여졌으므로, 이전 모든 체인의 기록은 그대로 디코딩됩니다.

네 가지 RPL 액션(태그 1417)은 토큰을 등록하고, Key 권한 토큰을 추가로 발행하고, 발행 권한을 넘기거나 포기하고, 보유분을 소각합니다. Bridge 또는 Program 권한을 지정한 RegisterToken은 거부됩니다. 토큰 전송 액션은 따로 없습니다. 토큰 전송은 None이며, RAND 결제와 구분되지 않습니다. RPL tokens를 참조하십시오. 네 가지 브리지 거버넌스 액션(태그 1821)은 발행 일시 중지, 그 해제, 그리고 제네시스 이후 브리지된 토큰 또는 추가 백킹의 등재입니다. PauseMints는 일시 중지만 할 수 있는 제네시스 pause key가 서명하며, 나머지 셋은 Dilithium2(PQ) 가디언의 정족수가 필요합니다. BridgeAttest도 ECDSA 정족수와 함께 이러한 정족수를 담습니다. 브리지 rules_v2에서 태그 22와 23은 PQ 가디언 집합 또는 pause key를 교체하며, 각각 현재 집합의 PQ 정족수와 브리지의 rotation_nonce 아래에서 이루어집니다. 태그 30과 31은 bridge.rotation 아래에서 같은 일을 하면서, 새 키 각각이 교체 메시지에 대해 만든 자체 Dilithium2 서명, 즉 해당 키를 실제로 보유하고 있다는 증명도 함께 담습니다. chain 20에서 교체는 보류 상태로 기록되고 delay_secs(86,400 s)가 지난 뒤에 효력이 발생합니다. 현재 pause key가 서명하는 CancelRotation(태그 32)은 보류 중인 교체를 효력이 발생하기 전에 취소합니다. Bridge를 참조하십시오.

AdmitValidator(태그 28)는 새 키를 승인하기 위한 validator 집합의 투표입니다. 스테이크의 3분의 2를 초과하여 보유한 validator들의 서명을 주소 오름차순으로 담습니다. staking.admission_by_vote(chain 20) 아래에서는 새 validator를 등록하게 되는 Bond는 그 주소가 이 방식으로 승인되지 않았다면 거부됩니다(Staking). Invoke(태그 33, RPL-2)는 증명이 프로그램의 선언된 상태 transition 하나를 보증하는 호출입니다. 읽고 쓴 셀, 번들의 소각 필드가 프로그램의 vault로 들여오는 것, 그리고 vault가 지급하거나 프로그램이 발행하는 노트가 여기에 포함됩니다. 선언된 모든 읽기가 여전히 유효하고 두 증명이 모두 검증되면 원장이 해당 전이를 적용합니다(Programs).

Deploy와 Call은 번들이 0의 자기 전송인 일반 차폐 트랜잭션입니다. 이 번들은 호출자 자신의 노트로 최소 수수료를 내기 위해 존재하며, 따라서 체인은 그 노트가 누구의 것이었는지 알 수 없습니다. RAND는 burn_r을 통해서만 풀을 떠납니다. Bond의 스테이크, RegisterAggregator의 본드, Invoke가 프로그램 vault에 넣는 예치금이 그 예입니다. 토큰은 burn_asset이 해당 토큰을 지정한 burn_a를 통해서만 풀을 떠나며, TokenBurn, BridgeBurn, Invoke에서 그렇게 됩니다. 그 밖의 모든 액션은 아무것도 소각하지 않으며, burn_asset = 0이면서 burn_a가 0이 아닌 번들은 어디서나 거부됩니다.

수락 순서

Ledger::validate_inner는 비용이 낮은 검사를 비싼 검사보다 먼저, 다음 순서로 수행합니다. 멤풀도 가십하기 전에 동일한 검사를 실행하므로, 잘못된 트랜잭션은 경계에서 한 번만 거부됩니다.

  1. 크기 상한 — 트랜잭션 전체 ≤ 체인의 max_block_bytes; 번들의 엔벨로프 네 개 각각과 액션이 담는 노트 엔벨로프는 정확히 제네시스의 envelope_bytes(chain 20에서 1,860, 이 필드가 없는 체인에서는 최대 2,048바이트); 번들 증명, 인증 증명, 호출 증명은 각각 ≤ max_proof_bytes(기본값 2 MiB, chain 20의 제네시스에서 4 MiB, chain 13~16에서는 8 MiB); 프로그램 ≤ 체인의 프로그램 상한(기본값 4,096 워드, chain 20의 제네시스에서 65,535); 호출 엔벨로프 ≤ max_call_envelope_bytes(기본값 18,432바이트, chain 20에서 65,536); 증명서(attestation) ≤ 16 KiB. 봉인된(가지치기된) 마커 형태의 번들 증명은 봉인 형태 동기화 밖에서는 여기서 거부되며(PrunedFormOutsideSync), 어떤 증명 작업보다도 먼저 거부됩니다.
  2. 체인 id가 일치합니다.
  3. 형태와 수수료 하한 — 번들이 없는 액션은 번들을 담지 않으며 그 외의 모든 액션은 정확히 하나를 담아야 합니다. check_burn_shape는 액션이 설정할 수 있는 세 가지 소각 필드를 정합니다. TokenBurn 또는 BridgeBurn은 자신의 asset과 amount를 burn_asset과 burn_a로 소각하고, Bond 또는 RegisterAggregator는 금액을 burn_r로 소각하며, 그 밖의 모든 액션은 아무것도 소각하지 않습니다. fee ≥ fee_floor(action). gas 섹션이 있는 체인(chain 20)에서 Call 또는 Invoke의 이 단계 하한은 선언 한도 1에서의 가스 규칙, 즉 증명과 입력 엔벨로프에 대한 BUNDLE_BASE + gas_price + byte_price × ⌈bytes / 1024⌉이며, 이는 어떤 검증보다도 먼저 알 수 있습니다.
  4. 앵커 — chain 20에서는 최근 1,024개 블록 종료 루트 중 하나이며, chain 20의 제네시스는 proof_window_blocks: 1024를 설정합니다(v0.6.8). 이 필드가 없는 체인에서는 chain 19까지와 같이 최근 256개 중 하나입니다.
  5. 시간 — chain 20에서는 [height − 1024, height] 안, 이 필드가 없으면 [height − 256, height] 안입니다. 하나의 제네시스 값이 두 윈도우를 모두 정합니다.
  6. 널리파이어와 커밋먼트 — 네 개의 널리파이어가 모두 서로 다르고 어느 것도 사용되지 않았어야 하며, 네 개의 커밋먼트가 모두 서로 다르고 어느 것도 이미 리프로 존재하지 않아야 합니다.
  7. 액션 검사 — 파우셋이 활성화되어 있어야 하고, 민트가 상한 미만이며 검증인의 서명을 받아야 합니다. chain 20에서는 추가로 제네시스 staking.faucet_recipients 허용 목록에 있는 수신자를 지정하고, 에포크의 faucet_budget_per_epoch 안에 들며, 제네시스 staking.faucet_minters 목록의 검증인이 서명해야 합니다(chain 16부터의 유효성 규칙). 2⁶³ 이상의 민트나 입금은 모든 체인에서 거부됩니다. 프로그램이 디코딩되어야 하고, 프로그램이 존재해야 합니다. 스테이킹 규칙(등록은 검증인이 알려지지 않은 경우에만 정확히 존재, 논스, 서명, 충분한 스테이크), BridgeAttest와 BridgeBurn 및 일시 중지·상장 액션에 대한 브리지 자체의 규칙, 등록 수수료를 포함한 RPL의 네 가지 액션에 대한 규칙, chain 20에서는 입장 투표의 정족수, 그리고 Invoke의 경우 전이의 형태, 저장된 셀에 대비한 선언된 읽기, 모든 지급에 대한 금고의 담보, 셀 수수료가 검사됩니다. 이 모두는 번들의 증명보다 먼저 실행되므로, 적용될 수 없는 트랜잭션은 STARK 검증 비용을 발생시키지 않습니다. hardening_v6(chain 16~20)에서는 두 가지 거부가 더 여기에 놓입니다. 어떤 호출도 보유할 수 없는 프로그램의 Deploy(ProgramUncallable), 그리고 정직한 증명자라면 쓰지 않을 필드가 헤더나 트랜스크립트에 들어 있는 번들 또는 호출 증명(non-canonical proof: …)이며, 둘 다 어느 증명이 검증되기 전에 거부됩니다. v0.6.8부터는 헤더가 가장 큰 테이블의 상한을 넘는 차수를 선언한 증명이 같은 판정으로 가장 먼저 거부되며(audit v6 ZKV-4), 이는 검사가 증명을 훑기 전에 이루어집니다.
  8. 번들 다이제스트 — 분리 인증 체인(chain 17 이후 모든 체인)에서는 인증 필드가 먼저 검사됩니다. 인증 증명이 없는 번들은 거부되고, 인증 증명이 공개하는 c(읽기만 하고 아직 검증하지 않음)는 auth_commit과 같아야 하며, chain 20에서는 인증 증명이 정확히 1,279의 GAS_LIMIT을 선언해야 합니다. 그런 다음 원장이 auth_commit을 포함한 번들의 공개 필드의 다이제스트를 다시 계산하며, 이는 증명이 공개한 값과 같아야 합니다. chain 20에서는 번들 증명도 정확히 제네시스 bundle_gas_limit인 20,479의 GAS_LIMIT을 선언해야 합니다.
  9. 번들 증명 — 제네시스에 고정된 hc_bundle과 트랜잭션의 바인딩에 대한 Machine::verify_public. 분리 인증 체인에서는 다음으로 인증 증명을 제네시스에 고정된 hc_auth와 같은 바인딩에 대해 검증합니다.
  10. 호출 증명과 그 티어 수수료 — Call 또는 Invoke의 경우, 먼저 증명의 헤더를 고정합니다(티어는 최대 14, keccak 테이블은 최대 2¹², sha256 테이블은 최대 2¹³, 배포된 프로그램의 높이이며 hardening_v6에서는 최소 2⁷ 행으로 하한을 두고, chain 20에서는 선언된 GAS_LIMIT이 헤더의 상한 gas_max를 넘지 않아야 합니다). 이어서 프로그램의 code_hash에 대한 호출의 STARK를, hardening_v6에서는 트랜잭션의 호출 바인딩에 대해 검증합니다. 수수료는 마지막에 검사하는데, 증명이 디코딩된 뒤에야 알 수 있기 때문입니다. chain 20에서는 적용 중인 가격으로 fee ≥ BUNDLE_BASE + gas_price × GAS_LIMIT + byte_price × ⌈bytes / 1024⌉이며, Invoke의 경우 생성된 셀마다 cell_fee가 더해집니다(수수료). gas 섹션이 없는 체인에서는 fee ≥ BUNDLE_BASE + call_fee(tier)입니다.

오류는 rand_sendTransaction에서 코드 -32000과 사유와 함께 반환됩니다. nullifier already spent, anchor is not one of the last 1024 roots(proof_window_blocks가 없는 체인에서는 256), time 12 is outside [476, 1500], fee 1000000 below minimum 2000000, the bundle's digest is not what its proof published, invalid bundle proof: …, unknown program …, already in mempool, conflicts with a pending transaction over <nullifier>, faucet is disabled on this chain이 있고, chain 14부터는 the bundle burns asset 2 on an action that burns no token, a burn of 5 is not allowed on this action, the bundle burns 399, not the 400 the action declares, wrong mint nonce: expected 1, got 0, wrong token index: expected 3, got 2가 있으며, chain 17부터는 this chain requires an auth proof on every bundle (genesis hc_auth)와 the auth proof publishes a different commitment than the bundle's auth_commit이 있고, chain 20에서는 envelope is 1348 bytes, this chain requires exactly 1860과 bundle proof declares gas limit …, this chain requires exactly 20479가 있습니다. 수락은 커밋이 아닙니다. 블록을 반환할 때까지 rand_getTransaction을 폴링하십시오.

유효하지 않은 트랜잭션을 하나라도 포함한 블록은 유효하지 않습니다. 멤풀은 대기 중인 모든 널리파이어와 커밋먼트를 인덱싱하고 충돌을 즉시 거부하므로, 같은 노트를 사용하는 두 번들이 하나의 블록을 공유하는 일은 없습니다.

크기

항목바이트
Word832
Envelope 하나인코딩 1,892(암호문 1,860), chain 18부터; chain 17까지는 1,380(1,348)
두 증명을 뺀 Bundle7,936 (널리파이어 4개, 커밋먼트 4개, 엔벨로프 4개, auth_commit)
두 증명을 뺀 전송 트랜잭션7,949
두 증명을 뺀 배포 트랜잭션, 100 워드 프로그램, 공개 입력 없음8,369
세 증명을 뺀 호출 트랜잭션, 입력 엔벨로프 없음7,990
민트 트랜잭션(번들 없음)5,761 (1,312바이트 Dilithium2 키와 2,420바이트 서명)
번들 증명, 티어 14, 프로덕션 프로파일chain 18에서 읽은 1,497,544와 1,504,260 (높이 97,821의 배포와 98,335의 호출, 2026-09-30); chain 14에서 측정한 1,422,116과 1,423,170 (zUSD 등록과 담보 상장, 2026-09-20)
인증 증명, 티어 10, 프로덕션 프로파일같은 chain 18 트랜잭션 두 건에서 1,370,827과 1,367,303

처음 여섯 행은 chain 18부터의 인코딩입니다. 메모 필드가 모든 엔벨로프에 512바이트를 더했고, 분리 인증이 인증 증명 자체의 바이트 앞에 번들에 40바이트를 더했습니다. 따라서 차폐 전송은 어떤 자산을 옮기든 chain 18부터(chain 20 포함) 전송 시 약 2.88 MB이며, 사실상 전부가 증명입니다. 번들 증명이 약 1.5 MB, 인증 증명이 1.37 MB입니다. chain 15까지는 증명이 하나였고 약 1.43 MB였습니다. 블록은 트랜잭션 2,000개(MAX_BLOCK_TXS)와 체인의 max_block_bytes만큼의 트랜잭션 바이트로 제한되며, 둘 다 합의 규칙입니다. 기본값은 4 MiB(MAX_BLOCK_BYTES)로 이런 전송을 하나 담을 수 있고, chain 20의 제네시스는 20 MiB로 일곱 개를 담을 여유가 있습니다. hc_auth가 있는 제네시스는 max_block_bytes를 max_proof_bytes의 세 배에 1 MiB를 더한 값 이상으로 설정해야 하는데, 트랜잭션이 세 개의 증명(번들, 인증, 호출)을 담을 수 있기 때문이며, 따라서 4 MiB 기본값은 거기서 거부됩니다. 블록 수준 집계가 대기 중인 해결책입니다. 증명을 참조하십시오.

다시 읽기

rand_getTransaction(hash)는 트랜잭션이 커밋될 때까지 null을 반환하고, 커밋된 뒤에는 블록 위치와 공개 필드만 반환합니다.

{
  "height": 192, "index": 0, "block_hash": "63f6…08",
  "tx": {
    "hash": "4f2c…e7", "chain_id": 20,
    "bundle": {
      "anchor": "6b1d…c4",
      "nullifiers": ["8c04…d1", "5e77…20", "03aa…6f", "e19b…42"],
      "commitments": ["2a9f…07", "b310…88", "77c1…0e", "5d20…b3"],
      "fee": "1000000", "burn_a": "0", "burn_r": "0", "burn_asset": 0, "time": 5,
      "proof_len": 1497544, "envelope_len": [1860, 1860, 1860, 1860],
      "auth_commit": "0bab…e8a1", "auth_proof_bytes": 1370827
    },
    "action": { "kind": "none" }
  }
}

전송(transfer)에는 from, to, nonce, 금액, asset 필드가 없으며, 저장된 블록에도 마찬가지로 없습니다. 노드에는 더 이상 가릴 정보가 없습니다. auth_commit과 auth_proof_bytes(auth 증명의 길이)는 chain 17부터 포함되어 있습니다. 번들에는 더미를 포함하여 항상 네 개의 슬롯이 있으며, RAND 전송과 모든 RPL 토큰 전송은 둘 다 "kind": "none"입니다. 모든 u64 금액은 chain 14부터 십진수 문자열입니다(fee, burn_a, burn_r, 액션의 amount). 인덱스, nonce, 높이, 길이는 계속 JSON 숫자입니다. 그 밖의 액션 종류는 공개 필드를 표시합니다. 해당 종류는 mint, deploy, call, bond, unbond, withdraw, bridge_attest, bridge_burn, register_token, token_mint, set_authority, token_burn, pause_mints, unpause_mints, register_bridged_token, list_backing, rotate_pq_guardians, rotate_pause_key, 네 가지 베스팅 종류인 claim_vested, revoke_vesting, bond_vested, unbond_vested, 그리고 v0.6.8부터 추가된 admit_validator, slash_equivocation, rotate_pq_guardians_v2, rotate_pause_key_v2, cancel_rotation, invoke이며, JSON-RPC reference에 나열된 대로입니다.

fullnode 커밋 c0878c3f(chain 21) 기준으로 2026-10-12에 마지막으로 검증되었습니다. 코드와 이 페이지의 내용이 다를 경우 코드가 옳습니다. 알려 주시기 바랍니다.