Glanz und Elend der atomaren Swaps

Was sind die Nachteile von atomaren Swaps und wie können Kanäle helfen? Was ist beim Hard Fork Constantinople wichtig geschehen, und was tun, wenn man nicht für Gas zahlen kann?

Die Hauptmotivation eines jeden Sicherheitsspezialisten ist der Wunsch, Verantwortung zu vermeiden.

Die Vorsehung war gnädig, ich verließ das ICO, ohne auf die erste irreversible Transaktion zu warten, stellte jedoch bald fest, dass ich an der Entwicklung einer Kryptobörse beteiligt war.

Ich bin entschieden nicht wie Maltschisch Kibaltschisch, und ein strenger Blick reicht aus, damit ich alle Schlüssel und Passwörter herausgebe. Daher bestand mein Hauptziel als Architekt darin, die glühende Spitze der Kryptoanalyse so weit wie möglich von den mir wichtigen Infrastrukturelementen zu entfernen.

Nicht deine Schlüssel, nicht deine Probleme.

Wir bauen ein Vermögensaustauschsystem und möchten die Zwischenlagerung dieser Vermögenswerte bei uns ausschließen, müssen jedoch die Sicherheit der Transaktion gewährleisten.

Man kann als Schiedsrichter in einem Streitfall auftreten und Transaktionen mit Wallets durchführen, die zwei von drei Unterschriften erfordern: von Käufer, Verkäufer und Escrow.

Wenn jedoch ein Teilnehmer escrow erfolgreich angreift, erhält er die gewünschten zwei Unterschriften.

Atomarer Swap — ein Austauschschema, bei dem ein Smart Contract als Garantie fungiert und nur ehrliches Verhalten zulässt.

Wie im Rätsel über den Wolf, die Ziege und den Kohl kannst du nur nach einem einzigen richtigen Szenario handeln und verlierst, wenn du davon abweichst.

Anstelle gefräßiger Tiere sorgt hier eine Hash-Funktion für Ordnung, in der es so schwierig ist, eine Kollision zu finden, dass man es gar nicht erst versuchen sollte.

Schritt eins: das Rätsel

Angenommen, Alice möchte an einem schönen Morgen Bitcoin gegen eine Handvoll „Krypto-Yuan“ an Bob übergeben.

  • Sie denkt sich ein großes Geheimnis aus
  • Erhält von ihm einen Hash
  • Überweist Bitcoin an einen Smart Contract, von dem Bob das Geld abholen kann, indem er das Geheimnis vorlegt (der Hash davon muss mit dem im Vertrag angegebenen übereinstimmen)
  • Falls Bob bis zum Abend nicht für seine Bitcoins kommt, kann Alice sie sich zurückholen.

Schritt zwei: die Falle

Bob tritt ins Spiel und überweist „Krypto-Euro“ an seinen Vertrag, der so geschrieben ist, dass:

  • Alice die „Krypto-Yen“ einfordern kann, indem sie eine geheime Zahl vorlegt.
  • Nicht früher als zum Mittag kann Bob, falls Alice nicht erscheint, seine Einzahlung zurückholen.

Schritt drei: Die Lösung im Köder

Alice kommt nach ihrem Geld und holt das Geld vom Vertrag von Bob, während sie ihr Geheimnis enthüllt.

Schritt vier: Das Rätsel ist gelöst

Bob sieht die Transaktion und erkennt mit scharfem Blick das Geheimnis, das Alice dem Vertrag präsentiert hat. Dieses Geheimnis nutzt er, um bereits seine Bitcoins abzuholen.

Wenn etwas schiefgeht

Sollte Alice plötzlich sterblich sein, holt Bob zu Mittag seine Yuan ab.

Im Gegenzug gibt Alice am Abend die Bitcoins zurück, falls der treulose Bob beschließt, das Geld bis bessere Zeiten zurückzuhalten.

Wenn Sie ein Bild bevorzugen, finden Sie auf Habr eine detailliertere und anschaulichere Erklärung der atomaren Swaps.

Der Unterschied zwischen den Timeouts soll uns vor der bösartigen Alice schützen, die Bob sein Geld in letzter Sekunde wegnimmt, während das Timeout abläuft, während er mit zitternden Fingern den Hex-Code in die Transaktion eingibt.

Die Teilnehmer können ihr Geld nicht verlieren, im schlimmsten Fall müssen sie auf die Rückgabe warten.

Unterstützung in BlockchainsDas ist ein einfaches, wie ein Schuh, Schema, das von den interagierenden Blockchains nur das Nötigste verlangt:

  • Unterstützung von Smart Contracts mit mindestens einem Verzweigungspunkt
  • Beide Blockchains müssen die gleichen Hash-Algorithmen unterstützen (vergessen Sie nicht, die Länge des Secrets zu überprüfen)
  • Zeitverriegelungen.

Auf den ersten Blick könnte man der Börse sagen: 'Lebwohl, unser Treffen war ein Fehler', aber es ist nicht so einfach.

Trotz all ihrer Vorteile zeichnen sich atomic-swap-Lösungen nicht durch Liquidität aus. Dies liegt zum großen Teil daran, dass im beliebtesten Paar BTC-USD der Fiat-Anteil nicht vollständig tokenisiert war.
Der Erfolg von USDT hat eine ganze Welle von stabilen Coins im ERC20-Format hervorgebracht, von dem custodialen USDC bis hin zu dem algorithmischen DAI.

Daher gehen wir der Einfachheit halber davon aus, dass Alice Bob Bitcoins gegen einige ERC20-Token verkauft und hoffen auf das Glück der Stabilizer, denn wir haben noch viele technische Probleme.

Geschwindigkeit

Bitcoin und Ethereum sind einzeln nicht besonders schnell, und hier müssen wir zuerst eine Einzahlung mit allen Bestätigungen abwarten, dann die zweite.

Das liegt daran, dass zunächst das Geld von dem Teilnehmer eingezahlt wird, der das Geheimnis kennt, und der Gegner auf die Endgültigkeit wartet und erst dann seinen Anteil überweist.

Außerdem haben wir es mit einem sehr volatilene Asset zu tun, sodass sich der Kurs in dieser Zeit erheblich ändern kann, und die Bedingungen danach zu ändern ist nicht einfach.

Vertraulichkeit

Jeder Austausch hinterlässt Artefakte auf beiden Blockchains. Ein aufmerksamer Beobachter kann identische Hashes in Smart Contracts bemerken und zu dem logischen Schluss kommen, dass hier ein Handel stattgefunden hat, aus dem zahlreiche Schlussfolgerungen gezogen werden können, von Kursentwicklungen bis hin zu steuerlichen Aspekten.

Es ist äußerst unangenehm, wenn die Börse über dein Geschäft Bescheid weiß – wenn es jeder weiß, ist es doppelt unangenehm.

Usability

Das Markenzeichen von Blockchain im Allgemeinen und Ethereum im Besonderen. Lassen Sie uns ansehen, welche Schritte Verkäufer und Käufer unternehmen müssen.

Aus der Sicht des Verkäufers ist alles vergleichsweise einfach: Es muss einfach Bitcoin an eine p2sh-Adresse überwiesen werden. Bei Ethereum ist das viel komplizierter.

VertragBetrachten wir einen durchschnittlichen Swap-Vertrag aus 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 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);
    }
}

Achtung! Verwenden Sie diesen und andere Verträge aus dem Artikel nicht in der Produktion, sie sind ausschließlich zu Demonstrationszwecken geschrieben. Vor allem diesen.

  • Bob muss die Token-Methode des Vertrags aufrufen reject, um dem Swapvertrag den Zugriff auf seine Tokens zu ermöglichen
  • Bob erstellt den Swap und den Vertrag mit der Methode transferFrom und holt die Tokens des Absenders auf seine Adresse
  • Alice in abheben enthüllt das Geheimnis und der Vertrag ruft auf Überweisung

Die meisten Wallets und Krypto-Börsen unterstützen reject Tokens nicht, und das aus gutem Grund.

Nutzer machen oft Fehler und überweisen einfach Tokens an den Vertrag, wonach die Tokens einfach verloren gehen. Die Kommentare auf Etherscan sind voll mit dem Jammern Unglücklicher.

Um den Vertrag aufzurufen, muss eine Gebühr in ETH bezahlt werden, was bedeutet, dass beide Teilnehmer sich vor Beginn des Geschäfts damit eindecken müssen, was nur wenige bereit sind zu tun.

Gasaldar

Zu Beginn sollte die Überprüfung des Absenders überall dort entfernt werden, wo es möglich ist, und davon ausgegangen werden, dass wir jemanden haben, der unter Gasüberschuss leidet und Verträge für alle Interessierten aufruft.

Modernisierter Vertrag

Vertrag Swapper {

    Struktur Swap {
        iERC20 token;
        Adresse empfänger;
        uint betrag;
        Adresse rückadresse;
        uint rückzahlungszeit;
    }

    Zuordnung (bytes32 =>  Swap) swaps;

    Funktion erstelle(iERC20 token, bytes32 hash, Adresse empfänger, uint betrag, Adresse rückadresse, uint rückzahlungszeit) public {
        require(swaps[hash].betrag == 0); // Hash einmal verwenden
        require(token.transferFrom(msg.sender, address(this), betrag));
        swaps[hash] = Swap(token, empfänger, betrag, rückadresse, rückzahlungszeit);
    }


    Funktion abheben(bytes memory geheim) public {
        bytes32 hash = sha256(geheim);
        Swap memory swap = swaps[hash];
        require(swap.betrag > 0);
        delete swaps[hash];
        swap.token.transfer(swap.empfänger, swap.betrag);
    }

    Funktion rückerstatten(bytes32 hash) public {
        Swap memory swap = swaps[hash];
        require(now > swap.rückzahlungszeit);
        delete swaps[hash];
        swap.token.transfer(swap.rückadresse, swap.betrag);
    }
}

Vertragsschlüssel-Dualismus und EIP 712

Wie wir wissen, kann eine Adresse im Ether ein Vertrag sein oder ein Subjekt, also ein Schlüssel.
Die Hauptaufgabe eines Schlüssels ist es, Nachrichten zu signieren.

Wir können den Bob-Vertrag als Absender verwenden, der alle notwendigen Übertragungen durchführt, nachdem er die Signatur des Bob-Schlüssels überprüft hat.

Jetzt kann jeder die Gebühr eines Teilnehmers sponsern, aber die Entscheidung trifft nur der, der den Schlüssel kennt.

Bob-Vertrag

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

Um mit den Signaturen komplexer Datenstrukturen in Ethereum zu arbeiten, gibt es einen Standard EIP 712, mehr darüber können Sie in dem Blog von Metamask

Teile und herrsche

Oft sieht das Szenario eines Hacks eines Ethereum-Vertrags so aus:

  • Ein Teilnehmer legt Mittel auf dem Vertrag an
  • Dann zieht er die Mittel ab
  • Etwas geht schief
  • Der Angreifer zieht das Geld immer wieder ab

Wenn wir zu unserem ersten Beispiel zurückkehren, geht etwas schief, wenn das Rätsel ein leerer Bytesatz ist.

Wie man eine Million stiehltErstellen eines Swaps mit dem Hash 0x66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925
Dies ist sha256 von 0x0000000000000000000000000000000000000000000000000000000000000000
Übergeben Sie das Geheimnis und holen Sie sich Ihre Token
Ein weiteres Mal übergeben und чужие abholen, alles wegen der Tatsache, dass 0 = 0

Indem wir für jeden Deal einen separaten Vertrag erstellen, können wir die Verträge auf EVM-Ebene isolieren.

Aber das ist noch nicht alles: Jetzt hat jeder Deal seine eigene Adresse, auf die Tokens von jedem Wallet oder Exchange übertragen werden können.

Verwaiste Verträge und create2

Doch jetzt müssen wir für jeden Deal einen Vertrag erstellen und warten, bis der Käufer das Arbeits-'Kryptofening' dorthin überweist. In dem Modell 'morgens Verträge, abends Geld' besteht immer die Gefahr, dass der Käufer abspringt, während das Ether zur Erstellung des Vertrags bereits ausgegeben wurde.

Kann man es nicht so machen, dass morgens das Geld und abends die Bytes kommen?

Im Hardfork Constantinople haben die Entwickler EIP 1014 die Anweisung create2 hinzugefügt, die einen neuen Vertrag an einer deterministischen Adresse erstellt.

keccak256( 0xff ++ Adresse ++ Salt ++ keccak256(init_code))[12:]

Dabei ist

  • Adresse — die Adresse der Fabrikvertrag
  • Salt — eine Zahl, deren Bedeutung wir in der nächsten Episode erfahren werden.
  • init_code — der Bytecode des Vertrags und die Parameter des Konstruktors.

FabrikDie Anweisung funktioniert nur über Assembly, daher sieht die Fabrik etwas einschüchternd aus:

Vertrag Factory {
  Ereignis Deployed(address addr, uint256 salt);

  Funktion create2(bytes memory code, uint256 salt) öffentlich {
    address addr;
    assembly {

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

    emit Deployed(addr, salt);
  }
}

Den Code Ihres Vertrags kann man mit Hilfe von web3 erhalten:

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

Aufgrund der begrenzten Unterstützung in Solidity kann das Gas für den Vertrag aufgrund bestimmter Eigenheiten von Ethereum falsch berechnet werden.

Es ist besonders freundlich, dass der Vertrag im Falle von unzureichendem Gas mit einem internen Fehler abstürzt, ohne dabei zu melden, dass das Gas nicht ausreicht, wie man es erwarten würde.

Jetzt können wir Tokens an Verträge überweisen, ohne sie im Voraus zu erstellen, und solange wir sie nicht im Netzwerk veröffentlichen, wird niemand erraten, was der Vertrag tatsächlich macht.

Der Rabe sticht dem Raben kein Auge aus

Es ist klar, dass echte Analysten, besonders solche, die gute Investitionen zur Bekämpfung von Feinden des Regimes durch Geldwäsche erhalten haben, durch solche kindischen Tricks nicht aufgehalten werden, und nach der Erstellung des Vertrags wird er trotzdem den Hash sehen.

Wie kann man erreichen, dass der Hash nicht sichtbar wird?

Der Swap wird off-chain durchgeführt: Die Teilnehmer tauschen Unterschriften zum Transfer auf den Swap-Vertrag aus, und dann wird das Geheimnis privat offengelegt.

Schritt für SchrittEs werden zwei „Multi-Signaturen“ erstellt, mit denen die Mittel abgehoben werden können, wenn die Unterschriften von Alice und Bob vorliegen.

Um zu verhindern, dass das Offline-Gehen eines der Teilnehmer zu einer Tragödie wird, fügen wir ein bewährtes Timeout hinzu.

Alice und Bob hinterlegen gleichzeitig ihre Einlagen.

  • Alice denkt sich ein Geheimnis aus und übermittelt Bob den Hash dieses Geheimnisses sowie die Unterschrift der Transaktion, die die Bitcoins auf die Swap-Adresse überträgt.
  • Bob übermittelt Alice die Unterschrift für die Token-Auszahlung auf den Swap-Vertrag mit dem angegebenen Hash.
  • Alice teilt Bob das Geheimnis mit.

In diesem Moment tritt Harmonie ein: Sowohl Alice als auch Bob können jederzeit den Deal abschließen. In dieser freundlichen Atmosphäre können sie ihre Unterschriften austauschen, um Geld auf die endgültigen Adressen abzuheben.

Für einen externen Beobachter sieht es so aus, als hätten die Gelder einen Vertrag mit einer Multi-Signatur von 2 aus 2 durchlaufen.

Außerdem ermöglicht dieses Schema beiden Parteien, gleichzeitig Einlagen zu leisten, da das Geheimnis bereits nach allen Bestätigungen übermittelt wird.

Level 2

Da wir Geld an eine Adresse auszahlen können, ohne die Zwischenübertragung zu veröffentlichen, steht nichts im Wege, Geld an mehrere Adressen auszuzahlen und unbegrenzt viele Zwischenübertragungen durchzuführen. Auch wenn dies kein notwendiges Set für den Tausch ist, ist es schwierig, mit dem Swapping aufzuhören, wenn man einmal damit begonnen hat.

Jetzt können Alice und Bob richtig durchstarten. Zum Beispiel, um automatisch den Durchschnittspreis zu berechnen, indem sie in Satoshis pro Sekunde tauschen, oder um einfach direkt den Market Maker mit dem Liquiditätsnehmer zu verbinden.

Schritt für Schritt

  • Der Verkäufer wählt ein Geheimnis und gibt dem Käufer den Hash des Geheimnisses sowie die Transaktionssignatur, wobei ein Teil der Mittel an die p2sh-Adresse des Swaps transferiert wird und der Rest an die Adresse des Verkäufers zurückgeht.
  • Der Käufer überträgt die Signatur, die es ermöglicht, Tokens und Wechselgeld an die Adresse des Empfängers vom Swap abzuheben.
  • Der Verkäufer enthüllt das Geheimnis.
  • Die Geschichte wiederholt sich mit einem neuen Geheimnis, wobei zusätzlich zum Swap und dem Wechselgeld auch die vorher gekauften Mittel an die Adresse des Käufers, die bereits an die Adresse des Verkäufers bezahlt wurden, ausgezahlt werden.

Jetzt steht uns der hochgeschwindigkeits-P2P-Handel zur Verfügung, wobei es wichtig ist, die Zeit im Auge zu behalten und den Deal vor dem Timeout abzuschließen.

Durch kleine Anpassungen an unseren Verträgen können wir unseren Kanälen Unsterblichkeit verleihen, was uns die Erstellung eines Netzwerks erheblich erleichtert.

Aber darüber werden wir in der nächsten Folge sprechen.

Quelle: habr.com

Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen 🔥 Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen | ProHoster