Skip to content

FastPQ ​

FastPQ é Iroha O que é ? STARK Proof path para efeitos de execução selecionados. Não substitui a execução normal da transacção ou o consenso. As transacções continuam a ser executadas ISI, IVM, e Sumeragi Como de costume; FastPQ Consuma o testemunho de execução determinista e transforma os efeitos apoiados em lotes de prova.

A integração do anfitrião atual tem três principais caminhos:

  • Transferências numéricas transparentes de ativos registadas durante a execução do bloco
  • Relais de faixa Nexus verificados cujo envelope de prova AXT contém uma ligação FastPQ
  • Auxiliares transparentes de prova de mensagem SCCP que envolvem uma prova FastPQ em um envelope aberto de verificação

Transferir o caminho da testemunha ​

Transferências numéricas transparentes criam uma transcrição de transferência estruturada quando a instrução muda os balanços.

  • a conta de origem, a conta de destino, a definição do ativo e o montante
  • Saldos do remetente e do destinatário antes e depois da transferência
  • o hash do ponto de entrada da transação utilizado como hash do lote
  • Digest de autoridade derivado da conta de apresentação
  • uma digestão de Poseidon para transcrições em delta único

As transferências de lote utilizam uma transcrição com múltiplos deltas. Nesse caso, a digestão Poseidon de delta único está ausente.

Na finalização do bloco, Iroha agrupa essas transcrições por hash de ponto de entrada. A testemunha de execução então carrega tanto os pacotes de transcrição originais quanto os lotes de transição FastPQ preparados para o prover.

Cada delta de transferência torna-se em duas linhas de transição:

Em fila .Forma da chavePre-valorPós-valor
Emissor de débitoasset/<asset-definition>/<source-account>balanço do remetente antessaldo do remetente após
Crédito do destinatárioasset/<asset-definition>/<destination-account>Balanço do destinatário antessaldo do destinatário após

Os valores numéricos são normalizados em unidades inteiras testemunhas. Um valor é rejeitado para lotes FastPQ se não puder ser representado como um u64 não negativo na escala decimal selecionada.

Contribuições públicas ​

Cada lote de transição FastPQ contém entradas públicas que vinculam a prova ao contexto do bloco e da execução:

IntroduçãoQue significa ?
dsidIdentificador de espaço de dados codificado como bytes de pequeno índice
slotTempo de criação do bloco convertido em nanossegundos
old_rootRaiz do estado dos pais derivada da testemunha de execução
new_rootA raiz pós-estado derivada da testemunha de execução .
perm_rootO compromisso de Poseidon com as autorizações para o papel ativo
tx_set_hashHash sobre transacção ordenada e hashes de ponto de entrada do time-trigger

O anfitrião utiliza fastpq-lane-balanced como o parâmetro canônico definido para esses lotes.

Modelo matemático ​

Esta seção descreve a aritmética implementada pelo provador e verificador Rust atual. Todas as operações de campo abaixo são sobre o campo prime Goldilocks:

F=Fp,p=264−232+1 F = \mathbb{F}_p,\qquad p = 2^{64} - 2^{32} + 1

FastPQ usa Poseidon2 em vez de F para compromissos de campo. A esponja tem largura t = 3, taxa r = 2 e capacidade 1. O hash absorve elementos de campo em blocos de taxa-2 e anexa um único elemento de campo 1 antes da permutação final:

HF(x0,…,xm−1)=Poseidon2⁡F(x0,…,xm−1,1) H_F(x_0,\ldots,x_{m-1}) = \operatorname{Poseidon2}_F(x_0,\ldots,x_{m-1},1)

As cordas de byte são empacotadas em membros pequenos endianos de 7 bytes para que cada membro seja estritamente inferior a p:

pack⁡(b)j=∑i=06b7j+i28i,0≤pack⁡(b)j<p \operatorname{pack}(b)_j = \sum_{i=0}^{6} b_{7j+i}2^{8i},\qquad 0 \leq \operatorname{pack}(b)_j < p

Os hashes de campo separados por domínio são representados como:

HD(m)=HF(∣pack⁡(D)∣,pack⁡(D),∣pack⁡(m)∣,pack⁡(m)) H_D(m) = H_F( |\operatorname{pack}(D)|,\operatorname{pack}(D), |\operatorname{pack}(m)|,\operatorname{pack}(m) )

Para hashes que começam a partir de digestões em byte-domain, FastPQ mapeia os primeiros oito bytes do pequeno endian no campo:

seed⁡(D)=le64⁡(Hash⁡(D)[0..8]) mod p \operatorname{seed}(D)= \operatorname{le64}(\operatorname{Hash}(D)[0..8])\bmod p

Aqui Hash significa o iroha_crypto::Hash::new de Iroha, um digesto Blake2bVar de 32 bytes, a menos que uma fórmula nomee explícitamente Poseidon2 ou SHA-256.

Aritmética de campo ​

O código Rust representa os elementos de campo como valores canônicos u64 no [0,p).

a+Fb=(a+b) mod p a +_F b = (a+b)\bmod p

a−Fb=(a−b) mod p a -_F b = (a-b)\bmod p

A multiplicação calcula primeiro o produto de 128 bits:

a⋅b=lo⁡+264hi⁡ a\cdot b = \operatorname{lo} + 2^{64}\operatorname{hi}

A redução de Goldilocks utiliza então a identidade:

264≡232−1(modp) 2^{64}\equiv2^{32}-1\pmod p

Se:

hi⁡=hi⁡lo+232hi⁡hi \operatorname{hi}=\operatorname{hi}_{lo}+2^{32}\operatorname{hi}_{hi}

Em seguida, o redutor calcula:

lo⁡+232hi⁡lo−hi⁡lo−hi⁡hi(modp) \operatorname{lo} +2^{32}\operatorname{hi}_{lo} -\operatorname{hi}_{lo} -\operatorname{hi}_{hi} \pmod p

A implementação adiciona ou subtrai condicionalmente p até que o resultado seja canônico. Os números inteiros assinados, tais como os deltas do balanço, são incorporados por:

field⁡(x)=x mod p,0≤field⁡(x)<p \operatorname{field}(x)=x\bmod p,\qquad 0\leq\operatorname{field}(x)<p

Poseidon2 Permutação ​

O estado de permutação Poseidon2 é:

x=(x0,x1,x2)∈F3 \mathbf{x}=(x_0,x_1,x_2)\in F^3

A sua caixa S é:

S(x)=x5 S(x)=x^5

FastPQ utiliza quatro rodadas completas, cinquenta e sete rodadas parciais, em seguida, mais quatro rodadas. Uma rodada completa com constantes redondas c_r = (c_{r,0}, c_{r,1}, c_{r,2}):

x′=M⋅[S(x0+cr,0)S(x1+cr,1)S(x2+cr,2)] \mathbf{x}' = M\cdot \begin{bmatrix} S(x_0+c_{r,0})\\ S(x_1+c_{r,1})\\ S(x_2+c_{r,2}) \end{bmatrix}

Uma rodada parcial é:

x′=M⋅[S(x0+cr,0)x1+cr,1x2+cr,2] \mathbf{x}' = M\cdot \begin{bmatrix} S(x_0+c_{r,0})\\ x_1+c_{r,1}\\ x_2+c_{r,2} \end{bmatrix}

Todas as adições e multiplicações são em F. A matriz canónica MDS é:

M=[0x982513a23d22b5920xa3115db8cf1d9c900x46ba684b9eee84b70xbe3dce25491db7680xfb0a6f731943519f0xfce5bd953cde18960xe624719c41eb1a090xd2221b0f1aa2ebc40x1ab5e60d03ad44bc] M= \begin{bmatrix} \texttt{0x982513a23d22b592} & \texttt{0xa3115db8cf1d9c90} & \texttt{0x46ba684b9eee84b7}\\ \texttt{0xbe3dce25491db768} & \texttt{0xfb0a6f731943519f} & \texttt{0xfce5bd953cde1896}\\ \texttt{0xe624719c41eb1a09} & \texttt{0xd2221b0f1aa2ebc4} & \texttt{0x1ab5e60d03ad44bc} \end{bmatrix}

O hash do campo começa a partir do estado zero. Para cada bloco de taxa completa-2 (u,v):

(x0,x1,x2)←Poseidon2⁡(x0+u,x1+v,x2) (x_0,x_1,x_2)\leftarrow \operatorname{Poseidon2}(x_0+u,x_1+v,x_2)

O bloco final acrescenta o 1 Elemento de enchimento antes de uma última permutação. x_0.

A entrada pública é obrigatória ​

O host codifica uma identificação de espaço de dados escrevendo seu valor u64 nos primeiros oito bytes do campo de 16bytes:

dsid_bytes⁡(d)[0..8]=le64⁡(d),dsid_bytes⁡(d)[8..16]=0 \operatorname{dsid\_bytes}(d)[0..8]=\operatorname{le64}(d), \qquad \operatorname{dsid\_bytes}(d)[8..16]=0

O tempo de criação do bloco é convertido de milissegundos em nanosegundos:

slot⁡=saturating_mul⁡(creation_time_ms⁡,1,000,000) \operatorname{slot}=\operatorname{saturating\_mul} (\operatorname{creation\_time\_ms},1{,}000{,}000)

O hash do conjunto de transações é um hash de domínio de byte sobre os hashes do ponto de entrada ordenados:

tx_set_hash⁡=Hash⁡(fastpq:v1:tx_set∥h0∥⋯∥hn−1) \operatorname{tx\_set\_hash} = \operatorname{Hash}( \texttt{fastpq:v1:tx\_set}\|h_0\|\cdots\|h_{n-1} )

onde h_i são classificados hashes de transações e pontos de entrada do time-trigger. Na prova pública IO, se perm_root ou tx_set_hash for tudo zero, o prover preenche os valores de fallback:

perm_root⁡={032,if there are no permission hashesHash⁡(fastpq:v1:perm_root∥p0∥⋯∥pn−1),otherwise \operatorname{perm\_root} = \begin{cases} 0^{32},& \text{if there are no permission hashes}\\ \operatorname{Hash}(\texttt{fastpq:v1:perm\_root}\|p_0\|\cdots\|p_{n-1}), & \text{otherwise} \end{cases}

tx_set_hash⁡fallback=Hash⁡(fastpq:v1:tx_set∥ordering_hash⁡) \operatorname{tx\_set\_hash}_{fallback} = \operatorname{Hash}(\texttt{fastpq:v1:tx\_set}\|\operatorname{ordering\_hash})

Normalização numérica ​

Para cada delta de transferência, a escala decimal-alvo é a escala máxima cortada ao longo da quantidade e ambos os instantâneos de equilíbrio:

s=max⁡(scale⁡(a),scale⁡(f0),scale⁡(f1),scale⁡(t0),scale⁡(t1)) s = \max( \operatorname{scale}(a), \operatorname{scale}(f_0), \operatorname{scale}(f_1), \operatorname{scale}(t_0), \operatorname{scale}(t_1) )

A. Numeric Valor com mantissa m e escala q só é aceita quando m >= 0 e q <= s. O seu FastPQ O valor do testemunho é:

norm⁡s(m,q)=m⋅10s−q \operatorname{norm}_s(m,q)=m\cdot10^{s-q}

O resultado normalizado deve corresponder a u64.

Ordenamento canônico ​

Antes da construção do rastro, o lote é classificado por chave de transição, grau de operação e índice de inserção original:

r(Transfer⁡)=0,r(Mint⁡)=1,r(Burn⁡)=2,r(RoleGrant⁡)=3,r(RoleRevoke⁡)=4,r(MetaSet⁡)=5 r(\operatorname{Transfer})=0,\quad r(\operatorname{Mint})=1,\quad r(\operatorname{Burn})=2,\quad r(\operatorname{RoleGrant})=3,\quad r(\operatorname{RoleRevoke})=4,\quad r(\operatorname{MetaSet})=5

O compromisso de encomenda é um hash do campo Poseidon2 sobre o domínio fastpq:v1:ordering e a codificação Norito das transições ordenadas:

ordering_hash⁡=HF(∣P(Do)∣,P(Do),∣P(E(T⋆))∣,P(E(T⋆))) \operatorname{ordering\_hash} = H_F( |P(D_o)|,P(D_o),|P(E(T^\star))|,P(E(T^\star)) )

onde P é embalagem de 7 bytes, E é Norito codificação, D_o é fastpq:v1:ordering, e T* É a lista de transição ordenada.

Equações de transferência ​

Para um montante de transferência a, saldo do remetente f e saldo do destinatário t, FastPQ valida os valores normalizados dos testemunhos antes da construção do rastreamento:

f0≥a f_0 \geq a

f1=f0−a f_1 = f_0 - a

t1=t0+a t_1 = t_0 + a

As linhas de transição então codificam:

Δsender=f1−f0=−a \Delta_{\text{sender}} = f_1 - f_0 = -a

Δreceiver=t1−t0=a \Delta_{\text{receiver}} = t_1 - t_0 = a

No interior do rastro, os deltas assinados são reduzidos para F:

δi=(post⁡i−pre⁡i) mod p \delta_i = (\operatorname{post}_i - \operatorname{pre}_i)\bmod p

A digestão opcional de transferência single-delta compromete a pré-imagem de transferência codificada:

dtransfer=PoseidonHashBytes⁡(E(from)∥E(to)∥E(asset)∥E(a)∥batch_hash) d_{\text{transfer}} = \operatorname{PoseidonHashBytes}( E(\text{from})\|E(\text{to})\|E(\text{asset})\|E(a)\|\text{batch\_hash} )

Para transcrições de transferência multi-delta, o formato atual exige que este digestamento de nível superior esteja ausente.

A digestão da autoridade de acolhimento para transcrições de transferência é:

dauthority=Hash⁡(iroha:fastpq:v1:authority|∥E(authority_account)) d_{\text{authority}} = \operatorname{Hash}(\texttt{iroha:fastpq:v1:authority|}\|E(\text{authority\_account}))

Linhas de rastreamento ​

Deixe a lista de transição ordenada conter n linhas reais. O comprimento do rastro é o próximo poder de dois:

N=2⌈log⁡2(max⁡(1,n))⌉ N = 2^{\lceil\log_2(\max(1,n))\rceil}

As filas 0..n-1 são ativas; as fileiras n..N-1 são fileiras de enchimento. Cada linha real possui um conjunto de selectores de operação:

sactive=stransfer+smint+sburn+srole_grant+srole_revoke+smeta_set s_{\text{active}} = s_{\text{transfer}}+ s_{\text{mint}}+ s_{\text{burn}}+ s_{\text{role\_grant}}+ s_{\text{role\_revoke}}+ s_{\text{meta\_set}}

Todas as colunas de selector são booleanas:

s(s−1)=0 s(s-1)=0

As linhas de pesquisa de permissão são exatamente as linhas de atribuição e revogação de funções:

sperm=srole_grant+srole_revoke s_{\text{perm}} = s_{\text{role\_grant}} + s_{\text{role\_revoke}}

Para as linhas de operação numérica:

δi=value_new⁡i,0−value_old⁡i,0 \delta_i = \operatorname{value\_new}_{i,0} - \operatorname{value\_old}_{i,0}

O construtor acompanha também os delta por ativo:

Ri(a)=Ri−1(a)+δifor transfer, mint, and burn rows of asset a R_i(a)=R_{i-1}(a)+\delta_i \quad\text{for transfer, mint, and burn rows of asset }a

Só as filas de menta e queimadura atualizam o contador de abastecimento:

Si(a)=Si−1(a)+{δi,if row i is mint or burn0,otherwise S_i(a)=S_{i-1}(a)+ \begin{cases} \delta_i,& \text{if row }i\text{ is mint or burn}\\ 0,& \text{otherwise} \end{cases}

Metadados e colunas de rastreamento do espaço de dados são hashes de campo derivados antes da materialização de linha:

metadata_hash⁡={0,if metadata is emptyHD(E(metadata)),otherwise \operatorname{metadata\_hash} = \begin{cases} 0,& \text{if metadata is empty}\\ H_D(E(\text{metadata})),& \text{otherwise} \end{cases}

dsid_trace⁡=HD(public_input_dsid⁡) \operatorname{dsid\_trace}=H_D(\operatorname{public\_input\_dsid})

O hash de metadados, o hash do espaço de dados e o slot são estáveis em linhas adjacentes de traços:

metadata_hash⁡i=metadata_hash⁡i+1 \operatorname{metadata\_hash}_i=\operatorname{metadata\_hash}_{i+1}

dsid⁡i=dsid⁡i+1 \operatorname{dsid}_i=\operatorname{dsid}_{i+1}

slot⁡i=slot⁡i+1 \operatorname{slot}_i=\operatorname{slot}_{i+1}

Transferir colunas de Merkle ​

As linhas de transferência carregam um caminho Merkle escasso de 32 níveis. Se uma prova host estiver faltando, o prover sintetiza um caminho determinista a partir da chave de linha, pré-equilíbrio e se a linha é o lado do remetente ou do receptor.

Para os caminhos sintéticos, o sal de sabor é fastpq:smt:from para as linhas de remetente e fastpq:smt:to para as linhagens de receptor:

K=Hash⁡(fastpq:smt:key|∥salt⁡∥key⁡) K = \operatorname{Hash}(\texttt{fastpq:smt:key|}\|\operatorname{salt}\|\operatorname{key})

V=Hash⁡(fastpq:smt:value|∥salt⁡∥le64⁡(balance⁡)) V = \operatorname{Hash}(\texttt{fastpq:smt:value|}\|\operatorname{salt}\|\operatorname{le64}(\operatorname{balance}))

bℓ=bit⁡ℓ(K) b_\ell = \operatorname{bit}_\ell(K)

sℓ=Hash⁡(fastpq:smt:sibling|∥le64⁡(ℓ)∥K∥le64⁡(balance⁡)∥salt⁡) s_\ell = \operatorname{Hash}( \texttt{fastpq:smt:sibling|}\| \operatorname{le64}(\ell)\|K\|\operatorname{le64}(\operatorname{balance})\|\operatorname{salt} )

As folhas sintéticas e os nós internos são:

L=Hash⁡(fastpq:smt:leaf|∥K∥V) L = \operatorname{Hash}( \texttt{fastpq:smt:leaf|}\| K\|V )

Nℓ+1=Hash⁡(fastpq:smt:node|∥left⁡ℓ∥right⁡ℓ) N_{\ell+1} = \operatorname{Hash}( \texttt{fastpq:smt:node|}\| \operatorname{left}_\ell\| \operatorname{right}_\ell )

O rastreamento registra o pedaço b_l, irmãos s_l, nó de entrada x_l, e nó de saída x_{l+1} Em todos os níveis. Com a convenção do ramo do código:

(left⁡ℓ,right⁡ℓ)={(sℓ,xℓ),bℓ=0(xℓ,sℓ),bℓ=1 (\operatorname{left}_\ell,\operatorname{right}_\ell)= \begin{cases} (s_\ell,x_\ell),& b_\ell=0\\ (x_\ell,s_\ell),& b_\ell=1 \end{cases}

Hashes de permissão ​

As linhas de atribuição e revogação da função hash o testemunho de permissão:

hperm=HF(P(role_id⁡∥permission_id⁡∥epoch⁡le)) h_{\text{perm}} = H_F(P(\operatorname{role\_id}\|\operatorname{permission\_id}\|\operatorname{epoch}_{le}))

A tabela de permissão host classifica as entradas por bytes de papel, bytes de permissões e bytes de época, então constrói uma árvore de Poseidon2 Merkle:

M0[j]=hperm,j M_0[j]=h_{\text{perm},j}

Mk+1[j]=HF(seed⁡(fastpq:v1:poseidon_node),Mk[2j],Mk[2j+1]) M_{k+1}[j] = H_F(\operatorname{seed}(\texttt{fastpq:v1:poseidon\_node}),M_k[2j],M_k[2j+1])

Os níveis de largura ímpar duplicam o elemento final.

O compromisso de rastrear ​

Para cada coluna de traços c, FastPQ primeiro interpola os valores da coluna sobre o domínio de traços e hashes o vetor do coeficiente:

Cc=HF(seed⁡(fastpq:v1:trace:column:c),coeffs⁡(c)) C_c = H_F( \operatorname{seed}(\texttt{fastpq:v1:trace:column:}c), \operatorname{coeffs}(c) )

A raiz de rastreamento é uma raiz de Poseidon2 Merkle sobre compromissos de coluna:

Rtrace=MerkleRoot⁡(C0,…,Cm−1) R_{\text{trace}} = \operatorname{MerkleRoot}(C_0,\ldots,C_{m-1})

O compromisso de rastreamento final é um hash em byte sobre o domínio, conjunto de parâmetros, forma do rastreamento, digestões de colunas e raiz do rastreador:

commitment⁡=Hash⁡(len⁡(Dc)∥Dc∥len⁡(parameter)∥parameter∥n∥N∥m∥C0∥⋯∥Cm−1∥Rtrace) \operatorname{commitment} = \operatorname{Hash}( \operatorname{len}(D_c)\|D_c\| \operatorname{len}(\text{parameter})\|\text{parameter}\| n\|N\|m\|C_0\|\cdots\|C_{m-1}\|R_{\text{trace}} )

onde D_c é fastpq:v1:trace_commitment.

Composição AIR ​

O valor de composição V1 AIR é uma combinação linear de resíduos locais em filas. A transcrição mostra dois desafios:

α0,α1∈F \alpha_0,\alpha_1 \in F

Para cada par de linhas adjacentes (i,i+1), o provador calcula:

Ai=∑jαj mod 2ρi,j A_i=\sum_j \alpha_{j\bmod2}\rho_{i,j}

Os resíduos rho são, na ordem do código:

ρ=s(s−1)for each selector column \rho=s(s-1) \quad\text{for each selector column}

ρ=sactive−(stransfer+smint+sburn+srole_grant+srole_revoke+smeta_set) \rho = s_{\text{active}} - (s_{\text{transfer}}+s_{\text{mint}}+s_{\text{burn}}+ s_{\text{role\_grant}}+s_{\text{role\_revoke}}+s_{\text{meta\_set}})

ρ=sperm−(srole_grant+srole_revoke) \rho = s_{\text{perm}}-(s_{\text{role\_grant}}+s_{\text{role\_revoke}})

ρ=sactive,i+1(1−sactive,i) \rho = s_{\text{active},i+1}(1-s_{\text{active},i})

Para as linhas com colunas numéricas:

ρ=(stransfer+smint+sburn)⋅((value_new⁡0−value_old⁡0)−δ) \rho = (s_{\text{transfer}}+s_{\text{mint}}+s_{\text{burn}}) \cdot ((\operatorname{value\_new}_{0}-\operatorname{value\_old}_{0})-\delta)

E para colunas de contexto de lote estável:

ρ=metadata_hash⁡i−metadata_hash⁡i+1 \rho = \operatorname{metadata\_hash}_i-\operatorname{metadata\_hash}_{i+1}

ρ=dsid⁡i−dsid⁡i+1 \rho = \operatorname{dsid}_i-\operatorname{dsid}_{i+1}

ρ=slot⁡i−slot⁡i+1 \rho = \operatorname{slot}_i-\operatorname{slot}_{i+1}

O verificador recalcula o A_i para as aberturas de filas recolhidas na amostra e verifica-o em relação ao valor da composição comprometido no âmbito da raiz Merkle da composição AIR.

Produto de pesquisa ​

O acumulador de pesquisa de permissões utiliza o desafio Fiat-Shamir gamma. Nas avaliações de extensão de baixo grau de s_perm e perm_hash, o produto em execução é:

z0=1 z_0=1

zi+1={zi⋅(wi+γ),sperm,i≠0zi,sperm,i=0 z_{i+1}= \begin{cases} z_i\cdot(w_i+\gamma),& s_{\text{perm},i}\ne0\\ z_i,& s_{\text{perm},i}=0 \end{cases}

Os registos de prova:

lookup_grand_product⁡=HF(z0,z1,…) \operatorname{lookup\_grand\_product}=H_F(z_0,z_1,\ldots)

Extensão de baixo grau ​

Que omega_T seja o gerador de domínio de rastreamento, omega_E o gerador do domínio de avaliação e g a compensação dos cosetos configurados. Para uma coluna de rastreio com valores v_i, a interpolação produz coeficientes a_j tais que:

f(ωTi)=vi f(\omega_T^i)=v_i

A extensão de baixo grau evalua o mesmo polinômio no coseto:

LDE⁡f(i)=f(g⋅ωEi) \operatorname{LDE}_f(i)=f(g\cdot\omega_E^i)

A implementação calcula isto multiplicando os coeficientes por poderes do coset compensado antes de FFT:

aj′=ajgj a'_j = a_j g^j

e, em seguida, a avaliação a' no domínio de avaliação.

A Comissão CPU FFT é uma transformação iterativa de radix-2 Cooley-Tukey sobre entradas invertidas em bits. L, Meio comprimento H=L/2, e raiz de estágio:

ωL=ωN/L \omega_L=\omega^{N/L}

Cada borboleta calcula:

u=xj u=x_j

v=xj+H⋅ωLj v=x_{j+H}\cdot\omega_L^j

xj′=u+v,xj+H′=u−v x_j'=u+v,\qquad x_{j+H}'=u-v

O inverso FFT realiza a mesma transformação com o omega^{-1} e escala pelo tamanho do domínio inverso:

IFFT⁡(x)=N−1⋅FFT⁡ω−1(x) \operatorname{IFFT}(x)=N^{-1}\cdot\operatorname{FFT}_{\omega^{-1}}(x)

As raízes do catálogo devem ser validadas antes da utilização:

ω2k=1 \omega^{2^k}=1

ω2k−1≠1(k>0) \omega^{2^{k-1}}\ne1\qquad(k>0)

Para domínios menores derivados da raiz do catálogo, o gerador é:

ωℓ=ωmax⁡2kmax⁡−ℓ \omega_{\ell}=\omega_{\max}^{2^{k_{\max}-\ell}}

Haches de filas e folhas ​

Após LDE, FastPQ hashes de cada linha em todos LDE Colunas. para m Colunas:

ri=HF(i,m,xi,0,xi,1,…,xi,m−1) r_i = H_F(i,m,x_{i,0},x_{i,1},\ldots,x_{i,m-1})

Se os hashes de linha ainda estiverem no domínio trace, em vez do domínio de avaliação, o prover interpola e estende essa coluna de hash de linha única com o mesmo processo coset LDE.

Aberturas de Merkle ​

Os valores LDE são agrupados em pedaços de:

Blde=8⋅fri_arity⁡ B_{\text{lde}}=8\cdot\operatorname{fri\_arity}

Cada pedaço de folha é:

Lj=HD(j∥vjB∥⋯∥vjB+B−1) L_j=H_D(j\|v_{jB}\|\cdots\|v_{jB+B-1})

Os pais de Merkle são:

Pj=HF(seed⁡(fastpq:v1:trace:node),L2j,L2j+1) P_j = H_F(\operatorname{seed}(\texttt{fastpq:v1:trace:node}),L_{2j},L_{2j+1})

Níveis ímpares duplicam o último nó. Os caminhos de consulta são verificados por hashing à esquerda ou à direita de acordo com a paridade do índice da folha de consulta em cada nível.

Para uma folha com índice i, o caminho (s_0,\ldots,s_{d-1}) é verificado contra a raiz R pela recorrência:

y0=Li y_0=L_i

yk+1={HF(seed⁡(fastpq:v1:trace:node),yk,sk),⌊i/2k⌋≡0(mod2)HF(seed⁡(fastpq:v1:trace:node),sk,yk),⌊i/2k⌋≡1(mod2) y_{k+1}= \begin{cases} H_F(\operatorname{seed}(\texttt{fastpq:v1:trace:node}),y_k,s_k), & \lfloor i/2^k\rfloor \equiv 0 \pmod 2\\ H_F(\operatorname{seed}(\texttt{fastpq:v1:trace:node}),s_k,y_k), & \lfloor i/2^k\rfloor \equiv 1 \pmod 2 \end{cases}

O cheque só é aprovado quando:

yd=R y_d=R

As folhas de linha AIR são:

Liair=HD(i∥m∥xi,0∥⋯∥xi,m−1) L^{\text{air}}_i = H_D(i\|m\|x_{i,0}\|\cdots\|x_{i,m-1})

As folhas de composição AIR são:

Licomp=HD(i∥Ai) L^{\text{comp}}_i = H_D(i\|A_i)

A abertura da consulta LDE também verifica se o valor aberto no índice de avaliação i está presente na sua peça autenticada:

chunk_index⁡=⌊iBlde⌋ \operatorname{chunk\_index}=\left\lfloor\frac{i}{B_{\text{lde}}}\right\rfloor

chunk_offset⁡=i mod Blde \operatorname{chunk\_offset}=i\bmod B_{\text{lde}}

chunk⁡[chunk_offset⁡]=vi \operatorname{chunk}[\operatorname{chunk\_offset}]=v_i

FRI Pulsão ​

FRI compromete-se a AIR Avaliações da composição. l, As amostras de transcrição são um desafio beta_l. A camada é empolhada para um múltiplo da aridade repetindo o último valor. Cada grupo de tamanho da aridade se dobra para:

yl+1,j=∑k=0a−1yl,ja+kβlk y_{l+1,j} = \sum_{k=0}^{a-1} y_{l,ja+k}\beta_l^k

onde a é a aridade de FRI. O verificador verifica, para cada cadeia de consultas amostrada, que:

yl+1,⌊i/a⌋=∑k=0a−1yl,⌊i/a⌋a+kβlk y_{l+1,\lfloor i/a\rfloor} = \sum_{k=0}^{a-1} y_{l,\lfloor i/a\rfloor a+k}\beta_l^k

e autentica cada grupo FRI aberto contra a raiz de camada FRI correspondente.

Transcrição de Fiat-Shamir ​

O catálogo de parâmetros canônicos rotula o hash da transcrição como SHA3-256. A implementação atual do prover e verificador deriva bytes de desafio com iroha_crypto::Hash::new, que é um digesto Blake2bVar de 32 bytes, depois reduz os primeiros oito bytes de pequena endea para F:

χ(tag)=le64⁡(Hash⁡(state∥len⁡(tag)∥tag)[0..8]) mod p \chi(\text{tag}) = \operatorname{le64}(\operatorname{Hash}(\text{state}\|\operatorname{len}(\text{tag})\|\text{tag})[0..8]) \bmod p

As chamadas de desafio adicionam o texto completo ao estado da transcrição.

  1. público IO, versão de protocolo, versão de parâmetro e nome do parâmetre
  2. LDE raiz e raiz de rastro
  3. gamma
  4. Desafios de composição AIR alpha_0, alpha_1
  5. Raiz de traço AIR e raiz de composição AIR
  6. lookup grande produto
  7. Roteiras de camadas FRI e desafios beta_l
  8. índices de consulta tomados em amostra

A amostragem de consulta continua a desenhar digestões de desafio de 32 bytes e a lê-las como pequenos fragmentos u64 até obter o número solicitado de índices únicos:

q=le64⁡(digest chunk) mod Neval q = \operatorname{le64}(\text{digest chunk})\bmod N_{\text{eval}}

O conjunto de amostras é devolvido em ordem ordenada.

Reprodução do verificador ​

O verificador recalcula, em primeiro lugar, o compromisso do lote:

commitment⁡expected=trace_commitment⁡(params⁡,batch⁡) \operatorname{commitment}_{expected} =\operatorname{trace\_commitment}(\operatorname{params},\operatorname{batch})

e requer:

commitment⁡expected=proof.trace_commitment⁡ \operatorname{commitment}_{expected} =\operatorname{proof.trace\_commitment}

Reconstrui também o público IO:

PublicIO⁡=(dsid⁡,slot⁡,old_root⁡,new_root⁡,perm_root⁡,tx_set_hash⁡,ordering_hash⁡,permission_hashes⁡) \operatorname{PublicIO}= (\operatorname{dsid},\operatorname{slot},\operatorname{old\_root}, \operatorname{new\_root},\operatorname{perm\_root}, \operatorname{tx\_set\_hash},\operatorname{ordering\_hash}, \operatorname{permission\_hashes})

Cada campo deve corresponder ao público da prova IO byte-for-byte. O verificador reconstrui a mesma transcrição e obtém a mesma:

γ,α0,α1,β0,…,βℓ−1,q0,…,qt−1 \gamma,\quad \alpha_0,\alpha_1,\quad \beta_0,\ldots,\beta_{\ell-1},\quad q_0,\ldots,q_{t-1}

Para cada consulta tomada pela amostra q, verifica-se:

MerkleVerify⁡(Rlde,L⌊q/Blde⌋,⌊q/Blde⌋,πlde) \operatorname{MerkleVerify}( R_{\text{lde}}, L_{\lfloor q/B_{\text{lde}}\rfloor}, \lfloor q/B_{\text{lde}}\rfloor, \pi_{\text{lde}} )

MerkleVerify⁡(Rair,Lqair,q,πair,current) \operatorname{MerkleVerify}( R_{\text{air}}, L^{\text{air}}_q, q, \pi_{\text{air,current}} )

MerkleVerify⁡(Rair,Lq+1 mod Nevalair,q+1 mod Neval,πair,next) \operatorname{MerkleVerify}( R_{\text{air}}, L^{\text{air}}_{q+1\bmod N_{\text{eval}}}, q+1\bmod N_{\text{eval}}, \pi_{\text{air,next}} )

e:

Aq=AIRComposition⁡(row⁡q,row⁡q+1,α0,α1) A_q = \operatorname{AIRComposition}( \operatorname{row}_q,\operatorname{row}_{q+1},\alpha_0,\alpha_1 )

A Comissão AIR A abertura da composição deve ser autenticada no R_air_composition. A Comissão FRI A cadeia começa a partir da mesma A_q e deve terminar num final autenticado FRI Folha debaixo do terminal FRI A raiz.

O que o Provérbio verifica ​

Antes de construir o rastreamento, o provador FastPQ canoniza a ordem do lote por chave de transição, grau de operação e ordem de inserção. As linhas de transferência também exigem metadados transcritos.

Para as transcrições de transferência, os controlos no lado do provedor incluem:

  • O saldo do remetente não deve fluir para baixo
  • sender_after deve ser igual a sender_before - amount
  • receiver_after deve ser igual a receiver_before + amount
  • A transcrição deve abranger todas as linhas de transferência do lote.
  • uma digestão de Poseidon com um único delta, quando presente, deve corresponder à imagem prévia da transcrição
  • desde que as provas de Merckle raras sejam decodificadas como versão 1; os caminhos faltantes são preenchidos com provas sintéticas deterministas.

O rastreamento contém colunas de selector para transferência, moeda, queima, atribuição de papéis, revogação de papéis , conjunto de metadados e linhas de pesquisa de permissões.

Provérbio Lane ​

irohad inicia a faixa de prover FastPQ na inicialização se o backend do prover pode ser iniciado. A faixa é uma tarefa de fundo com uma fila limitada. Depois que um bloco produz um testemunho de execução, o caminho de commit apresenta um trabalho de prover contendo o hash do bloco, altura, vista e testemunho.

Se a faixa não estiver em execução ou a fila estiver cheia, o trabalho é ignorado e o processamento normal do bloco continua. Isso significa que a faixa de verificação de fundo não é uma entrada de transação ou um portal de consenso. É um caminho de produção de prova sobre estado que já foi executado.

A faixa constrói um provedor com:

text
parameter = "fastpq-lane-balanced"
execution_mode = auto | cpu | gpu
poseidon_mode = auto | cpu | gpu

auto Deixa o provador escolher o backend disponível. cpu Execução de pins para o CPU. gpu preferências GPU Execução, com CPU fallback em que o backend não pode utilizar os kernels solicitados.

Verificação ​

A verificação de prova FastPQ reconstitui o compromisso canônico do lote e repõe a transcrição pública. O verificador verifica a versão do protocolo, a versão definida por parâmetros, os limites de reprodução, o compromisso de rastreamento, as entradas públicas, as aberturas Merkle da amostra, as abertura AIR e a cadeia de consultas FRI.

Os limites de repetição por defeito incluem:

Limite .Default .
Linhas de transição256
Tamanho da carga útil do lote256 KiB
FRI camadas16
Questões abertas128

Nexus Relais verificados ​

Nexus AXT Os envelopes de prova podem incorporar um AxtFastpqBinding. Quando RegisterVerifiedLaneRelay Executa, Iroha:

  1. Verifica o envelope de relevo da faixa e o material de prova FastPQ
  2. verifica o espaço de dados e a raiz do manifesto
  3. Decodifica o envelope de prova AXT
  4. Requer um fastpq_binding
  5. Reconstrui o lote FastPQ a partir dessa ligação.
  6. Decodifica a prova embutida FastPQ
  7. liga ao verificador FastPQ sobre o lote e a prova reconstruídos

Se a verificação for bem-sucedida, Iroha armazena um VerifiedLaneRelayRecord contendo a referência do relevo, o envelope original, o hash da carga útil de prova, a altura da verificação, a raiz do manifesto e a ligação à FastPQ.

Os envelopes de relevo de faixa também carregam material de prova compacto FastPQ. O material é um digesto sobre o ID da faixa, id do espaço de dados, altura do bloco, altura de verificação, hash de cabeçalho de bloco, hash de liquidação e raiz do manifesto. Um relevo só é admissível quando contém um material de prova QC e válido FastPQ.

AXT Matemática vinculativa ​

Para os envelopes Nexus AXT, AxtFastpqBinding é canonizado antes da repetição de prova. Valores padrão do parâmetro vazio para fastpq-lane-balanced; id e versão padrão de verificador vazio parafastpq e v1; o tipo de reivindicação é recortado e reduzido.

As entradas públicas AXT FastPQ são hashes de byte deterministas:

dsid⁡=dsid_bytes⁡(source_dsid⁡) \operatorname{dsid}=\operatorname{dsid\_bytes}(\operatorname{source\_dsid})

slot⁡=le64⁡(source_tx_commitment⁡[0..8]) \operatorname{slot}=\operatorname{le64}(\operatorname{source\_tx\_commitment}[0..8])

old_root⁡=Hash⁡(fastpq-json:old_root∥source_tx_commitment⁡∥policy_commitment⁡∥effect_type⁡) \operatorname{old\_root} = \operatorname{Hash}( \texttt{fastpq-json:old\_root}\| \operatorname{source\_tx\_commitment}\| \operatorname{policy\_commitment}\| \operatorname{effect\_type} )

new_root⁡=Hash⁡(fastpq-json:new_root∥source_tx_commitment⁡∥claim_digest⁡∥effect_type⁡) \operatorname{new\_root} = \operatorname{Hash}( \texttt{fastpq-json:new\_root}\| \operatorname{source\_tx\_commitment}\| \operatorname{claim\_digest}\| \operatorname{effect\_type} )

perm_root⁡=Hash⁡(fastpq-json:perm_root∥policy_commitment⁡∥verifier_id⁡∥verifier_version⁡) \operatorname{perm\_root} = \operatorname{Hash}( \texttt{fastpq-json:perm\_root}\| \operatorname{policy\_commitment}\| \operatorname{verifier\_id}\| \operatorname{verifier\_version} )

tx_set_hash⁡=Hash⁡(fastpq-json:tx_set_hash∥source_tx_commitment⁡∥claim_digest⁡∥witness_commitment⁡) \operatorname{tx\_set\_hash} = \operatorname{Hash}( \texttt{fastpq-json:tx\_set\_hash}\| \operatorname{source\_tx\_commitment}\| \operatorname{claim\_digest}\| \operatorname{witness\_commitment} )

As chaves de transição AXT são:

key⁡(prefix⁡,x,y)=prefix⁡∥/∥x∥/∥y \operatorname{key}(\operatorname{prefix},x,y)= \operatorname{prefix}\|\texttt{/}\|x\|\texttt{/}\|y

No pedido de authorization é inserida uma linha relativa à concessão de títulos:

role_id⁡=claim_digest⁡ \operatorname{role\_id}=\operatorname{claim\_digest}

permission_id⁡=witness_commitment⁡ \operatorname{permission\_id}=\operatorname{witness\_commitment}

epoch⁡=le64⁡(policy_commitment⁡[0..8]) \operatorname{epoch}= \operatorname{le64}(\operatorname{policy\_commitment}[0..8])

A reivindicação compliance inserir duas linhas de metadados: uma para a política e outra para os espaços de dados alvo.

Para tx_predicate e value_conservation, é utilizado um valor de efeito explícito quando a ligação contém uma quantidade fonte ou destino positiva. Caso contrário, o código deriva um valor determinístico limitado:

bounded⁡(d,min⁡,span⁡)=min⁡+(le64⁡(d[0..8]) mod max⁡(span⁡,1)) \operatorname{bounded}(d,\min,\operatorname{span}) = \min + (\operatorname{le64}(d[0..8])\bmod\max(\operatorname{span},1))

Em seguida, usam-se as mesmas equações de transferência:

sender_after⁡=sender_before⁡−a \operatorname{sender\_after}=\operatorname{sender\_before}-a

receiver_after⁡=receiver_before⁡+a \operatorname{receiver\_after}=\operatorname{receiver\_before}+a

Os IDs de conta do remetente e do destinatário sintéticos são gerados a partir de sementes-chave:

seed⁡=Hash⁡(label⁡∥entropy⁡)[0..32] \operatorname{seed}= \operatorname{Hash}(\operatorname{label}\|\operatorname{entropy})[0..32]

O hash do lote de transferência é:

batch_hash⁡=Hash⁡(label⁡∥corridor⁡∥source_tx_commitment⁡∥claim_digest⁡) \operatorname{batch\_hash} = \operatorname{Hash}( \operatorname{label}\| \operatorname{corridor}\| \operatorname{source\_tx\_commitment}\| \operatorname{claim\_digest} )

O manifesto de lote AXT é digestado por SHA-256 sobre a codificação Norito da ligação canônica:

manifest_digest⁡=SHA256⁡(E(canonical_binding⁡)) \operatorname{manifest\_digest} = \operatorname{SHA256}(E(\operatorname{canonical\_binding}))

SCCP Prova de mensagem transparente ​

A caixa auxiliar SCCP também usa FastPQ para provas transparentes de mensagens transversais. Este caminho é separado da faixa de provedor de fundo irohad. Ele constrói um lote FastPQ diretamente a partir de um pacote e manifesto de prova de mensagem SCCP, em seguida, enrola a prova resultante para verificação aberta.

O lote SCCP utiliza o fastpq-lane-balanced e três transições de metadados:

A chave .Operação
sccp:transparent:v1:statementMetaSet
sccp:transparent:v1:contextMetaSet
sccp:transparent:v1:payloadMetaSet

As suas entradas públicas são derivadas da prova interna transparente SCCP:

Introdução FastPQSCCP fonte
dsidOs primeiros 16 bytes de uma digestão Blake2b sobre a declaração hash
slotAltura da finalidade
old_rootHash de carga útil
new_rootRaiz de compromisso
perm_rootO bloqueio de finalidade hash
tx_set_hashHash de declaração

Os codificadores canônicos SCCP escrevem números inteiros pequenos e codificam matrizes de bytes de comprimento variável como:

vec⁡(x)=le32⁡(∣x∣)∥x \operatorname{vec}(x)=\operatorname{le32}(|x|)\|x

A cadeia de byte de entrada pública transparente é:

P=version⁡∥message_id⁡∥payload_hash⁡∥le32⁡(target_domain⁡)∥commitment_root⁡∥le64⁡(finality_height⁡)∥finality_block_hash⁡ P = \operatorname{version}\| \operatorname{message\_id}\| \operatorname{payload\_hash}\| \operatorname{le32}(\operatorname{target\_domain})\| \operatorname{commitment\_root}\| \operatorname{le64}(\operatorname{finality\_height})\| \operatorname{finality\_block\_hash}

Os bytes de declaração transparentes são a concatenação da versão, família de cadeias, domínios locais e contrapartes, modelo de segurança, governança de âncora, codec de conta, modelo de finalidade, alvo do verificador, família de backend do verificador; campos de cadeia/backend/manifesto prefixados em comprimento; hash vinculativo de destino; A chave de codec da conta, tipo de carga útil, bytes de entrada pública e hash da carga útil.

statement_hash⁡=Blake2bVar⁡32(sccp:transparent:statement:v1∥statement⁡) \operatorname{statement\_hash} = \operatorname{Blake2bVar}_{32}( \texttt{sccp:transparent:statement:v1}\|\operatorname{statement} )

O id do espaço de dados FastPQ para este caminho de prova é os primeiros dezesseis bytes de outro digestão Blake2b pré-fixado:

dsid⁡=Blake2bVar⁡32(sccp:transparent:fastpq:dsid:v1∥statement_hash⁡)[0..16] \operatorname{dsid} = \operatorname{Blake2bVar}_{32}( \texttt{sccp:transparent:fastpq:dsid:v1}\|\operatorname{statement\_hash} )[0..16]

O lote de SCCP FastPQ é exatamente:

(sccp:transparent:v1:statement,∅,statement⁡,MetaSet⁡) (\texttt{sccp:transparent:v1:statement},\varnothing,\operatorname{statement},\operatorname{MetaSet})

(sccp:transparent:v1:context,∅,E(inner_proof⁡),MetaSet⁡) (\texttt{sccp:transparent:v1:context},\varnothing,E(\operatorname{inner\_proof}),\operatorname{MetaSet})

(sccp:transparent:v1:payload,∅,canonical_payload⁡,MetaSet⁡) (\texttt{sccp:transparent:v1:payload},\varnothing,\operatorname{canonical\_payload},\operatorname{MetaSet})

em seguida, ordenado pela mesma regra de encomenda FastPQ.

O compromisso com o verificador OpenVerify é SHA-256 em relação ao nome do backend da mensagem SCCP e ao descritivo canônico do verificador FastPQ:

vk_hash⁡=SHA256⁡(message_backend⁡∥verifier_descriptor⁡) \operatorname{vk\_hash} = \operatorname{SHA256}( \operatorname{message\_backend}\|\operatorname{verifier\_descriptor} )

O crudo FastPQ A prova é Norito- codificado em um StarkFriOpenProofV1, em seguida, embrulhado em um OpenVerifyEnvelope com backend Stark. SCCP A verificação reconstrói a mesma FastPQ a colheita do pacote e do manifesto, verifica os metadados do envelope de verificação aberto e chama o FastPQ Verificador e prova do lote reconstruído.

Setos de parâmetros ​

O catálogo dos parâmetros canônicos expõe dois conjuntos de parâmetres. fastpq-lane-balanced.

ParâmetroPropósitoCampoHashesFRI
fastpq-lane-balancedtransmissão de provedor equilibradaGoldilocks extensão quadráticaCompromissos de Poseidon2, catálogo SHA3 etiquetaArity 8, blowup 8, 46 consultas
fastpq-lane-latencyrotas sensíveis à latênciaGoldilocks extensão quadráticaCompromissos de Poseidon2, catálogo SHA3 etiquetaArity 16, explosão 16, 34 consultas

Ambos visam segurança de 128-bit e usam um tamanho de domínio rastreado de 2^16. O código de repetição da transcrição Rust V1 atualmente deriva bytes de desafio Fiat-Shamir com iroha_crypto::Hash::new em vez de invocar diretamente SHA3-256.

As constantes exatas do catálogo utilizadas pelo provador Rust são:

Constantemente .fastpq-lane-balancedfastpq-lane-latency
target_security128128
grinding_bits2321
trace_log_size1616
trace_root0x002a247f81c6f8500x6a9f4eb38fb9b892
lde_log_size1920
lde_root0x60263388dbbf9b2a0x9c9c3a571b6f89ac
permutation_size65,53665,536
lookup_log_size1920
omega_coset0x6af325e825ad5c180x3a5fd4171e3c3a4d
fri_arity816
fri_blowup816
fri_max_reductions86
fri_queries4634

Configuração ​

A configuração FastPQ está inserida em zk.fastpq.

toml
[zk.fastpq]
execution_mode = "auto"
poseidon_mode = "auto"

# Optional telemetry labels.
device_class = "apple-m4"
chip_family = "m4"
gpu_kind = "integrated"

# Optional Metal backend tuning.
metal_queue_fanout = 3
metal_queue_column_threshold = 24
metal_max_in_flight = 5
metal_threadgroup_width = 128
metal_trace = false
metal_debug_enum = false
metal_debug_fused = false

Os mesmos rótulos de execução e telemetria podem ser substituídos a partir do irohad:

shell
irohad --fastpq-execution-mode auto
irohad --fastpq-poseidon-mode cpu
irohad --fastpq-device-class apple-m4
irohad --fastpq-chip-family m4
irohad --fastpq-gpu-kind integrated

As variáveis ambientais são também suportadas para os campos de configuração. FastPQ- as variáveis específicas incluem:

  • FASTPQ_EXECUTION_MODE
  • FASTPQ_POSEIDON_MODE
  • FASTPQ_DEVICE_CLASS
  • FASTPQ_CHIP_FAMILY
  • FASTPQ_GPU_KIND
  • FASTPQ_METAL_QUEUE_FANOUT
  • FASTPQ_METAL_COLUMN_THRESHOLD
  • FASTPQ_METAL_MAX_IN_FLIGHT
  • FASTPQ_METAL_THREADGROUP
  • FASTPQ_METAL_TRACE
  • FASTPQ_DEBUG_METAL_ENUM
  • FASTPQ_DEBUG_FUSED

Métricas ​

Quando a telemetria está habilitada, FastPQ exporta métricas para seleção de backend e comportamento de execução do Metal:

Metrica .Que significa ?
fastpq_execution_mode_totalModo de execução solicitado e resolvido por backend e rótulos do dispositivo
fastpq_poseidon_pipeline_totalPedido e resolvido caminho do oleoduto de Poseidon
fastpq_metal_queue_depthLimite de fila de metal, contagem máxima no voo, número de expedição e janela de amostragem
fastpq_metal_queue_ratioAs filas de metais ocupadas e as proporções de sobreposição
fastpq_zero_fill_duration_msDurada de enchimento zero para corridas de Metal
fastpq_zero_fill_bandwidth_gbpsLargura de banda derivada zero-fill

Para triagem geral de desempenho, utilize-as com os sinais de consenso e filas listados em Performance and Metrics .