Uniswap v4 Hook Security Analysis: Arkitektura, Mga Banta, at Mga Pinakamabuting Pamamaraan

icon MarsBit
I-share
AI summary iconSummary

Pagkatapos ng paglunsad ng Uniswap v4 sa mainnet, ang mekanismo ng Hook ay naging isa sa pinakamalaking inobasyon sa DeFi. Ang Flaunch, isang platform para sa paglunsad ng memecoin sa Base chain, ay gumagamit ng Hook upang maabot ang fixed presale price at automatic listing清算 mechanism; ang liquidity protocol na Bunni v2 ay gumagamit ng Hook upang buuin ang programmable liquidity at re-collateralization model; sa loob ng taong ito, ang mga token tulad ng SATO, uPEG (Unipeg), at Slonks na may kaugnayan sa mga玩法 ng Hook ay nakapagbigay ng mga pagtaas na nasa dekada.

Sa kabilang panig ng pag-unlad ng ekosistema ng Hook, lumalaki rin ang mga serbisyo na nagpapakita ng mga kakulangan sa pagpapatupad ng Hook. Ang artikulong ito ay magsisimula sa mekanismo ng Hook ng Uniswap v4, at susuriin nito nang paulit-ulit ang pangunahing call stack upang tulungan ang mga proyekto na maunawaan ang mga posibleng butas dito.

Uniswap v4 Hook na Seguridad

1. Introduksyon

Ang pinakamalaking pagbabago sa arkitektura ng Uniswap v4 kumpara sa v3 ay ang pagpapakilala ng mekanismo ng Hook: nagbibigay-daan sa mga developer na i-attach ang kanilang sariling contract sa mga pangyayari sa buhay ng liquidity pool, at magdagdag ng anumang lohika sa mga punto tulad ng swap, pagdaragdag o pagbawas ng liquidity, at pag-initialize.

Ang ilang pangunahing pagbabago sa v4 ay sumusunod:

Singleton pattern: Ang lahat ng estado ng mga pool ay pinamamahalaan ng isang solong PoolManager contract, hindi na kailangang i-deploy ang hiwalay na contract para sa bawat pool

- Flash accounting: Ang mga pagbabago sa intermediate balance sa proseso ng transaksyon ay tanging nakarehistro sa transient storage, at iisang pagkakataon lang na isinasagawa ang pagkakasundo sa wakas ng transaksyon

-Mekanismo ng Hook: Bawat pool ay maaaring makabind ng isang Hook contract, at ang PoolManager ay magpapalabas ng callback sa contract sa mga mahahalagang punto (beforeInitialize, beforeSwap, afterAddLiquidity, atbp.)

- Hindi maaaring palitan ang Hook: Kapag natapos na ang pag-initialize ng pool, ang nakabigay na Hook address ay permanenteng nakafiksa (hindi maaaring baguhin ang Hook address na nakabigay sa pool, ngunit ang kakayahan ng Hook contract na i-upgrade ay nakadepende sa kanyang implementasyon)

Image

Sa panahon ng v3, kailangan lang ng mga developer na maniwala sa Uniswap protocol mismo; sa panahon ng v4, ang kaligtasan ng bawat pool ay nakadepende sa hook na nakabigay dito. Ang hook ay nagpalawak sa AMM mula sa isang fixed financial primitive hanggang sa isang programmable financial infrastructure, ngunit ang security model ay naging fragmented mula sa “protocol-level” patungo sa “pool-level”.

2. Arkitektura ng Hook

2.1 PoolManager at ang unlock/callback model

Ang pangunahing kontrato ng v4 ay ang singleton na PoolManager. Ang anumang pagbabago sa estado ng pool (swap, pagdaragdag o pagbawas ng liquidity) ay kailangang unang tawagan ang PoolManager.unlock() upang makakuha ng isang beses na callback permission, at matapos ay tapusin ang partikular na aksyon sa unlockCallback(). Sa wakas ng buong proseso, susuriin ng PoolManager kung balanse ang libro:

Image

Diretang i-revert ang NonzeroDeltaCount != 0, ito ang pangunahing pagkakabound sa v4 flash accounting. Anumang Hook ay maaaring pansamantala magpalit ng balanse sa panahon ng pagpapatupad, ngunit kailangan nito na i-settle ang sarili nito bago matapos ang transaksyon, kung hindi ay babalik ang buong transaksyon.

Ang bawat pool ay ikinakilanlang may natatanging PoolKey structure, na naglalaman ng field na hooks:

Image

Ang PoolId ay kalkulahin gamit ang keccak256(PoolKey), kaya ang iba’t ibang hooks address ay magdudulot ng iba’t ibang pool. Ito ay nangangahulugan din na ang PoolManager ay hindi magpapatotoo kung ang isang Hook address ay dating ginamit para sa ibang pool; ang isang parehong Hook contract ay maaaring i-bind ng maraming pool nang sabay-sabay.

Image

2.2 Ang pag-encode ng permission bit sa address

Isang hindi intuisyong disenyo ng v4 ay: ang pahintulot ng Hook ay hindi tinukoy ng isang variable sa loob ng contract, kundi sa address kung saan ipinapalabas ang Hook contract.

Ang PoolManager ay nag-e-verify kung kailangang i-call ang Hook sa isang partikular na punto ng buhay ng proseso sa pamamagitan ng pagsusuri sa mga low 14 na bit ng Hook address:

Image

Halimbawa, BEFORE_SWAP_FLAG = 1 << 7. Kung ang 7th bit ng Hook address ay 1, tatawagan ng PoolManager ang beforeSwap() ng Hook bago ang swap; kung hindi, kahit na ang Hook contract ay nag-implement ng beforeSwap(), hindi ito maiiwan ng PoolManager.

Ibig sabihin nito na ang Hook ay dapat i-deploy gamit ang CREATE2 + salt upang kalkulahin ang address at lumikha ng isang address na may mababang bahagi na may pantay na pagsasamantala. Binigay ng Uniswap ang HookMiner tool para sa layuning ito:

Image

Kapag hindi tugma ang mga bit ng pahintulot at ang pagpapatupad ng mga punsiyon, lumalabas ang dalawang uri ng problema:

(1) Nilikha ang isang hook function, ngunit ang address ay hindi encoded sa tamang permission bit—hindi maiiwasan ng PoolManager na tawagan ang function na ito, ang lohika ay parang walang kwenta

(2) Ang address ay nag-encode ng isang permission bit, ngunit ang hook ay hindi nag-implement ng kaugnay na function—ang PoolManager ay maaaring mag-revert sa callback, na nagdudulot ng DOS o pagkabigo sa pag-verify ng return value, na nagresulta sa pagkakabigo ng kaugnay na operasyon.

Ito ay isang natural na hadlang sa pag-upgrade ng Hook: kung ang Hook ay maaaring i-upgrade sa pamamagitan ng proxy, ang address ng deployment ay mananatiling hindi nagbabago sa panahon ng upgrade, kaya ang pag-upgrade ay maaaring baguhin lamang ang implementasyon ng mga umiiral na hook function, at hindi magdagdag ng mga uri ng hook. Upang mag-iwan ng espasyo para sa pagpapalawig sa hinaharap, kailangan mong i-pre-allocate ang lahat ng posibleng mga bit ng pahintulot sa oras ng unang deployment.

2.3 BaseHook at isang karaniwang nakakalimutang trap sa pagkontrol ng pag-access

Ang BaseHook abstract contract na ibinigay ng Uniswap v4 periphery ay maaaring maging basehan ng mga developer para makapag-implement ng custom Hook. Ang isang mahalagang gamit ng BaseHook ay ang pagbibigay ng onlyPoolManager modifier sa function na unlockCallback():

Image

Ngunit — mayroon dito isang napakadaling makalimutang design trap — ang mga naunang bersyon ng BaseHook ay nagdagdag lamang ng onlyPoolManager sa unlockCallback, at walang proteksyon sa iba pang hook callback functions (beforeSwap, afterSwap, beforeAddLiquidity, atbp.). Dapat ng magdagdag ng access control ang mga developer ng Hook nang eksplisito.

3. Pagbasa ng code ng buhay ng Hook

Halimbawa ng isang exact-input swap, ang sumusunod ay ang pagsusuri sa buong call stack mula sa pagpapahintulot ng transaksyon ng user hanggang sa pagkumpleto.

3.1 Pag-initialize at Pagkakabit ng Hook

Maaaring tawagan ng sinuman ang PoolManager.initialize() upang lumikha ng bagong pool:

Image

Ang isValidHookAddress ay nagpapatotoo lamang sa pagkakatugma ng mga pahintulot sa address at ang field na fee, hindi ito pinapatotohanan kung ang Hook ay nasa ibang pool na, o kung ang Hook ay “nagpapahintulot” na tanggapin ang PoolKey na ito. Kung ang Hook ay hindi isinasaad ang whitelist o logika ng single-pool binding sa beforeInitialize, maaaring magbuo ng sinuman ng isang bagong pool na gumagamit ng parehong Hook, ngunit may anumang token pair, at makapag-trigger ng lahat ng susunod na callback ng Hook.

3.2 beforeSwap at BeforeSwapDelta

Ang entry point ng swap ay ang PoolManager.swap(), na tumatawag sa Hooks.beforeSwap() bago ipatupad ang pangunahing logika ng swap:

Image

Ang return value ng beforeSwap ay isang triple (bytes4, BeforeSwapDelta, uint24):

- bytes4: Dapat katumbas ng IHooks.beforeSwap.selector, k otherwise, babalik agad ng PoolManager

- BeforeSwapDelta: Ang Hook ay nag-aadjust ng delta para sa specified token at unspecified token sa swaps na ito

- uint24: halagang sakop ng dinamikong LP fee (tanging epektibo kapag naka-activate ang dinamikong fee sa pool)

Ang BeforeSwapDelta ay isang alias ng int256, kung saan ang mataas na 128 bit ay ang delta ng specified token (yung token na isinpecify ng user), at ang mababang 128 bit ay ang delta ng unspecified token:

Image

Dapat tandaan na ang semantika ng BeforeSwapDelta ay ang Hook ay dapat mag-return ng positibong halaga kapag kinukuha ang bayarin, at negatibong halaga kapag ibinabalik ang token. Madaling maliin ng mga developer ang sign; samantala, ang ugnayan ng specified at unspecified ay nakadepende sa params.zeroForOne at ang sign ng amountSpecified, at isang maliit na pagkakamali sa pagsusulat ay maaaring magdulot ng pagkakamali sa token.

Ang PoolManager ay direktang dadagdagan ang specifiedDelta na ibinigay ng beforeSwap sa amountToSwap:

Image

Ang isang mahalagang kahulugan na nakapaloob dito ay ang Hook ay maaaring pigilan ang dami ng swap. Kapag ang hookDeltaSpecified ay katumbas ng -params.amountSpecified, ang amountToSwap ay diretso nang magiging zero, na nagpapahiwatig na ang Hook ay buong kinontrol ang swap na ito—ito ang tinatawag na Async Hook o Custom Curve Hook.

Ang Async Hook ay isa sa pinakamataas na panganib na disenyo pattern sa v4: ito ay nagpapalit ng sariling lohika ng Hook sa swap logic ng Uniswap. Kung may butas ang Hook o ito ay masama nang sarili nito, ang mga pondo ng user ay hindi na nakakasalalay sa native pricing logic ng Uniswap, kundi pangunahing nakadepende sa tamang implementasyon ng Hook.

3.3 Delta Settlement at NonzeroDeltaCount

Hindi agad sinisimulan ng beforeSwap at afterSwap na ibinabalik na delta ang paglipat ng pondo, kundi ito ay tinitiyak sa loob ng internal ledger ng PoolManager:

Image

Kapag ang kumulativong delta ng isang token ay mula sa zero ay naging hindi zero, ang NonzeroDeltaCount ay tumataas; at bumababa naman ito kapag bumalik sa zero. Tulad na nabanggit sa 2.1, kung ang NonzeroDeltaCount != 0 nang matapos ang unlock(), babalikin ang buong transaksyon.

Balansahan ng Hook ang sariling delta sa pamamagitan ng dalawang aksyon: settle() (pagpapadala sa PoolManager) at take() (pagkuha mula sa PoolManager):

Image

Ang seguridad na ibinibigay ng mekanismong ito ay malinaw: lahat ay kailangang i-balance ang kanilang account sa wakas. Ngunit ito ay nagtitiyak lamang ng “conservation ng accounting”, hindi ng “tamang accounting”. Kung ang Hook ay bumalik ng isang masamang nilikhang delta sa beforeSwap, gagawin nang tapat ng PoolManager ang pag-record batay sa delta na iyon, kahit anong mangyari—kung ang huling resulta ay i-balance, matagumpay ang transaksyon—kahit na ito ay nangangahulugan na ang Hook ay maaaring magpapalit ng business state upang gawing mali ang sistema na isipin na may-ari ng attacker ang ilang ari-arian, samantalang ang PoolManager ay hindi makakakilala ng ganitong uri ng error sa antas ng negosyo.

Ang nakaraang pangyayari sa seguridad ng Cork Protocol ay dulot ng butas sa kanyang Hook, at bago ito masakop, ito ay nagsagawa na ng apat na pagsusuri mula sa mga kompanya ng audit. Pagkatapos ng pangyayari, natuklasan namin:

Sa tatlo sa apat na audit, ang sakop ay hindi kasama ang CorkHook contract.

- Ang tanging auditor ng CorkHook ay nakakita ng ilang mga problema sa code at nagsumite ng mga rekomendasyon para sa pagpapabuti, ngunit hindi lubos na kinabibilangan ang mga isyu sa pagkontrol ng pag-access

- May isa pang auditor na nagbigay ng malinaw na rekomendasyon sa kanilang ulat: “an interesting follow-up engagement would be to prove the invariants for the CorkHook functions that are being invoked by different components verified within the scope of this engagement”. Ang rekomendasyong ito ay may malakas na kahalagahan mula sa pananaw ng pagtataya pagkatapos ng insidente.

Ito ay nagpapakita ng isang bagong blind spot sa panahon ng v4 Hook: ang pagtaas ng komplikasyon ng protokol ay nagiging isang desisyon sa kaligtasan ang pagtukoy sa sakop. Ang mga ugnayan ng Hook sa iba pang mga kontrato ng protokol ay napakahaba; ang pag-audit lamang sa kontrato ng Hook ay hindi sapat upang makita ang mga problema sa komposisyon sa pagitan ng mga kontrato; sa kabilang banda, ang pag-audit sa mga kapaligirang kontrato habang isinasaalang-alang ang Hook sa labas ng sakop ay magiging sanhi ng pagkakalimot sa pinakamalaking attack surface sa panahon ng v4.

4. Pagninilay

Kapag isinasalin ang protocol mechanism at ang Cork attack side by side, maaaring maipagkakailang ang ilang pangunahing punto ng v4 Hook security model:

(1) Kung ang Hook callback function ay nakadepende sa call context na ibinibigay ng PoolManager, dapat ito ay eksplisitong limitahan na tanging ng PoolManager ang maaaring mag-call. Hindi gagawin ng BaseHook ito para sa developer; ito ang pinakamadaling magkakaroon ng kontrabida sa disenyo sa pagitan ng v4 at karaniwang karanasan sa audit ng smart contract.

(2) Ang ugnayan ng Hook at ang pool ay hindi pinipigilan ng PoolManager. Dapat ng sarili nilang implemntin ng mga developer ang whitelist ng pool o single-pool binding sa beforeInitialize.

(3) Ang mga pahintulot sa address ng hook ay dapat magkasya nang tumpak sa implementasyon ng punsiyon. Ang kalkuladong address ay dapat maglalaman na ng lahat ng posibleng pahintulot na maaaring ma-expand sa hinaharap.

(4) Ang Async / Custom Curve Hook ay isang hoàn na kustomisadong implementasyon ng swap. Walang anumang proteksyon mula sa antas ng protokolo ng Uniswap at kailangang ma-audit batay sa pamantayan ng “puno ng sariling financial contract”.

(5) Ang “conservation” ng delta accounting ay hindi katumbas ng “tama”. Ang NonzeroDeltaCount == 0 ay nagpapatotoo lamang na balanse ang ledger sa huli, hindi naman nagpapatotoo na ang laman nito ay hindi pinagmaliwanag.

(6) Ang kalituhan sa uri ng token sa iba't ibang merkado ay isang bagong anyo ng panganib sa panahon ng v4. Kapag pinapayagan ng protokolo ang mga gumagamit na lumikha ng mga merkado, kinakailangan ang semantic validation ng token at hindi dapat mag-asa lamang sa pag-check ng interface.

Bawat Hook ay isang hiwalay na domain ng pagkakatiwala, at ang kaligtasan ng bawat pool ay tinukoy ng kanyang nakabigkis na Hook. Dahil dito, ang kumplikadong pag-audit ng Hook ay hindi na “pag-audit ng isang code”, kundi “pag-audit ng isang buong sub-protocol”—ang pagbabagong ito ay nangangahulugan ng isang pag-unlad sa metodolohiya para sa mga proyekto at mga auditor.

Tingnan ang orihinal na teksto

Disclaimer: Ang information sa page na ito ay maaaring nakuha mula sa mga third party at hindi necessary na nagre-reflect sa mga pananaw o opinyon ng KuCoin. Ibinigay ang content na ito para sa mga pangkalahatang informational purpose lang, nang walang anumang representation o warranty ng anumang uri, at hindi rin ito dapat ipakahulugan bilang financial o investment advice. Hindi mananagot ang KuCoin para sa anumang error o omission, o para sa anumang outcome na magreresulta mula sa paggamit ng information na ito. Maaaring maging risky ang mga investment sa mga digital asset. Pakisuri nang maigi ang mga risk ng isang produkto at ang risk tolerance mo batay sa iyong sariling kalagayang pinansyal. Para sa higit pang information, mag-refer sa aming Terms ng Paggamit at Disclosure ng Risk.