Sjaj i siromaštvo atomic swaps

Što su loše atomarne zamjene i kako im kanali pomažu, što se važno dogodilo u hard forku Constantinople i što učiniti kada nemate čime platiti plin.

Glavna motivacija svakog stručnjaka za sigurnost je želja da izbjegne odgovornost.

Providnost je bila milostiva, napustio sam ICO, ne čekajući prvu nepovratnu transakciju, ali ubrzo sam se našao na razvoju kriptoburze.

Ja sam odlučno ne Malčik Kibalčiš, i jedan strogi pogled je dovoljno da predam sve ključeve i lozinke. Stoga je moj glavni cilj kao arhitekta bio postaviti užareni ubod kriptoanalize što dalje od menadžerskih elemenata infrastrukture.

Nisu tvoji ključevi, nisu tvoji problemi.

Gradimo sustav razmjene sredstava i želimo isključiti posredno skladištenje tih sredstava kod nas, ali moramo osigurati sigurnost transakcije.

Možeš biti suca u spornoj situaciji i obavljati transakcije s novčanicima koji zahtijevaju dva od tri potpisa: kupca, prodavatelja i escrow-a.

Međutim, ako sudionik uspješno napadne escrow, onda dobiva potrebna dva potpisa.

Atomarna zamjena je shema razmjene gdje jamac je pametan ugovor koji dopušta samo pošteno ponašanje.

Kao u zagonetki o vuku, ovci i kupusu, možeš djelovati samo prema jedinom ispravnom scenariju i nositi gubitke ako od njega odstupiš.

Samo umjesto proždrljivih životinja, red osigurava hash funkcija, u kojoj je tako teško pronaći koliziju da se ne isplati ni započinjati.

Korak prvi: zagonetka.

Pretpostavimo da u jednom lijepom jutru Alisa želi prenijeti Bobu bitcoin za šaku “kriptojvana”.

  • Ona postavlja neki veliki tajnu.
  • Dobiva od njega hash.
  • Prenosi bitcoine na pametni ugovor, s kojeg novac može preuzeti Bob, nudeći tajnu (hash od nje mora biti jednak onome navedenom u ugovoru).
  • Ako Bob ne dođe po svoje bitcoine do večeri, Alisa ih može uzeti natrag.

Korak drugi: mamac.

Bob ulazi u igru i šalje “kriptoeure” na svoj ugovor, koji je napisan tako da:

  • Alisa može uzeti “kriptojene” nudeći tajni broj.
  • Ne ranije od ručka, Bob, u slučaju nepojave Alise može vratiti depozit.

Korak treći: rješenje mamca.

Alisa dolazi po svoj novac i preuzima novac s Bobovog ugovora, otkrivajući pritom svoju tajnu.

Zadnji korak: zagonetka je riješena

Transakciju vidi Bob i svojim oštrim okom iz nje izdvaja tajnu koju je Alisa predstavila ugovoru. Tu tajnu koristi da povuče svoje bitcoine.

Kad nešto pođe po zlu

Ako Alisa iznenada postane smrtnica, Bob će u podne uzeti svoje juane.

S druge strane, Alisa će do večeri vratiti bitcoin ako se pokvarenjak Bob odluči zadržati novac do boljih vremena.

Ako preferirate sliku umjesto teksta, na Habr-u vas čeka detaljnije i slikovitije objašnjenje funkcioniranja atomskih swapova.

Razlika među vremenskim ograničenjima osmišljena je da nas zaštiti od zloćudne Alise koja uzima Bobov novac u samom posljednjem trenutku, dok on drhtavim prstima unosi heks u transakciju.

Sudionici ne mogu izgubiti svoj novac, maksimalno će morati pričekati povrat.

Podrška u blockchainimaTo je jednostavna, kao čizma, shema koja zahtijeva od interaktivnih blockchaina vrlo malo:

  • Podrška pametnim ugovorima s barem jednim grananjem
  • Oba blockchaina moraju podržavati iste algoritme hashiranja (ne zaboravite provjeriti duljinu tajne)
  • Vremenska zaključavanja.

Na prvi pogled, već možemo reći burzi "zbogom, naše susret je bila greška", ali nije tu kraj.

Unatoč svim svojim prednostima, rješenja na atomic swap ne oduševljavaju likvidnošću. Uvelike zbog toga što je u najpopularnijem paru BTC-USD fiatni dio bio nedovoljno tokeniziran.
Uspjeh USDT-a doveo je do cijele eksplozije stabilnih kovanica formata ERC20 za svaki ukus, od najkontejnerskog USDC-a do najsloženijeg DAI-a.

Stoga, zbog jednostavnosti, dalje razmatramo da Alisa prodaje Boba bitcoine za neke ERC20 tokene, nadajući se sreći stabilizatora, budući da nas još čeka mnogo tehničkih problema.

Brzina

Bitcoin i Ethereum, svaki posebno, nisu previše brzi, a evo, prvo moramo čekati jedan depozit sa svim potvrdama, a zatim drugi.

Sve to zato što prvo novac uplaćuje sudionik kojem je poznata tajna, dok protivnik čeka konačnost i tek tada prenosi svoj dio.

Osim toga, imamo posla s vrlo volatilnom imovinom, pa se tijekom tog vremena tečaj može značajno promijeniti, a promjena uvjeta postaje teška.

Povjerljivost

Svaka razmjena ostavlja artefakte na oba blockchaina. Pažljiv promatrač može primijetiti iste hash-e u pametnim ugovorima i logično zaključiti da se ovdje dogodila transakcija, iz čega se može izvesti mnoštvo zaključaka, od tečajeva do poreznih.

Kada berza zna o tvojim poslovima — to je vrlo neugodno, kada o tome zna svatko — to je dvostruko neugodno.

Upotrebljivost

Posebnost blockchaina općenito i Ethereuma posebno. Pogledajmo kakve će korake trebati poduzeti prodavač i kupac.

Iz perspektive prodavača sve je relativno jednostavno: samo treba prenijeti Bitcoin na p2sh adresu. S Ethereom je to daleko složenije.

UgovorPogledajmo prosječni ugovor za razmjenu prema GitHubu:

contract iERC20 {
    function totalSupply() public view returns (uint256);
    function transfer(address receiver, uint numTokens) public returns (bool);
    function balanceOf(address tokenOwner) public view returns (uint);
    function approve(address delegate, uint numTokens) public returns (bool);
    function allowance(address owner, address delegate) public view returns (uint);
    function transferFrom(address owner, address buyer, uint numTokens) public returns (bool);
}

contract Swapper {

    struct Swap {
        iERC20 token;
        bytes32 hash;
        uint amount;
        uint refundTime;
        bytes32 secret;
    }

    mapping (address => mapping(address => Swap)) swaps;

    function create(iERC20 token, bytes32 hash, address receiver, uint amount, uint refundTime) public {
        require(swaps[msg.sender][receiver].amount == 0); // check is swap with given hash already exists
        require(token.transferFrom(msg.sender, address(this), amount)); // transfer locked tokens to swap contract
        swaps[msg.sender][receiver] = Swap(token, hash, amount, refundTime, 0x00); //create swap
    }
    
    function hashOf(bytes32 secret) public pure returns(bytes32) {
        return sha256(abi.encodePacked(secret));
    }


    function withdraw(address owner, bytes32 secret) public {
        Swap memory swap = swaps[owner][msg.sender];
        require(swap.secret == bytes32(0));
        require(swap.hash == sha256(abi.encodePacked(secret))); // swap exists
        swaps[owner][msg.sender].secret = secret;
        swap.token.transfer(msg.sender, swap.amount);
    }

    function refund(address receiver) public {
        Swap memory swap = swaps[msg.sender][receiver];
        require(now > swap.refundTime);
        delete swaps[msg.sender][receiver];
        swap.token.transfer(msg.sender, swap.amount);
    }
}

Pažnja! Nemojte koristiti ovaj i druge ugovore iz članka u produkciji, napisani su isključivo za demonstraciju. Osobito ovaj.

  • Bob mora pozvati metodu ugovora s tokenima approve, dajući ugovoru za razmjenu pristup svojim tokenima
  • Bob stvara razmjenu i ugovor pomoću metode transferFrom koja preuzima tokene pošiljatelja na svoju adresu
  • Alisa u withdraw otkriva tajnu i poziva ugovor transfer

Većina novčanika i kriptoburzi ne podržava approve tokene, i nije bez razloga.

Sami korisnici često griješe i jednostavno prebacuju tokene na ugovor, nakon čega tokene jednostavno nestaju. Komentari na Etherscan puni su jauka nesretnika.

Da bi se pozvao ugovor, potrebno je platiti naknadu u ETH-u, što znači da se oba učesnika moraju opskrbiti njime prije početka transakcije, a to malo koga zanima.

Gazgolder

Za početak, trebali bismo ukloniti provjeru pošiljatelja svugdje gdje god je to moguće i pretpostaviti da imamo nekoga tko pati od viška plina koji poziva ugovore za sve zainteresirane.

Modernizirani ugovor

contract Swapper {

    struct Swap {
        iERC20 token;
        address receiver;
        uint amount;
        address refundAddress;
        uint refundTime;
    }

    mapping (bytes32 => Swap) swaps;

    function create(iERC20 token, bytes32 hash, address receiver, uint amount, address refundAddress, uint refundTime) public {
        require(swaps[hash].amount == 0); // use hash once
        require(token.transferFrom(msg.sender, address(this), amount));
        swaps[hash] = Swap(token, receiver, amount, refundAddress, refundTime);
    }


    function withdraw(bytes memory secret) public {
        bytes32 hash = sha256(secret);
        Swap memory swap = swaps[hash];
        require(swap.amount > 0);
        delete swaps[hash];
        swap.token.transfer(swap.receiver, swap.amount);
    }

    function refund(bytes32 hash) public {
        Swap memory swap = swaps[hash];
        require(now > swap.refundTime);
        delete swaps[hash];
        swap.token.transfer(swap.refundAddress, swap.amount);
    }
}

Dijalekt ugovora i ključeva te EIP 712

Kao što znamo, adresa u eteru može biti ugovor ili subjekt, odnosno ključ.
Glavna zadaća ključa je potpisivanje nekih poruka.

Možemo koristiti kao pošiljatelja Bob-ugovor koji obavlja sve potrebne korake, prvo provjerivši potpis Boba-ključa.

Sada, tko god može sponzorirati naknadu učesnika, ali odluku donosi samo onaj tko zna ključ.

Bob-ugovor

library EIP712ProxyLibrary {
    function hashCommand(address sender, iERC20 token, Swapper swapper, bytes32 hash, address receiver, uint amount, address refundAddress, uint refundTime) public view returns(bytes32);
}

contract ProxyBob {
    address owner;

    constructor(address _owner) public {
        owner = _owner;
    }

    function createSwap(Swapper swapper, iERC20 token, bytes32 hash, address receiver, uint amount, address refundAddress, uint refundTime, uint8 v, bytes32 r, bytes32 s) public {
        require(owner == ecrecover(EIP712ProxyLibrary.hashCommand(address(this), token, swapper, hash, receiver, amount, refundAddress, refundTime), v, r, s));
        token.approve(address(swapper), amount);
        swapper.create(token, hash, receiver, amount, refundAddress, refundTime);
    }
}

Za rad s potpisima složenih struktura podataka u Ethereum postoji standard EIP 712, više možete pročitati o njemu u blogu novčanika Metamask

Dijeli i vladaj

Često scenarij napada na Ethereum ugovor izgleda ovako:

  • Sudionik stavlja sredstva na ugovor
  • Zatim uzima sredstva
  • Nešto nije u redu
  • Napadač uzima novac iznova i iznova

Ako se vratimo našem prvom primjeru, nešto nije u redu ako je misterij prazna serija bajtova.

Kako ukrasti milijunStvaramo swap s hash-om 0x66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925
To je sha256 od 0x0000000000000000000000000000000000000000000000000000000000000000
Prenosimo tajnu i uzimamo svoje tokene
Ponovno prenosimo i uzimamo tuđe, sve zbog toga što 0 = 0

Kreirajući za svaku transakciju zaseban ugovor, možemo izolirati ugovore na razini EVM-a.

Ali to nije sve: sada svaka transakcija ima svoju adresu, na koju se mogu prenijeti tokeni s bilo kojeg novčanika ili burze.

Ostavljeni ugovori i create2

Ali sada za svaku transakciju moramo stvarati ugovor i čekati da kupac prenese svoj radni "kriptovent". U shemi "ujutro ugovori, navečer novac" uvijek postoji opasnost da kupac odustane, a eter za stvaranje ugovora je već potrošen.

Zar se ne može napraviti tako da ujutro novac, a navečer bajtovi?

U hardforku Constantinople programeri EIP 1014 dodali su instrukciju create2 koja stvara novi ugovor na determinističkoj adresi

keccak256( 0xff ++ address ++ salt ++ keccak256(init_code))[12:]

Gdje

  • address — adresa fabrike ugovora
  • salt — neko broj, čija svrha ćemo saznati u sljedećoj epizodi
  • init_code — bajt kod ugovora i parametri konstruktora.

FabrikaInstrukcija radi samo putem assembly, pa fabrika izgleda pomalo zastrašujuće:

contract Factory {
  event Deployed(address addr, uint256 salt);

  function create2(bytes memory code, uint256 salt) public {
    address addr;
    assembly {

      addr := create2(0, add(code, 0x20), mload(code), salt)
    }

    emit Deployed(addr, salt);
  }
}

Kod vašeg ugovora može se dobiti pomoću web3:

const MyContract = new web3.eth.Contract(ABI, {})
const code = MyContract.deploy({
    data: BYTECODE,
    arguments: ConstructorArgs  
}).encodeABI();
const factory = new web3.eth.Contract(FACTORY_ABI, factoryAddress);
tx = factory.methods.create2(code, salt);

Zbog ograničene podrške u solidity, plin za ugovor može se izračunati pogrešno zbog nekih nijansi etera.

Osobito je simpatično to što u slučaju nedostatka plina ugovor pada s unutarnjom pogreškom, ne prijavljujući pritom da je plina ponestalo, kao što bi se moglo očekivati.

Sada možemo prelaziti tokene na ugovore bez prethodnog stvaranja, a sve dok ih ne objavimo na mreži, nitko neće naslutiti što ugovor zapravo radi.

Vrana vrani oči ne vadi.

Jasno je da pravog analitičara, posebno onog koji je dobio dobre investicije za borbu protiv neprijatelja režima pranjem novca, takve dječje trikove neće zaustaviti, i nakon stvaranja ugovora on će svejedno vidjeti hash.

Kako učiniti da hash ne bude vidljiv?

Sam swap prenosimo na off-chain: sudionici razmjenjuju potpise za prijenos na swap-ugovor, a zatim se tajna privatno otkriva.

Korak po korak.Stvaraju se dva “multisig” ugovora, s kojih se sredstva mogu podići uz potpise Alise i Boba.

Kako ne bi ispadanjem nekoga od sudionika u offline postavilo tragediju, dodajemo stari dobri vremenski limit.

Alisa i Bob paralelno uplaćuju depozite.

  • Alisa smišlja tajnu i predaje Bobu hash tajne te potpis transakcije koja prenosi bitcoine na adresu swap-a.
  • Bob predaje Alisi potpis za isplatu tokena na swap-ugovor s smišljenim hashom.
  • Alisa obavještava Boba o tajni.

U tom trenutku nastaje harmonija: i Alisa i Bob mogu u bilo kojem trenutku završiti transakciju. U takvom prijateljskom okruženju mogu razmijeniti potpise za isplatu novca na konačne adrese.

Za vanjskog promatrača to izgleda kao da su novci prošli kroz ugovor s multipotpisom 2 od 2.

Osim toga, takva shema omogućava obje strane da istovremeno polože depozit, jer se tajna smišlja već nakon svih potvrda.

Nivo 2.

Budući da možemo isplaćivati novac na jednu adresu i ne objavljivati međutrajnu transakciju, ništa nam ne može smetati da isplaćujemo novac na nekoliko adresa i obavljamo neograničen broj međutrajnih transakcija. Nije da je to nužan set za razmjenu, ali kad započnete sakupljanje swap-a, teško je stati.

Sada će Alisa i Bob moći maksimalno iskoristiti situaciju. Na primjer, automatski izračunati prosječnu cijenu, razmjenjujući po satoshiju u sekundi, ili jednostavno izravno povezati tržišnog kreatora i primatelja likvidnosti.

Korak po korak.

  • Prodavatelj smišlja tajnu i predaje kupcu hash tajne te potpis transakcije gdje se dio sredstava prenosi na p2sh adresu swap-a, a ostatak se vraća na adresu prodavatelja.
  • Kupac prenosi potpis koji omogućuje isplatu tokena i povrata na adresu primatelja.
  • Prodavač otkriva tajnu
  • Povijest se ponavlja s novom tajnom, uz to se dodaje isplata ranije kupljenog na adresu kupca i već plaćenog na adresu prodavača.

Sada nam je dostupna visoko brza p2p trgovina, bitno je pratiti vrijeme i zatvoriti posao prije isteka.

Međutim, malo prilagodivši naše ugovore, možemo darovati našim kanalima besmrtnost, što će nam znatno olakšati stvaranje mreže.

Ali o tome ćemo govoriti u sljedećoj epizodi.

Izvor: habr.com

Kupite pouzdan hosting za web stranice s DDoS zaštitom, VPS VDS servere 🔥 Kupite pouzdan hosting za web stranice s DDoS zaštitom, VPS VDS servere | ProHoster