Creazione di un sistema di verificazione formale da zero. Parte 1: VM di caratteri in PHP è Python

A verificazione formale hè a verificazione di un prugramma o algoritmu cù l'aiutu di un altru.

Questu hè unu di i metudi più putenti chì vi permette di truvà tutte e vulnerabilità in u prugramma o pruvà chì ùn esiste micca.

Una descrizzione più dettagliata di a verificazione formale pò esse vistu in l'esempiu di risolve u prublema di Lupu, caprettu è col in u mo articulu precedente.

In questu articulu, aghju passatu da a verificazione formale di u prublema à i prugrammi è discrivu cumu
cumu pudete cunvertisce in sistemi di regule formale automaticamente.

Per fà questu, aghju scrittu u mo analogu di una macchina virtuale, nantu à i principii simbolichi.

Analizeghja u codice di u prugramma è u traduce in un sistema di equazioni (SMT), chì pò esse digià risoltu in modu programmaticu.

Siccomu l'infurmazioni nantu à i calculi simbolichi sò presentati in Internet piuttostu fragmentariamente,
Descriveraghju brevemente ciò chì hè.

I calculi simbolichi sò un modu per eseguisce simultaneamente un prugramma nantu à una larga gamma di dati è sò u principale strumentu per a verificazione formale di u prugramma.

Per esempiu, pudemu stabilisce e cundizioni di input induve u primu argumentu pò piglià qualsiasi valore pusitivu, u sicondu pò esse negativu, u terzu pò esse zero, è l'argumentu di output hè, per esempiu, 42.

I calculi simbolichi in una corsa ci daranu una risposta s'ellu hè pussibule per noi di ottene u risultatu desideratu è un esempiu di un settore di tali paràmetri di input. O prova chì ùn ci sò micca tali paràmetri.

Inoltre, pudemu stabilisce l'argumenti di input in generale cum'è tutti i pussibuli, è sceglie solu l'output, per esempiu, a password di l'amministratore.

In questu casu, truveremu tutte e vulnerabilità di u prugramma, o avemu a prova chì a password di l'amministratore hè sicura.

Pò esse vistu chì l'esekzione classica di un prugramma cù dati di input specifichi hè un casu speciale di un simbolicu.

Dunque, u mo caratteru VM pò ancu travaglià in u modu di emulazione di una macchina virtuale standard.

In i cumenti à l'articulu precedente, si pò ancu truvà una critica ghjusta di a verificazione formale cù una discussione di e so debule.

I prublemi principali sò:

  1. L'esplosione cumminatoria, postu chì a verificazione formale eventualmente si basa nantu à P = NP
  2. A gestione di e chjama à u sistema di fugliali, rete è altre almacenamentu esternu hè più difficiuli di verificà
  3. Bugs in a specificazione, quandu u cliente o u programatore hà cuncipitu una cosa, ma ùn l'hà micca descrittu accuratamente in u TOR.

In u risultatu, u prugramma serà verificatu è rispettu à a specificazione, ma farà una cosa completamente diversa da ciò chì i creatori aspittàvanu da ellu.

Siccomu in questu articulu cunsiderà principarmenti l'applicazione di a verificazione formale in a pratica, ùn aghju micca battutu a mo fronte contr'à u muru per avà, è sceglite un sistema induve a cumplessità algoritmica è u numeru di chjamati esterni sò minimi.

Siccomu i cuntratti intelligenti si adattanu à queste esigenze in u megliu modu, a scelta hè cascata nantu à i cuntratti RIDE da a piattaforma Waves: ùn sò micca Turing-complete, è a so cumplessità massima hè limitata artificialmente.

Ma li cunsideremu solu da u latu tecnicu.

In più di tuttu, per qualsiasi cuntratti, a verificazione formale serà soprattuttu in a dumanda: in regula, hè impussibile di correggere un errore di cuntrattu dopu chì hè stata lanciata.
È u prezzu di tali errori pò esse assai altu, postu chì quantità abbastanza grande di fondi sò spessu almacenati in cuntratti intelligenti.

U mo caratteru VM hè scrittu in PHP è Python, è usa Z3Prover di Microsoft Research per risolve e formule SMT resultanti.

À u so core hè una ricerca multi-transazzione putente, chì
permette di truvà suluzioni o vulnerabilità, ancu s'ellu ci vole parechje transazzione.
Ancu i Mitrilu, unu di i frameworks di caratteri più putenti per truvà e vulnerabilità Ethereu, solu aghjustatu sta capacità uni pochi mesi fà.

Ma vale a pena nutà chì i cuntratti etere sò più cumplessi è anu Turing-completezza.

PHP traduce u codice fonte di u cuntrattu intelligenti RIDE in un script python, in quale u prugramma hè presentatu cum'è un sistema Z3 SMT-compatibile di stati cuntratti è e so cundizioni di transizione:

Creazione di un sistema di verificazione formale da zero. Parte 1: VM di caratteri in PHP è Python

Avà descriveraghju ciò chì succede à l'internu, in più dettagliu.

Ma prima, uni pochi di parolle nantu à a lingua RIDE smart contract.

Hè una lingua di prugrammazione funzionale è basata in l'espressione chì hè pigra da u disignu.
RIDE funziona in isolamentu in u blockchain, è pò estrae è scrive infurmazioni da u almacenamentu assuciatu cù a billetera di l'utilizatore.

Un cuntrattu RIDE pò esse attaccatu à ogni billetera, è u risultatu di l'esekzione serà solu TRUE o FALSE.

TRUE significa chì u cuntrattu intelligente permette a transazzione, è FALSE chì pruibisce.
Un esempiu simplice: u script pò pruibisce u trasferimentu se u saldo di a billetera hè menu di 100.

Cum'è un esempiu, vi piglià u listessu lupu, capra, è col, ma digià prisentatu in forma di un cuntrattu intelligenti.

L'utilizatore ùn serà micca capaci di ritirà soldi da a billetera nantu à quale u cuntrattu hè implementatu finu à ch'ellu manda à tutti à l'altra parte.

#Извлекаем положение всех объектов из блокчейна
let contract = tx.sender
let human= extract(getInteger(contract,"human"))
let wolf= extract(getInteger(contract,"wolf"))
let goat= extract(getInteger(contract,"goat"))
let cabbage= extract(getInteger(contract,"cabbage"))

#Это так называемая дата-транзакция, в которой пользователь присылает новые 4 переменные.
#Контракт разрешит её только в случае если все объекты останутся в сохранности.
match tx {
case t:DataTransaction =>
   #Извлекаем будущее положение всех объектов из транзакции
   let newHuman= extract(getInteger(t.data,"human")) 
   let newWolf= extract(getInteger(t.data,"wolf"))
   let newGoat= extract(getInteger(t.data,"goat"))
   let newCabbage= extract(getInteger(t.data,"cabbage"))
   
   #0 обозначает, что объект на левом берегу, а 1 что на правом
   let humanSide= human == 0 || human == 1
   let wolfSide= wolf == 0 || wolf == 1
   let goatSide= goat == 0 || goat == 1
   let cabbageSide= cabbage == 0 || cabbage == 1
   let side= humanSide && wolfSide && goatSide && cabbageSide

   #Будут разрешены только те транзакции, где с козой никого нет в отсутствии фермера.
   let safeAlone= newGoat != newWolf && newGoat != newCabbage
   let safe= safeAlone || newGoat == newHuman
   let humanTravel= human != newHuman 

   #Способы путешествия фермера туда и обратно, с кем-то либо в одиночку.
   let t1= humanTravel && newWolf == wolf + 1 && newGoat == goat && newCabbage == cabbage 
   let t2= humanTravel && newWolf == wolf && newGoat == goat + 1 && newCabbage == cabbage
   let t3= humanTravel && newWolf == wolf && newGoat == goat && newCabbage == cabbage + 1
   let t4= humanTravel && newWolf == wolf - 1 && newGoat == goat && newCabbage == cabbage
   let t5= humanTravel && newWolf == wolf && newGoat == goat - 1 && newCabbage == cabbage
   let t6= humanTravel && newWolf == wolf && newGoat == goat && newCabbage == cabbage - 1
   let t7= humanTravel && newWolf == wolf && newGoat == goat && newCabbage == cabbage
   let objectTravel = t1 || t2 || t3 || t4 || t5 || t6 || t7
   
   #Последняя строка в разделе транзакции описывает разрешающее транзакцию условие.
   #Переменные транзакции должны иметь значения 1 или 0, все объекты должны
   #быть в безопасности, а фермер должен переплывать реку в одиночку 
   #или с кем-то на каждом шагу
   side && safe && humanTravel && objectTravel
case s:TransferTransaction =>
   #Транзакция вывода средств разрешена только в случае если все переплыли на другой берег
   human == 1 && wolf == 1 && goat == 1 && cabbage == 1

#Все прочие типы транзакций запрещены
case _ => false

}

PHP prima estrae tutte e variàbili da u cuntrattu intelligente in a forma di e so chjave è l'espressione booleana currispundente.

cabbage: extract ( getInteger ( contract , "cabbage" ) )
goat: extract ( getInteger ( contract , "goat" ) )
human: extract ( getInteger ( contract , "human" ) )
wolf: extract ( getInteger ( contract , "wolf" ) )
fState: human== 1 && wolf== 1 && goat== 1 && cabbage== 1
fState: 
wolf: 
goat: 
cabbage: 
cabbageSide: cabbage== 0 || cabbage== 1
human: extract ( getInteger ( contract , "human" ) )
newGoat: extract ( getInteger ( t.data , "goat" ) )
newHuman: extract ( getInteger ( t.data , "human" ) )
goatSide: goat== 0 || goat== 1
humanSide: human== 0 || human== 1
t7: humanTravel && newWolf== wolf && newGoat== goat && newCabbage== cabbage
t3: humanTravel && newWolf== wolf && newGoat== goat && newCabbage== cabbage + 1
t6: humanTravel && newWolf== wolf && newGoat== goat && newCabbage== cabbage - 1
t2: humanTravel && newWolf== wolf && newGoat== goat + 1 && newCabbage== cabbage
t5: humanTravel && newWolf== wolf && newGoat== goat - 1 && newCabbage== cabbage
t1: humanTravel && newWolf== wolf + 1 && newGoat== goat && newCabbage== cabbage
t4: humanTravel && newWolf== wolf - 1 && newGoat== goat && newCabbage== cabbage
safeAlone: newGoat != newWolf && newGoat != newCabbage
wolfSide: wolf== 0 || wolf== 1
humanTravel: human != newHuman
side: humanSide && wolfSide && goatSide && cabbageSide
safe: safeAlone || newGoat== newHuman
objectTravel: t1 || t2 || t3 || t4 || t5 || t6 || t7

PHP poi li cunvertisce in una discrizzione di u sistema python compatible Z3Prover SMT.
I dati sò impannillati in un ciclu, induve e variabili di almacenamento ricevenu l'indice i, l'indici di transazzione i + 1, è e variàbili cù espressioni stabiliscenu e regule per a transizione da u statu precedente à u prossimu.

Questu hè u core di a nostra macchina virtuale, chì furnisce un mutore di ricerca multi-transazzione.

fState:  And( And( And( human[Steps]  ==  1 , wolf[Steps]  ==  1 )  , goat[Steps]  ==  1 )  , cabbage[Steps]  ==  1 )  
final:  fState[Steps] 
fState:   
wolf:   
goat:   
cabbage:   
cabbageSide:  Or( cabbage[i]  ==  0 , cabbage[i]  ==  1 )  
goatSide:  Or( goat[i]  ==  0 , goat[i]  ==  1 )  
humanSide:  Or( human[i]  ==  0 , human[i]  ==  1 )  
t7:  And( And( And( humanTravel[i] , wolf  ==  wolf[i] )  , goat[i+1]  ==  goat[i] )  , cabbage  ==  cabbage[i] )  
t3:  And( And( And( humanTravel[i] , wolf  ==  wolf[i] )  , goat[i+1]  ==  goat[i] )  , cabbage  ==  cabbage[i] + 1 )  
t6:  And( And( And( humanTravel[i] , wolf  ==  wolf[i] )  , goat[i+1]  ==  goat[i] )  , cabbage  ==  cabbage[i] - 1 )  
t2:  And( And( And( humanTravel[i] , wolf  ==  wolf[i] )  , goat[i+1]  ==  goat[i] + 1 )  , cabbage  ==  cabbage[i] )  
t5:  And( And( And( humanTravel[i] , wolf  ==  wolf[i] )  , goat[i+1]  ==  goat[i] - 1 )  , cabbage  ==  cabbage[i] )  
t1:  And( And( And( humanTravel[i] , wolf  ==  wolf[i] + 1 )  , goat[i+1]  ==  goat[i] )  , cabbage  ==  cabbage[i] )  
t4:  And( And( And( humanTravel[i] , wolf  ==  wolf[i] - 1 )  , goat[i+1]  ==  goat[i] )  , cabbage  ==  cabbage[i] )  
safeAlone:  And( goat[i+1] != wolf , goat[i+1] != cabbage )  
wolfSide:  Or( wolf[i]  ==  0 , wolf[i]  ==  1 )  
humanTravel:  human[i] != human[i+1] 
side:  And( And( And( humanSide[i] , wolfSide[i] )  , goatSide[i] )  , cabbageSide[i] )  
safe:  Or( safeAlone[i] , goat[i+1]  ==  human[i+1] )  
objectTravel:  Or( Or( Or( Or( Or( Or( t1[i] , t2[i] )  , t3[i] )  , t4[i] )  , t5[i] )  , t6[i] )  , t7[i] )  
data:  And( And( And( side[i] , safe[i] )  , humanTravel[i] )  , objectTravel[i] )  

I cundizzioni sò ordinati è inseriti in un mudellu di scrittura cuncepitu per descriverà u sistema SMT in python.

Template viotu


import json

from z3 import *

s = Solver()

  
  
    
Steps=7
Num= Steps+1

$code$



#template, only start rest
s.add(data + start)

#template
s.add(final)




ind = 0

f = open("/var/www/html/all/bin/python/log.txt", "a")



while s.check() == sat:
  ind = ind +1
  

  print ind
  m = s.model()
  print m

  print "traversing model..." 
  #for d in m.decls():
	#print "%s = %s" % (d.name(), m[d])

  
 
  f.write(str(m))
  f.write("nn")
  exit()
  #s.add(Or(goat[0] != s.model()[data[0]] )) # prevent next model from using the same assignment as a previous model



print "Total solution number: "
print ind  

f.close()
 


Per l'ultimu statu di a catena sana, i reguli chì sò stabiliti in a seccione di transazzione di trasferimentu sò applicati.

Questu significa chì Z3Prover cercherà esattamente tali setti di stati chì vi permettenu in fine di ritirà fondi da u cuntrattu.

In u risultatu, avemu automaticamente un mudellu SMT cumplettamente funziunale di u nostru cuntrattu.
Pudete vede chì hè assai simili à u mudellu da u mo articulu precedente, chì aghju compilatu a manu.

Template finitu


import json

from z3 import *

s = Solver()

  
  
    
Steps=7
Num= Steps+1

human = [ Int('human_%i' % (i + 1)) for i in range(Num) ]
wolf = [ Int('wolf_%i' % (i + 1)) for i in range(Num) ]
goat = [ Int('goat_%i' % (i + 1)) for i in range(Num) ]
cabbage = [ Int('cabbage_%i' % (i + 1)) for i in range(Num) ]
nothing= [  And( human[i] == human[i+1], wolf[i] == wolf[i+1], goat[i] == goat[i+1], cabbage[i] == cabbage[i+1] )   for i in range(Num-1) ]


start= [ human[0] == 1, wolf[0] == 0, goat[0] == 1, cabbage[0] == 0 ]

safeAlone= [  And( goat[i+1] != wolf[i+1] , goat[i+1] != cabbage[i+1] )   for i in range(Num-1) ]
safe= [  Or( safeAlone[i] , goat[i+1]  ==  human[i+1] )   for i in range(Num-1) ]
humanTravel= [  human[i] != human[i+1]  for i in range(Num-1) ]
cabbageSide= [  Or( cabbage[i]  ==  0 , cabbage[i]  ==  1 )   for i in range(Num-1) ]
goatSide= [  Or( goat[i]  ==  0 , goat[i]  ==  1 )   for i in range(Num-1) ]
humanSide= [  Or( human[i]  ==  0 , human[i]  ==  1 )   for i in range(Num-1) ]
t7= [  And( And( And( humanTravel[i] , wolf[i+1]  ==  wolf[i] )  , goat[i+1]  ==  goat[i] )  , cabbage[i+1]  ==  cabbage[i] )   for i in range(Num-1) ]
t3= [  And( And( And( humanTravel[i] , wolf[i+1]  ==  wolf[i] )  , goat[i+1]  ==  goat[i] )  , cabbage[i+1]  ==  cabbage[i] + 1 )   for i in range(Num-1) ]
t6= [  And( And( And( humanTravel[i] , wolf[i+1]  ==  wolf[i] )  , goat[i+1]  ==  goat[i] )  , cabbage[i+1]  ==  cabbage[i] - 1 )   for i in range(Num-1) ]
t2= [  And( And( And( humanTravel[i] , wolf[i+1]  ==  wolf[i] )  , goat[i+1]  ==  goat[i] + 1 )  , cabbage[i+1]  ==  cabbage[i] )   for i in range(Num-1) ]
t5= [  And( And( And( humanTravel[i] , wolf[i+1]  ==  wolf[i] )  , goat[i+1]  ==  goat[i] - 1 )  , cabbage[i+1]  ==  cabbage[i] )   for i in range(Num-1) ]
t1= [  And( And( And( humanTravel[i] , wolf[i+1]  ==  wolf[i] + 1 )  , goat[i+1]  ==  goat[i] )  , cabbage[i+1]  ==  cabbage[i] )   for i in range(Num-1) ]
t4= [  And( And( And( humanTravel[i] , wolf[i+1]  ==  wolf[i] - 1 )  , goat[i+1]  ==  goat[i] )  , cabbage[i+1]  ==  cabbage[i] )   for i in range(Num-1) ]
wolfSide= [  Or( wolf[i]  ==  0 , wolf[i]  ==  1 )   for i in range(Num-1) ]
side= [  And( And( And( humanSide[i] , wolfSide[i] )  , goatSide[i] )  , cabbageSide[i] )   for i in range(Num-1) ]
objectTravel= [  Or( Or( Or( Or( Or( Or( t1[i] , t2[i] )  , t3[i] )  , t4[i] )  , t5[i] )  , t6[i] )  , t7[i] )   for i in range(Num-1) ]
data= [ Or(  And( And( And( side[i] , safe[i] )  , humanTravel[i] )  , objectTravel[i] )   , nothing[i]) for i in range(Num-1) ]


fState=  And( And( And( human[Steps]  ==  1 , wolf[Steps]  ==  1 )  , goat[Steps]  ==  1 )  , cabbage[Steps]  ==  1 )  
final=  fState 




#template, only start rest
s.add(data + start)

#template
s.add(final)




ind = 0

f = open("/var/www/html/all/bin/python/log.txt", "a")



while s.check() == sat:
  ind = ind +1
  

  print ind
  m = s.model()
  print m

  print "traversing model..." 
  #for d in m.decls():
	#print "%s = %s" % (d.name(), m[d])

  
 
  f.write(str(m))
  f.write("nn")
  exit()
  #s.add(Or(goat[0] != s.model()[data[0]] )) # prevent next model from using the same assignment as a previous model



print "Total solution number: "
print ind  

f.close()
 


Dopu à u lanciu, Z3Prover risolve u cuntrattu intelligente è mostra una catena di transazzione per noi, chì ci permetterà di ritirà fondi:

Winning transaction chain found:
Data transaction: human= 0, wolf= 0, goat= 1, cabbage= 0
Data transaction: human= 1, wolf= 0, goat= 1, cabbage= 1
Data transaction: human= 0, wolf= 0, goat= 0, cabbage= 1
Data transaction: human= 1, wolf= 1, goat= 0, cabbage= 1
Data transaction: human= 0, wolf= 1, goat= 0, cabbage= 1
Data transaction: human= 1, wolf= 1, goat= 1, cabbage= 1
Data transaction: human= 1, wolf= 1, goat= 1, cabbage= 1
Transfer transaction

In più di u cuntrattu di traversu, pudete sperimentà cù i vostri cuntratti o pruvate stu esempiu simplice, chì hè risoltu in transazzione 2.

let contract = tx.sender
let a= extract(getInteger(contract,"a"))
let b= extract(getInteger(contract,"b"))
let c= extract(getInteger(contract,"c"))
let d= extract(getInteger(contract,"d"))

match tx {
case t:DataTransaction =>
let na= extract(getInteger(t.data,"a")) 
let nb= extract(getInteger(t.data,"b"))
let nc= extract(getInteger(t.data,"c"))
let nd= extract(getInteger(t.data,"d"))
   
   nd == 0 || a == 100 - 5
case s:TransferTransaction =>
   ( a + b - c ) * d == 12

case _ => true

}

Siccomu questa hè a prima versione, a sintassi hè assai limitata è pò esse bugs.
In i prossimi articuli, aghju pensatu à copre u sviluppu ulteriore di a VM, è dimustrà cumu pudete creà cuntratti intelligenti verificati formalmente cun ellu, è micca solu risolve.

A macchina virtuale di caratteri hè dispunibule à http://2.59.42.98/hyperbox/
Dopu avè purtatu u codice fonte di u caratteru VM in ordine è aghjunghje cumenti quì, pensa à mette in github in accessu liberu.

Source: www.habr.com

Add a comment