Glans og fattigdom ved atomudvekslinger

Hvad er de problemer, atomare swaps medfører, og hvordan kan kanaler hjælpe? Hvad var vigtigt i hardforken Constantinople, og hvad gør man, når man ikke har penge til gas.

Den primære motivation for enhver sikkerhedsspecialist er ønsket om at undgå ansvar.

Forsynet var mig venligt stemt; jeg forlod ICO, før jeg nåede den første irreversible transaktion, men opdagede hurtigt, at jeg arbejdede på udviklingen af en kryptobørs.

Jeg er bestemt ikke en Kibalchish-dreng; et strengt blik er nok til, at jeg afleverer alle nøgler og adgangskoder. Derfor var mit primære mål som arkitekt at placere det glødende stykke kryptering så langt væk fra de infrastrukturelementer, jeg holder af.

Ikke dine nøgler, ikke dine problemer.

Vi bygger et system til udveksling af aktiver og ønsker at undgå mellemopbevaring af disse aktiver, men vi skal sikre sikkerheden af handlen.

Man kan fungere som dommer i en tvist og udføre handler med tegnebøger, der kræver to ud af tre underskrifter: køberen, sælgeren og escrow.

Men hvis én deltager succesfuldt angriber escrow, får de de to ønskede underskrifter.

En atomar swap er en udvekslingsordning, hvor en smart kontrakt fungerer som garant, og som kun tillader ærlig adfærd.

Ligesom i gåden om ulven, gæssen og kålen kan du kun handle efter det eneste rigtige scenarie og lide tab, hvis du afviger fra det.

Men i stedet for grådige dyr sikrer en hash-funktion ordren, hvor det er så vanskeligt at finde en kollision, at det ikke engang er værd at begynde.

Trin et: gåden.

Lad os antage, at Alice en smuk morgen ønsker at overføre bitcoin til Bob for en håndfuld 'krypto-yuan'.

  • Hun tænker på en stor hemmelighed.
  • Hun får en hash fra ham.
  • Hun overfører bitcoins til smart kontrakten, som Bob kan tage penge fra ved at fremvise hemmeligheden (hashen skal svare til den, der er angivet i kontrakten).
  • Hvis Bob ikke henter sine bitcoins inden aften, kan Alice tage dem tilbage.

Trin to: lokkemaden.

Bob går ind i spillet og overfører 'krypto-euro' til sin kontrakt, som er skrevet på en sådan måde, at:

  • Alice kan tage 'krypto-yen' ved at fremvise det hemmelige tal.
  • Ikke tidligere end kl. 12 kan Bob, hvis Alice ikke møder op, få sin indbetaling tilbage.

Trin tre: løsningen i lokkemaden.

Alice kommer for at hente sine penge og tager pengene fra Bobs kontrakt, samtidig med at hun afslører sin hemmelighed.

Afslutningsskridt: gåden er løst

Bob ser transaktionen og udtrækker hemmeligheden, som Alice præsenterede til kontrakten. Han bruger denne hemmelighed til at få fat i sine bitcoins.

Når noget går galt

Hvis Alice pludselig er dødelig, tager Bob sine yuan til frokost.

På sin side returnerer Alice bitcoin ved aften, hvis den forræderiske Bob beslutter at holde pengene til bedre tider.

Hvis du foretrækker billeder frem for tekst, er der en mere detaljeret og visuel forklaring for dig på Habré. arbejde af atomare swaps.

Forskellen mellem timeoutene er designet til at beskytte os mod den skurkagtige Alice, som tager Bobs penge i aller sidste øjeblik, mens han med rystende hænder indtaster hex i transaktionen.

Deltagerne kan ikke miste deres penge, højst bliver de nødt til at vente på tilbagebetalingen.

Support i blokkeDet er en simpel skema, der kræver at de interagerende blockchains kun lidt:

  • Support for smarte kontrakter med mindst én forgrening
  • Begge blockchains skal understøtte de samme hashing-algoritmer (glem ikke at tjekke hemmelighedens længde)
  • Tidslåse.

Ved første øjekast kan man allerede sige til børsen "farvel, vores møde var en fejltagelse", men ikke så hurtigt.

På trods af deres fordele imponerer løsninger på atomic swap ikke med likviditet. Meget af dette skyldes, at den mest populære par BTC-USD ikke var helt tokeniseret.
Succesen med USDT har affødt en hel bølge af stabile mønter i ERC20-format i enhver smag, fra de mest forvaltningsprægede USDC til de mest algoritmisk styrede DAI.

Derfor vil vi for enkelhedens skyld i det følgende overveje, at Alice sælger bitcoins til Bob for nogle ERC20 tokens og håber på stabilisatorernes held, heldigvis har vi mange flere tekniske problemer.

Hastighed

Bitcoin og Ethereum er ikke hurtige hver for sig, og nu må vi vente først på en indbetaling med alle bekræftelser og derefter på en anden.

Det hele skyldes, at det først er den deltager, som kender hemmeligheden, der indbetaler pengene, mens modstanderen venter på finaliteten og først derefter overfører sin del.

Derudover arbejder vi med en meget volatil aktiv, så i mellemtiden kan kursen ændre sig betydeligt, og det er allerede svært at ændre betingelserne.

Fortrolighed

Enhver udveksling efterlader artefakter på begge blockchains. En opmærksom observatør kan bemærke ens hashes i smarte kontrakter og drage en logisk konklusion om, at en handel er blevet gennemført, hvilket kan føre til en række overvejelser fra kursmæssige til skatteforhold.

Når børsen kender til dine aktiviteter, er det ekstremt ubehageligt; når det kender det alle, er det dobbelt så ubehageligt.

Brugervenlighed

Kernen i blockchain generelt og Ethereum i særdeleshed. Lad os se på, hvilke skridt sælgeren og køberen skal tage.

Set fra sælgers perspektiv er alt relativt simpelt: man skal blot overføre bitcoin til en p2sh-adresse. Med Ethereum er det meget mere komplekst.

KontraktLad os se på en gennemsnitlig kontrakt til 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);
    }
}

Opmærksomhed! Brug ikke denne og andre kontrakter fra artiklen i produktionen; de er kun skrevet til demonstration. Især denne.

  • Bob skal kalde token-kontraktens metode approve, hvilket giver swapkontrakten adgang til sine tokens
  • Bob opretter swap og kontrakt ved hjælp af metoden transferFrom får tokens fra afsenderens adresse
  • Alice i withdraw afslører hemmeligheden og kontrakten kaldes overførsel

De fleste tegnebøger og kryptobørser understøtter ikke approve tokens, og det er ikke uden grund.

Brugerne selv begår ofte fejl og overfører blot tokens til kontrakten, hvorefter tokens blot går tabt. Kommentarerne på Etherscan er fyldt med klager fra de uheldige.

For at kalde en kontrakt, skal man betale et gebyr i ETH, så begge parter skal have det klar, før handeln begynder, hvilket ikke mange ønsker at gøre.

Gasudvinding

For det første bør vi fjerne afsenderkontrollen overalt, hvor det er muligt, og antage, at vi har nogen, der lider af overskud af gas og kalder kontrakter for alle, der ønsker det.

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

Kontrakt-nøgle dualisme og EIP 712

Som vi ved, kan en adresse i Ethereum være en kontrakt, og den kan også være et subjekt, eller med andre ord, en nøgle.
Hovedaktiviteten for nøglen er at signere nogle beskeder.

Vi kan bruge Bob-kontrakten som afsender, som udfører alle nødvendige overførsler, efter at han har kontrolleret Bob-nøglens signatur.

Nu kan enhver sponsorere deltagerens gebyr, men beslutningen tages kun af den, der kender nøglen.

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

Der findes en standard til arbejde med signaturer på komplekse datatyper i Ethereum EIP 712, du kan læse mere om det i Metamask tegnebogens blog

Del og hersk

Ofte ser et hack-scenario for et Ethereum-kontrakt sådan ud:

  • Deltageren indskyder midler i kontrakten
  • Derefter trækker de midlerne tilbage
  • Noget går galt
  • Angriberen trækker pengene igen og igen

Hvis vi vender tilbage til vores første eksempel, går noget galt, hvis mysteriet er et tomt byte-array.

Hvordan man stjæler en millionOpretter vi en swap med en hash 0x66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925
Dette er sha256 af 0x0000000000000000000000000000000000000000000000000000000000000000
Sender hemmeligheden og trækker vores tokens
Sender igen og trækker andres, alt sammen fordi 0 = 0

Ved at oprette en separat kontrakt for hver handel kan vi isolere kontrakter på EVM-niveau.

Men det er ikke alt: nu har hver handel sin egen adresse, hvor tokens kan overføres fra enhver tegnebog eller børs.

Forladte kontrakter og create2

Men nu skal vi oprette en kontrakt for hver handel og vente på, at køberen overfører deres 'krypto-venlighed'. I ordningen 'kontrakter om morgenen, penge om aftenen' er der altid risiko for, at køberen trækker sig, mens ether til at oprette kontrakten allerede er brugt.

Kan vi ikke gøre det sådan, at pengene er om morgenen, og bytes om aftenen?

I hardforken Constantinople tilføjede udviklerne EIP 1014 en instruktion create2, der opretter en ny kontrakt på en deterministisk adresse

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

Where

  • adresse — fabrikens kontraktsadresse
  • salt — et vilkårligt tal, hvis betydning vi lærer i næste episode
  • init_code — bytekode til kontrakt og konstruktørens parametre.

FabrikInstruktionen fungerer kun via assembly, så fabrikken ser noget skræmmende ud:

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 din kontrakt kan opnås ved hjælp af 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å grund af den begrænsede support i solidity kan gas til kontrakten muligvis beregnes forkert på grund af nogle nuancer i ether.

Især er det sødt, at i tilfælde af utilstrækkelig gas falder kontrakten med en intern fejl uden at angive, at der ikke var tilstrækkelig gas, som man kunne forvente.

Nu kan vi oversætte tokens til kontrakter uden at oprette dem på forhånd, og mens vi ikke offentliggør dem på netværket, vil ingen have en anelse om, hvad kontrakten gør.

Ravn til ravn mister ikke øjet

Det er tydeligt, at en ægte analytiker, især en der har fået gode investeringer til at bekæmpe regimets fjender gennem hvidvaskning af penge, ikke vil stoppes af sådanne barnlige tricks, og efter oprettelsen af kontrakten vil han stadig se hashen.

Hvordan kan vi sikre, at hashen ikke bliver afsløret?

Selve swap'en flyttes til off-chain: deltagerne udveksler underskrifter for at overføre til swap-kontrakten, og derefter afsløres hemmeligheden privat.

Trin for trinDer oprettes to 'multisigs', fra hvilke midler kan trækkes ved tilstedeværelsen af underskrifter fra Alice og Bob.

For at en offline-afgang fra nogen af deltagerne ikke bliver en tragedie, tilføjer vi den gode gamle timeout.

Alice og Bob indbetaler samtidigt

  • Alice finder på en hemmelighed og giver Bob hashen af hemmeligheden samt underskriften på transaktionen, der overfører bitcoins til swap-adressen.
  • Bob giver Alice underskriften til udbetaling af tokens til swap-kontrakten med den angivne hash.
  • Alice informerer Bob om hemmeligheden.

På dette tidspunkt opnås der harmoni: både Alice og Bob kan til enhver tid afslutte handlen. I sådan en venlig atmosfære kan de udveksle underskrifter for at udbetale penge til de endelige adresser.

For en udenforstående observerer det, som om pengene passerede gennem en kontrakt med multipelsignatur 2 af 2.

Denne ordning giver desuden begge parter mulighed for samtidig at indbetale, da hemmeligheden opfindes først efter alle bekræftelser.

Niveau 2

Da vi kan udbetale penge til en enkelt adresse og ikke offentliggøre den mellemliggende transaktion, er der intet, der forhindrer os i at udbetale penge til flere adresser og udføre et ubegribelig antal mellemledstransaktioner. Det er ikke nødvendigvis et sæt, der er nødvendigt for udveksling, men når man først er begyndt at samle swaps, er det svært at stoppe.

Nu kan Alice og Bob virkelig give den gas. For eksempel automatisk beregne den gennemsnitlige pris, ved at handle med satoshis per sekund, eller blot direkte forbinde market maker og likviditetsmodtager.

Trin for trin

  • Selgeren finder på en hemmelighed og giver køberen hashen af hemmeligheden samt underskriften til transaktionen, hvor en del af midlerne overføres til p2sh-adressen for swap'en, mens resten returneres til sælgerens adresse.
  • Kunden giver en underskrift, der tillader at overføre tokens og change til modtagerens adresse.
  • Sælgeren afslører hemmeligheden
  • Historien gentager sig med en ny hemmelighed, der tilføjer overførsel af tidligere købte tokens til køberens adresse og allerede betalt til sælgerens adresse til swap og change.

Nu har vi adgang til hurtig p2p-handel; det vigtigste er at holde øje med tiden og afslutte handlen inden timeout.

Men ved lidt at justere vores kontrakter kan vi give vores kanaler udødelighed, hvilket vil forenkle opbygningen af netværket betydeligt.

Men det vil vi fortælle om i næste episode.

Kilde: habr.com

Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere 🔥 Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere | ProHoster