Blizgesio ir vargo atominiai apsikeitimai

Kodėl atominiai mainai yra blogi ir kaip jiems padės kanalai, kokie svarbūs dalykai įvyko Konstantinopolio kietajame šake ir ką daryti, kai neturite iš ko mokėti už dujas.

Pagrindinė bet kurio saugumo specialisto motyvacija – noras išvengti atsakomybės.

Apvaizda buvo gailestinga, aš išėjau iš ICO, nelaukdamas pirmojo negrįžtamo sandorio, tačiau netrukus atradau save kuriant kriptovaliutų keityklą.

Aš tikrai nesu Malchish Kibalchish, ir man užtenka vieno griežto žvilgsnio, kad atiduočiau visus raktus ir slaptažodžius. Todėl pagrindinis mano, kaip architekto, tikslas buvo kuo toliau nuo man brangių infrastruktūros elementų nukelti įkaitusią kriptoanalizės geluonį.

Ne tavo raktai, ne tavo problemos

Kuriame turto mainų sistemą ir norime panaikinti tarpinį šio turto saugojimą, tačiau privalome užtikrinti sandorio saugumą.

Prieštaringoje situacijoje galite veikti kaip teisėjas ir atlikti sandorius su piniginėmis, kurioms reikia dviejų iš trijų parašų: pirkėjo, pardavėjo ir sąlyginio deponavimo.

Tačiau jei dalyvis sėkmingai atakuoja sąlyginį deponavimą, jis gauna reikiamus du parašus.

Atominis apsikeitimas yra mainų schema, kai garantas yra protinga sutartis, leidžianti tik sąžiningai elgtis.

Tarsi mįslė apie vilką, ožką ir kopūstą, gali veikti tik pagal vienintelį teisingą scenarijų ir nuo jo nukrypus patirti nuostolių.

Tik vietoj godžių gyvūnų tvarką suteikia maišos funkcija, kurioje taip sunku rasti susidūrimą, kad neverta pradėti.

Pirmas žingsnis: mįslė

Tarkime, kad Alisa vieną gražų rytą nori duoti Bobui bitkoiną už saują „kriptovaliutų“.

  • Ji kuria didelę paslaptį
  • Gauna maišos iš jo
  • Perveda bitkoinus į išmaniąją sutartį, iš kurios Bobas gali atsiimti pinigus pateikdamas paslaptį (jos maiša turi būti lygi sutartyje nurodytai)
  • Jei Bobas vakare nepasirodys savo bitkoinų, Alisa gali juos grąžinti sau.

Antras žingsnis: masalas

Bobas pradeda žaisti ir perveda „kripto eurus“ į savo sutartį, kuri surašyta taip:

  • Alisa gali atimti „kripto-jenas“ pateikdama slaptą numerį
  • Ne anksčiau nei prieš pietus, Bobas gali grąžinti užstatą, jei Alisa nepasirodys.

Trečias žingsnis: atsakymas yra masalas

Alisa ateina už savo pinigus ir paima pinigus iš Bobo sutarties, atskleisdama savo paslaptį.

Paskutinis žingsnis: mįslė įminta

Bobas mato sandorį ir savo erelio akimi ištraukia iš jo paslaptį, kurią Alisa pateikė sutartyje. Jis naudoja šią paslaptį, kad atimtų savo bitkoinus.

Kai viskas klostosi ne taip

Jei Alisa staiga pasirodo esanti mirtinga, Bobas per pietus paima savo juanį.

Savo ruožtu Alisa grąžina bitkoiną vakare, jei klastingasis Bobas nusprendžia laikyti pinigus iki geresnių laikų.

Jei jums labiau patinka paveikslėlis, o ne tekstas, Habré jums siūlo išsamesnį ir vaizdesnį. paaiškinimas, kaip veikia atominiai apsikeitimai.

Skirtumas tarp skirtojo laiko yra skirtas apsidrausti nuo piktybiškos Alisos, kuri paskutinę akimirką paima Bobo pinigus, o skirtasis laikas baigiasi, kai jis drebančiais pirštais įeina į sandorį.

Dalyviai negali prarasti pinigų, jiems teks palaukti grąžinimo.

Blockchain palaikymasTai paprasta schema, kuriai iš sąveikaujančių blokų grandinių nieko nereikia:

  • Išmaniųjų sutarčių su bent vienu filialu palaikymas
  • Abi blokų grandinės turi palaikyti tuos pačius maišos algoritmus (nepamirškite patikrinti slapto ilgio)
  • Laiko užraktai.

Iš pirmo žvilgsnio jau galima biržai pasakyti „sudie, mūsų susitikimas buvo klaida“, bet taip nebuvo.

Nepaisant visų privalumų, atominio apsikeitimo sprendimai nežavi savo likvidumu. Daugiausia dėl to, kad populiariausioje BTC-USD poroje fiat dalis nebuvo visiškai žetonizuota.
USDT sėkmė paskatino visą bangą stabilių monetų ERC20 formatu kiekvienam skoniui, nuo labiausiai saugomų USDC iki algoritmiškiausių DAI.

Todėl dėl paprastumo mes toliau ginčijame, kad Alisa parduoda Bobo bitkoinus už kai kuriuos ERC20 žetonus, ir tikimės, kad stabilizatoriai pasiseks, nes vis dar turime daug daugiau techninių problemų.

Pagreitinti

„Bitcoin“ ir „Ethereum“ atskirai nėra labai greiti, bet čia pirmiausia reikia sulaukti vieno depozito su visais patvirtinimais, tada – antrojo.

Taip yra todėl, kad paslaptį žinantis dalyvis pirmiausia įneša pinigus, o oponentas laukia baigtumo ir tik tada perveda savo dalį.

Be to, turime reikalą su labai nepastoviu aktyvu, todėl per šį laiką kursas gali gana smarkiai pasikeisti, o pakeisti sąlygas nebėra lengva.

Konfidencialumas

Bet koks keitimas palieka artefaktus abiejose blokų grandinėse. Dėmesingas stebėtojas gali pastebėti tas pačias maišas išmaniosiose sutartyse ir padaryti logišką išvadą, kad sandoris buvo baigtas, iš kurio galima padaryti daug išvadų nuo valiutos kurso iki mokesčių.

Kai birža žino apie tavo reikalus, tai be galo nemalonu, kai apie tai žino visi, dvigubai nemalonu.

Naudojimo

Stiprioji blokų grandinės pusė ir ypač eteris. Pažiūrėkime, kokius gestus turės daryti pardavėjas ir pirkėjas.

Pardavėjo požiūriu viskas gana paprasta: tereikia pervesti Bitcoin į p2sh adresą. Su eteriu viskas yra daug sudėtingiau.

SutartisPažvelkime į sutarties vidurkį, palyginti su „Github“, dėl apsikeitimo:

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);
    }
}

Dėmesio! Nenaudokite šios ir kitų sutarčių iš straipsnio gamyboje, jos parašytos tik demonstravimo tikslais. Ypač šis.

  • Bobas turi iškviesti žetonų sutarties metodą approve, suteikiant apsikeitimo sutarčiai prieigą prie jos žetonų
  • Bobas sukuria apsikeitimo sandorį ir sudaro sutartį naudodamas šį metodą transferFrom nuneša siuntėjo žetonus jūsų adresu
  • Alisa withdraw atskleidžia paslaptį ir sutartis ragina transfer

Dauguma piniginių ir kriptovaliutų biržų nepalaiko approve žetonų ir dėl geros priežasties.

Patys vartotojai dažnai klysta ir tiesiog perkelia žetonus į sutartį, o po to žetonai tiesiog prarandami. Etherscan komentaruose gausu nelaimingųjų dejonių.

Ir norint sudaryti sutartį, reikia sumokėti komisinį mokestį ETH, o tai reiškia, kad abu dalyviai turi sukaupti atsargų prieš pradėdami sandorį, ir tik nedaugelis nori tai padaryti.

Dujų laikiklis

Gera vieta pradėti yra pašalinti siuntėjo čekį, kai tik įmanoma, ir manyti, kad kažkas kenčia nuo perteklinių dujų skambučių sutarčių visiems atvykėliams.

Patobulinta sutartis

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);
    }
}

Sutartinis dualizmas ir EIP 712

Kaip žinome, adresas eteryje gali būti sutartis arba dalykas, tai yra raktas.
Pagrindinis rakto darbas yra pasirašyti kai kuriuos pranešimus.

Bob-kontraktą galime naudoti kaip siuntėją, kuris atlieka visus reikiamus leidimus, prieš tai patikrinęs Bob-key parašą.

Dabar kiekvienas gali remti dalyvio komisiją, tačiau sprendimą priima tik tas, kuris žino raktą.

Bobo sutartis

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);
    }
}

Ethereum turi standartą darbui su sudėtingų duomenų struktūrų parašais EIP 712 m, daugiau apie tai galite perskaityti Metamask piniginės tinklaraštis

Padalinkite ir užkariaukite

Dažnai Ethereum sutarties įsilaužimo scenarijus atrodo taip:

  • Dalyvis įneša lėšas į sutartį
  • Tada jis paima lėšas
  • Kažkas negerai
  • Užpuolikas vėl ir vėl paima pinigus

Jei grįšime į pirmąjį pavyzdį, kažkas negerai, jei mįslė yra tuščias baitų rinkinys.

Kaip pavogti milijonąSukurkite apsikeitimo sandorį su maiša 0x66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925
Tai yra sha256 iš 0x0000000000000000000000000000000000000000000000000000000000000000
Perduodame paslaptį ir paimame savo žetonus
Persiunčiame dar kartą ir paimame kieno nors kito, nes 0 = 0

Kiekvienam sandoriui sudarydami atskirą sutartį, galime išskirti sutartis EVM lygiu.

Bet tai dar ne viskas: dabar kiekviena operacija turi savo adresą, į kurį galite pervesti žetonus iš bet kurios piniginės ar biržos.

Atsisakyta sutarčių ir sukurti2

Bet dabar kiekvienam sandoriui turime sudaryti sutartį ir laukti, kol pirkėjas ten perkels darbo „kriptografiją“. Schemoje „sutartys ryte, pinigai vakare“ visada yra pavojus, kad pirkėjas nukris, o eteris jau išleistas kuriant sutartį.

Ar galima padaryti taip, kad ryte turėtum pinigų, o vakare – baitus?

Kūrėjai Konstantinopolio kietajame šakute EIP 1014 m pridėta „create2“ instrukcija, kuri sukuria naują sutartį deterministiniu adresu

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

kur

  • adresas – gamyklos sutarties adresas
  • druska - tam tikras skaičius, kurio reikšmę sužinosime kitoje serijoje
  • init_code – sutarties baito kodas ir konstruktoriaus parametrai.

GamyklaInstrukcija veikia tik surinkus, todėl gamykla atrodo šiek tiek baisiai:

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);
  }
}

Sutarties kodą galite gauti naudojant web3:

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

Dėl riboto kietumo atramos dujos kontraktui gali būti neteisingai apskaičiuojamos dėl kai kurių eterio subtilybių.

Ypač smagu, kad pritrūkus dujų sutartis sugenda su vidine klaida, nepranešant, kad pritrūko dujų, kaip ir galima tikėtis.

Dabar žetonus galime perkelti į sutartis iš anksto jų nesukūrę, o kol nepaskelbsime jų tinkle, niekas neatspės, ką tiksliai daro sutartis.

Varnas varnui akies neišrėks

Aišku, kad tikro analitiko, ypač gavusio gerų investicijų į kovą su režimo priešais pinigų plovimu, tokie vaikiški triukai nesustabdys, o sukūręs kontraktą vis tiek pamatys maišą.

Kaip išvengti maišos rodymo?

Pačią apsikeitimo sandorį perkeliame į offchain: dalyviai pasikeičia parašais dėl perėjimo į mainų sutartį, o tada paslaptis atskleidžiama privačiai.

Žingsnis po žingsnioSukuriami du „multisigiai“, iš kurių galima atsiimti lėšas, jei Alisa ir Bobas turi parašus.

Kad nė vienas iš dalyvių neprisijungus netaptų tragedija, pridėsime seną gerą skirtąjį laiką.

Alisa ir Bobas lygiagrečiai deda indėlius

  • Alisa atspėja paslaptį ir duoda Bobui paslapties maišą bei operacijos parašą, kuris perkelia bitkoinus į apsikeitimo adresą.
  • Bobas duoda Alisai parašą, kad atimtų mainų sutarties žetonus su paslėpta maiša.
  • Alisa išduoda Bobui paslaptį.

Šiuo metu ateina harmonija: ir Alisa, ir Bobas gali bet kada užbaigti sandorį. Tokioje draugiškoje aplinkoje jie gali pasikeisti parašais, kad išimtų pinigus galutiniais adresais.

Išoriniam stebėtojui atrodo, kad pinigai buvo sudaryti pagal 2 iš 2 multisig sutartį.

Ši schema taip pat leidžia abiem šalims atlikti indėlį vienu metu, nes paslaptis atskleidžiama po visų patvirtinimų.

Lygis 2

Kadangi galime atsiimti pinigus vienu adresu ir neskelbti tarpinės operacijos, niekas netrukdo išsiimti pinigų keliais adresais ir atlikti neribotą skaičių tarpinių operacijų. Tai nereiškia, kad tai būtinas rinkinys mainams, tačiau pradėjus rinkti apsikeitimo sandorį sunku sustoti.

Dabar Alisa ir Bobas galės suktis iš visų jėgų. Pavyzdžiui, automatiškai apskaičiuokite vidutinę kainą keisdami satoshi per sekundę arba tiesiog tiesiogiai susiekite rinkos formuotoją ir likvidumo gavėją.

Žingsnis po žingsnio

  • Pardavėjas padaro paslaptį ir suteikia pirkėjui paslapties maišą ir sandorio parašą, kai dalis lėšų pervedama į p2sh apsikeitimo adresą, o likusi dalis grąžinama pardavėjo adresu.
  • Pirkėjas pateikia parašą, leidžiantį išsiųsti mainų žetonus ir keitimą gavėjo adresu.
  • Pardavėjas atskleidžia paslaptį
  • Istorija kartojasi su nauja paslaptimi, o be apsikeitimo ir keitimo pridedamas anksčiau pirkto pirkėjo adresu ir jau sumokėto pardavėjo adresu atsiėmimas.

Dabar turime prieigą prie didelės spartos p2p prekybos, svarbiausia sekti laiką ir užbaigti sandorį prieš pasibaigiant skirtajam laikui.

Tačiau šiek tiek pakoreguodami savo sutartis galime savo kanalams suteikti nemirtingumo, o tai labai palengvins tinklo kūrimą.

Bet apie tai kalbėsime kitame epizode.

Šaltinis: www.habr.com

Добавить комментарий