Lekcije za programere iz druge najveće pljačke u istoriji digitalnih valuta

Ovaj tekst je prevod sa bloga freeCodeCamp. Njegov autor je Hasib Kureši, softverski inženjer, pisac i blockchain entuzijasta.

prenosimo - 3. Avgust, 2017.

Dvadeseti jul, dan kada je izvedena druga po redu najveća pljačka u istoriji digitalnih valuta od strane jednog hakera.

Oko 21č po našem vremenu, nepoznati napadač je iskoristio sigurnosni propust u Parity multi-signature novčaniku na Etereum mreži, iscrpljujući iznos od 31.000.000$ u Eteru iz tri masivna novčanika u samo nekoliko minuta. Da je imao još koji sat, haker bi mogao da se izvuče sa preko 105.000.000$ iz nebezbednih novčanika.

Ali neko ga je zaustavio.

Oglasivši alarm, grupa dobronamernih white-hat hakera iz Etereum zajednice se brzo organizovala. Analizirali su napad i shvatili da ne postoji način da povrate pokradena sredstva, a mnogi novčanici su i dalje bili ranjivi. Kako je vreme bilo od suštinske važnosti, imali su samo jednu preostalu opciju: da sami hakuju preostale novčanike pre nego što to uradi sam napadač.

Iskoristivši iste ranjivosti, dobri hakeri hakovali su sve preostale novčanike kojima je pretio rizik i isušili njihove račune, uspešno sprečavajući napadača da se domogne preostalih 77,000,000$.

Da, dobro ste pročitali.

Da bi sprečili hakera da opljačka još koju banku, white-hat hakeri su napisali softver čiji je zadatak bio da opljačka sve preostale banke na svetu. Kada je novac bio sigurno ukraden, započeli su proces vraćanja sredstava odgovarajućim nosiocima računa. Ljudi čiji je novac sačuvan ovim herojskim podvigom su sada u procesu povraćaja sredstava.

To je izvanredna priča koja ima značajne implikacije za svet kriptovaluta.

Važno je razumeti da za ovu eksploataciju nije kriva ranjivost Etereuma ili samog Parity-ja. Kriva je ranjivost u uobičajenom smart contract kodu koje je Parity klijent koristio kako bi korisnicma isporučio multi-signature novčanike.

Sve ovo je prilično komplikovano, tako da, da bismo razjasnili ovo svima, ovaj post je podeljen u tri dela:

  1. Šta se tačno dogodilo? Objašenje Etereuma, smart contract protokola i multi-signature novčanika.
  2. Kako su izveli napad? Tehničko objašnjenje napada (specijalno za programere).
  3. Šta sada? Implikacije napada na budućnost i sigurnost smart contracta.

Ukoliko ste upoznati sa Etereumom i kripto svetom, možete preskočiti ovaj deo i nastaviti da čitate sledeću sekciju.

Šta se tačno dogodilo?

U ovoj priči ključna su tri elementa: Etereum, smart contracti i digitalni novčanici.

Etereum je digitalna valuta nastala 2013. godine — pune četiri godine od puštanja Bitkoina. Za to vreme je narastao u drugu najveću digitalnu valutu u svetu po tržištnoj kapitalizaciji  — 20 biliona $, dok je Bitkoin na prvom mestu sa 40 biliona $.

Kao i sve kripto-valute, Etereum je potomak Bitkoin protokola, s namerom da unapredi dizajn Bitkoina. Ali nemojte se prevariti: iako je digitalna valuta nalik Bitkoinu, Etereum je mnogo moćniji.

Dok Bitkoin koristi svoj blockchain da implementira knjigu monetarnih transakcija, Etereum koristi svoj blockchain da beleži tranziciju stanja u gigantskom distribuiranom računaru. Etereumova digitalna valuta, eter, u suštini je sporedni efekat napajanja ovog masivnog računara.

Rečeno na drugi način, Etereum je bukvalno računar koji obuhvata ceo svet. Svako ko pokrene Etereum softver na svom računaru učestvuje u operacijama ovog svetskog računara, Etereum Virtualne Mašine (EVM). S obzirom na to da je EVM dizajnirana da bude računarski nezavisna (Tjuring potpuna), može da uradi gotovo sve što može biti izraženo računarskim programom.

Dozvolite mi da budem emocionalan: ovo je ludilo. Kripto svet je napržen oko potencijala Etereuma, koji je doživeo vrtoglavi rast u poslednjih 6 meseci.

Developerska zajednica se okupila iza Etereuma i vlada veliko uzbuđenje oko toga šta sve može da se izgradi na EVM  — i to nas dovodi do smart contracta.

Smart contracti su jednostavno računarski programi koji se pokreću na EVM. Nalik su normalnom ugovoru, na mnogo načina, osim što im ne trebaju advokati i sudije da bi ih tumačili. Umesto toga, kompajliraju se u bajtkod i nedvosmisleno ih tumači EVM. Zahvaljujući ovim programima, digitalnu valutu možete transferovati (između ostalog) isključivo na pravilima zasnovanim ovim ugovorom.

Naravno, postoje stvari koje normalni ugovori mogu da urade za razliku od smart contracta —  smart contracti ne mogu sa lakoćom ostvariti interakciju sa stvarima koje nisu u blokchainu. Ali smart contracti takođe mogu stvari koje normalni ugovori ne mogu, kao što je izvršavanje niza pravila u potpunosti kroz neprobojnu kriptografiju.

To nas dovodi do pojma novčanika. U svetu digitalnih valuta, novčanici su način na koji se čuvaju sredstva. Pristup svom novčaniku imate zahvaljujući tajnoj šifri u suštini, poznatoj i kao privatni ključ (malo pojednostavljeno).

Postoji mnogo različitih novčanika koji savetuju različita sigurnosna svojstva, kao što je ograničavanje sume koja se podiže. Među najpopularnijim je multi-signature novčanik.

Kod multi-signature novčanika postoji nekoliko privatnih ključeva koji mogu otključati novčanik, ali samo jedan nije dovoljan da bi se otključao. Ako vaš novčanik podržava tri ključa na primer, možete odrediti da je potrebno pružiti bar dva od tri kako bi ga uspešno otključali.

To znači da ako ste vi, vaš otac i vaša majka ponaosob potpisnici ovog novčanika, čak i da kriminalci hakuju vašu majku i ukradu njen privatni ključ, i dalje ne bi imali pristup vašim sredstvima. To vodi do mnogo jače sigurnosne garancije, tako da je multi-signature standard što se tiče sigurnosti novčanika.

Ovo je vrsta novčanika koju je haker napao.

Pa šta je pošlo po zlu? Da li su uspeli da razbiju privatne ključeve? Da li su koristili kvantne računare, ili neku vrstu najsavremenijeg faktoring algoritma.

Ne, sa kriptografijom je bilo sve u redu. Način na koji su to uradili je bio skoro pa smešno jednostavan: otkrili su da je programer napravio grešku u kodu koja im je dozvoljavala da ponovo inicijaliziraju novčanike, skoro nalik vraćanju na fabrička podešavanja. Nakon toga, mogli su da postave sebe za nove vlasnike, i da izađu sa svime.7

Šta se tačno dogodilo?

Ono što sledi je tehničko objašnjenje šta se tačno dogodilo. Ako niste programer, slobodno preskočite ovaj deo, s obzirom na to da će biti pretežno programerski.

Etereum ima prilično jedinstven programerski model. Na Etereumu, kod pišete objavljujući contracte (koje možete posmatrati kao objekte), a transakcije se izvršavaju pozivajući metode na ove objekte kako bi promenili njihovo stanje.

Da bi ste pokrenuli kod na Etereumu, prvo morate deploy-ovati contract (deploy-ovanje je samo po sebi transakcija), što će vas koštati malu količinu Etera. Potom morate pozvati metode na taj ugovor da biste ostvarili interakciju sa njim, što vas košta još Etera. Kao što pretpostavljate, ovo podstiče programera da optimizuje svoj kod kako bi ujedno smanjio transakciju i smanjio troškove računanja.

Jedan od načina da se smanje troškovi je korišćenje biblioteka. Ukoliko vaš kod poziva zajedničku biblioteku koje je već deploy-ovana ranije, ne morate opet da deploy-ujete zajednički kod. U Etereumu, principi DRY (Don’t Repeat Yourself) koda će vam direktno uštedeti novac.

Uobičajeni multi-sig novčanici u Parity-ju su radili upravo to. Držali su referencu na zajedničku eksternu biblioteku koja je sadržala logiku inicijalizacije novčanika. Ova zajednička bibliotka je referncirana javnim ključem contracta biblioteke.

// FIELDS
address constant _walletLibrary = 0xa657491c1e7f16adb39b9b60e87bbb8d93988bc3;

Biblioteka je pozivana na nekoliko mesta, putem EVM instrukcije zvane DELEGATECALL, koja radi sledeće: za bilo koju metodu koja poziva DELEGATECALL, pozvaće istu metodu na ugovor na koji se delegira, ali koristeći kontekst trenutnog ugovora. U suštini nalik pozivu super funkcije, samo bez nasleđivanja. (Ekvivalent u JavaScriptu bi bio

OtherClass.functionName.apply(this, args).)

Evo i primera na njihovom multi-sig novčaniku: isOwner metoda samo delegira isOwner metodu zajedničke biblioteke novčanika, koristeći trenutno stanje contracta:

function isOwner(address _addr) constant returns (bool) {

return _walletLibrary.delegatecall(msg.data);

}

Sve je to prilično nedužno. Multi-sig novčanik je sam po sebi imao sve potrebne provere odobrenja i bili su sigurni da rigorozno sprovode autorizaciju nad svim osetljivim akcijama koje se odnose na stanje novčanika.

Ali su napravili jednu kritičnu grešku.

Solidity vam dozvoljava da definišete “fallback metodu”. To je metoda koja se poziva kad god nema metode koja odgovara nazivu date metode. Definiše se tako što se izostavi ime:

 function() {

// do stuff here for all unknown methods

}

Parity tim je odlučio da dopusti svakoj nepoznatoj metodi koja šalje Eter contractu uobičajeno deponovanje poslatog Etera.

function() payable {

// payable is just a keyword that means this method can receive/pay Ether

 

if (msg.value > 0) {

// just being sent some cash?

Deposit(msg.sender, msg.value);

}

throw;

}

Ali otišli su i korak dalje i upravo tu su napravili kritičnu grešku. Ispod je tačan kod koji je napadnut.

function() payable {

// just being sent some cash?

if (msg.value > 0)

Deposit(msg.sender, msg.value);

else if (msg.data.length > 0)

_walletLibrary.delegatecall(msg.data);

}

U osnovi:

Ukoliko metoda nije definisana unutar ugovora…

I nema Etera koji se šalje u transakciji…

A ima podataka u message payloadu…

Onda će pozvati potpuno istu metodu kao da je definisana u _walletLibrary, ali u kontekstu ovog contracta.

Pomoću ovoga, napadač je pozvao metodu initWallet(), koja nije bila definisana u multi-sig ugovoru, ali je bila definisana u zajedničkoj biblioteci novčanika.

function initWallet(address[] _owners, uint _required, uint _daylimit) {

initDaylimit(_daylimit);

initMultiowned(_owners, _required);

}

Koja poziva initMultiowned metodu…

function initMultiowned(address[] _owners, uint _required) {

m_numOwners = _owners.length + 1;

m_owners[1] = uint(msg.sender);

m_ownerIndex[uint(msg.sender)] = 1;

for (uint i = 0; i < _owners.length; ++i)

{

m_owners[2 + i] = uint(_owners[i]);

m_ownerIndex[uint(_owners[i])] = 2 + i;

}

m_required = _required;

}

Da li uočavate šta se tu dešava? Napadač je u suštini ponovo inicijalizirao contract delegirajuću metodu biblioteke, prepisujući vlasnike na originalnom contractu. Oni i bilo koji niz vlasnika koje proslede kao argument će postati novi vlasnici.

S obzirom na to da oni sada kontrolišu čitav novčanik, bez problema mogu da izvuku ostatak balansa. I upravo to su i uradili.

initWallet: http://bit.ly/2wp1ZTk

Transfer: http://bit.ly/2unGZe4

Dakle, šta je na kraju bila ranjivost? Mogli biste da tvrdite da su postojale dve.

Prva, što initWallet i initMultiowned u biblioteci novčanika nisi obeležene sa internal (nalik private metodama, što bi sprečilo delegiranje ovog poziva) i što ove metode nisu proveravale da li je novčanik već inicijalizovan. Bilo koja provera bi učinila ovaj hack nemogućim.

Druga ranjivost je bila raw delegateCall. Ovo možete smatrati ekvivalentom raw eval iskazu, koji se pokreće na stringu pruženom od strane korisnika. U pokušaju da bude sažet, ovaj ugovor je koristio metaprogramiranje kao proxy potencijalnih poziva metoda ka biblioteci koja leži ispod. Sigurniji pristup tome bi bio da se izlistaju specifične metode koje su dozvoljene za pozivanje od strane korisnika.

Problem je, naravno, u tome što je to skuplje u potrošnji gasa (jer ima više koje treba proceniti). Ali kada se radi o bezbednosti, verovatno bi trebalo da pređemo preko ove zabrinutosti prilikom pisanja smart contracta za prenos ogromnih količina novca.

I to je bio napad.

Bila je to lukava caka, ali kada vam ukažu na to, čini se gotovo elementarno. Napadač je potom skočio na ovu ranjivost na tri najveća novčanika koja je mogao da nađe — ali sudeći po vremenu transakcije, radio je to u potpunosti manuelno.

Grupa white-hat hakera je radila na mnogo većoj skali koristeći skripte, zato su i uspeli da pobede napdača. S obzirom na to, malo je verovatno da je napadač bio veoma sofisticiran u tome kako je planirao napad.

Možda se pitate — zašto jednostavno ne obrnu hak, kao što su uradili sa DAO hakom?

Nažalost, to nije zaista moguće. DAO hak je bio jedinstven po tome što je napadač izvukao DAO u child DAO, sredstva su bila zamrznuta danima unutar smart contracta pre nego što su mogla biti puštena napadaču.

To je sprečilo ukradena sredstva da odu u opticaj, tako da su ukradeni Eteri efektivno smešteni. To je dalo dovoljno vremena Etereum zajednici da sprovedu kvorum o tome kako se baviti napadom.

Kod ovog napada, napadač je odmah ukrao sredstva i mogao je da krene da ih troši. Hard fork bio bi nepraktičan – šta uraditi povodm svih transakcija koje su otišle u nepovrat? Šta sa ljudima koji su nevino trgovali sa napadačem? Jednom kada eter bude opran i uđe u regularne tokove, isto je kao sa falsifikovanim novčanicama — lako je zaustaviti ih kada su sve u jednom koferu, ali kada potencijalno svako može imati falsifikovane novčanice, praktično je nemoguće vratiti vreme.

Dakle transakcija se ne može obrnuti. Gubitak od 31M $ stoji. Skupa, ali nephodna lekcija.

Dakle šta bi trebalo da izvučemo iz ovoga?

Šta ovaj napad znači za Etereum?

Ovde ima nekoliko važnih činjenica.

Prvo, upamtite, ovo nije bila mana u Etereumu ili u smart contractima generalno. Radije, u pitanju je programerska greška u određenom ugovoru.

Dakle ko su ti naivni programeri koji su napisali to? Trebalo je da znaju bolje, zar ne?

To je bila unakrsna saradnja između Etereum fondacije (bukvalno kreatori Etereuma),  glavni tim Parity-ja i članovi open-source zajednice. Sve je podvrgnuto ozbiljnoj kolegijalnoj reviziji. To je bukvalno najviši standard programiranja koji postoji u Etereum ekosistemu.

Ovi programeri su ljudi. Oni prave greške. Kao i recenzenti koji su pregledali ovaj kod.

Pročitao sam neke komentare na Redditu i HackerNewsu i između ostalog i ovaj: “Kako očigledna greška! Kako je uopšte moguće da im je to promaklo?” (Ignorišući da je “očigledno” bila ranjivost koja je uneta u januaru i tek sada otkrivena.)

Kada vidim ovakve odgovore, jasno mi je da ti ljudi koji su komentarisali nisu profesionalni programeri. Za ozbiljnog programera, rekacija bi bila: dođavola, kako glupa greška. Drago mi je da nisam taj koji je napravio.

Ovakve greške se rutinski prave u programiranju. Svi programi nose rizik programerske greške. Potrebno je da odbacimo razmišljanje “da su bili pažljiviji ovo se ne bi dogodilo”. Na određenom nivou, biti pažljiv nije dovoljno.

Kako programi skaliraju do ne-trivijalne složenosti, morate uzeti u obzir da programi nisu ispravni. Nikakva količina ljudske marljivosti ili testiranja nije dovoljna da spreči sve moguće bagove. Čak i organizacije kao što su Google ili NASA prave programerske greške, uprkos ekstrenomnoj strogosti koju primenjuju na svoj najkritičniji kod.

Hteli bismo da se osvrnemo na prakse pouzdanosti u kompanijama kao što su Google i Airbnb. Kada god se pojavi bug na produkciji ili dođe do prekida rada, oni izvrše obdukcijsku analizu i podele je unutar kompanije. U ovim obdukcijama, praksa je da se nikada ne svaljuje krivica na pojedince.

Svaljivanje krivice na pojednice je besmisleno, zato što svi programeri, bez obzira na to koliko iskusni, imaju nemalu verovatnoću da naprave grešku. Umesto toga, svrha obdukcije je da identifikuje šta je u procesu dozvolilo grešci da bude deploy-ovana.

Problem nije u tome da je programer zaboravio da doda internal biblioteci novčanika, ili što je napravio raw delegateCall bez provere koja se metoda poziva.

Problem je što mu je njegov set alata dozvolio takve greške.

Kako ekosistem smart contracta evoluira, trebalo bi da evoluira u pravcu otežavanja pravljenja takvih grešaka, a to znači čineći sigurnost contracta podrazumevanom.

Do me dovodi so sledeće tačke.

Jačina je slabost kada su u pitanju programski jezici. Što je jači i izražajniji programski jezik, to je kompleksniji njegov kod. Solidity je izuzetno kompleksan jezik, podseća na Javu.

Kompleksnost je neprijatelj sigurnosti. Kompleksni programi su teži za razumevanje i teže je identifikovati granične slučajeve. Mislim da su jezici kao što je Viper (održavan od strane Vitalika Buterina) obećavajući korak u tom pravcu. Viper podrazumevano uključuje osnovne sigurnosne mehanizme, kao što su ograničeni konstruktori petlji, nedopuštanje integer overflowa i prevencija drugih osnovnih bagova o koji programer ne bi trebalo da ima razloga da misli.

Što vam jezik manje dopušta da uradite, lakše je analizirati i dokazati svojstva contracta. Sigurnost je komplikovana zato što je jedini način da se dokaže pozitivna izjava kao “ovaj contract je siguran” da se opovrgne svaki mogući vektor napada: “ovaj contract ne može biti ponovo incijalizovan”, “njegovim sredstvima se ne može pristupiti osim ako niste vlasnik”, itd. Što je manje mogućih vektora napda koje treba uzeti u obzir, lakše je razviti siguran contract.

Jednostavniji programerski model takođe dozvoljava stvari kao što su fromalna verifikacija i generisanje automatskih testova. Ovo su oblasti pod aktivnim istraživanjem, ali kao što su contracti inkorporirali najsavremeniju kriptografiju, takođe bi trebalo da počnu sa usvajanjem vodećih stvari koje se tiču dizajna programskih jezika.

Tu je i veća lekcija.

Većina programera koja ulazi u ovaj prostor, uključujući i mene, dolazi sa pozadinom web developmenta, a i blockchain set alata je dizajniran tako da bude prijemčiv web developerima. Solidity je postigao ogromno usvajanje u developerskoj zajednici zbog svoje sličnosti sa drugim formama programiranja. Na neki način, to može da bude i njegova mana.

Problem je u tome što je blockchain programiranje fundementalno drugačije od web developmenta.

Dozvolite da objasnim.

Pre ere client-server web modela, većina programiranja se radila za upakovani potrošački softver ili na embeded sistemima. To je bilo pre doba automatskih ažuriranja softvera. U ovim programima, isporučeni proizvod je bio konačan — objavljivala se jedna forma softvera na svakih 6 meseci, a ako je postojao bag, taj bag je morao da sačeka sledeće izdanje. Zbog tog dugačkog ciklusa developmenta, sve verzije softvera su rigorozno testirane pod svim zamislivim okolnostima.

Web development je daleko više opraštajuć. Kada pustite loš kod na web server, nije toliko strašno ako sadrži kritičnu grešku — možete se samo vratiti na prethodnju verziju, ili nastaviti sa popravkom i sve je u redu jer vi kontrolišete server. Ili ako se desi najgore i dođe do proboja ili curenja podataka, uvek možete zaustaviti curenje gašenjem servera i diskonektovanjem sa mreže.

Ova dva modela razvoja su fundementalno različita. Samo u nečemu poput web developmenta možete primeniti moto “Kreći se brzo i lomi stvari usput”.

Većina programera danas se učila na modelu web developmenta. Nažalost, blockchain sigurnosni model je srodniji starom modelu.

Kod blockchaina, kod je suštinski nepopravljiv. Jednom kada deploy-ujete loš smart contract, svako je slobodan da ga napadne koliko god dugo i jako želi, i nema povratka ako ga se dočepaju. Osim ako ne ugradite inteligentne sigurnosne mehanizme u svoj contract, ukoliko postoji bag ili je izvršen uspešan napad, ne postoji način da isključite server i popravite grešku. Biti na Etereumu po definiciji znači da svako poseduje vaš server.

Postoji izreka u cybersecurity-u “napad je uvek lakši od odbrane”. Blockchain oštro množi ovu neuravnoteženost. Mnogo je lakše napadati zato što imate pristup kodu svakog contracta, znate koliko ima novca u njemu i imate onoliko vremena koliko vam treba da pokušate da izvedete napad. A kada vam napad uspe, potencijalno možete ukrasti sav novac.

Zamislite da pravite softver za automate za prodaju. Ali umesto baga koji vam dopušta da ukradete slatkiše iz jednog automata, bug vam dozvoljava da istovremeno kradete slatkiše iz svih mašina na svetu koje pokreće ovaj softver. Da, tako radi blockchain.

U slučaju napada, odbrana je ekstremno teška. White-hat hakeri u Parity slučaju su demonstrirali koliko su im bile ograničene opcije za odbranu — nije bilo načina da se obezbedi ili razmontira contract, ili da se hakuje nazad ukradeni novac; sve što su mogli da urade je da hakuju preostale ranjive novčanike pre samog napadača.

Možda bi to moglo da izgleda kao mračna budućnost.

Ali ne mislim da je blockchain programiranju odzvonilo. Šta više, ovo potvrđuje ono što svi već znaju: ovaj ekosistem je mlad i nesazreo. Biće potrebno dosta rada da se razviju trening i disciplina kako bi se smart contracti tretirali kao što banke tretiraju svoje ATM softvere. Ali dotle moramo doći kako bi blockchain bio uspešan na duže staze.

To ne znači da samo programeri treba da sazrevaju i vežbaju. To znači i da treba napraviti alate i jezike koji sve ovo olakšavaju i obezbeđuju rigorozne garancije u vezi sa našim kodom.

Još uvek je rano. Etereum je projekat u toku i menja se brzo. Ne bi trebalo da tretirate Etereum kao banku ili kao zamenu za finansijsku infrastrukturu. I definitivno ne bi trebalo da skladištite bilo kakav novac u novčaniku koji vam nije ugodno da izgubite.

Ali uprkos svemu tome, i dalje mislim da će Etereum pobediti na duže staze. A evo i zašto: developerska zajednica iza Etereuma je ono što ga čini moćnim.

Etereum neće živeti ili umreti zbog novca. Živeće ili umreti zbog developera koji se bore za njega.

Liga white-hat hakera koji su se udružili i odbranili ranjive novčanike nije to uradila zbog novca. Uradili su to zato što veruju u ovaj ekosistem. Oni žele da Etereum uspe. Oni žele da vide kako se njihova vizija budućnosti ostvaruje. I nakon svih spekulacija i profitiranja, upravo ovi ljudi će odvesti zajednicu u budućnost. Oni su razlog zašto će Etereum pobediti na duže staze — ili ako napuste Etereum, njihovo napuštanje biće razlog zašto je izgubio.

Ovaj napad je važan. Prodrmaće ljude. Nateraće zajednicu da ozbiljno sagleda najbolje prakse sigurnosti. Nateraće programersku zajednicu da tretira smart contract programiranje sa mnogo većom rigoroznošću nego sada.

Ali ovaj napad nije prodrmao snagu ljudi koji rade na izgradnji ovih stvari. U tom smislu ovo je privremena prepreka.

Na kraju, napadi kao što je ovaj su dobri za rast zajednice. Teraju vas da se prizovete pameti i držite otvorene oči. Boli, a i novinari će najverovatnije napraviti ružnu priču. Ali svaka rana čini zajednicu jačom i dovodi nas bliže dubljem razumevanju tehnologije blockchaina — njegovih opasnosti, ali i neverovatnog potencijala.

P.S. Ako si programer i želiš da naučiš više o sigrnosti smart contracta, ovo je vrlo dobar resurs.