STARKs(可擴展透明知識演示)是壹種創建證明的技術,其中f(x)=y,其中f可能需要很長時間來計算,但這個證明可以很快得到驗證。斯塔克是“雙重展開”:對於壹個需要t步的計算,大概需要O(t * log(t))步才能完成這個證明,這可能是最優的情況,需要通過0 ~O(log2(t))來驗證,對於壹個中等大小的t值,比原來的計算要快很多。STARKs還具有隱私保護“零知識證明”的特性。雖然我們把這種用例應用到它上面,完成可驗證的延遲功能,但是我們並不需要這種性質,所以我們不用擔心。
首先,請說明幾件事:
此代碼尚未完全審核;在實際使用案例中,無法保證。
這部分代碼還沒有達到理想狀態(用Python語言寫的)。
STARKs的“真實情況”出於特定的應用效率原因,傾向於使用二進制字段而不是素數字段;然而,它們確實表明這裏編寫的代碼是合法的和可用的。
沒有真正的辦法利用史塔克。這是壹個非常廣泛的加密和數學架構,不同的應用程序有不同的設置,並不斷研究以降低證明者和驗證者的復雜性,同時提高可用性。
本文希望大家能了解模運算和素數域是如何工作的。
它與多項式概念、插值和估計相結合。
現在,讓我們互相認識壹下吧!
MIMC
以下是斯塔克的功能展示:
def mimc(inp,steps,round _ constants):start _ time = time . time()for I in range(steps-1):InP =(InP * * 3+round _ constants,337,85,inv=True) [46,169,29,149,126,262,140,93]>;& gt& gtf = poly_utils素數域(337)>& gt& gt[f.eval_poly_at([46,169,29,149,126,262,140,93],f.exp(85,i)) for i in range(8)] [3,1,4,1,5,9,2,6]
傅立葉變換取[x [0]...x [n-1]]作為輸入,它的目標是輸出x [0]+x [1]+...+x [n-1]作為第壹個元素,而x [0]+x [660]快速傅立葉變換可以做到這壹點,方法是將數據分成兩半,兩邊進行FFT,然後合並結果。
上圖解釋了FFT是如何處理信息的。註意FFT是如何復制數據兩次並粘合它,直到得到壹個元素。
現在,我們把各個部分結合起來,看看整體是怎樣的:def MK _ MIMC _ proof (InP,steps,round _ constants),生成運行MIMC函數的執行結果的證明,其中給定的輸入是步數。首先,有壹些斷言函數:
#計算x坐標的集合xs = get _ power _ cycle(root _ of _ unity,module)column =[]for I in range(len(xs)//4):x _ poly = f . la grange _ interp _ 4([xs[I+len(xs)* j//4]for j in range(4)]values[I+len(values)* j//4]for j in range(4)]column . append(f . eval _ poly _ at(x _ poly,special_x))
展開因子是我們將拉伸的計算軌跡(執行MIMC函數的壹組“中間值”)。
m2 = merkelize(column) #偽隨機選擇要采樣的y個索引# (m2[1]是列的Merkle根)ys = get _偽隨機_索引(m2[1],len(column),40) #計算多項式中值的Merkle分支和ys中y的列分支=[]:branch . append([MK _ branch(m2,y)] + [mk_branch(m,y + (len(xs) // 4) * j) for j
我們需要步驟數乘以展開因子最多是2 ^ 32,因為當k > 32:00時,我們沒有2 k次的單位根。
computational _ trace _多項式= inv_fft(computational_trace,modulus,subroot)p _ evaluations = FFT(computational _ trace _多項式,modulus,root_of_unity)
我們的第壹個計算將是得到計算軌跡;也就是說,所有計算出的中間值,從輸入到輸出。
斷言步驟& lt= 2**32 // extension_factor斷言is_a_power_of_2(steps)和is _ a _ power _ of _ 2(len(round _ constants))斷言len(round _ constants)& lt;步伐
然後,我們將把計算軌跡轉換成多項式,把連續值“放下”在單位根g(其中g步= 1)的連續冪的軌跡上,然後我們將計算單位根g2的連續冪,其中G2步* 8 = 1(註意g2^8 = g)。
#為範圍內的I生成計算跟蹤computational _ trace =[InP](steps-1):computational _ trace . append((computational _ trace[-1]* * 3+round _ constants[I % len(round _ constants)])% module)output = computational _ trace[-1]
黑色:g1動力。紫色:g2的力量。橙色:1。妳可以把連續的單位根想象成這樣排列的圓。我們沿著g1的冪“放置”計算軌跡,然後擴展到計算同壹個多項式在中間值(也就是g2的冪)的值。
我們可以把MIMC的循環常數轉換成多項式。因為這些循環常數鏈出現的頻率非常高(在我們的測試中每64步出現壹次),所以最終證明它們形成了64階的多項式,並且它的表達式和展開式可以很容易地在外面計算出來:
skips2 = steps//len(round _ constants)constants _ mini _多項式= fft(round_constants,modulus,f.exp(subroot,skips2),inv = True)constants _多項式=[0 if I % skip S2 else constants _ mini _多項式[I//skip S2]for I in range(steps)]constants _ mini _ extension = FFT(constants _ mini _多項式,modulus,f.exp(root_of_unity,skip S2))
假設有8192步,64個循環常數。這就是我們想要做的:我們正在做FFT來計算作為g1128的函數的循環常數。然後我們在它們之間加很多零,完成g1本身的功能。因為g1128大約每64步循環壹次,所以我們知道g1的功能也是壹樣的。這個擴展我們只統計512步,因為我們知道這個擴展每512步之後就會重復壹次。現在,我們像Fibonacci的情況壹樣計算C(P(x)),除了這壹次,需要註意的是,我們不是以系數的形式計算多項式;而是根據高階單位根的連續冪來計算多項式。
P的C _ of _需要滿足Q(x) = C(P(x),P(g1*x),k(x)= P(g 1 * x)-P(x)* * 3–k(x);目標是對於我們放入計算軌道的任意x(除了最後壹步,因為最後壹步之後沒有下壹步),計算軌道中的下壹個值等於上壹個值,加上循環常數。與Fibonacci在第1部分中的例子不同,其中如果壹個計算步驟在K向量中,則下壹個將是k+1向量,我們將低階單位根(g1)的連續冪放在計算軌跡中,因此如果壹個計算步驟在x = g1i中,則下壹個將在g1i中。因此,對於低階單位根(g1)的每壹次冪,我們希望最終都是P(x*g1) = P(x)**3+K(x),或者P(x * g 1)-P(x)* * 3——因此,Q(x)在低階單位根g的所有連續次冪上都等於零(最後壹次除外)。
#創建復合多項式使得# C(P(x),P(g1*x),K(x))= P(g 1 * x)-P(x)* * 3-K _ of _ P _ evaluations =[(P _ evaluations[(I+extension _ factor)% precision]-f . exp(P _ evaluations[I],3)-constants _ mini _ extension[I % len(constants _ mini _ extension)])%範圍內I的模(precision)] print('計算的C(P,K
壹個代數定理證明,如果Q(x)在所有這些x坐標中都等於零,那麽最小多項式的乘積在所有這些x坐標中都等於零:z(x)=(x–x _ 1)*(x–x _ 2)*…*(x–x _ n)。通過證明Q(x)在任意單個坐標上等於零,我們想要證明是非常困難的,因為驗證這樣壹個證明比運行原始計算需要更長的時間,我們將用間接的方式證明Q(x)是Z(x)的乘積。我們要做什麽?通過證明D(x) = Q(x)/Z(x),用FRI證明其實是多項式,不是分數。
我們選擇低階單位根和高階單位根的具體排列,因為事實證明,計算Z(x)並除以Z(x)非常簡單:Z的表達式是兩項的壹部分。
需要註意的是,Z的分子和分母是直接計算出來的,然後利用批量模逆的方法將Z的除法轉化為乘法,再將Q(x)的值逐點乘以Z(X)的逆。需要註意的是,除了最後壹個,低階單位根的冪都可以得到Z(x) = 0,所以如果包含它的逆計算,這個計算就會被中斷。這是非常不幸的,雖然我們會通過簡單修改隨機檢查和FRI算法來堵住這個漏洞,所以即使我們計算出錯也沒關系。
因為Z(x)可以簡潔地表示,所以我們還可以得到另壹個好處:驗證者可以快速計算出任意特殊x的Z(x ),而不需要事先做任何計算。對於證明者來說,我們可以接受證明者必須處理大小等於步數的多項式,但是我們不希望驗證者做同樣的事情,因為我們希望驗證過程足夠簡潔。
# compute d(x)= q(x)/z(x)# z(x)=(x^steps-1)/(x-x _ atlast _ step)z _ num _ evaluations =[xs[(I *步數)%精度] - 1 for i in range(精度)]z _ num _ inv = f . multi _ inv(z _ num _ evaluations)z _ den _ evaluations =[xs[I]-last _ step _ position for I in range(精度)]d _ evaluations =[CP * z
在幾個隨機點上進行概念檢測D(x) * Z(x) = Q(x),這樣就可以驗證轉移約束,每壹步計算都是前壹步的有效結果。但是我們還想驗證邊界約束,其中計算的輸入和輸出就是證明者所說的。只要求證明者提供P (1)、D (1)、P (last _ step)和D(last_step)的值,這些值非常脆弱;沒有證據表明這些值都在同壹個多項式中。因此,我們使用類似的多項式除法技術:
# Compute interpolant of ((1,input),(x_atlast_step,output))interpolant = f . la grange _ interp _ 2([1,last_step_position],[inp,output])I _ evaluations =[f . eval _ poly _ at(interpolant,x)for x in xs]zero poly 2 = f . mul _ polys([-1,1],[-last_step_position
所以,我們的論點如下。證明者想證明P(1) ==輸入,P(last_step) ==輸出。如果我們取I(x)為插值,則是經過(1,input)和(last_step,output)亮點的直線,所以P(x)–I(x)在這個亮點會等於零。所以會證明P(x)-I(x)是P(x)-I(x)的乘積,我們會通過提高商來證明這壹點。
紫色:計算軌跡多項式(P)。綠色:插值(I)(註意插值是如何構造的,等於在x = 1處輸入(應該是計算軌跡的第壹步),在x = g處輸出(steps-1)(應該是計算軌跡的最後壹步)。紅色:p-I .黃色:x = 1,x = g(步數-1)處等於0的最小多項式(即Z2)。粉色:(p–I)/z2。
現在,我們來看看P,D,b的默克爾根。
現在我們需要證明P,D,B其實是多項式,是最大正確階。但是FRI證明又大又貴,我們又不想要三個FRI證明,所以我們計算P,D,B的偽隨機線性組合,並在此基礎上進行FRI證明:
#計算它們的Merkle根MTree = merkelize([pval . to _ bytes(32,' big') + dval.to_bytes(32,' big') + bval.to_bytes(32,' big') for pval,dval,bval in zip(p_evaluations,d_evaluations,b _ evaluations)])print(' Computed hash root ')
除非三個多項式都有正確的低階,否則幾乎不可能有隨機選擇的線性組合,所以這就足夠了。
我們想證明D的階小於2 *步,P和B的階小於步,所以我們實際上用的是P,P * x步,B,B步,D的隨機組合,可以看到這部分組合小於2 *步。
現在,讓我們檢查所有的多項式組合。我們首先得到許多隨機指標,然後提供這些指標上的默克爾分支的多項式:
k 1 = int . from _ bytes(Blake(MTree[1]+b ' \ x 01 ',' big ')k2 = int . from _ bytes(Blake(MTree[1]+b ' \ x02 ',' big ')k3 = int . from _ bytes(Blake(MTree[1]+b ' \ x03 ',' big ')k4 = int . from _ bytes(Blake(MTree[65438我們甚至懶得用系數形式計算它;我們只需計算範圍內I的求值root _ of _ unity _ to _ the _ steps = f . exp(root _ of _ unity,steps) powers = [1],precision):powers . append(powers[-1]* root _ of _ unity _ to _ the _ steps % module)l _ evaluations =[(d _ evaluations[I]+p _ evaluations[I]* k 1+p _ evaluations[I]* k2 * powers[I
get _ pseudo random _ indexes函數返回[0…precision-1]範圍內的隨機索引,exclude_multiples_of參數不給出特定參數倍數的值。這保證了我們不會沿著原來的計算軌跡采樣,否則會得到錯誤的答案。
該證明由壹組默克爾根、隨機分支和隨機線性組合的低階證明組成:
#在偽隨機坐標處對Merkle樹進行壹些抽查,排除#倍數的` extension _ factor ` branches =[]samples = spot _ check _ security _ factor positions = get _偽隨機_ indexes(l _ MTree[1],precision,samples,exclude _ multiples _ of = extension _ factor)中的位置:branches . append(MK _ branch(MTree,pos))branches . append(MK _ branch(MTree,(pos+skips)%)%)branches . append(MK _ branch(l _ MTree,pos))
整個證明中最長的部分是默克爾樹的分支,還有FRI證明,它是由更多的分支組成的。這是驗證器的重要結果:
o = [mtree[1],l_mtree[1],branches,prove_low_degree(l_evaluations,root_of_unity,steps * 2,modulus,exclude _ multiples _ of = extension _ factor)]
在每個位置,證明者都需要提供壹個默克爾證明,這樣驗證者就可以檢查這個默克爾證明,檢查C(P(x),P(g1*x),K(x) = Z(x) * D (X)和B (X) * Z2 (X)+I (X)。驗證器還將檢查線性組合是否正確,然後調用。
對於I,pos in enumerate(positions):x = f . exp(G2,pos) x_to_the_steps = f.exp(x,steps)mbranch 1 = verify _ branch(m _ root,pos,branch[I * 3])mbranch 2 = verify _ branch(m _ root,(pos+skips)%precision,branch[I * 3+1])l _ of _ x = verify _ branch(l _ root,pos,branch[I * 3+2],output _ as _ int = skips2)) # Check轉換約束Q(x)= Z(x)* D(x)assert(P _ of _ g 1x-P _ of _ x * * 3-k _ of _ x-Z value * D _ of _ x)% module = = 0 # Check邊界約束B(x) * Z2(x) + I(x) = P(x)插值函數= f.lagrange_interp_2([1,last_step_position]),[inp,output)zero poly 2
其實並沒有成功完成;證明了交叉多項式檢驗和FRI檢驗所需抽查次數的可靠性分析是非常困難的。但這是全部代碼,至少不用擔心瘋狂優化。當我運行上面的代碼時,我們將得到STARK證明,這將花費證明的300-400倍(例如,壹個需要0.2秒的MIMC計算需要60秒來證明)。這使得在4核機器上計算MIMC的STARK實際上比逆向計算MIMC要快。也就是說,在python語言中,這是實現起來相對低效的,而且還會證明運行時間比例會有所不同。同時,還值得指出的是,MIMC的斯塔克證明的成本非常低,因為MIMC幾乎是完全可計算的,而且其數學形式非常簡單。對於平均值的計算,會有不太明確的計算(比如檢查壹個數是大於還是小於另壹個數),它的計算成本可能更高,大概會是10000-50000次。