Lesk a bída atomových swapů

Co je špatného na atomických swapech a jak jim mohou pomoci kanály, co důležitého se stalo v hardforku Constantinople a jak se vypořádat, když nemáte na zaplacení plynu.

Hlavní motivací každého bezpečnostního specialisty je touha vyhnout se odpovědnosti.

Prozřetelnost byla shovívavá, opustil jsem ICO, aniž bych čekal na první nevratnou transakci, ale brzy jsem se ocitl při vývoji kryptoburzy.

Rozhodně nejsem Chlapec Kibalčik a jeden přísný pohled stačí, abych odevzdal všechny klíče a hesla. Proto byla mojí hlavní cílem jako architekta umístit žhavý bod kryptoanalyzy co nejdál od mých cenných prvků infrastruktury.

Nejsou to tvoje klíče, nejsou to tvoje problémy.

Budujeme systém výměny aktiv a chceme vyloučit meziskladování těchto aktiv u sebe, ale musíme zajistit bezpečnost transakce.

Můžete fungovat jako rozhodčí v sporné situaci a provádět transakce s peněženkami, které vyžadují dva ze tří podpisů: kupujícího, prodávajícího a escrow.

Pokud však účastník úspěšně zaútočí na escrow, získá hledané dva podpisy.

Atomický swap je mechanismus výměny, kde zárukou je smart kontrakt, který povoluje pouze čestné chování.

Jakoby v hádance o vlka, kozu a zelí můžete jednat pouze podle jedné správné strategie a nesete ztráty, pokud se od ní odchýlíte.

Jen místo žravých zvířat zajišťuje pořádek hash funkce, ve které je tak obtížné najít kolizi, že ani nestojí za to začínat.

Krok první: hádanka

Předpokládejme, že Alice jednoho krásného rána chce poslat Bobovi bitcoiny za hromádku "kryptojuanů".

  • Představí si nějaké velké tajemství.
  • Dostane od něj hash.
  • Převede bitcoiny na smart kontrakt, ze kterého si může Bob peníze vybrat, když předloží tajemství (hash od něj musí být shodný s tím, který je uveden ve smlouvě).
  • Pokud se Bob pro své bitcoiny do večera neobjeví, může si je Alice vzít zpět.

Krok druhý: návnada.

Do hry vstupuje Bob a převádí "kryptoevra" na svůj kontrakt, který je napsán tak, že:

  • Alice si může vzít "kryptoyeny", pokud předloží tajné číslo.
  • Ne dříve než po obědě může Bob, pokud se Alice nedostaví, vrátit vklad.

Krok třetí: rozluštění v návnadě.

Alisa přichází pro své peníze a vybírá je z Bobovy smlouvy, přičemž odhaluje své tajemství.

Konečný krok: hádanka byla rozluštěna

Transakci vidí Bob a orlím zrakem vyčleňuje ze ní tajemství, které Alisa předložila ve smlouvě. Toto tajemství využívá, aby si vzal své bitcoiny.

Když něco jde špatně

Pokud se Alisa náhle stane smrtelnou, Bob si odpoledne vezme své jüany.

Na oplátku Alisa k večeru vrací bitcoin, pokud zrádný Bob nehodlá zadržet peníze na lepší časy.

Pokud dáváte přednost obrázkům před textem, na Habr pro vás je podrobnější a názornější vysvětlení fungování atomických swapů.

Rozdíl mezi časovými limitami má za cíl chránit nás před zlomyslnou Alisou, která bere Bobovy peníze v poslední chvíli, a časový limit vyprší, zatímco on třesoucíma se prsty zadává hex do transakce.

Účastníci nemohou ztratit své peníze, maximálně budou muset počkat na navrácení.

Podpora v blockchainechJe to jednoduchá, jako bax, schéma, která vyžaduje od interagujících blockchainů jen málo:

  • Podpora chytrých kontraktů s alespoň jedním rozvětvením
  • Oba blockchany musí podporovat stejné algoritmy hashování (nezapomeňte zkontrolovat délku tajemství)
  • Časové zámky.

Na první pohled už lze říci burze 'sbohem, naše setkání byla chyba', ale není tomu tak.

Navzdory všem svým přednostem řešení na atomickém swapu neohromují likviditou. Do značné míry proto, že v nejpopulárnějším páru BTC-USD nebyla fiat část plně tokenizována.
Úspěch USDT způsobil celou vlnu stabilních mincí ve formátu ERC20 podle libosti, od nejkonzervativnějšího USDC po nejvíce algoritmický DAI.

Proto o jednoduchosti my dále uvažujeme tak, že Alisa prodává Bobovi bitcoiny za nějaké tokeny ERC20, a doufáme v úspěch stabilizátorů, dobrá zpráva je, že máme ještě mnohem více technických problémů.

Rychlost

Bitcoin a Ethereum samostatně nejsou příliš rychlé, a teď musíme čekat nejprve na jednu zálohu se všemi potvrzeními, poté na druhou.

To je všechno proto, že nejprve peníze vkládá účastník, který zná tajemství, a protihráč čeká na konečnost a teprve poté převádí svou část.

Kromě toho se zabýváme velmi volatilním aktivem, takže během této doby se kurz může výrazně změnit a změnit podmínky už není tak snadné.

Ochrana soukromí

Jakákoli výměna zanechává artefakty na obou blockchainech. Pozorný pozorovatel si může všimnout stejných hashů ve smart kontraktech a učinit logický závěr, že došlo k transakci, což může vyvolat spoustu dedukcí od kurzových až po daňové.

Když o tvých záležitostech ví burza — je to velmi nepříjemné, když o tom ví každý — je to dvakrát nepříjemné.

Uživatelská přívětivost

Jednoduchost blockchainu obecně a Etherea zvlášť. Podívejme se, jaké kroky musí podniknout prodávající a kupující.

Z pohledu prodávajícího je vše relativně jednoduché: stačí převést Bitcoin na p2sh adresu. S Ethereem je to mnohem složitější.

SmlouvaPodívejme se na průměrný kontrakt pro swap na 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);
    }
}

Pozor! Nepoužívejte tento a další kontrakty z článku v produkci, jsou napsány výhradně k demonstraci. Zvlášť tento.

  • Bob by měl zavolat metodu tokenového kontraktu approve, aby dal swappovému kontraktu přístup k jeho tokenům
  • Bob vytváří swap a kontrakt pomocí metody transferFrom na svůj adresu převzít tokeny odesílatele
  • Alica ve withdraw odhaluje tajemství a smlouva vyžaduje přenos

Většina peněženek a kryptoburz nepodporuje approve tokeny, a není to bezdůvodné.

Uživatelé často chybují a jednoduše převádějí tokeny na smlouvu, po čemž tokeny jednoduše zmizí. Komentáře na Etherscan jsou plné nářků nešťastných.

A k vyvolání smlouvy je potřeba zaplatit poplatek v ETH, což znamená, že oba účastníci by si měli před zahájením obchodu připravit, a tím se málokdo chce zabývat.

Gazgolder

Nejprve je dobré odstranit kontrolu odesílatele všude, kde je to možné, a předpokládat, že máme někoho, kdo trpí přebytkem plynu a vyvolává smlouvy pro všechny zájemce.

Modernizovaná smlouva

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

Smluvně-klíčový dualismus a EIP 712

Jak víme, adresa na ethereu může být smlouvou, nebo může být subjektem, tedy klíčem.
Hlavní činností klíče je podepisovat nějaké zprávy.

Můžeme použít jako odesílatele smlouvu Bob, která provede všechny potřebné kroky, přičemž předtím zkontroluje podpis klíče Boba.

Nyní může kdokoliv sponzorovat poplatek účastníka, ale rozhodnutí přijímá pouze ten, kdo zná klíč.

Bob-kontakt

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

Pro práci s podpisy složitých datových struktur v Ethereu existuje standard EIP 712, podrobnosti o něm si můžete přečíst v blogu peněženky Metamask

Dělit a vládnout

Často scénář hacku Ethereum kontraktu vypadá takto:

  • Účastník vloží prostředky do kontraktu
  • Poté vybere prostředky
  • Něco se pokazí
  • Útočník vybírá peníze znovu a znovu

Pokud se vrátíme k našemu prvnímu příkladu, něco se pokazí, pokud je záhadou prázdná sada bajtů.

Jak ukrást milionVytváříme swap s hash 0x66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925
To je sha256 od 0x0000000000000000000000000000000000000000000000000000000000000000
Předáváme tajemství a vybíráme své tokeny
Předáváme znovu a vybíráme cizí, vše proto, že 0 = 0

Vytvářením samostatného kontraktu pro každou transakci můžeme izolovat kontrakty na úrovni EVM.

Ale to není všechno: nyní má každá transakce svou adresu, na kterou lze převést tokeny z jakékoli peněženky nebo burzy.

Opomenuté kontrakty a create2

Ale nyní pro každou transakci musíme vytvořit kontrakt a čekat, až kupující převede do něj své „kryptoměnové peníze“. V schématu „ráno kontrakty, večer peníze“ vždy existuje nebezpečí, že kupující odpadne, a ether na vytvoření kontraktu již byl utracen.

Není možné udělat tak, aby ráno byly peníze a večer bajty?

V hard forku Constantinople vývojáři EIP 1014 přidali instrukci create2, která vytváří nový kontrakt na determinované adrese

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

Kde

  • adresa — adresa kontraktu továrny
  • salt — nějaké číslo, jehož význam se dozvíme v příští epizodě
  • init_code — bajtový kód kontraktu a parametry konstruktoru.

TovárnaInstrukce funguje pouze prostřednictvím assembly, takže továrna vypadá poněkud strašidelně:

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

Kód vašeho kontraktu lze získat pomocí 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);

Kvůli omezené podpoře v solidity může být plyn pro kontrakt vypočítán nesprávně kvůli některým nuancím etherea.

Obzvlášť milé je, že v případě nedostatku plynu kontrakt spadne s vnitřní chybou, aniž by přitom hlásil, že plyn došel, jak by se dalo očekávat.

Nyní můžeme převádět tokeny na kontrakty, aniž bychom je vytvářeli předem, a dokud je nezveřejníme na síti, nikdo nezjistí, co kontrakt skutečně dělá.

Vrána vraně oko nevykougne.

Je jasné, že skutečného analytika, zvlášť toho, který získal dobré investice na boj s nepřáteli režimu praním peněz, takové dětské triky nezastaví, a po vytvoření kontraktu stejně uvidí hash.

Jak udělat, aby hash nebyl odhalen?

Samotný swap přesouváme na off-chain: účastníci si vyměňují podpisy pro převod na swap-kontrakt, a poté se tajemství soukromě odhalí.

Krok za krokem.Vznikají dva „multisig“, ze kterých lze vybrat prostředky za přítomnosti podpisů Alice a Boba.

Aby odchod do offline nikoho z účastníků nebyl tragédií, přidáme starý dobrý timeout.

Alice a Bob paralelně vkládají depozity.

  • Alice si vymyslí tajemství a předá Bobovi hash tajemství a podpis transakce, která převádí bitcoiny na adresu swapu.
  • Bob předává Alici podpis pro výběr tokenů na kontrakt swapu se vymyšleným hashem.
  • Alice sděluje Bobovi tajemství.

V tuto chvíli nastává harmonie: Alice i Bob mohou kdykoliv dokončit obchod. V takovém přátelském prostředí si mohou vyměnit podpisy pro výběr peněz na konečné adresy.

Pro externího pozorovatele to vypadá, jako by peníze prošly přes kontrakt s multipodpisem 2 z 2.

A také tento systém umožňuje oběma stranám provádět vklad zároveň, protože tajemství se vymýšlí už po všech potvrzeních.

Úroveň 2.

Pokud můžeme vybírat peníze na jednu adresu a nezveřejňovat mezitransakci, nic nám nebrání vybírat peníze na několik adres a provádět neomezené množství mezitransakcí. Není to nutně výbava pro výměnu, ale pokud jste začali sbírat swap, těžko se zastavit.

Nyní se Alice a Bob mohou rozjet naplno. Například automaticky vypočítávat průměrnou cenu, směňováním po satoshi za sekundu, nebo jednoduše přímo spojit tvůrce trhu a příjemce likvidity.

Krok za krokem.

  • Prodejce si vymýšlí tajemství a dává kupujícímu hash tajemství a podpis transakce, kde je část prostředků převedena na p2sh adresu swapu a zbytek se vrací na adresu prodejce.
  • Zákazník předává podpis, který umožňuje vybrání tokenů a zbytku na adresu příjemce.
  • Prodávající odhaluje tajemství.
  • Historie se opakuje s novým tajemstvím, přičemž k svopu a zbytku se přidává i výběr dříve zakoupeného na adresu kupujícího a již uhrazeného na adresu prodávajícího.

Nyní máme k dispozici vysokorychlostní P2P obchodování, hlavně je důležité sledovat čas a uzavřít obchod před vypršením.

Avšak, malou úpravou našich smluv můžeme darovat našim kanálům nesmrtelnost, což nám výrazně zjednoduší vytváření sítě.

Ale o tom se zmíníme v dalším díle.

Zdroj: habr.com

Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster