Preskoči na sadržaj

Uvidi

Kontrolna lista spremnosti pametnih ugovora za audit

30 provjera prije audita pametnih ugovora: opseg i dokumentacija, testovi, Foundry fuzz i invariant testovi, Slither trijaža, kontrola pristupa, integracije i implementacija.

Inženjerski tim sigmacode.io8 min čitanja

Na ovoj stranici (9)
  1. 1. Opseg i dokumentacija
  2. 2. Higijena koda i statička analiza
  3. 3. Testovi
  4. 4. Fuzzing i testiranje invarijanti
  5. 5. Kontrola pristupa i nadogradivost
  6. 6. Vanjske integracije
  7. 7. Implementacija i rad u produkciji
  8. 8. Organizacija audita
  9. Kako vam možemo pomoći

Audit pametnih ugovora je skup i strogo vremenski ograničen. Auditori imaju fiksan broj dana, a svaki sat koji potroše na otkrivanje šta bi kod trebao raditi, praćenje commita koji se stalno mijenja ili pisanje testova koje niste napisali sat je koji ne provode na logici zbog koje se stvarno mogu izgubiti sredstva. Izvještaj se tada puni nedostajućim NatSpec komentarima, plutajućim pragmama i netestiranim revert putanjama umjesto problemima za čije ste pronalaženje platili.

Dobro pripremljena kodna baza za isti budžet dobija dublji audit. Ova kontrolna lista sažima ono što pripremimo prije predaje koda vanjskim auditorima: 30 konkretnih provjera koje pokrivaju dokumentaciju, higijenu koda, testiranje, kontrolu pristupa, integracije, implementaciju i organizaciju. Ne zamjenjuje audit, nego osigurava da vrijeme audita ode tamo gdje je važno.

1. Opseg i dokumentacija#

Auditori mogu provjeriti ponašanje samo u odnosu na namjeru koju razumiju, pa ta namjera mora biti zapisana.

  • Zamrznite commit hash. Predajte auditorima jedan označeni commit i ne mijenjajte kod koji se pregleda tokom audita. Ispravke nastaju na zasebnoj grani i pregledaju se u rundi provjere ispravki.
  • Objavite listu fajlova u opsegu s nSLOC-om. Navedite svaki ugovor u opsegu s putanjom i brojem normaliziranih linija koda te izričito navedite šta nije u opsegu (biblioteke, mockovi, skripte, već auditirani fajlovi). Auditori procjenjuju i planiraju na osnovu nSLOC-a, pa tačan broj sprječava iznenađenja na obje strane.
  • Napišite pregled arhitekture. Jedna do dvije stranice koje opisuju ugovore, kako se međusobno pozivaju, koji ugovori drže sredstva i glavne korisničke tokove. Jednostavan dijagram poziva i tokova tokena auditorima štedi sate reverse engineeringa.
  • Specificirajte ponašanje i invarijante prozom. Opišite šta svaka vanjska funkcija mora raditi i koja svojstva uvijek moraju vrijediti, npr. „zbir korisničkih salda jednak je totalAssets" ili „samo timelock može mijenjati naknade". Te izjave postaju osnova i za pregled auditora i za vaše vlastite testove invarijanti.
  • Dokumentirajte poznate probleme i prihvaćene rizike. Navedite ograničenja kojih ste već svjesni i dizajnerske odluke koje prihvatate, poput kompromisa centralizacije ili nepodržanih vrsta tokena. Tako ne završe u izvještaju, a auditori vide gdje ste već sve promislili.

2. Higijena koda i statička analiza#

Šum u kodu troši vrijeme audita i stvara nalaze male vrijednosti, pa ga uklonite prije nego što ga auditori vide.

  • Fiksirajte verziju kompajlera i dođite do nula upozorenja. Koristite tačnu pragma solidity 0.8.x; umjesto plutajuće ^0.8.0 te uskladite verziju, broj optimizer runova, via_ir i evm_version u foundry.toml s onim što ćete implementirati. Svako upozorenje kompajlera treba ispraviti ili svjesno obrazložiti.
  • Fiksirajte i popišite zavisnosti. Zaključajte OpenZeppelin Contracts i svaku drugu biblioteku na tačno izdanje, npr. označeni v5.x submodule ili tačnu verziju u package.json. Navedite sve zavisnosti u dokumentaciji za audit i označite sve kopirane ili izmijenjene fajlove.
  • Uklonite mrtvi kod, TODO-e i debug ispis. Obrišite nekorištene funkcije, zakomentirani kod, console.log importe i zaostale testne pomoćnike iz ugovora u opsegu. Otvoreni TODO-i upućuju na nedovršenu logiku i bit će tako i prijavljeni.
  • Dovršite NatSpec za vanjske i javne funkcije. Svaka vanjska i javna funkcija treba imati @notice, @param, @return i, gdje je relevantno, napomenu o kontroli pristupa i revertima. Koristite custom errors i emitirajte evente za svaku promjenu stanja koja je važna izvan lanca.
  • Pokrenite Slither i trijažirajte svaki nalaz. Pokrenite slither na zamrznutom commitu i svaki nalaz označite kao ispravljen, lažno pozitivan ili prihvaćen, uz obrazloženje u jednoj rečenici. Drugi alat poput Aderyna često pronađe drukčije obrasce, a podijeljena trijaža auditorima pokazuje koji su automatizirani nalazi već riješeni.

3. Testovi#

Testovi auditorima pokazuju kako se kod treba koristiti i omogućuju im da brzo napišu proof of concept.

  • Pokrijte svaku vanjsku funkciju i objavite izvještaj. Svaka vanjska i javna funkcija treba barem jedan test uobičajenog scenarija. Generirajte izvještaj o pokrivenosti linija i grana s forge coverage i objasnite nepokrivene grane umjesto da ih skrivate.
  • Testirajte revert putanje i rubne slučajeve. Pomoću vm.expectRevert provjerite da neovlašteni pozivi, neispravni parametri, nulti iznosi i granične vrijednosti revertiraju s očekivanim custom errorom. U netestiranim revert putanjama skrivaju se greške validacije i kontrole pristupa.
  • Pokrenite fork testove na stvarnim integracijama. Ako protokol komunicira s vanjskim ugovorima poput DEX-ova, lending tržišta ili oracla, testirajte na mainnet ili L2 forku s fiksiranim brojem bloka. Mockovi dokazuju samo da vaš kod radi s vašim pretpostavkama o drugoj strani.

4. Fuzzing i testiranje invarijanti#

Fuzzing pronalazi kombinacije ulaza na koje niko nije pomislio, a testovi invarijanti provjeravaju da sistem ostaje konzistentan kroz nizove poziva.

  • Fuzzajte svaku funkciju s numeričkim ulazima. Napišite Foundry fuzz testove za iznose, vremenske oznake, izračune udjela i logiku naknada te ograničite ulaze s bound() umjesto pretjeranog vm.assume. Tipične mete su smjer zaokruživanja i preljev pri ekstremnim vrijednostima.
  • Napišite stateful testove invarijanti s handlerima. Koristite Foundry invariant testiranje s handler ugovorima koji pozivaju sistem u realističnim nizovima iz perspektive više aktera. Nakon svakog niza poziva provjerite solventnost, očuvanje vrijednosti i svojstva kontrole pristupa.
  • Uskladite zapisane invarijante i testni paket. Svaka invarijanta iz specifikacije treba imati svoj test invarijante, a svaki test invarijante treba se moći povezati sa specifikacijom. Pokrećite paket u CI-ju sa smislenim brojem runova i dubinom, a ne samo lokalno sa zadanim postavkama.

5. Kontrola pristupa i nadogradivost#

Privilegovane funkcije i nadogradnje mogu pomaknuti ili zaključati svu imovinu u sistemu, pa model povjerenja mora biti izričit.

  • Popišite svaku privilegovanu ulogu i funkciju. Dokumentirajte svaku ulogu, koje funkcije smije pozivati, ko je drži i koji je najgori scenario ako joj je ključ kompromitovan. To je odjeljak o pretpostavkama povjerenja koji auditori prvi traže.
  • Stavite administratorske ovlasti iza multisiga i timelocka. Kritičnim parametrima i nadogradnjama treba upravljati multisig poput Safea, uz TimelockController ili jednakovrijednu odgodu za promjene koje utiču na sredstva korisnika. Dokumentirajte prag potpisnika i trajanje odgode.
  • Provjerite storage layout nadogradivih ugovora. Za proxyje koristite nadogradive ugovore iz OpenZeppelin v5 s ERC-7201 namespaced storageom i validirajte nadogradnje OpenZeppelinovim alatima za nadogradnje. Ako je nadogradnja u opsegu, priložite razliku storage layouta između verzija.
  • Zaštitite inicijalizatore i funkcije nadogradnje. Pozovite _disableInitializers() u konstruktoru implementacije, ispravno koristite modifikatore initializer i reinitializer te ograničite _authorizeUpgrade kod UUPS proxyja (ERC-1822). Nezaštićeni implementacijski ugovor je nalaz koji auditori nikada ne bi trebali morati prijaviti.

6. Vanjske integracije#

Svaki vanjski poziv je pretpostavka o tuđem kodu, a svaku pretpostavku treba zapisati i testirati.

  • Obradite zastarjele podatke i ispad oracla. Provjerite updatedAt u odnosu na heartbeat feeda, odbacite cijene jednake nuli ili negativne te na L2 mrežama provjerite sequencer uptime feed. Odlučite i dokumentirajte šta protokol radi kada oracle nije dostupan.
  • Uzmite u obzir posebnosti tokena. Navedite koje su vrste tokena podržane te obradite ili izričito isključite fee-on-transfer, rebasing, tokene s brojem decimala različitim od 18 i nestandardne ERC-20 tokene. Za transfere koristite SafeERC20 i mjerite razliku salda gdje je bitan stvarno primljeni iznos.
  • Mapirajte površine za reentrancy. Popišite svaki vanjski poziv i transfer tokena, slijedite obrazac checks-effects-interactions i koristite ReentrancyGuard (ili ReentrancyGuardTransient uz EIP-1153) gdje se stanje dijeli. Uzmite u obzir cross-function i read-only reentrancy te callbackove ERC-721, ERC-1155 i ERC-777 tokena.
  • Razmotrite MEV i front-running. Dodajte ograničenja slippagea i rokove swapovima i depozitima, zaštitite ERC-4626 vaultove od inflacionog napada prvog deponenta i pregledajte svu logiku koja zavisi od redoslijeda transakcija. Dokumentirajte koje rizike redoslijeda prihvatate.

7. Implementacija i rad u produkciji#

Auditirani kod je siguran samo onoliko koliko je siguran način na koji se implementira i održava.

  • Neka deploy skripte budu ponovljive, a parametri dokumentirani. Implementirajte Foundry skriptama sa zamrznutog commita, držite svaki parametar konstruktora i inicijalizatora u verzioniranoj konfiguraciji te uključite skripte u opseg ili barem u paket za predaju. Ispravan ugovor implementiran s pogrešnim parametrima i dalje je neispravan.
  • Uvježbajte implementaciju na testnetu s verificiranim izvornim kodom. Pokrenite istu deploy skriptu na testnetu i na mainnet forku, verificirajte izvorni kod na block exploreru i provjerite jesu li uloge završile na predviđenim adresama. Dajte auditorima testnet adrese kako bi mogli raditi sa stvarnom implementacijom.
  • Pripremite pauzu, runbook i nadzor. Odlučite ko smije pauzirati šta, napišite kratki runbook za incidente i postavite upozorenja za promjene uloga, nadogradnje, velika povlačenja i stanja pauze. Auditori pregledaju hitne putanje, pa one moraju postojati prije audita.

8. Organizacija audita#

Dobra organizacija održava tempo audita i osigurava da se runda ispravki doista iskoristi.

  • Imenujte tehničku kontakt osobu i kanal. Odredite jednog developera koji poznaje kodnu bazu i može odgovoriti na pitanja u roku od nekoliko sati te dogovorite zajednički kanal za trajanje audita. Svako neodgovoreno pitanje usporava pregled.
  • Rezervirajte vrijeme za ispravke i rundu njihove provjere. Planirajte kapacitet developera odmah nakon primitka izvještaja i unaprijed dogovorite da auditori provjere ispravke. Neprovjerene ispravke mogu unijeti nove greške nakon što je audit zaključen.
  • Otkrijte prethodne audite i preglede. Podijelite ranije izvještaje o auditu, status njihovih ispravki i sve interne preglede. Auditori se tada mogu fokusirati na promjene i provjeriti da se raniji nalazi nisu vratili.

Kako vam možemo pomoći#

Nudimo Audit-Readiness Sprint fiksnog opsega u trajanju od jedne do dvije sedmice: model prijetnji, analizu nedostataka testnog paketa, Foundry fuzz i testove invarijanti, Slither trijažu, prioritiziranu listu ispravki i paket za predaju auditorima s opsegom, dokumentacijom i poznatim problemima. Nismo firma za audit i sprint ne zamjenjuje vanjski audit; on priprema vaš kod kako bi auditori svoje vrijeme mogli posvetiti logici. Pojedinosti su na našoj stranici s cijenama i među našim uslugama za Blockchain i Web3.

Ako želite besplatnu procjenu za svoju kodnu bazu, opišite nam svoj projekat. Prije nego što podijelite kod, rado ćemo potpisati obostrani NDA.

Imate projekt na umu?

Recite nam šta gradite. Senior inženjer odgovara u roku od sat vremena radnim danom — a zatim dobijate poštenu procjenu, jasan opseg i ponudu po fiksnoj cijeni ili po koracima, obično za nekoliko radnih dana.

Radije prvo pišete? Pišite nam

Razgovarat ćete direktno s Ing. Ismet Mesic, Tech lead.