Vad Àr dÄligt med atomÀra swappar och hur kan kanaler hjÀlpa dem, vad som hÀnde med hÄrdgaffeln Constantinople och hur ska man agera nÀr man inte har rÄd att betala för gas.
Huvudmotivation för varje sÀkerhetsspecialist Àr en önskan att undvika ansvar.
Försynen var nÄdig, jag lÀmnade ICO:t utan att vÀnta pÄ den första irreversibla transaktionen, men snart befann jag mig istÀllet med att utveckla en kryptobörs.
Jag Àr definitivt inte Malchish Kibalchish, och en enda strÀng blick rÀcker för att jag ska ge upp alla nycklar och lösenord. DÀrför var mitt huvudsakliga mÄl som arkitekt att placera den heta stinger av kryptografi sÄ lÄngt bort som möjligt frÄn de infrastrukturella delar jag höll kÀrt.
Inte dina nycklar, inte dina problem.
Vi bygger ett system för tillgÄngshandel och vill utesluta mellanlagring av dessa tillgÄngar hos oss, men mÄste sÀkerstÀlla att transaktionen Àr sÀker.
Vi kan agera som domare i en tvistig situation och genomföra transaktioner med plÄnböcker som krÀver tvÄ av tre signaturer: köparens, sÀljarens och escrow.
Men om en deltagare framgÄngsrikt attackerar escrow, fÄr han de tvÄ önskade signaturerna.
En atomÀr swap Àr en byteordning dÀr en smart kontrakt Àr garant som endast tillÄter Àrligt beteende.
Som i gÄtan om vargen, geten och kÄlen kan du endast agera enligt det enda korrekta scenariot och du fÄr förluster om du avviker frÄn det.
Men istÀllet för giriga djur sÀkerstÀlls ordningen genom en hash-funktion, dÀr det Àr sÄ svÄrt att hitta en kollision att det inte ens Àr vÀrt att börja.
Steg ett: gÄtan.
Anta att Alice en vacker morgon vill överföra Bitcoin till Bob mot en nÀve "krypto-yuaner".
- Hon vÀljer en stor hemlighet.
- FÄr en hash frÄn honom.
- Ăverför Bitcoin till en smart kontrakt, frĂ„n vilken Bob kan hĂ€mta pengarna genom att presentera hemligheten (hashet mĂ„ste överensstĂ€mma med det som anges i kontraktet).
- Om Bob inte kommer för sina Bitcoin till kvÀllen kan Alice ta tillbaka dem.
Steg tvÄ: lockbetet.
Bob gÄr in i spelet och överför "krypto-euro" till sin kontakt, som Àr skriven pÄ ett sÄdant sÀtt att:
- Alice kan ta "krypto-yuanerna" om hon presenterar det hemliga numret.
- Inte tidigare Àn vid lunchtid kan Bob, om Alice inte dyker upp, ÄterfÄ insÀttningen.
Steg tre: lösningen pÄ lockbetet.
Alice kommer för sina pengar och tar pengarna frÄn Bobs kontrakt, samtidigt som hon avslöjar sin hemlighet.
Avslutande steg: gÄtan Àr löst
Bob ser transaktionen och med sitt skarpa öga plockar han ut hemligheten som Alice presenterade för kontraktet. Denna hemlighet anvÀnder han för att hÀmta sina bitcoins.
NÀr nÄgot gÄr fel
Om Alice plötsligt blir dödlig, hÀmtar Bob sina yuan vid lunch.
I sin tur Äterbetalar Alice bitcoins pÄ kvÀllen om den svikna Bob vÀljer att hÄlla kvar pengarna tills bÀttre tider.
Om du föredrar bilder framför text, finns det en mer detaljerad och visuell förklaring av atomÀra swaps pÄ Habr. .
Skillnaden mellan tidsgrÀnser Àr avsedd att skydda oss frÄn den illvilliga Alice, som snor Bobs pengar i sista stund, medan han med darrande fingrar skriver in hex i transaktionen.
Deltagarna kan inte förlora sina pengar, max fÄr de vÀnta pÄ ÄterbÀringen.
Stöd i blockkedjorDetta Àr ett enkelt schema, som krÀver vÀldigt lite frÄn de interagerande blockkedjorna:
- Stöd för smarta kontrakt med minst en förgrening
- BÄda blockkedjorna mÄste stödja samma hash-algoritmer (glöm inte att kontrollera lÀngden pÄ hemligheten)
- TidslÄser.
Vid första anblick verkar det möjligt att sÀga till börsen "farvÀl, vÄrt möte var ett misstag", men sÄ enkelt Àr det inte.
Trots sina fördelar imponerar lösningarna pÄ atomic swap inte med likviditet. Mycket av detta beror pÄ att den mest populÀra paret BTC-USD inte var helt tokeniserad.
FramgÄngen för USDT har gett upphov till en hel vÄg av stabila mynt i ERC20-format, frÄn det mest förvarade USDC till det mest algoritmiska DAI.
DÀrför kommer vi för enkelhetens skull att resonera vidare om att Alice sÀljer bitcoins till Bob för nÄgra ERC20-tokens, och hoppas pÄ stabilisatorernas framgÄng, eftersom vi har mÄnga fler tekniska problem.
Hastighet
Bitcoin och Ethereum Àr kanske inte sÀrskilt snabba pÄ egen hand, och hÀr mÄste vi först vÀnta pÄ en insÀttning med alla bekrÀftelser, sedan den andra.
Detta beror pÄ att pengarna först sÀtts in av deltagaren som kÀnner till hemligheten, medan motparten vÀntar pÄ finaliteten och först dÀrefter överför sin del.
Dessutom hanterar vi en mycket volatil tillgÄng, sÄ under denna tid kan kursen förÀndras avsevÀrt, och det Àr svÄrt att Àndra villkoren.
Sekretess
Varje utbyte lÀmnar artefakter pÄ bÄda blockkedjorna. En noggrann iakttagare kan mÀrka identiska hashvÀrden i smarta kontrakt och dra logiska slutsatser, allt frÄn valutakursförÀndringar till skattefrÄgor.
NĂ€r börsen kĂ€nner till dina affĂ€rer â Ă€r det extremt obehagligt; nĂ€r alla kĂ€nner till dem â Ă€r det dubbelt obehagligt.
AnvÀndarvÀnlighet
KÀrnan i blockkedjan, och speciellt Ethereum. LÄt oss titta pÄ vilka ÄtgÀrder som sÀljaren och köparen mÄste vidta.
Ur sÀljarens perspektiv Àr allt relativt enkelt: man behöver bara överföra Bitcoin till en p2sh-adress. Med Ethereum Àr det mycket mer komplicerat.
KontraktLÄt oss titta pÄ ett genomsnittligt kontrakt för swap pÄ 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);
}
}
Varning! AnvÀnd inte detta och andra kontrakt frÄn artikeln i produktion, de Àr skrivna endast för demonstrationsÀndamÄl. SÀrskilt detta.
- Bob mÄste kalla pÄ tokenkontraktets metod
approve, vilket ger swap-kontraktet tillgÄng till sina tokens - Bob skapar en swap och kontraktet med metoden
transferFromhÀmtar avsÀndarens tokens till sin adress - Alice i
withdrawavslöjar hemligheten och kontraktet kallasöverföring
De flesta plÄnböcker och kryptobörser stödjer inte approve tokens, och inte utan anledning.
AnvÀndarna gör ofta misstag och överför bara tokens till kontraktet, vilket gör att tokens bara gÄr förlorade. Kommentarer pÄ Etherscan Àr fulla med klagomÄl frÄn olyckliga anvÀndare.
För att kalla kontraktet mÄste man betala en avgift i ETH, vilket innebÀr att bÄda parter mÄste ha detta innan affÀren inleds, och det Àr det fÄ som vill syssla med.
Gasbolaget
Först bör man ta bort avsÀndarverifiering dÀr det Àr möjligt och anta att vi har nÄgon som lider av överflödig gas och kallar kontrakt för alla som vill.
Moderniserat 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);
}
}
Kontrakts-nyckeldualism och EIP 712
Som vi vet kan en adress pÄ Ethereum vara ett kontrakt, eller sÄ kan det vara ett subjekt, d.v.s. en nyckel.
Nyckelns huvuduppgift Àr att signera meddelanden.
Vi kan anvÀnda Bob-kontraktet som avsÀndare, som utför alla nödvÀndiga pass, efter att ha verifierat Bob-nyckelns signatur.
Nu kan vem som helst sponsra en deltagaravgift, men beslutsrÀtten har endast den som kÀnner till nyckeln.
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);
}
}
För att arbeta med signaturer av komplexa datatyper i Ethereum finns det en standard , du kan lÀsa mer om det i
Dela och hÀrska
Ofta ser ett scenario för att hacka en Ethereum-kontrakt ut sÄ hÀr:
- Deltagaren sÀtter in medel i kontraktet
- Sen tar de ut medlen
- NÄgot gÄr fel
- Angriparen tar pengarna om och om igen
Om vi ÄtergÄr till vÄrt första exempel, gÄr nÄgot fel om gÄtan Àr en tom uppsÀttning bytes.
Hur man stjÀl en miljonSkapa en swap med en hash 0x66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925
Detta Àr sha256 av 0x0000000000000000000000000000000000000000000000000000000000000000
Vi överför hemligheten och hÀmtar vÄra tokens
Vi överför igen och hÀmtar andras, allt för att 0 = 0
Genom att skapa ett separat kontrakt för varje affÀr kan vi isolera kontrakten pÄ EVM-nivÄ.
Men det Àr inte allt: nu har varje affÀr sin egen adress, dit man kan överföra tokens frÄn vilken plÄnbok eller börs som helst.
Ăvergivna kontrakt och create2
Men nu mÄste vi skapa ett kontrakt för varje affÀr och vÀnta pÄ att köparen överför sitt arbetsrelaterade "kryptovaluta". I schemat "kontrakt pÄ morgonen, pengar pÄ kvÀllen" finns alltid risk att köparen drar sig ur, medan eter för att skapa kontraktet redan har spenderats.
Kan vi inte göra sÄ att pengarna Àr pÄ morgonen och bytes pÄ kvÀllen?
I hard fork Constantinople lade utvecklarna till en instruktion create2, som skapar ett nytt kontrakt pÄ en deterministisk adress
keccak256( 0xff ++ address ++ salt ++ keccak256(init_code))[12:]
DĂ€r
- address â adressen till fabriks-kontraktet
- salt â ett nummer vars syfte vi kommer att förstĂ„ i nĂ€sta avsnitt
- init_code â byte-koden för kontraktet och konstruktörens parametrar.
FabrikenInstruktionen fungerar endast genom assembly, sÄ fabriken ser lite skrÀmmande 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);
}
}
Din kontraktskod kan erhÄllas med hjÀlp av web3:
const MyContract = new web3.eth.Contract(ABI, {})
const code = MyContract.deploy({
data: BYTECODE,
arguments: constructorArgs
}).encodeABI();
const factory = new web3.eth.Contract(FACTORY_ABI, factoryAddress);
tx = factory.methods.create2(code, salt);
PÄ grund av begrÀnsat stöd i solidity kan gasen för kontraktet berÀknas felaktigt pÄ grund av vissa subtiliteter i eter.
SÀrskilt trevligt Àr det att i hÀndelse av otillrÀcklig gas misslyckas kontraktet med ett internt fel, utan att ange att gasen inte rÀckte, som man kanske skulle förvÀnta sig.
Nu kan vi översÀtta tokens till kontrakt utan att förbereda dem i förvÀg, och tills vi publicerar dem pÄ nÀtet kommer ingen att förstÄ vad kontraktet gör.
KrÄkan hackar inte varandras ögon.
Det Àr uppenbart att en riktig analytiker, sÀrskilt en som fÄtt bra investeringar för att bekÀmpa fiender till systemet genom penningtvÀtt, inte stoppas av sÄdana barnsliga knep, och efter skapandet av kontraktet kommer han ÀndÄ att se hash.
Hur gör man sÄ att hash inte avslöjas?
SjÀlva swapen flyttar vi till off-chain: deltagarna byter signaturer för överföring till swap-kontraktet, och sedan avslöjas hemligheten privat.
Steg för stegTvÄ "multisig"-kontrakt skapas, frÄn vilka medel kan hÀmtas med signaturer frÄn Alice och Bob.
För att en eventuell avstÀngning av nÄgon av deltagarna inte ska bli en tragedi, lÀgger vi till den gamla goda timeouten.
Alice och Bob sÀtter in insÀttningar parallellt.
- Alice föreslÄr en hemlighet och överlÀmnar till Bob hash av hemligheten och signaturen av transaktionen som överför bitcoin till swap-adressen.
- Bob överlÀmnar en signatur till Alice för uttag av tokens till swap-kontraktet med den föreslagna hashen.
- Alice meddelar Bob hemligheten.
Vid det hÀr laget intrÀder harmoni: bÄde Alice och Bob kan nÀr som helst avsluta affÀren. I en sÄdan vÀnlig miljö kan de byta signaturer för att ta ut pengar till slutadresser.
För en utomstÄende observatör ser det ut som om pengarna har passerat genom ett kontrakt med multiparaf som krÀver 2 av 2.
Dessutom tillÄter ett sÄdant schema bÄda parter att sÀtta in insÀttningar samtidigt, eftersom hemligheten föreslÄs först efter alla bekrÀftelser.
NivÄ 2
Eftersom vi kan ta ut pengar till en adress och inte publicera den interimistiska transaktionen, finns det inget som hindrar oss frÄn att ta ut pengar till flera adresser och utföra ett obegrÀnsat antal interimistiska transaktioner. Det Àr inte nödvÀndigt för en swap, men nÀr man vÀl har börjat samla swap Àr det svÄrt att stanna.
Nu kan Alice och Bob ge sig ut pÄ djupet. Till exempel, automatiskt berÀkna det genomsnittliga priset genom att byta satsar per sekund, eller helt enkelt koppla samman marknadsgöraren och likviditetsmottagaren direkt.
Steg för steg
- SÀljaren föreslÄr en hemlighet och ger köparen hash av hemligheten och signaturen av transaktionen dÀr en del av medlen överförs till p2sh-adressen för swapen, medan resten ÄtergÄr till sÀljarens adress.
- Kunden överför en signatur som möjliggör att tokens och vÀxelpengar kan överföras till mottagarens adress.
- SÀljaren avslöjar hemligheten
- Historien upprepas med en ny hemlighet, dÀr tidigare köpt som redan har betalats till sÀljaren lÀggs till vÀxeln och vÀxelpengarna.
Nu har vi tillgÄng till högsnabb p2p-handel, det Àr viktigt att hÄlla ett öga pÄ tiden och stÀnga affÀren innan tidsgrÀnsen.
Men genom att justera vÄra kontrakt lite kan vi ge vÄra kanaler odödlighet, vilket kommer att förenkla nÀtverksuppbyggnaden avsevÀrt.
Men det fÄr vi berÀtta mer om i nÀsta avsnitt.
KĂ€lla: habr.com
