Was sind die Nachteile atomarer Swaps und wie können Kanäle dabei helfen? Was ist im Hard Fork Constantinople wichtig passiert und wie soll man sich verhalten, wenn man nichts hat, um Gas zu bezahlen?
Die Hauptmotivation eines jeden Sicherheitsexperten ist der Wunsch, Verantwortung zu vermeiden.
Das Schicksal war gnädig, ich verließ das ICO, ohne auf die erste irreversible Transaktion zu warten, fand mich aber bald als Entwickler einer Krypto-Börse wieder.
Ich bin entschieden nicht Maltschisch Kibaltschisch, und ein strenger Blick reicht aus, damit ich alle Schlüssel und Passwörter herausgebe. Daher war mein Hauptziel als Architekt, die glühende Nadel der Kryptoanalyse so weit wie möglich von den mir wichtigen Elementen der Infrastruktur zu entfernen.
Nicht deine Schlüssel, nicht deine Probleme.
Wir bauen ein System zum Austausch von Vermögenswerten und möchten die Zwischenlagerung dieser Vermögenswerte bei uns ausschließen, müssen jedoch die Sicherheit des Geschäfts gewährleisten.
Man kann als Schiedsrichter in einer strittigen Situation auftreten und Geschäfte mit Wallets abwickeln, die zwei von drei Unterschriften benötigen: vom Käufer, Verkäufer und Treuhänder.
Wenn jedoch ein Teilnehmer den Treuhänder erfolgreich angreift, erhält er die beiden gewünschten Unterschriften.
Ein atomarer Swap ist ein Austauschschema, bei dem ein Smart Contract als Garanti fungiert, der nur ehrliches Verhalten zulässt.
Ähnlich wie im Rätsel über den Wolf, die Ziege und den Kohl kannst du nur nach dem einzigen richtigen Szenario handeln und trägst Verluste, wenn du davon abweichst.
Nur anstelle von gefräßigen Tieren sorgt eine Hash-Funktion dafür, dass es so schwierig ist, eine Kollision zu finden, dass es sich nicht einmal lohnt, damit zu beginnen.
Schritt eins: das Rätsel.
Angenommen, Alice möchte an einem schönen Morgen Bob Bitcoin gegen eine Handvoll „Kryptoeuros“ überweisen.
- Sie denkt sich ein großes Geheimnis aus.
- Sie erhält von ihm einen Hash.
- Sie überweist die Bitcoins 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 nach seinen Bitcoins fragt, kann Alice sie sich zurückholen.
Schritt zwei: der Köder.
Es tritt Bob in das Spiel ein und überweist „Kryptoeuros“ an seinen Vertrag, der so geschrieben ist, dass:
- Alice die „Kryptoyen“ abholen kann, indem sie eine geheime Zahl vorlegt.
- Nicht vor dem Mittag kann Bob, im Falle einer Nichterscheinen von Alice, die Einzahlung zurückbekommen.
Schritt drei: die Lösung im Köder.
Alice kommt, um ihr Geld zu holen, und nimmt das Geld aus Bobs Vertrag mit, wobei sie ihr Geheimnis enthüllt.
Der abschließende Schritt: Das Rätsel ist gelöst
Bob sieht die Transaktion und entnimmt mit scharfem Blick das Geheimnis, das Alice dem Vertrag präsentiert hat. Dieses Geheimnis nutzt er, um seine Bitcoins zurückzuholen.
Wenn etwas nicht nach Plan läuft
Falls Alice plötzlich sterblich wird, nimmt Bob mittags seine Yuan.
Alice gibt am Abend die Bitcoins zurück, falls der treulose Bob sich entscheidet, das Geld bis zu besseren Zeiten zurückzuhalten.
Wenn Sie Bilder dem Text vorziehen, gibt es auf Habr eine ausführlichere und anschauliche .
Der Unterschied zwischen Timeouts soll uns vor der böswilligen Alice schützen, die Bobs Geld im letzten Moment entzieht, während der verzweifelte Bob mit zitternden Fingern die Hexadezimalangaben in die Transaktion eingibt.
Die Teilnehmer können ihr Geld nicht verlieren, bestenfalls müssen sie auf die Rückkehr warten.
Unterstützung in BlockchainsEs handelt sich um ein einfaches, unkompliziertes Schema, das von den interagierenden Blockchains nur das Nötigste verlangt:
- Unterstützung von Smart Contracts mit mindestens einer Verzweigung
- Beide Blockchains müssen die gleichen Hash-Algorithmen unterstützen (vergessen Sie nicht, die Länge des Geheimnisses zu überprüfen)
- Time-Locks.
Auf den ersten Blick könnte man der Börse "Auf Wiedersehen, unser Treffen war ein Fehler" sagen, aber dem ist nicht so.
Trotz aller Vorteile überzeugen Lösungen mit atomic swap nicht mit ihrer Liquidität. Das liegt zum großen Teil daran, dass in dem beliebtesten Paar BTC-USD der Fiat-Anteil nicht vollständig tokenisiert war.
Der Erfolg von USDT hat eine ganze Welle von Stablecoins im ERC20-Format hervorgebracht, vom custodial USDC bis zum algorithmischen DAI.
Deshalb sprechen wir der Einfachheit halber davon, dass Alice Bitcoins gegen irgendwelche ERC20-Token an Bob verkauft und hoffen auf das Glück der Stabilisatoren, denn wir haben noch viele technischere Probleme.
Geschwindigkeit
Bitcoin und Ethereum sind jeweils nicht besonders schnell, und wir müssen zunächst eine Einzahlung mit allen Bestätigungen abwarten, dann eine weitere.
Das liegt daran, dass zunächst der Teilnehmer mit dem bekannten Geheimnis Geld einzahlt, während der Gegner auf die Finalität wartet und erst danach seinen Teil überweist.
Außerdem haben wir es mit einem sehr volatilen Vermögenswert zu tun, sodass sich der Kurs in dieser Zeit erheblich ändern kann, und es ist bereits schwierig, die Bedingungen zu ändern.
Vertraulichkeit
Jede Transaktion hinterlässt Artefakte in beiden Blockchain-Netzen. Ein aufmerksamer Beobachter kann dieselben Hashes in den Smart Contracts bemerken und zu dem logischen Schluss kommen, dass eine Transaktion stattgefunden hat, was eine Vielzahl von Schlussfolgerungen von Kursfragen bis hin zu steuerlichen Aspekten nach sich ziehen kann.
Wenn die Börse über deine Geschäfte Bescheid weiß, ist das äußerst unangenehm; wenn es jeder weiß, ist das doppelt unangenehm.
Benutzerfreundlichkeit
Das Besondere an Blockchain im Allgemeinen und Ethereum im Besonderen. Lassen Sie uns ansehen, welche Schritte Verkäufer und Käufer unternehmen müssen.
Aus Sicht des Verkäufers ist alles relativ einfach: Man muss einfach Bitcoin an die p2sh-Adresse schicken. Mit Ethereum ist es deutlich komplizierter.
VertragBetrachten wir einen durchschnittlichen GitHub-Vertrag für Swap:
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); // überprüfen, ob der Swap mit dem bestimmten Hash bereits existiert
require(token.transferFrom(msg.sender, address(this), amount)); // gesperrte Token an den Swap-Vertrag übertragen
swaps[msg.sender][receiver] = Swap(token, hash, amount, refundTime, 0x00); // Swap erstellen
}
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 existiert
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 zur Demonstration geschrieben. Insbesondere dieser.
- Bob muss die Funktion des Token-Vertrags aufrufen
reject, um dem Swap-Vertrag den Zugriff auf seine Tokens zu gewähren - Bob erstellt den Swap und den Vertrag mithilfe der Methode
transferFromzieht die Tokens des Senders auf seine Adresse - Alice in
withdrawenthüllt das Geheimnis und ruft den Vertrag auftransfer
Die meisten Wallets und Kryptowährungsbörsen unterstützen nicht reject Token, und das aus gutem Grund.
Die Benutzer machen oft Fehler und übertragen einfach Token auf den Vertrag, woraufhin die Token einfach verloren gehen. Die Kommentare auf Etherscan sind voll von Klagen Unglücklicher.
Um den Vertrag aufzurufen, müssen Gebühren in ETH bezahlt werden, d.h. beide Teilnehmer müssen sich davor eindecken, was nur wenige tatsächlich tun möchten.
Gasgolder
Zunächst sollte die Überprüfung des Absenders überall dort entfernt werden, wo es möglich ist, und man sollte annehmen, dass wir jemanden haben, der unter einem Übermaß an Gas leidet und Verträge für alle, die wollen, aufruft.
Modernisierter Vertrag
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);
}
}
Vertraglich-schlüssel Dualismus und EIP 712
Wie wir wissen, kann eine Adresse im Ethereum ein Vertrag oder ein Subjekt, also ein Schlüssel, sein.
Die Hauptaufgabe des Schlüssels besteht darin, irgendwelche Nachrichten zu unterschreiben.
Wir können Bob-Vertrag als Absender verwenden, der alle notwendigen Übergaben vornimmt, nachdem die Unterschrift von Bob-Schlüssel überprüft wurde.
Jetzt kann jeder die Gebühr des 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);
}
}
Für die Arbeit mit den Unterschriften komplexer Datenstrukturen im Ethereum gibt es einen Standard , mehr darüber können Sie lesen in
Teile und herrsche
Oft sieht das Szenario eines Ethereum-Vertrags-Hacks so aus:
- Ein Teilnehmer legt Geld in den Vertrag
- Dann zieht er das Geld ab
- Etwas funktioniert nicht
- Der Angreifer holt das Geld immer wieder zurück
Wenn wir zu unserem ersten Beispiel zurückkehren, funktioniert etwas nicht, wenn das Rätsel ein leerer Byte-Satz ist.
Wie man eine Million stiehltErstellen eines Swaps mit einem Hash 0x66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925
Das ist sha256 von 0x0000000000000000000000000000000000000000000000000000000000000000
Übertragen des Geheimnisses und Abholen unserer Token
Ein weiteres Mal übergeben und fremde abholen, alles nur weil 0 = 0
Indem wir für jede Transaktion einen eigenen Vertrag erstellen, können wir die Verträge auf EVM-Ebene isolieren.
Aber das ist noch nicht alles: Jetzt hat jede Transaktion ihre eigene Adresse, an die Tokens von jedem Wallet oder Exchange überwiesen werden können.
Verwaiste Verträge und create2
Aber jetzt müssen wir für jede Transaktion einen Vertrag erstellen und darauf warten, dass der Käufer dorthin seine harte "Krypto-Überweisung" überweist. In dem Schema "morgens Verträge, abends Geld" besteht immer die Gefahr, dass der Käufer abspringt, während das Ether zur Erstellung des Vertrags schon ausgegeben wurde.
Kann man nicht so machen, dass morgens Geld, und abends Bytes?
Im Hard Fork Constantinople haben die Entwickler anweisung create2 hinzugefügt, die einen neuen Vertrag an einer deterministischen Adresse erstellt
keccak256( 0xff ++ Adresse ++ Salz ++ keccak256(init_code))[12:]
Wo
- Adresse — Adresse der Fabrik
- Salz — irgendeine Zahl, deren Bedeutung wir in der nächsten Folge erfahren werden
- init_code — Bytecode des Vertrags und Konstruktorparameter.
FabrikDie Anweisung funktioniert nur über Assembly, deshalb sieht die Fabrik etwas einschüchternd aus:
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);
}
}
Den Code Ihres Vertrags können Sie mit web3 erhalten:
const MyContract = new web3.eth.Contract(ABI, {})
const code = MyContract.deploy({
data: BYTECODE,
arguments: konstruktorArgs
}).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 einiger Feinheiten von Ether möglicherweise falsch berechnet werden.
Besonders hübsch ist, dass der Vertrag im Fall von unzureichendem Gas mit einem internen Fehler abstürzt, ohne mitzuteilen, dass nicht genug Gas vorhanden war, wie man es erwarten könnte.
Jetzt können wir Token auf Verträge übertragen, ohne sie vorher zu erstellen, und solange wir sie nicht im Netzwerk veröffentlichen, wird niemand erraten, was der Vertrag tatsächlich tut.
Der Rabe pickt dem Raben kein Auge aus.
Es ist klar, dass ein echter Analyst, insbesondere einer, der gute Investitionen zur Bekämpfung der Feinde des Regimes durch Geldwäsche erhalten hat, sich durch solche Kinderspielchen nicht aufhalten lässt. Nach der Erstellung des Vertrags wird er trotzdem den Hash sehen.
Wie kann man vermeiden, dass der Hash sichtbar wird?
Den Swap selbst verlagern wir off-chain: Die Teilnehmer tauschen Unterschriften für die Übertragung auf den Swap-Vertrag aus, und dann wird das Geheimnis privat offenbart.
Schritt für Schritt.Es werden zwei "Multisig" erstellt, von denen aus Mittel abgehoben werden können, wenn die Unterschriften von Alice und Bob vorhanden sind.
Damit der Ausstieg eines der Teilnehmer aus dem Offline-Modus keine Tragödie wird, fügen wir einen altbewährten Timeout hinzu.
Alice und Bob zahlen gleichzeitig Einlagen ein.
- Alice denkt sich ein Geheimnis aus und überträgt Bob den Hash des Geheimnisses und die Transaktionsunterschrift, die Bitcoins an die Swap-Adresse überträgt.
- Bob übergibt Alice die Unterschrift für den Abzug von Token zum Swap-Vertrag mit dem ausgedachten 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 einer so freundlichen Umgebung können sie ihre Unterschriften austauschen, um Geld an die endgültigen Adressen abzuheben.
Für einen äußeren Beobachter sieht es so aus, als ob Geld durch einen Vertrag mit einer 2 von 2 Multisignatur geflossen ist.
Außerdem ermöglicht dieses Schema beiden Parteien, gleichzeitig eine Einzahlung vorzunehmen, da das Geheimnis bereits nach allen Bestätigungen festgelegt wird.
Stufe 2.
Da wir Geld auf eine Adresse abheben und die Zwischenübertragung nicht veröffentlichen können, steht es uns frei, Geld auf mehrere Adressen abzuheben und unbegrenzt viele Zwischenübertragungen vorzunehmen. Es ist nicht so, als wäre dies eine notwendige Voraussetzung für den Austausch, aber wenn man erst einmal begonnen hat, einen Swap zusammenzustellen, ist es schwer aufzuhören.
Jetzt können Alice und Bob richtig durchstarten. Zum Beispiel automatisch den Durchschnittspreis berechnen, indem sie in Satoshis pro Sekunde tauschen, oder einfach den Market Maker direkt mit dem Liquiditätsempfänger verbinden.
Schritt für Schritt.
- Der Verkäufer denkt sich ein Geheimnis aus und gibt dem Käufer den Hash des Geheimnisses und die Transaktionsunterschrift, wobei ein Teil der Mittel an die p2sh-Adresse des Swaps überwiesen wird, und der Rest an die Adresse des Verkäufers zurückkehrt.
- Der Käufer überträgt eine Unterschrift, die es erlaubt, Token und Wechselgeld an die Adresse des Empfängers zu übertragen.
- Der Verkäufer enthüllt das Geheimnis
- Die Geschichte wiederholt sich mit einem neuen Geheimnis, wobei zusätzlich zum Swap und Wechselgeld auch die Übertragung zuvor gekaufter und bereits bezahlter Token an die Adresse des Käufers hinzugefügt wird.
Jetzt steht uns der hochgeschwindigkeits P2P-Handel zur Verfügung, wichtig ist es, die Zeit im Auge zu behalten und den Deal vor Ablauf der Frist abzuschließen.
Wenn wir unsere Verträge ein wenig anpassen, können wir unseren Kanälen das Unsterblichkeit schenken, was es uns erheblich erleichtert, ein Netzwerk zu schaffen.
Aber darüber werden wir in der nächsten Folge berichten.
Quelle: habr.com
