It meitsjen fan in formeel ferifikaasjesysteem fanôf it begjin. Diel 1: Virtuele masine foar karakter yn PHP en Python

Formele ferifikaasje is de ferifikaasje fan ien programma of algoritme mei in oar.

Dit is ien fan 'e machtichste metoaden wêrmei jo alle kwetsberens yn in programma kinne fine of bewize dat se net bestean.

In mear detaillearre beskriuwing fan formele ferifikaasje kin sjoen wurde yn it foarbyld fan it oplossen fan it probleem fan Wolf, geit en koal yn myn foarige artikel.

Yn dit artikel gean ik fan formele ferifikaasje fan problemen nei programma's en beskriuw ik hoe
hoe kinne se wurde omsetten yn formele regel systemen automatysk.

Om dit te dwaan skreau ik myn eigen analoog fan in firtuele masine, mei symboalyske prinsipes.

It parseart de programmakoade en fertaalt it yn in systeem fan fergelikingen (SMT), dat al programmatysk oplost wurde kin.

Sûnt ynformaasje oer symboalyske berekkeningen wurdt presintearre op it ynternet nochal fragmintarysk,
Ik sil koart beskriuwe wat it is.

Symboalyske berekkening is in manier om tagelyk in programma út te fieren op in breed skala oan gegevens en is it wichtichste ark foar formele programmaferifikaasje.

Wy kinne bygelyks ynfierbetingsten ynstelle wêrby't it earste argumint alle positive wearden kin nimme, it twadde negatyf, it tredde nul, en it útfierargumint, bygelyks 42.

Symboalyske berekkeningen yn ien run jouwe ús it antwurd op oft it mooglik is foar ús te krijen it winske resultaat en in foarbyld fan in set fan sokke ynfier parameters. Of bewiis dat d'r gjin sokke parameters binne.

Boppedat kinne wy ​​de ynfier-arguminten op alle mooglike ynstelle, en allinich de útfier selektearje, bygelyks it behearderwachtwurd.

Yn dit gefal sille wy alle kwetsberens fan it programma fine of bewiis krije dat it wachtwurd fan de behearder feilich is.

It kin opmurken wurde dat de klassike útfiering fan in programma mei spesifike ynfiergegevens in spesjaal gefal is fan symboalyske útfiering.

Dêrom kin myn karakter VM ek wurkje yn emulaasjemodus fan in standert firtuele masine.

Yn 'e kommentaren nei it foarige artikel kin men earlike krityk fine op formele ferifikaasje mei in diskusje oer syn swakkens.

De wichtichste problemen binne:

  1. Combinatorial eksploazje, sûnt formele ferifikaasje komt úteinlik del op P = NP
  2. It ferwurkjen fan petearen nei it bestânsysteem, netwurken en oare eksterne opslach is dreger te ferifiearjen
  3. Bugs yn 'e spesifikaasje, doe't de klant of programmeur ien ding bedoelde, mar it net genôch genôch beskreau yn' e technyske spesifikaasje.

As resultaat sil it programma wurde ferifiearre en foldogge oan 'e spesifikaasje, mar sil wat folslein oars dwaan as wat de makkers derfan ferwachte.

Om't ik yn dit artikel benammen it gebrûk fan formele ferifikaasje yn 'e praktyk beskôgje, sil ik no myn holle net tsjin 'e muorre slaan, en sil ik in systeem kieze wêr't de algoritmyske kompleksiteit en it oantal eksterne petearen minimaal binne.

Sûnt tûke kontrakten it bêste passe by dizze easken, foel de kar op RIDE-kontrakten fan it Waves-platfoarm: se binne net Turing folslein, en har maksimale kompleksiteit is keunstmjittich beheind.

Mar wy sille beskôgje se allinnich út de technyske kant.

Neist alles sil formele ferifikaasje fral yn 'e fraach wêze foar alle kontrakten: it is normaal ûnmooglik om in kontraktflater te korrigearjen nei't it is lansearre.
En de kosten fan sokke flaters kinne tige heech wêze, om't frij grutte bedraggen fan fûnsen faak opslein wurde op tûke kontrakten.

Myn symboalyske firtuele masine is skreaun yn PHP en Python, en brûkt Z3Prover fan Microsoft Research om de resultearjende SMT-formules op te lossen.

Yn syn kearn is in krêftige multi-transactional sykopdracht, dy't
kinne jo fine oplossings of kwetsberens, sels as it fereasket in protte transaksjes.
Sels it Mythril, ien fan 'e machtichste symboalyske kaders foar it finen fan Ethereum-kwetsberens, hat dizze mooglikheid allinich in pear moannen lyn tafoege.

Mar it is de muoite wurdich opskriuwen dat eter kontrakten binne komplekser en Turing kompleet.

PHP fertaalt de boarnekoade fan it RIDE smart kontrakt yn in python skript, wêryn it programma wurdt presintearre as in Z3 SMT-kompatibel systeem fan kontrakt steaten en betingsten foar harren transysjes:

It meitsjen fan in formeel ferifikaasjesysteem fanôf it begjin. Diel 1: Virtuele masine foar karakter yn PHP en Python

No sil ik yn mear detail beskriuwe wat der binnen bart.

Mar earst in pear wurden oer de RIDE-tûke kontrakttaal.

It is in funksjonele en ekspresje-basearre programmeartaal dy't lui is troch ûntwerp.
RIDE rint yn isolemint binnen de blockchain en kin ynformaasje ophelje en skriuwe fan opslach dy't keppele is oan de brûker syn portemonnee.

Jo kinne in RIDE-kontrakt oan elke beurs heakje, en it resultaat fan útfiering sil allinich TRUE of FALSE wêze.

TRUE betsjut dat it tûke kontrakt de transaksje lit, en FALSE betsjut dat it it ferbiedt.
In ienfâldich foarbyld: in skript kin in oerdracht ferbiede as it saldo fan 'e wallet minder is dan 100.

As foarbyld sil ik deselde wolf, geit en koal nimme, mar al presintearre yn 'e foarm fan in tûk kontrakt.

De brûker sil net by steat wêze om te lûken jild út de beurs dêr't it kontrakt is ynset oant hy hat stjoerd elkenien nei de oare kant.

#Извлекаем положение всех объектов из блокчейна
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 ekstrahearret earst alle fariabelen út it tûke kontrakt yn 'e foarm fan har kaaien en de oerienkommende Booleaanske ekspresjefariabele.

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 konvertearret se dan yn in Z3Prover SMT-kompatibele systeembeskriuwing yn Python.
De gegevens wurde ferpakt yn in lus, dêr't opslach fariabelen ûntfange yndeks i, transaksje fariabelen yndeks i + 1, en fariabelen mei útdrukkings set de regels foar oergong fan de foarige steat nei de folgjende.

Dit is it heule hert fan ús firtuele masine, dy't in sykmasjine foar meardere transaksjes leveret.

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] )  

De betingsten wurde sortearre en ynfoege yn in skriptsjabloan ûntworpen om it SMT-systeem yn Python te beskriuwen.

Lege sjabloan


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()
 


Foar de lêste steat yn 'e heule keten wurde de regels tapast dy't oantsjutte binne yn' e seksje oerdrachttransaksje.

Dit betsjut dat Z3Prover sil sykje nei krekt sokke sets fan betingsten dy't úteinlik tastean fûnsen te lûken út it kontrakt.

As gefolch krije wy automatysk in folslein funksjoneel SMT-model fan ús kontrakt.
Jo kinne sjen dat it heul gelyk is oan it model út myn foarige artikel, dat ik mei de hân kompilearre.

Foltôge sjabloan


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()
 


Nei lansearring lost Z3Prover it tûke kontrakt op en leveret ús in keatling fan transaksjes wêrmei't wy fûnsen kinne weromlûke:

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

Neist it fearboatkontrakt kinne jo eksperimintearje mei jo eigen kontrakten of besykje dit ienfâldige foarbyld, dat wurdt oplost yn 2 transaksjes.

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

}

Om't dit de earste ferzje is, is de syntaksis heul beheind en kinne d'r bugs wêze.
Yn 'e folgjende artikels plan ik de fierdere ûntwikkeling fan' e VM te dekken, en sjen te litten hoe't jo formeel ferifiearre tûke kontrakten kinne meitsje mei har help, en net allinich oplosse.

It karakter firtuele masine is beskikber by http://2.59.42.98/hyperbox/
Nei it pleatsen fan de boarnekoade fan 'e symboalyske VM yn oarder en it tafoegjen fan opmerkingen, bin ik fan plan om it op GitHub te pleatsen foar fergese tagong.

Boarne: www.habr.com

Add a comment