DEV Community

Cover image for Ethereum iz ugla developera: EVM, gas i pametni ugovori
Dora Milen
Dora Milen

Posted on

Ethereum iz ugla developera: EVM, gas i pametni ugovori

Ethereum se često opisuje kao kriptovaluta, blockchain ili platforma za pametne ugovore. Sve tri tvrdnje su delimično tačne, ali developeru ne daju dovoljno precizan mentalni model.

Najkorisnije je posmatrati Ethereum kao javnu, distribuiranu state mašinu.

Postoji globalno stanje koje obuhvata naloge, njihove balanse, izvršni kod pametnih ugovora i podatke koje ti ugovori čuvaju. Transakcije predstavljaju ulaze u sistem. Ethereum Virtual Machine, odnosno EVM, deterministički izvršava te ulaze i proizvodi novo stanje. Hiljade nezavisnih računara zatim proveravaju da je prelazak iz starog u novo stanje urađen prema istim pravilima.

Rezultat nije naročito brz niti jeftin računar. Rezultat je računar čije stanje ne kontroliše jedan administrator, cloud nalog, kompanija ili baza podataka.

Upravo to je osnovna vrednost Ethereuma za developera.

Šta je Ethereum, precizno

Ethereum je decentralizovana mreža računara koji održavaju zajedničko stanje i izvršavaju programabilna pravila. Njegova nativna valuta je ether, sa oznakom ETH, ali ETH nije ceo sistem. ETH se koristi za plaćanje izvršavanja, zaštitu mreže kroz staking i prenos vrednosti između naloga.

Na običnom backendu funkcija može da promeni red u PostgreSQL tabeli:

HTTP zahtev -> aplikacija -> baza -> novo stanje
Enter fullscreen mode Exit fullscreen mode

Na Ethereumu sličan tok izgleda ovako:

potpisana transakcija -> mreža -> EVM -> blok -> novo zajedničko stanje
Enter fullscreen mode Exit fullscreen mode

Ključna razlika je u tome ko ima pravo da prihvati promenu.

U klasičnoj aplikaciji poslednju reč ima operator infrastrukture. Može promeniti bazu, vratiti backup, izmeniti autorizaciju ili ugasiti servis. Na Ethereumu pravila tranzicije stanja izvršava mreža nezavisnih čvorova. Operator jednog čvora ne može samostalno promeniti rezultat.

Ethereum zato ima smisla kada više strana treba da koristi isti sistem, ali nijedna strana ne bi trebalo da ima neograničenu administrativnu kontrolu nad njim.

To može biti:

  • razmena tokenizovane vrednosti
  • escrow između strana koje se međusobno ne poznaju
  • upravljanje zajedničkim treasuryjem
  • tržište digitalne imovine
  • sistem glasanja i izvršavanja odluka
  • registar vlasništva ili dozvola
  • dokaz da je određeni podatak postojao u određenom obliku
  • interoperabilni identitet i reputacija
  • finansijski protokol koji drugi ugovori mogu direktno koristiti

Ako aplikacija ima jednog vlasnika, jednu bazu i korisnike koji tom vlasniku veruju, blockchain često samo povećava složenost.

Ethereum nije distribuirani web server

Pametni ugovor nije zamena za REST API, Kubernetes cluster ili serverless funkciju.

EVM ima nekoliko važnih ograničenja:

  • izvršavanje se plaća
  • svi čvorovi moraju dobiti isti rezultat
  • ugovor ne može samostalno poslati HTTP zahtev
  • persistent storage je skup
  • javni podaci su vidljivi svima
  • potvrda promene nije trenutna
  • jednom objavljen kod nije jednostavno zameniti
  • greška može imati direktne finansijske posledice

Ethereum zato obično predstavlja samo jedan deo sistema.

Realna aplikacija često sadrži:

flowchart LR
    U[Browser ili mobilna aplikacija]
    W[Wallet]
    R[Ethereum JSON-RPC]
    C[Pametni ugovor]
    I[Indexer]
    D[Klasična baza]
    S[Objektni ili decentralizovani storage]

    U --> W
    W --> R
    R --> C
    C --> I
    I --> D
    U --> D
    U --> S

Frontend koristi wallet za autorizaciju i potpisivanje. RPC endpoint povezuje aplikaciju sa Ethereum čvorom. Pametni ugovor čuva minimalno kritično stanje. Indexer obrađuje događaje i gradi bazu pogodnu za pretragu. Veliki fajlovi i nekritični podaci ostaju van lanca.

To je hibridna arhitektura, a ne pokušaj da se cela aplikacija smesti u blockchain.

Globalno stanje i Ethereum nalozi

Ethereum poznaje dve osnovne vrste naloga:

  • Externally Owned Account, skraćeno EOA
  • contract account

EOA je tradicionalno kontrolisan privatnim ključem. Contract account je kontrolisan kodom koji se nalazi na njegovoj adresi.

Svaki nalog ima adresu i može posedovati ETH ili tokene. Međutim, samo nalog koji može autorizovati transakciju pokreće promenu globalnog stanja. Pametni ugovor se izvršava kada primi poziv u okviru već pokrenute transakcije.

Stanje naloga uključuje elemente kao što su:

  • nonce
  • ETH balans
  • hash izvršnog koda
  • root persistent storagea

nonce sprečava ponovno izvršavanje iste potpisane transakcije i određuje redosled transakcija jednog pošiljaoca.

Važno je razlikovati nalog od walleta. Nalog je deo Ethereum stanja. Wallet je softver ili hardverski uređaj koji upravlja ključevima i komunicira sa aplikacijama.

Savremeni Ethereum dodatno zamagljuje staru granicu između EOA i contract account modela. Transakcije tipa 4, uvedene kroz EIP-7702, omogućavaju EOA adresi da delegira izvršavanje postojećem kodu pametnog ugovora. To otvara prostor za batching, sponzorisanje naknada, recovery mehanizme i naprednije kontrole naloga.

Praktična posledica za autora ugovora je da ne treba pretpostavljati da je nalog bez trajno postavljenog koda nužno jednostavan korisnički nalog. Posebno je opasno zasnivati autorizaciju na pretpostavkama o tx.origin. Autorizacija treba da bude eksplicitna i dizajnirana tako da pozivalac može biti pametni ugovor. [2][3]

Kako izgleda Ethereum transakcija

Transakcija je kriptografski autorizovana instrukcija za promenu stanja.

Tipična transakcija sadrži:

  • chainId
  • nonce
  • adresu primaoca
  • količinu ETH koja se šalje
  • ulazne podatke
  • gas limit
  • parametre naknade
  • kriptografski potpis

Ako polje primaoca nije postavljeno, transakcija može predstavljati kreiranje novog ugovora. Ako je primalac EOA, najčešće se radi o prenosu ETH. Ako je primalac contract account, input određuje funkciju koja se poziva i njene argumente.

Ethereum koristi više tipova transakcija.

Tip Namena
0 Legacy transakcija sa jednim gasPrice poljem
1 Transakcija sa access listom
2 Standardna EIP-1559 transakcija
3 Blob transakcija za rollup data availability
4 Transakcija sa EIP-7702 authorization listom

Za većinu aplikativnih transakcija wallet ili SDK formira odgovarajući tip. Developer ipak treba da razume polja, jer greške sa nonce, chainId, gas limitom ili parametrima naknade mogu dovesti do zaglavljenih, preplaćenih ili odbijenih transakcija.

Životni ciklus transakcije izgleda ovako:

  1. Aplikacija konstruiše zahtev za transakciju.
  2. Wallet prikazuje korisniku šta potpisuje.
  3. Privatni ključ potpisuje serijalizovane podatke.
  4. Potpisana transakcija šalje se Ethereum čvoru.
  5. Čvor proverava potpis, nonce, balans i osnovnu validnost.
  6. Transakcija ulazi u lokalni mempool i propagira se mrežom.
  7. Validator je uključuje u execution payload bloka.
  8. Ostali čvorovi ponovo izvršavaju transakciju.
  9. Receipt postaje dostupan nakon uključivanja u blok.
  10. Blok kasnije dobija status safe, a zatim finalized.

Slanje transakcije zato nije isto što i uspešno izvršavanje.

Dobijeni transaction hash znači da je zahtev prihvaćen za propagaciju, ne da je operacija završena. Aplikacija mora sačekati receipt i proveriti njegov status. Za operacije veće vrednosti može biti potrebno čekati dodatne potvrde ili finalizaciju. [3][5]

Šta se događa unutar Ethereum čvora

Ethereum čvor nije jedan monolitni proces. Njegova arhitektura sastoji se od najmanje dve povezane komponente:

  • execution client
  • consensus client

Execution client održava EVM stanje, prima transakcije, upravlja mempoolom i izvršava execution payload. Consensus client implementira proof-of-stake pravila, prati validatore, attestations, fork choice i finalizaciju.

Operator validatora obično pokreće i validator client koji upravlja validacionim ključevima i obavlja dužnosti predlaganja ili potvrđivanja blokova.

Tok bloka može se pojednostaviti ovako:

sequenceDiagram
    participant User
    participant EL as Execution client
    participant CL as Consensus client
    participant Validators
    participant Nodes

    User->>EL: Potpisana transakcija
    EL->>EL: Validacija i mempool
    CL->>EL: Zahtev za execution payload
    EL-->>CL: Transakcije i novo state stanje
    CL->>Validators: Predlog beacon bloka
    Validators->>Validators: Ponovno izvršavanje i attestations
    Validators->>Nodes: Propagacija prihvaćenog bloka

Ethereum vreme deli na slotove. Jedan slot traje 12 sekundi, a 32 slota čine jednu epohu. U svakom slotu bira se validator koji može predložiti blok, ali slot može ostati prazan ako izabrani validator ne obavi svoju dužnost.

Finalnost se utvrđuje glasanjem validatora o checkpointima na granicama epoha. Kada potrebna supervećina staked ETH podrži odgovarajuće checkpoint veze, stariji checkpoint postaje finalizovan.

Za frontend je često dovoljno prikazati transakciju nakon prvog uključenja u blok, uz jasno stanje poput pending, confirmed ili finalized. Backend koji poravnava račune, pušta sredstva ili pokreće nepovratnu eksternu operaciju treba da ima strožu politiku. [4][5]

EVM: deterministički runtime za pametne ugovore

Ethereum Virtual Machine izvršava bytecode pametnih ugovora.

EVM je stack mašina sa maksimalnom dubinom stacka od 1024 elementa. Osnovna reč je široka 256 bita, što odgovara kriptografskim operacijama i formatima koje Ethereum često koristi.

Tokom izvršavanja ugovor koristi nekoliko različitih prostora za podatke:

Prostor Trajanje Može se menjati Tipična namena
Stack Jedan execution frame Da Operandi EVM instrukcija
Memory Jedan poziv Da Privremeni nizovi i strukture
Calldata Jedan poziv Ne Argumenti funkcije
Storage Trajno Da Stanje ugovora
Transient storage Jedna transakcija Da Deljenje privremenog stanja između internih poziva
Logs Trajno u receipt podacima Ne iz ugovora Događaji za offchain potrošače

Persistent storage je najskuplji jer promenu moraju obraditi i održavati čvorovi mreže. Zbog toga se na lancu ne čuvaju slike, dokumenti, kompletni JSON objekti ili veliki aplikativni zapisi ako za to ne postoji izuzetno dobar razlog.

Češći obrazac je:

  • podatak ostaje van lanca
  • na lancu se čuva njegov hash
  • ugovor čuva vlasništvo, status ili dozvolu
  • event beleži istoriju promene
  • indexer pravi model pogodan za frontend

EVM je deterministička. Ako dva ispravna čvora izvrše istu transakciju nad istim prethodnim stanjem, moraju dobiti isti rezultat. Zbog toga ugovor ne može direktno čitati vreme sa interneta, cenu sa berze ili rezultat sportskog meča.

block.timestamp dolazi iz samog bloka i može se koristiti samo kao približna vremenska referenca u granicama protokola. Podaci iz spoljnog sveta zahtevaju oracle ili drugi eksplicitni mehanizam za unos podataka. [6][7]

Pametni ugovor je javni program sa trajnim stanjem

Pametni ugovor je program postavljen na Ethereum adresu. Korisnici i drugi ugovori pozivaju njegove funkcije slanjem ABI kodiranih podataka.

Najpopularniji jezik je Solidity. Kompajler prevodi Solidity u:

  • EVM bytecode
  • deployment bytecode
  • ABI
  • metadata podatke

ABI opisuje funkcije, argumente, povratne vrednosti, događaje i greške. JavaScript biblioteka ne poziva Solidity funkciju direktno. Ona koristi ABI da napravi binarni calldata, šalje ga čvoru i dekodira rezultat.

Prva četiri bajta poziva funkcije predstavljaju selector:

keccak256("transfer(address,uint256)")[0:4]
Enter fullscreen mode Exit fullscreen mode

Ostatak calldata sadrži ABI kodirane argumente.

Ovaj detalj postaje važan kada se analiziraju revertovi, proxy ugovori, multisig transakcije ili sirovi podaci iz block explorera.

Ugovori su međusobno kompozabilni. Jedan ugovor može pozvati drugi u istoj transakciji. Ako bilo koji poziv u lancu izvršavanja revertuje, promene se uglavnom vraćaju do odgovarajuće granice poziva.

To omogućava atomske operacije: više koraka može uspeti zajedno ili se cela operacija poništava.

Atomsko izvršavanje je jedna od najvažnijih osobina Ethereuma. Na klasičnom backendu isti rezultat često zahteva distribuirane transakcije, zaključavanje, compensating actions i koordinaciju između različitih servisa.

Gas: cena ograničenog izvršavanja

Gas je obračunska jedinica za količinu EVM rada.

Svaka instrukcija ima gas cenu. Čitanje memorije, hashiranje, pozivanje drugog ugovora i menjanje storagea ne koštaju isto. Transakcija navodi maksimalnu količinu gasa koju sme da potroši.

Stvarni trošak približno se računa kao:

trošak u ETH = gasUsed × effectiveGasPrice
Enter fullscreen mode Exit fullscreen mode

Kod standardne EIP-1559 transakcije cena po jedinici gasa sastoji se od:

  • base fee
  • priority fee

Base fee određuje protokol na osnovu prethodnih blokova i spaljuje se. Priority fee predstavlja napojnicu validatoru. Pošiljalac postavlja i maxFeePerGas, čime ograničava koliko je spreman da plati.

Gas limit nije isto što i konačan trošak. To je gornja granica potrošnje. Neiskorišćen gas se ne naplaćuje kao izvršen gas.

Ako transakcija revertuje, već potrošen gas ostaje potrošen. Mreža je ipak morala da izvrši instrukcije kako bi ustanovila da operacija nije validna.

Koliko korišćenje Ethereuma košta

Ne postoji jedna tačna cena.

Trošak zavisi od:

  • mreže na kojoj se izvršava
  • trenutne potražnje za block spaceom
  • broja izvršenih EVM instrukcija
  • količine promenjenog storagea
  • veličine calldata
  • drugih ugovora koji se pozivaju
  • L1 data troška ako se koristi rollup

Pre slanja transakcije aplikacija treba da koristi procenu gasa, najčešće preko eth_estimateGas, i aktuelne podatke o naknadama. Procena nije garancija, jer se stanje može promeniti između simulacije i uključivanja u blok.

Trošak produkcionog sistema ne obuhvata samo gas. U budžet ulaze:

  • razvoj i testiranje ugovora
  • security review ili audit
  • RPC infrastruktura
  • indexer i baza
  • monitoring
  • upravljanje ključevima
  • multisig operacije
  • incident response
  • trošak nadogradnje i migracije

Za većinu aplikacija sa velikim brojem korisničkih transakcija direktno korišćenje Ethereum Mainneta nije ekonomski optimalno. Mainnet se češće koristi kao settlement i security sloj, dok se izvršavanje pomera na L2 mreže. [8]

Layer 2 nije odvojena tema

Ethereum scaling strategija zasniva se prvenstveno na rollup mrežama.

Rollup izvršava transakcije izvan Ethereum L1, grupiše rezultate i objavljuje podatke ili kriptografske dokaze na L1. Time se trošak deli između većeg broja korisnika.

Dve glavne porodice su:

  • optimistic rollups
  • zero-knowledge rollups

Optimistic rollup pretpostavlja da je objavljeni rezultat ispravan dok ga neko ne ospori u dozvoljenom periodu. ZK rollup objavljuje validity proof kojim dokazuje ispravnost tranzicije stanja.

Oba pristupa donose dodatne komponente:

  • sequencer
  • bridge ugovore
  • batch objavljivanje
  • poseban model finalnosti
  • različite mehanizme izlaska
  • dodatni rizik implementacije i upravljanja

Blob transakcije uvedene kroz EIP-4844 omogućile su rollup mrežama da jeftinije objavljuju privremene podatke na Ethereum. Kasniji PeerDAS mehanizam omogućava čvorovima da uzorkuju delove blob podataka umesto da svaki čvor preuzima svaki blob u celosti. Kapacitet blobova može se prilagođavati kroz posebne promene parametara bez čekanja kompletnog velikog upgradea. [9][10]

Za aplikativnog developera odluka zato nije samo „Ethereum ili nešto drugo“. Realniji izbor je:

  • Ethereum L1
  • general-purpose Ethereum L2
  • application-specific rollup
  • centralizovani backend
  • kombinacija navedenih sistema

L2 može smanjiti naknade, ali ne uklanja potrebu da razumete bridge rizik, dostupnost sequencera, finalnost i razliku između L2 potvrde i L1 settlementa.

Realan use-case: registar softverskih artefakata

Zamislimo nekoliko kompanija koje zajednički koriste softverske pakete. Svaka kompanija objavljuje build artefakte, ali nijedna ne želi da druga strana bude jedini administrator registra.

Cilj nije da se ZIP, container image ili SBOM smeste na Ethereum. To bi bilo skupo i nepraktično.

Na lancu se mogu čuvati:

  • identitet publishera
  • hash artefakta
  • hash metapodataka
  • identifikator izdanja
  • vreme objavljivanja

Sam artefakt može ostati u object storageu, container registryju ili content-addressed mreži. Potrošač pre deploymenta računa hash preuzetog fajla i poredi ga sa hashom koji je publisher zabeležio na lancu.

Minimalni ugovor može izgledati ovako:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract ReleaseRegistry {
    struct Release {
        bytes32 artifactDigest;
        bytes32 metadataDigest;
        uint64 publishedAt;
    }

    mapping(bytes32 => Release) private releases;

    error EmptyDigest();
    error AlreadyPublished(bytes32 key);

    event ReleasePublished(
        bytes32 indexed key,
        address indexed publisher,
        bytes32 indexed releaseId,
        bytes32 artifactDigest,
        bytes32 metadataDigest
    );

    function keyFor(
        address publisher,
        bytes32 releaseId
    ) public pure returns (bytes32) {
        return keccak256(abi.encode(publisher, releaseId));
    }

    function publish(
        bytes32 releaseId,
        bytes32 artifactDigest,
        bytes32 metadataDigest
    ) external returns (bytes32 key) {
        if (
            artifactDigest == bytes32(0) ||
            metadataDigest == bytes32(0)
        ) {
            revert EmptyDigest();
        }

        key = keyFor(msg.sender, releaseId);

        if (releases[key].publishedAt != 0) {
            revert AlreadyPublished(key);
        }

        releases[key] = Release({
            artifactDigest: artifactDigest,
            metadataDigest: metadataDigest,
            publishedAt: uint64(block.timestamp)
        });

        emit ReleasePublished(
            key,
            msg.sender,
            releaseId,
            artifactDigest,
            metadataDigest
        );
    }

    function get(
        address publisher,
        bytes32 releaseId
    )
        external
        view
        returns (
            bytes32 artifactDigest,
            bytes32 metadataDigest,
            uint64 publishedAt
        )
    {
        Release memory release = releases[
            keyFor(publisher, releaseId)
        ];

        return (
            release.artifactDigest,
            release.metadataDigest,
            release.publishedAt
        );
    }
}
Enter fullscreen mode Exit fullscreen mode

Ovaj ugovor namerno ne pokušava da rešava sve.

Nema globalnog administratora, upgrade mehanizma, liste odobrenih publishera ni revocation modela. Namespace svakog izdanja vezan je za adresu publishera, tako da dve organizacije mogu koristiti isti releaseId bez konflikta.

U produkcionom sistemu trebalo bi definisati:

  • kako se publisher adrese registruju
  • da li publisher koristi multisig
  • kako se rotiraju kompromitovani ključevi
  • može li se izdanje opozvati
  • ko može objaviti revocation
  • koji hash algoritam i canonical format se koriste
  • šta tačno znači metadataDigest
  • šta se dešava ako storage sa artefaktom postane nedostupan
  • da li L2 pruža dovoljan nivo sigurnosti za konkretan slučaj

Blockchain potvrđuje da je određena adresa objavila određeni digest. Ne potvrđuje da je softver bezbedan, da publisher ima dobru reputaciju ili da je privatni ključ korišćen od strane prave osobe.

To je važna granica sistema.

Integracija iz JavaScript aplikacije

Ethereum execution client izlaže JSON-RPC interfejs. Biblioteke poput ethers.js obavijaju RPC metode i koriste ABI za kodiranje poziva.

Za čitanje ugovora dovoljan je provider. Za promenu stanja potreban je signer.

Skraćena browser integracija može izgledati ovako:

import {
    BrowserProvider,
    Contract,
    id,
    keccak256,
    toUtf8Bytes
} from "ethers";

const registryAddress = "0x...";
const registryAbi = [
    "function publish(bytes32,bytes32,bytes32) returns (bytes32)",
    "function get(address,bytes32) view returns " +
        "(bytes32,bytes32,uint64)"
];

const provider = new BrowserProvider(window.ethereum);
const signer = await provider.getSigner();

const registry = new Contract(
    registryAddress,
    registryAbi,
    signer
);

const file = document.querySelector("input[type=file]").files[0];
const artifactBytes = new Uint8Array(await file.arrayBuffer());

const releaseId = id("@example/api@2.4.0");
const artifactDigest = keccak256(artifactBytes);

// Ovaj string mora biti napravljen determinističkom
// canonical JSON procedurom.
const canonicalMetadata =
    '{"commit":"8fd5...","platform":"linux-amd64"}';

const metadataDigest = keccak256(
    toUtf8Bytes(canonicalMetadata)
);

const tx = await registry.publish(
    releaseId,
    artifactDigest,
    metadataDigest
);

const receipt = await tx.wait();

console.log({
    transactionHash: receipt.hash,
    blockNumber: receipt.blockNumber
});
Enter fullscreen mode Exit fullscreen mode

Korišćenje običnog JSON.stringify nije dovoljno ako različiti sistemi mogu proizvesti drugačiji redosled ključeva ili različitu reprezentaciju istih podataka. Pre hashiranja treba definisati canonical format i tretirati ga kao deo protokola.

Za backend čitanje koristi se JsonRpcProvider:

import { Contract, JsonRpcProvider, id } from "ethers";

const provider = new JsonRpcProvider(process.env.RPC_URL);

const registry = new Contract(
    process.env.REGISTRY_ADDRESS,
    [
        "function get(address,bytes32) view returns " +
            "(bytes32,bytes32,uint64)"
    ],
    provider
);

const releaseId = id("@example/api@2.4.0");

const [
    artifactDigest,
    metadataDigest,
    publishedAt
] = await registry.get(
    process.env.PUBLISHER_ADDRESS,
    releaseId
);

console.log({
    artifactDigest,
    metadataDigest,
    publishedAt: Number(publishedAt)
});
Enter fullscreen mode Exit fullscreen mode

view poziv se obično izvršava preko eth_call. On ne menja stanje i korisnik ne šalje transakciju, pa nema onchain gas naknade. RPC provider i dalje može imati infrastrukturna ograničenja ili sopstveni model naplate.

State-changing poziv kreira transakciju, zahteva potpis i troši gas. [11][12]

Zašto frontend ne treba da koristi blockchain kao bazu

Moguće je pozivati getter funkcije za svaki ekran, ali to retko predstavlja dobru produkcionu arhitekturu.

JSON-RPC je pogodan za:

  • proveru aktuelnog stanja
  • simulaciju poziva
  • dobijanje receipt podataka
  • slanje potpisanih transakcija
  • čitanje manjeg broja ugovora i logova

Nije idealan za složene upite poput:

  • sva izdanja publishera sortirana po datumu
  • pretraga po delu naziva
  • statistika aktivnosti u poslednjih 90 dana
  • spajanje podataka iz više ugovora
  • full-text search
  • filtriranje po offchain metapodacima

Za to se koriste events i indexer.

Ugovor emituje ReleasePublished. Indexer prati blokove, dekodira događaj pomoću ABI-ja i upisuje rezultat u PostgreSQL ili drugi query-friendly sistem.

Indexer mora da obrađuje:

  • privremene chain reorganizacije
  • ponovno procesiranje blokova
  • idempotentne upise
  • pomeranje safe i finalized heada
  • promene adrese ugovora
  • ABI verzije
  • nestanak ili ograničenje RPC providera

Baza indexera predstavlja izvedeni prikaz blockchain stanja. Ako se ošteti, može se ponovo izgraditi iz logova, pod uslovom da postoji pristup potrebnoj istoriji.

Razvojni workflow koji liči na ozbiljan softver

Pametan ugovor ne bi trebalo prvo pisati u browser editoru, a zatim odmah objaviti na Mainnetu.

Razumniji tok je:

  1. Definisati trust model.
  2. Odvojiti onchain od offchain odgovornosti.
  3. Formalizovati invariants.
  4. Implementirati najmanji potreban ugovor.
  5. Pisati unit i fuzz testove.
  6. Testirati neuspešne i zlonamerne pozive.
  7. Pokrenuti sistem na lokalnoj development mreži.
  8. Deployovati na Sepolia testnet.
  9. Integrisati frontend, backend i indexer.
  10. Simulirati migraciju i incident response.
  11. Verifikovati source code ugovora.
  12. Sprovesti nezavisan security review.
  13. Tek zatim razmatrati produkcionu mrežu.

Sepolia je preporučeni javni testnet za razvoj ugovora i aplikacija. Hoodi je namenjen prvenstveno protokolskom razvoju i testiranju validatora. Testnet ETH nema istu ekonomsku vrednost kao Mainnet ETH i ne treba koristiti iste naloge i privatne ključeve u oba okruženja. [13]

Unit testovi treba da provere očekivano ponašanje. Fuzz testovi treba da pokušaju da pronađu ulaze koji krše invariants.

Za prethodni registar neki od invariants mogu biti:

  • objavljen digest se ne može prepisati
  • jedan publisher ne može menjati namespace drugog publishera
  • prazan digest se nikada ne upisuje
  • emitovani event odgovara upisanom stanju
  • funkcija keyFor uvek vraća isti rezultat za iste ulaze

Testiranje samo happy patha nije dovoljno. Napadač bira ulaze, redosled poziva i ugovore koji učestvuju u transakciji.

Najčešće greške klasičnih developera

Pretpostavka da je private podatak tajan

Solidity oznaka private ograničava pristup iz drugih ugovora na nivou jezika. Podatak u storageu i dalje je vidljiv svakome ko čita blockchain.

Lozinke, API ključevi, privatni dokumenti i nešifrovani lični podaci ne pripadaju javnom blockchainu.

Korišćenje tx.origin za autorizaciju

tx.origin predstavlja originalni EOA na početku lanca poziva, ne neposrednog pozivaoca. Autorizacija zasnovana na njemu može biti ranjiva i nije kompatibilna sa savremenim modelima pametnih naloga.

Koristite eksplicitne role, msg.sender, potpise ili dobro definisan authorization sloj.

Neproveravanje povratne vrednosti low-level poziva

call, delegatecall i staticcall imaju specifično ponašanje. Low-level call vraća status koji aplikacija mora proveriti. Pogrešna upotreba delegatecall može omogućiti izvršavanje stranog koda u storage kontekstu pozivaoca.

External call pre izmene lokalnog stanja

Ako ugovor šalje kontrolu drugom ugovoru pre ažuriranja sopstvenog stanja, druga strana može pokušati reentrant poziv.

Checks-effects-interactions obrazac, pull payments i odgovarajući guard mehanizmi smanjuju rizik, ali ne zamenjuju razumevanje celog call grapha.

Neograničene petlje nad rastućim storageom

Petlja koja prolazi kroz sve korisnike možda radi u testu sa deset naloga, ali može postati neizvršiva kada lista poraste. Gas limit bloka predstavlja hardversko i protokolsko ograničenje.

Iteraciju je često bolje pomeriti u indexer ili podeliti u više ograničenih transakcija.

Slepo kopiranje token ili proxy implementacija

Standardi poput ERC-20 opisuju interfejs i očekivano ponašanje, ali ne rešavaju automatski autorizaciju, supply politiku, upgrade administraciju ili ekonomsku sigurnost.

Za standardne komponente prednost imaju proverene biblioteke iz objavljenih paketa. Kopiranje delova biblioteke u sopstveni projekat otežava praćenje verzija i bezbednosnih ispravki.

Mešanje uključenja u blok i finalnosti

Receipt potvrđuje da je transakcija uključena u određeni blok. Ne znači da je blok već finalizovan.

Sistem treba da definiše koliko potvrda čeka i koje operacije zahteva da blok bude safe ili finalized.

Upgradeable ugovor bez plana upravljanja

Proxy omogućava promenu implementacije, ali uvodi privilegovani upgrade ključ, storage layout rizik i mogućnost da administrator promeni pravila sistema.

„Upgradeable“ nije isto što i „bezbednije“. To je drugačiji trust model koji mora biti vidljiv korisnicima.

Operativna bezbednost je deo aplikacije

Sigurnost pametnog ugovora ne završava se kada Solidity kod prođe audit.

Produkcioni sistem mora zaštititi:

  • deployer ključ
  • upgrade administratora
  • treasury
  • oracle naloge
  • relayer ključeve
  • RPC credentials
  • CI/CD pipeline
  • package registry
  • DNS i frontend
  • indexer infrastrukturu

Privilegovane operacije ne bi trebalo držati iza jednog browser walleta zaposlenog. Multisig, hardverski walleti, jasno odvojene role i timelock mehanizmi omogućavaju kontrolisaniji operativni model.

Događaje koji menjaju kritične parametre treba pratiti offchain. Monitoring može upozoriti na:

  • promenu administratora
  • upgrade implementacije
  • neuobičajeno veliki transfer
  • pauziranje protokola
  • dodelu privilegovane role
  • promenu oracle izvora
  • neočekivanu učestalost poziva

Jednostavniji ugovor obično ima manju površinu napada. Modularnost je korisna, ali svaki dodatni proxy, bridge, oracle ili governance sloj uvodi nove failure modeove. [14]

Kada Ethereum ima smisla

Ethereum je dobar kandidat kada su istovremeno važni:

  • zajedničko stanje između nezavisnih strana
  • javna proverljivost
  • programabilna pravila
  • otpornost na jednostranu administrativnu izmenu
  • digitalna imovina ili prava koja ugovor direktno kontroliše
  • interoperabilnost sa postojećim ugovorima i tokenima
  • mogućnost da korisnik sam čuva i autorizuje svoju imovinu

Naročito je koristan kada aplikacija ne bi trebalo da bude samo proizvod jedne kompanije, već protokol koji mogu koristiti drugi proizvodi.

Pametni ugovor tada liči na javni backend čija pravila, stanje i istoriju mogu proveriti svi učesnici.

Kada ga ne treba koristiti

Ethereum verovatno nije odgovarajući izbor ako:

  • aplikacija zahteva privatne podatke u osnovnom modelu
  • jedan operator već ima legitimno puno ovlašćenje
  • transakcije moraju biti besplatne i trenutno potvrđene
  • podaci se često menjaju i nemaju dugoročnu vrednost
  • potreban je intenzivan CPU ili storage workload
  • poslovna pravila moraju često i nepredvidivo da se menjaju
  • korisnici ne mogu upravljati ključevima niti koristiti posredovani wallet
  • nema realne potrebe za nezavisnom proverom stanja

Blockchain ne uklanja poverenje. On ga preraspoređuje.

Umesto poverenja u jednog administratora, sistem može zahtevati poverenje u kod ugovora, governance, oracle, wallet, bridge, sequencer, frontend i upravljanje ključevima. Dobar dizajn jasno opisuje svaku od tih pretpostavki.

Šta developer zapravo dobija

Ethereum nije privlačan zato što predstavlja neobičan način čuvanja podataka. Njegova vrednost je u kombinaciji nekoliko osobina:

  • izvršavanje je determinističko
  • stanje je javno proverljivo
  • transakcije su kriptografski autorizovane
  • pravila su programabilna
  • ugovori su međusobno kompozabilni
  • digitalna imovina može biti deo samog execution modela
  • više koraka može se izvršiti atomski
  • aplikacija može biti dostupna nezavisno od jednog backend operatora

Cena tih osobina su složeniji razvoj, ograničen throughput, promenljive naknade, javnost podataka i mnogo veća odgovornost za bezbednost.

Najbolji način da se razume Ethereum nije pravljenje još jednog tokena. Korisniji eksperiment je uzeti mali problem sa jasnom trust granicom, zapisati jedno kritično stanje u ugovor i izgraditi ostatak sistema kao normalnu aplikaciju.

Tek tada postaje očigledno šta pripada blockchainu, a šta nikada nije trebalo stavljati na njega.

Sponzorstvo

Ovaj članak je sponzorisan od strane Volet.com. Na Volet.com možete kupiti, prodati, čuvati i razmenjivati podržane kriptovalute i fiat valute.

Više informacija dostupno je na stranici Volet Srbija.

Za otvaranje naloga možete koristiti Volet.com pozivni link.

Izvori

  1. Technical introduction to Ethereum
  2. Ethereum accounts
  3. Ethereum transactions
  4. Ethereum nodes and clients
  5. Proof-of-stake consensus
  6. Ethereum Virtual Machine
  7. Introduction to smart contracts
  8. Ethereum gas and fees
  9. Ethereum scaling
  10. Fusaka protocol upgrade
  11. Ethereum JSON-RPC API
  12. ethers.js documentation
  13. Ethereum networks and testnets
  14. Smart contract security

Top comments (0)