Glimt og nød ved atomiske bytter

Hva er galt med atomære swaps og hvordan kan kanaler hjelpe dem, hva som skjedde i hardforgangen Constantinople og hva å gjøre når man ikke har noe å betale for gass.

Hovedmotivasjonen til enhver sikkerhetsspesialist er ønsket om å unngå ansvar.

Provvidens var nådig, jeg forlot ICO uten å vente på den første irreversible transaksjonen, men snart fant jeg meg selv med utviklingen av en kryptobørs.

Jeg er definitivt ikke Malych Kibalchish, og ett hardt blikk er nok til at jeg gir fra meg alle nøkler og passord. Derfor var mitt hovedmål som arkitekt å plassere det glødende stikket av kryptoanalyse så langt unna de dyrebare elementene i infrastrukturen som mulig.

Ikke dine nøkler, ikke dine problemer

Vi bygger et system for bytte av aktiva og ønsker å eliminere mellomlagring av disse aktivaene hos oss, men vi må forsikre sikkerheten i transaksjonen.

Man kan fungere som dommer i en tvist og gjennomføre transaksjoner med lommebøker som krever to av tre signaturer: kjøper, selger og escrow.

Men hvis en deltaker lykkes med å angripe escrow, får de de ettertraktede to signaturene.

Atomær swap er en bytteordning der garantisten er en smart kontrakt som bare tillater ærlig oppførsel.

Som i gåten om ulven, geita og kålen kan du bare handle etter det eneste riktige scenariet og lider tap hvis du avviker fra det.

Bare i stedet for sultne dyr sørger en hash-funksjon for orden, der det er så vanskelig å finne kollisjoner at man ikke engang bør begynne.

Trinn én: gåten

La oss si at Alice en vakker morgen ønsker å overføre bitcoin til Bob for en håndfull 'kryptoenheter'.

  • Hun tenker ut en stor hemmelighet
  • Får fra ham en hash
  • Overfører bitcoins til smartkontrakten, fra hvilken Bob kan hente pengene ved å fremlegge hemmeligheten (hashen fra den må være lik den spesifiserte i kontrakten)
  • Hvis Bob ikke henter sine bitcoins innen kvelden, kan Alice ta dem tilbake.

Trinn to: lokkemat

Bob trer inn i bildet og overfører 'krypto euro' til sin kontrakt, som er skrevet slik at:

  • Alice kan ta 'krypto yen' ved å fremlegge et hemmelig nummer
  • Ikke før på ettermiddagen kan Bob, hvis Alice ikke møter opp, få tilbake sitt depositum.

Trinn tre: løsningen på lokkematen

Alice comes for her money and takes the money from Bob's contract, revealing her secret in the process.

Final step: the riddle is solved

Bob sees the transaction and, with an eagle's eye, extracts from it the secret presented by Alice to the contract. He uses this secret to reclaim his bitcoins.

When something goes wrong

If Alice suddenly turns out to be mortal, Bob takes his yuan at lunch.

In turn, Alice returns the bitcoin by evening if treacherous Bob decides to hold onto the money for better times.

If you prefer an image to text, there is a more detailed and visual explanation on Habr for you of how atomic swaps work.

The difference between timeouts is designed to protect us from malicious Alice, who takes Bob's money at the last moment, while he is nervously typing the hex into the transaction as the timeout expires.

Participants cannot lose their money; at most, they will have to wait for a refund.

Support in blockchainsThis is a scheme as simple as a boot, which requires the interacting blockchains to do very little:

  • Support for smart contracts with at least one branching
  • Both blockchains must support the same hashing algorithms (do not forget to check the length of the secret)
  • Timelocks.

At first glance, one could already say to the exchange "goodbye, our meeting was a mistake," but not so fast.

With all their advantages, solutions based on atomic swaps are not remarkable for liquidity. Much of this is because in the most popular pair BTC-USD, the fiat part was not fully tokenized.
The success of USDT has spawned a whole wave of stablecoins in the ERC20 format to suit all tastes, from the most custodial USDC to the most algorithmic DAI.

Therefore, for simplicity, we will discuss further that Alice sells Bob bitcoins for some ERC20 tokens, and we hope for the stabilizers' luck, since we still have many more technical problems.

Hastighet

Bitcoin and Ethereum are individually not too fast, and here we have to wait first for one deposit with all confirmations, then the second one.

This is all because the participant who knows the secret first deposits money, and the opponent waits for finality and only then transfers their part.

I tillegg håndterer vi en svært volatil eiendel, så i løpet av denne tiden kan kursen endre seg betydelig, og det er allerede vanskelig å endre betingelsene.

Konfidensialitet

Hver utveksling etterlater seg artefakter på begge blokkjedene. En observant person kan legge merke til identiske hasher i smarte kontrakter og dra logiske konklusjoner om at en transaksjon har funnet sted, noe som kan føre til en rekke slutninger fra kursmessige til skattemessige.

Når børsen vet om dine aktiviteter — er det ekstremt ubehagelig, men når alle vet om det — er det dobbelt så ubehagelig.

Brukervennlighet

Den spesifikke styrken til blockchain generelt og Ethereum spesielt. La oss se på hvilke tilpasninger som må gjøres av selgeren og kjøperen.

Fra selgerens synspunkt er det forholdsvis enkelt: du må bare overføre Bitcoin til en p2sh-adresse. Med Ethereum er ting mye mer komplisert.

ContractLa oss se på en gjennomsnittlig kontrakt for swap fra GitHub:

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

Oppmerksomhet! Ikke bruk denne og andre kontrakter fra artikkelen i produksjon, de er skrevet utelukkende for demonstrasjon. Spesielt denne.

  • Bob må kalle tokenkontraktens metode approve, og gi swap-kontrakten tilgang til sine tokens.
  • Bob oppretter en swap og kontrakt ved hjelp av metoden transferFrom som tar tokens fra avsenderens adresse.
  • Alice i withdraw avslører hemmeligheten og kontrakten kaller transfer

De fleste lommebokene og kryptobørsene støtter ikke approve tokens, og det med god grunn.

Brukerne selv gjør ofte feil og overfører bare tokens til kontrakten, hvoretter tokens rett og slett går tapt. Kommentarene på Etherscan er fulle av klager fra de uheldige.

Og for å kalle kontrakten må man betale et gebyr i ETH, noe som betyr at begge parter må ha dette på lager før de starter handelen, og det er det få som ønsker å gjøre.

Gassholder

For det første bør man fjerne senderkontrollen der det er mulig, og anta at det er noen som lider av overskudd av gass og kaller kontrakter for alle som ønsker det.

Oppgradert kontrakt

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); // bruk hash én gang
        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);
    }
}

Kontrakt-nøkkel dualisme og EIP 712

Som vi vet, kan en adresse i Ethereum være en kontrakt, og kan også være et subjekt, som er nøkkelen.
Hovedoppgaven til nøkkelen er å signere noen beskjeder.

Vi kan bruke Bob-kontrakten som avsender, som gjør alle nødvendige passeringer etter å ha sjekket signaturen til Bob-nøkkelen.

Nå kan hvem som helst sponse gebyret for deltageren, men det er bare den som kjenner nøkkelen som tar beslutningen.

Bob-kontrakt

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

For å jobbe med signaturer av komplekse datatyper i Ethereum, finnes det en standard EIP 712, du kan lese mer om det i bloggen til Metamask-lommeboken

Del og hersk

Ofte ser et angrepsscenario for Ethereum-kontrakter slik ut:

  • Deltakeren setter inn midler på kontrakten
  • Deretter tar de ut midlene
  • Noe går galt
  • Angriperen tar pengene flere ganger

Hvis vi går tilbake til vårt første eksempel, går noe galt dersom gåten er en tom byte-array.

Hvordan stjele en millionVi lager et bytte med en hash 0x66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925
Dette er sha256 av 0x0000000000000000000000000000000000000000000000000000000000000000
Vi overfører hemmeligheten og tar våre tokens
Vi overfører igjen og tar andres, alt fordi 0 = 0

Ved å lage en egen kontrakt for hver handel kan vi isolere kontraktene på EVM-nivå.

Men det er ikke alt: nå har hver handel sin egen adresse, som man kan overføre tokens til fra enhver lommebok eller børs.

Forlatte kontrakter og create2

Men nå må vi lage kontrakter for hver handel og vente til kjøperen overfører sitt arbeidsinnsats “krypto-pengeinnskudd”. I oppsettet “kontrakter om morgenen, penger om kvelden” er det alltid en risiko for at kjøperen faller fra, og Ether til å lage kontrakten er allerede brukt.

Kan vi ikke gjøre det slik at pengene er om morgenen, og byte er om kvelden?

I hardforken Constantinople la utviklerne EIP 1014 til en instruksjon create2, som lager en ny kontrakt på en deterministisk adresse

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

Hvor

  • adresse — adressen til fabrikk-kontrakten
  • salt — et tall, hvis betydning vi vil finne ut i neste episode
  • init_code — bytekoden til kontrakten og konstruktørens parametere.

FabrikkInstruksjonen fungerer kun via assembly, så fabrikk ser litt skremmende ut:

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

Koden til kontrakten din kan hentes ved hjelp av web3:

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

På grunn av begrenset støtte i solidity kan gassen for kontrakten beregnes feil på grunn av noen nyanser av Ether.

Spesielt er det hyggelig at i tilfelle av gassmangel, faller kontrakten med en intern feil, uten å informere om at gass var utilstrekkelig, som man kunne forvente.

Nå kan vi overføre tokens til kontrakter uten å opprette dem på forhånd, og inntil vi publiserer dem på nett, vil ingen gjette hva kontrakten faktisk gjør.

Kråke kråke øye ikke hulter.

Det er klart at en ekte analytiker, spesielt en som har fått gode investeringer for å bekjempe regimets fiender gjennom hvitvasking av penger, ikke vil bli stanset av slike barnslige triks, og etter opprettelsen av kontrakten vil han fortsatt se hashen.

Hvordan gjøre slik at hashen ikke blir synlig?

Selve swapen flytter vi til off-chain: deltakerne utveksler signaturer for å overføre til swap-kontrakten, og deretter avsløres hemmeligheten privat.

Steg for stegDet opprettes to 'multisigs', hvor midlene kan hentes hvis både Alice og Bob signer.

For å unngå at noen av deltakerne går offline uten konsekvenser, legger vi til en god, gammeldags timeout.

Alice og Bob setter inn innskudd parallelt.

  • Alice tenker ut en hemmelighet og sender Bob hashen av hemmeligheten og signaturen på transaksjonen som overfører bitcoins til swapens adresse.
  • Bob sender Alice signaturen for å ta ut tokens til swap-kontrakten med hashen han fikk.
  • Alice informerer Bob om hemmeligheten.

I dette øyeblikket oppstår harmoni: både Alice og Bob kan i prinsippet avslutte handelen når som helst. I et så vennlig miljø kan de utveksle signaturer for å ta ut penger til de endelige adressene.

For en utenforstående observatør ser det ut som pengene har gått gjennom en kontrakt med multipodunderskrift 2 av 2.

Og en slik ordning tillater begge parter å sette inn samtidig, da hemmeligheten tenkes ut etter alle bekreftelser.

Nivå 2

Siden vi kan ta ut penger til én adresse og ikke publisere mellomliggende transaksjoner, er det ingenting som hindrer oss i å ta ut penger til flere adresser og gjøre et ubegrenset antall mellomliggende transaksjoner. Ikke at dette er et nødvendig sett for bytte, men hvis man har begynt å samle swap, er det vanskelig å stoppe.

Nå kan Alice og Bob virkelig ta det ut: for eksempel automatisk beregne gjennomsnittlig pris ved å bytte per satoshi i sekundet, eller bare direkte koble markedsmaker og likviditetsmottaker.

Steg for steg

  • Selger tenker ut en hemmelighet og gir kjøper hashen av hemmeligheten og signaturen på transaksjonen hvor en del av midlene overføres til en p2sh adresse for swap, mens restbeløpet tilbakeføres til selgerens adresse.
  • Kunden overfører en signatur som gjør at tokens og endringer kan føres over til mottakerens adresse.
  • Selger avslører hemmeligheten
  • Historien gjentar seg med en ny hemmelighet, samtidig som det legges til tidligere kjøpt og allerede betalt til selgers adresse til svopet og endringene.

Nå har vi tilgang til høyhastighets p2p-handel, det viktigste er å følge med på tiden og lukke avtalen før tidsfristen.

Imidlertid, med litt justering av kontraktene våre, kan vi gi kanalene våre udødelighet, noe som vil forenkle nettverksopprettingen vår.

Men vi vil fortelle om dette i neste episode.

Kilde: habr.com

Kjøp pålitelig hosting for nettsteder med DDoS-beskyttelse, VPS VDS-servere 🔥 Kjøp pålitelig hosting for nettsteder med DDoS-beskyttelse, VPS VDS-servere | ProHoster