Mik a hátrányai az atomikus swapoknak és hogyan segíthetnek a csatornák, mi történt fontos a Constantinople hard forknál, és mit tegyünk, ha nincs miből gázra fizetni.
Bármely biztonsági szakember fő motivációja a felelősség elkerülése.
A Gondviselés irgalmas volt, elhagytam az ICO-t, mielőtt a első visszafordíthatatlan tranzakcióra vártam volna, de hamarosan a kriptotőzsde fejlesztése közepette találtam magam.
Határozottan nem vagyok Kiberchev fiú, és elég egy szigorú pillantás, hogy minden kulcsot és jelszót átadjak. Ezért, mint építész, a fő célom az volt, hogy a kriptoanalízis forró tűsarkát a lehető legmesszebb helyezzem el az általam kedvelt infrastruktúra elemeitől.
Nem a kulcsaid, nem a problémáid.
Egy eszközcsere rendszert építünk, és szeretnénk elkerülni az eszközök közvetlen tárolását, de biztosítani kell a tranzakció biztonságát.
Lehetsz bíró vitás helyzetekben, és tranzakciókat végezhetsz olyan pénztárcákkal, amelyekhez három aláírás szükséges: a vevőé, az eladóé és az eszközt kezelőé.
Ha azonban a résztvevő sikeresen támadja meg az eszközt kezelőt, akkor megkapja a kívánt két aláírást.
Az atomikus swap egy olyan csere, ahol a garanciat a smart contract biztosítja, amely csak becsületes viselkedést engedélyez.
Mint a farkas, a kecske és a káposzta rejtvényében, csak egyetlen helyes forgatókönyv szerint működhetsz, és veszteségeket szenvedsz el, ha eltérsz tőle.
Csak a falánk állatok helyett a sorrendet a hash függvény biztosítja, amelyben annyira nehéz ütközést találni, hogy nem is érdemes elkezdeni.
Első lépés: rejtvény.
Tegyük fel, hogy egy szép reggelen Alice bitcoint szeretne adni Bobnak egy marék „kriptóforintért”.
- Kitalál egy nagy titkot.
- Kap tőle egy hash-t.
- Bitcoint utal a smart contractra, ahonnan Bob pénzt vehet fel, ha bemutatja a titkot (a hash-nek meg kell egyeznie a szerződésben megadottal).
- Ha Bob nem jön el a bitcoint keresni este, Alice vissza tudja venni őket magának.
Második lépés: csali.
Bob belép a játékba, és „kriptóeurót” utal a saját szerződésére, amelyet úgy írtak meg, hogy:
- Alice vissza tudja venni a „kriptóforintot”, ha bemutatja a titkos számot.
- Bob nem korábban délután, a résztvevő Alice távollétében vissza tudja kapni a letétet.
Harmadik lépés: a csali megfejtése.
Alisa eljön a pénzéért, és elviszi Bob szerződéséből a pénzt, miközben felfedi a titkát.
Záró lépés: a rejtély megoldva
A tranzakciót Bob látja, és sólymosszemével kiszűri belőle a titkot, amelyet Alisa a szerződéshez mutatott. Ezt a titkot arra használja, hogy visszaszerezze a saját bitcoinjait.
Amikor valami nem működik megfelelően
Ha Alisa hirtelen halandóvá válik, Bob az ebédidőben visszaszerzi a jüanokat.
Cserébe Alisa délután visszaadja a bitcoint, ha a megbízhatatlan Bob úgy dönt, hogy visszatartja a pénzt jobb időkre.
Ha kedveli a képeket a szövegekkel szemben, a Habr-on részletesebb és szemléletesebb magyarázatot talál. .
A timeoutok közötti különbség azért van, hogy megvédjen minket a gonosz Alisától, aki az utolsó pillanatban elveszi Bob pénzét, és a timeout lejár, míg Bob remegő ujjaival beüti a hex kódot a tranzakcióba.
A résztvevők nem veszíthetik el a pénzüket, legfeljebb várniuk kell a visszatérésre.
Támogatás a blokkláncokbanEz egy olyan egyszerű, mint a faék, séma, amely a kölcsönhatásban álló blokkláncoktól alig igényel valamit:
- Okos szerződések támogatása legalább egy elágazással
- Mindkét blokkláncnak ugyanazokat a hash algoritmusokat kell támogatnia (ne feledje ellenőrizni a titok hosszát)
- Zárolások.
Első pillantásra már mondhatnánk a tőzsdének 'viszlát, a találkozónk hibás volt', de nem itt van a vége.
Mindezek ellenére az atomic swap megoldások nem tűnnek túl likvidnek. Nagy részben azért, mert a legnépszerűbb BTC-USD párban a fiat rész nem volt teljesen tokenizálva.
Az USDT sikere hagyományos ERC20 szabványú stabilcoinok egész hullámát indította el, a legkevésbé letétbe helyezett USDC-től a legalgoritmusosabb DAI-ig.
Ezért a könnyebb érthetőség kedvéért azt feltételezzük, hogy Alisa bitcoint ad el Bobnak valamilyen ERC20 tokenért, és bízunk a stabilizátorok szerencséjében, mivel még sok technikai probléma vár ránk.
Sebesség
A bitcoin és az Ethereum külön-külön nem túl gyors, és itt először egy betétre és minden igazolásra kell várnunk, majd a másodikra.
Ez azért van, mert először az a résztvevő helyezi el a pénzt, akinek ismeretes a titok, míg az ellenfél a véglegességre vár, és csak ezután utalja át a saját részét.
Emellett nagyon volatilis eszközzel van dolgunk, így ebben az időszakban az árfolyam jelentősen változhat, és nehéz lesz megváltoztatni a feltételeket.
Adatvédelem
Bármely tranzakció nyomokat hagy mindkét blokkláncon. Egy figyelmes megfigyelő észlelheti az egyforma hash-ket az okos szerződésekben, és logikus következtetéseket vonhat le a tranzakciókról, egészen az árfolyamoktól kezdve a adókötelezettségekig.
Ha a tőzsde tud a dolgaidról —az rendkívül kellemetlen, ha mindenki tud róla —ez még kétszer kellemetlenebb.
Használhatóság
A blokklánc és különösen az Ethereum erőssége. Nézzük meg, milyen lépéseket kell tennie az eladónak és a vevőnek.
Eladó szempontjából minden viszonylag egyszerű: csak bitcoint kell átutalnia egy p2sh címre. Az Ethereum esetében azonban a dolog sokkal bonyolultabb.
SzerződésNézzük meg a GitHubon található átlagos swap szerződést:
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); // ellenőrizd, hogy létezik-e már swap a megadott hash-sel
require(token.transferFrom(msg.sender, address(this), amount)); // zárolt tokenek átutalása a swap szerződéshez
swaps[msg.sender][receiver] = Swap(token, hash, amount, refundTime, 0x00); // swap létrehozása
}
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 létezik
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);
}
}
Figyelem! Ne használd ezt és más szerződéseket a cikkben éles környezetben, ezek kizárólag bemutató célokra íródtak. Különösen ezt.
- Bobnak a token szerződés metódusát kell hívnia
approve, hogy hozzáférést adjon a swap szerződésnek a tokenjeihez - Bob létrehozza a swapot és a szerződést a
transferFrommetódus segítségével, amely átkonvertálja a küldő tokenjeit a saját címére - Alice a
withdrawfelfedi a titkot és a szerződés hívásaátutalás
A pénztárcák és kriptovaluta tőzsdék többsége nem támogatja approve tokeneket, és nem véletlenül.
A felhasználók gyakran tévednek, és egyszerűen átutalják a tokeneket a szerződésre, ami után a tokenek egyszerűen elvesznek. Az Etherscan megjegyzései tele vannak a szerencsétlenek jajkiáltásaival.
Ahhoz, hogy szerződést hívjunk, ETH-t kell fizetni, tehát mindkét félnek rendelkeznie kell ezzel az ügyletek előtt, amit kevesen akarnak megszervezni.
Gazdálkodó
Először érdemes eltávolítani a küldő ellenőrzését mindenhol, ahol csak lehet, és feltételezni, hogy van valaki, aki a gázfelesleggel küzd, és szerződéseket hív fel minden kíváncsi arc számára.
Modernizált szerződés
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); // használja a hash-t egyszer
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);
}
}
Szerződés- és kulcsdualizmus, valamint az EIP 712
Ahogy tudjuk, az ether címe lehet szerződés, de lehet külön alany is, azaz kulcs.
A kulcs fő feladata valamilyen üzenetek aláírása.
Használhatjuk Bob-szerződést küldőként, amely elvégzi az összes szükséges folyamatot, először is ellenőrizve Bob-kulcs aláírását.
Most bárki szponzorálhatja a résztvevő díját, de csak az dönt, akinek ismeretes a kulcs.
Bob-szerződés
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);
}
}
A bonyolult adatstruktúrák aláírásával kapcsolatos munka során Ethereumban van egy szabvány , többet olvashatsz róla a
Osztályozd és uralkodj
Gyakran az Ethereum szerződések feltörésének forgatókönyve így néz ki:
- A résztvevő pénzt helyez el a szerződésbe
- Aztán felveszi a pénzt
- Valami nem stimmel
- A támadó újra és újra elviszi a pénzt
Ha visszatérünk az első példánkhoz, valami nem stimmel, ha a rejtély egy üres bájtsorozat.
Hogyan lophatunk el egy milliótHash-swap létrehozása 0x66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925
Ez a sha256 a 0x0000000000000000000000000000000000000000000000000000000000000000
Titkosítunk és visszavesszük a tokenjeinket
Még egyszer átadjuk és ellopjuk a másikét, mindezt azért, mert 0 = 0
Minden tranzakcióhoz külön szerződés létrehozásával izolálhatjuk a szerződéseket az EVM szintjén.
De ez még nem minden: most minden tranzakciónak van egy címe, ahová tokeneket lehetett átutalni bármely pénztárcából vagy tőzsdéről.
Elhagyott szerződések és create2
De most minden tranzakcióhoz létre kell hoznunk egy szerződést és meg kell várnunk, hogy a vásárló átutalja a munka “kriptojuttatását”. A "reggel szerződések, este pénz" sémában mindig fennáll a veszély, hogy a vásárló eltűnik, és az ether már el lett költve a szerződés létrehozására.
Nem lehetne úgy csinálni, hogy reggel pénz, este bájtok?
A Constantinople hard fork során a fejlesztők hozzáadtak egy create2 utasítást, amely egy új szerződést hoz létre egy determinisztikus címen
keccak256( 0xff ++ cím ++ só ++ keccak256(init_code))[12:]
Ahol
- cím — a gyári szerződés címe
- só — egy szám, amelynek értelmét a következő epizódban fogjuk megtudni
- init_code — a szerződés bájtódja és a konstruktor paraméterei.
GyárAz utasítás csak assembly-n keresztül működik, ezért a gyár kicsit félelmetesen néz ki:
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);
}
}
A szerződésed kódját a web3 segítségével nyerheted ki:
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);
A Solidity korlátozott támogatása miatt a gáz a szerződéshez helytelenül számolható a néhány ether finomság miatt.
Különösen aranyos, hogy gázhiány esetén a szerződés belső hibával bukik el, anélkül, hogy jelezné, hogy a gáz nem volt elegendő, ahogy azt várnánk.
Most már tokeneket tudunk átutalni szerződésekre anélkül, hogy előre létrehoznánk őket, és amíg nem tesszük közzé őket a hálózaton, senki sem fogja kitalálni, hogy pontosan mit csinál a szerződés.
A varjú nem vájja ki a másik varjú szemét
Nyilvánvaló, hogy egy igazi elemzőt, különösen, aki jó befektetéseket kapott a rendszer ellenségei ellen a pénzmosás révén, ilyen gyermeteg trükkök nem állítanak meg, és a szerződés létrehozása után úgyis meg fogja látni a hash-t.
Hogyan lehet úgy megoldani, hogy a hash ne kerüljön napvilágra?
Magát a swapot off-chain-ként kezeljük: a résztvevők aláírásokat cserélnek a swap-szerződésre való átutaláshoz, majd titokban felfedik a titkot.
Lépésről lépésreKét „multi-sig” jön létre, amelyekből akkor lehet kiutalni a forrást, ha Alice és Bob aláírásai is rendelkezésre állnak.
Annak érdekében, hogy bárki kimenetele az offline résztvevők közül ne legyen tragédia, adjunk hozzá egy régi jó időkorlátot.
Alice és Bob párhuzamosan befizetik a betéteket
- Alice titkot talál ki, és átadja Bobnak a titok hash-ét, valamint a tranzakció aláírását, amely a bitcoint a swap címére utalja.
- Bob átadja Alicenek az aláírást a swap-szerződésre való tokenek kiutalásához a kitalált hashel.
- Alice közli Bobbal a titkot.
Ekkor elérkezik a harmónia pillanata: Alice és Bob bármikor befejezhetik az üzletet. Ebben a barátságos légkörben aláírásokat válthatnak az alapok végső címekre való kiutalásához.
A külső szemlélő számára úgy tűnik, mintha a pénz áthaladt volna egy 2 az 2-es multi-sig szerződésen.
Ezen felül ez a séma lehetővé teszi mindkét fél számára, hogy egyszerre végezzenek befizetést, mivel a titok kitalálása már az összes megerősítés után történik.
Level 2
Most, hogy pénzt tudunk kiutalni egy címre, és nem publikálunk közbenső tranzakciót, semmi sem gátol abban, hogy pénzt utaljunk át több címre és korlátlan számú közbenső tranzakciót végezzünk. Nem mintha ez szükséges lenne a swaphoz, de ha egyszer elkezdtél swapot gyűjteni, nehéz megállni.
Most Alice és Bob teljesen kibővülhetnek. Például automatikusan kiszámíthatják az átlagos árat, miközben satoshit váltanak másodpercenként, vagy egyszerűen közvetlenül összekapcsolhatják a piaci készítőt és a likviditás fogadóját.
Lépésről lépésre
- Az eladó titkot talál ki és átadja a vásárlónak a titok hash-ét, valamint a tranzakció aláírását, ahol a forrás egy része a swap p2sh címére kerül, a maradék pedig az eladó címére visszatér.
- A vásárló aláírási jogosultságot ad, amely lehetővé teszi a swap tokenek és a visszajáró összeg átutalását a kedvezményezett címére.
- Az eladó felfedi a titkot
- A történet új titokkal ismétlődik, miközben a swap-hoz és a visszajáró összeghez hozzáadódik a korábban vásárolt összeg átutalása a vásárló címére, amely már kifizetésre került az eladó címére.
Mostantól elérhető a nagy sebességű p2p kereskedelem, a lényeg, hogy figyeljünk az időre, és a tranzakciót a lejárati idő előtt zárjuk le.
Azonban, ha egy kicsit módosítjuk a szerződéseinket, örökéletet adhatunk csatornáinknak, ami jelentősen megkönnyíti a hálózat létrehozását.
De erről a következő epizódban fogunk beszélni.
Forrás: habr.com
