อย่าเพิ่งเขียนคำสั่ง need สำหรับฟังก์ชันเฉพาะ แต่เขียน need คำสั่งสำหรับโปรโตคอลของคุณ การตรวจสอบการปฏิบัติตามข้อกำหนดของฟังก์ชัน (ข้อกำหนด) - ประสิทธิผล (ผลกระทบ) - การโต้ตอบ (การโต้ตอบ) + ความไม่แปรผันของโปรโตคอล (Iniants) หรือโหมด FREI-PI สามารถช่วยให้สัญญาของคุณมีความปลอดภัยมากขึ้น เนื่องจากเป็นการบังคับให้นักพัฒนามุ่งเน้นไปที่การรักษาความปลอดภัยระดับฟังก์ชัน นอกจากนี้ ระวังด้วย สำหรับค่าคงที่ในระดับโปรโตคอล
ในเดือนมีนาคม พ.ศ. 2566 Euler Finance ถูกแฮ็กและสูญเสียเงิน 200 ล้านดอลลาร์ ออยเลอร์ไฟแนนซ์เป็นตลาดให้กู้ยืมที่ผู้ใช้สามารถฝากหลักประกันและยืมกับมันได้ มีคุณลักษณะเฉพาะบางอย่าง อันที่จริงแล้วเป็นตลาดให้กู้ยืมที่เทียบได้กับ Compound Finance และ Aave
คุณสามารถอ่านชันสูตรพลิกศพเกี่ยวกับการแฮ็กนี้ได้ที่นี่ เนื้อหาหลักคือการขาดการตรวจสอบสุขภาพในฟังก์ชันเฉพาะ ทำให้ผู้ใช้สามารถทำลายความไม่แปรปรวนพื้นฐานของตลาดการให้ยืมได้
หัวใจหลักของโปรโตคอล DeFi ส่วนใหญ่จะเปลี่ยนแปลงไม่ได้ ซึ่งเป็นคุณสมบัติของสถานะโปรแกรมที่คาดว่าจะเป็นจริงเสมอ นอกจากนี้ยังเป็นไปได้ที่จะมีค่าคงที่หลายค่า แต่โดยทั่วไปแล้วค่าคงที่เหล่านี้สร้างขึ้นจากแนวคิดหลัก นี่คือตัวอย่างบางส่วน:
สิ่งที่ผิดพลาดกับ Euler Finance ไม่จำเป็นว่าจะต้องเพิ่มคุณสมบัติ ไม่เขียนการทดสอบ หรือไม่ปฏิบัติตามแนวทางปฏิบัติที่ดีที่สุดแบบดั้งเดิม พวกเขาตรวจสอบการอัปเกรดและทำการทดสอบแล้ว แต่ก็ยังไม่ผ่านช่องโหว่ ปัญหาหลักคือพวกเขาลืมเกี่ยวกับค่าคงที่หลักของตลาดการให้ยืม (เช่นเดียวกับผู้สอบบัญชี!)
*หมายเหตุ: ฉันไม่ได้พยายามจะเลือกออยเลอร์ พวกเขาเป็นทีมที่มีความสามารถ แต่นี่เป็นกรณีล่าสุด *
คุณอาจจะคิดว่า “ใช่แล้ว นั่นเป็นเหตุผลว่าทำไมพวกเขาถึงถูกแฮ็ก พวกเขาลืมคำสั่งที่จำเป็น” ใช่และไม่.
แต่ทำไมพวกเขาถึงลืมคำสั่งร้องขอ?
รูปแบบทั่วไปที่แนะนำสำหรับ Solidity Developer คือรูปแบบ Checks-Effects-Interactions มีประโยชน์มากในการกำจัดข้อบกพร่องที่เกี่ยวข้องกับการกลับเข้ามาใหม่ และมักจะเพิ่มจำนวนการตรวจสอบอินพุตที่นักพัฒนาต้องทำ _แต่_ ก็มักจะเกิดปัญหามองไม่เห็นป่าสำหรับต้นไม้
สิ่งที่สอนนักพัฒนาคือ: “ก่อนอื่นฉันเขียนคำสั่งร้องขอของฉัน จากนั้นฉันทำการตรวจสอบ จากนั้นบางทีฉันอาจโต้ตอบใดๆ จากนั้นฉันก็จะปลอดภัย” ปัญหาคือ บ่อยกว่านั้น มันกลายเป็นส่วนผสมของการตรวจสอบและผลกระทบ – ไม่เลวใช่ไหม การโต้ตอบยังคงเป็นที่สิ้นสุด ดังนั้นการกลับเข้ามาใหม่จึงไม่ใช่ปัญหา แต่บังคับให้ผู้ใช้มุ่งเน้นไปที่ฟังก์ชันเฉพาะและการเปลี่ยนสถานะแต่ละสถานะมากกว่าบริบททั่วโลกที่กว้างขึ้น ซึ่งหมายความว่า:
รูปแบบการโต้ตอบตรวจสอบความถูกต้องเพียงอย่างเดียวทำให้นักพัฒนาลืมเกี่ยวกับค่าคงที่หลักของโปรโตคอลของตน
ยังคงเป็นรูปแบบที่ยอดเยี่ยมสำหรับนักพัฒนา แต่ควรรักษาความแปรปรวนของโปรโตคอลไว้เสมอ (จริงๆ แล้วคุณยังควรใช้ CEI อยู่!)
ยกตัวอย่าง ตัวอย่างนี้จากสัญญา SoloMargin ของ dYdX (ซอร์สโค้ด) ซึ่งเป็นตลาดให้ยืมและสัญญาซื้อขายโดยใช้เลเวอเรจ นี่เป็นตัวอย่างที่ดีของสิ่งที่ฉันเรียกว่ารูปแบบ Function Requirements-Effects-Interactions + Protocol Iniants หรือรูปแบบ FREI-PI
ด้วยเหตุนี้ ฉันจึงเชื่อว่านี่เป็นตลาดการให้กู้ยืมเพียงแห่งเดียวในตลาดการให้กู้ยืมระยะเริ่มต้นที่ไม่มีช่องโหว่ที่เกี่ยวข้องกับตลาด Compound และ Aave ไม่มีปัญหาโดยตรง แต่ส้อมของพวกเขามี และ bZx ถูกแฮ็กหลายครั้ง
ตรวจสอบรหัสด้านล่างและสังเกตสิ่งที่เป็นนามธรรมต่อไปนี้:
Checks-Effects-Interactions ยังคงดำเนินการตามปกติ เป็นที่น่าสังเกตว่าการโต้ตอบการตรวจสอบความถูกต้องกับการตรวจสอบเพิ่มเติมนั้นไม่เทียบเท่ากับ FREI-PI ซึ่งมีความคล้ายคลึงกันแต่มีเป้าหมายที่แตกต่างกันโดยพื้นฐาน ดังนั้น นักพัฒนาควรมองว่าพวกเขาแตกต่างกัน: FREI-PI เป็นนามธรรมที่สูงกว่า มีเป้าหมายที่ความปลอดภัยของโปรโตคอล ในขณะที่ CEI มีเป้าหมายที่ความปลอดภัยในการทำงาน
โครงสร้างของสัญญานี้น่าสนใจมาก - ผู้ใช้สามารถดำเนินการตามที่พวกเขาต้องการ (ฝาก ยืม ซื้อขาย โอน เลิกกิจการ ฯลฯ) ในห่วงโซ่ของการดำเนินการ ต้องการฝาก 3 โทเค็นที่แตกต่างกัน ถอนโทเค็นที่ 4 และชำระบัญชีหรือไม่? เป็นการโทรเพียงครั้งเดียว
นี่คือพลังของ FREI-PI: ผู้ใช้สามารถทำอะไรก็ได้ตามต้องการภายในโปรโตคอล ตราบใดที่ค่าคงที่ของตลาดสินเชื่อหลักยังคงอยู่เมื่อสิ้นสุดการโทร: ผู้ใช้รายเดียวไม่สามารถดำเนินการใดๆ ที่จะทำให้บัญชีใดๆ ไม่ปลอดภัยได้ หรือตำแหน่งหลักประกันที่ไม่ปลอดภัยมากกว่า สำหรับสัญญานี้ ดำเนินการใน _verifyFinalState ตรวจสอบหลักประกันของแต่ละบัญชีที่ได้รับผลกระทบเพื่อให้แน่ใจว่าข้อตกลงนั้นดีกว่าตอนที่เริ่มธุรกรรม
มี invariant เพิ่มเติมบางอย่างที่รวมอยู่ในฟังก์ชันนี้ ซึ่งช่วยเสริม invariants หลักและช่วยในฟังก์ชันข้างเคียง เช่น การปิดตลาด แต่มันเป็นการตรวจสอบหลักที่ทำให้โปรโตคอลมีความปลอดภัยอย่างแท้จริง
ปัญหาอีกประการหนึ่งของ FREI-PI คือแนวคิดที่เน้นเอนทิตี ใช้ตลาดการให้กู้ยืมและสมมติค่าคงที่หลักเป็นตัวอย่าง:
ในทางเทคนิคแล้ว นี่ไม่ใช่ค่าคงที่เพียงอย่างเดียว แต่มีไว้สำหรับเอนทิตีผู้ใช้ (ยังคงเป็นค่าคงที่ของโปรโตคอลหลัก และโดยปกติค่าคงที่ของผู้ใช้จะเป็นค่าคงที่ของโปรโตคอลหลัก) โดยทั่วไปแล้วตลาดการให้กู้ยืมจะมีหน่วยงานเพิ่มเติมอีก 2 แห่ง:
การเปลี่ยนแปลงไม่ได้เพิ่มเติมทุกรายการทำให้รับประกันโปรโตคอลได้ยากขึ้น ดังนั้นยิ่งน้อยยิ่งดี นี่คือสิ่งที่ Dan Elitzer พูดไว้ในบทความของเขาที่ชื่อว่า: Why DeFi is Broken and How to Fix It #1 Oracle-less Protocol (คำใบ้: บทความไม่ได้บอกว่า oracles เป็นปัญหา)
##ออราเคิล
สำหรับนักทำนาย ให้ใช้ประโยชน์จาก Cream Finance มูลค่า 130 ล้านดอลลาร์ ความไม่เปลี่ยนรูปหลักของเอนทิตี oracle:
ปรากฎว่า การตรวจสอบ oracles ขณะรันไทม์ด้วย FREI-PI นั้นยุ่งยาก แต่สามารถทำได้ด้วยความรอบคอบ โดยทั่วไปแล้ว Chainlink เป็นตัวเลือกที่ดีที่จะพึ่งพาเป็นส่วนใหญ่ ซึ่งตอบสนองความเปลี่ยนรูปส่วนใหญ่ไม่ได้ ในกรณีที่พบไม่บ่อยของการจัดการหรือความประหลาดใจ การมีการป้องกันที่ลดความยืดหยุ่นเพื่อสนับสนุนความถูกต้องอาจเป็นประโยชน์ (เช่น การตรวจสอบว่าค่าที่ทราบล่าสุดนั้นมากกว่าค่าปัจจุบันไม่กี่เปอร์เซ็นต์) นอกจากนี้ ระบบ SoloMargin ของ dYdX ยังทำงานได้ดีกับ DAI oracle ของพวกเขา นี่คือรหัส (ถ้าคุณไม่สามารถบอกได้ ฉันคิดว่ามันเป็นระบบสัญญาอัจฉริยะที่ซับซ้อนที่สุดเท่าที่เคยเขียนมา)
สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการประเมินของออราเคิลและการเน้นความสามารถของทีมออยเลอร์ พวกเขาได้เขียนบทความดีๆ เกี่ยวกับการจัดการราคาออราเคิล Uniswap V3 TWAP ด้วยคอมพิวเตอร์
การสร้างค่าคงที่สำหรับเอนทิตีที่มีการจัดการนั้นยากที่สุด สาเหตุหลักมาจากความจริงที่ว่าบทบาทส่วนใหญ่ของพวกเขาคือการเปลี่ยนแปลงค่าคงที่อื่นๆ ที่มีอยู่ ที่กล่าวว่า หากคุณสามารถหลีกเลี่ยงการใช้บทบาทการบริหารได้ คุณควรทำ
โดยพื้นฐาน ค่าคงที่หลักของเอนทิตีที่ได้รับการจัดการอาจเป็น:
การตีความ: ผู้ดูแลระบบสามารถทำสิ่งต่างๆ ที่ไม่ควรจบลงด้วยการไม่ทำลายค่าคงที่ เว้นแต่ว่าพวกเขาจะเปลี่ยนแปลงสิ่งต่างๆ อย่างมากเพื่อปกป้องเงินของผู้ใช้ (เช่น การย้ายสินทรัพย์ไปยังสัญญาช่วยเหลือคือการลบค่าคงที่) นอกจากนี้ ผู้ดูแลระบบควรได้รับการพิจารณาว่าเป็นผู้ใช้ ดังนั้น ค่าคงที่ของผู้ใช้ในตลาดการให้ยืมหลักควรถือไว้สำหรับพวกเขาด้วย (หมายความว่าพวกเขาไม่สามารถโจมตีผู้ใช้รายอื่นหรือโปรโตคอล) ขณะนี้ การดำเนินการบางอย่างของผู้ดูแลระบบไม่สามารถตรวจสอบในขณะรันไทม์ผ่าน FREI-PI ได้ แต่ด้วยค่าคงที่ที่เพียงพอในที่อื่น หวังว่าปัญหาส่วนใหญ่จะลดลง ฉันพูดในขณะนี้ เพราะใคร ๆ ก็สามารถจินตนาการได้ว่าใช้ระบบพิสูจน์ zk ที่อาจตรวจสอบสถานะทั้งหมดของสัญญา (ต่อผู้ใช้ ต่อ oracle ฯลฯ)
เป็นตัวอย่างของการที่ผู้ดูแลระบบทำลายการไม่เปลี่ยนรูป ให้ดำเนินการจัดการแบบผสมผสานที่ทำให้ตลาด cETH หยุดชะงักในเดือนสิงหาคม 2022 โดยพื้นฐานแล้ว การอัปเกรดนี้ทำลายความสามารถในการเปลี่ยนแปลงไม่ได้ของ Oracle: Oracle ให้ข้อมูลแบบเรียลไทม์ที่แม่นยำและ (ค่อนข้าง) เนื่องจากไม่มีฟังก์ชันการทำงาน Oracle สามารถให้ข้อมูลที่ไม่ถูกต้องได้ การตรวจสอบ FREI-PI แบบรันไทม์ การตรวจสอบว่า Oracle ที่ได้รับผลกระทบสามารถให้ข้อมูลแบบเรียลไทม์ สามารถป้องกันไม่ให้สิ่งนี้เกิดขึ้นกับการอัปเกรด สิ่งนี้สามารถรวมเข้ากับ _setPriceOracle เพื่อตรวจสอบว่าสินทรัพย์ทั้งหมดได้รับข้อมูลแบบเรียลไทม์หรือไม่ สิ่งที่ดีเกี่ยวกับ FREI-PI สำหรับบทบาทผู้ดูแลระบบคือ บทบาทของผู้ดูแลระบบค่อนข้างไม่อ่อนไหวต่อราคา (หรืออย่างน้อยควรเป็น) ดังนั้นการใช้ก๊าซมากขึ้นจึงไม่ใช่ปัญหาใหญ่
ดังนั้นในขณะที่ค่าคงที่ที่สำคัญที่สุดคือค่าคงที่หลักของโปรโตคอล อาจมีค่าคงที่ที่มีเอนทิตีเป็นศูนย์กลางซึ่งค่าคงที่หลักต้องถือไว้ อย่างไรก็ตาม ชุดค่าคงที่ที่ง่ายที่สุด (และเล็กที่สุด) น่าจะปลอดภัยที่สุด เรียบง่ายเป็นสิ่งที่ดี ตัวอย่างที่โดดเด่นคือ Uniswap…
AMM สามารถมีค่าคงที่พื้นฐานที่ง่ายที่สุดของ DeFi ดั้งเดิม: tokenBalanceX * tokenBalanceY == k (เช่น รุ่นผลิตภัณฑ์คงที่) ทุกฟังก์ชันใน Uniswap V2 จะหมุนรอบค่าคงที่ง่ายๆ นี้:
ความลับด้านความปลอดภัยของ Uniswap V2: แกนหลักคือความไม่เปลี่ยนรูปที่เรียบง่าย และฟังก์ชันทั้งหมดมีให้บริการ เอนทิตีอื่นๆ เพียงอย่างเดียวที่สามารถโต้แย้งได้คือธรรมาภิบาล ซึ่งสามารถเปิดสวิตช์ค่าธรรมเนียมได้ ซึ่งไม่แตะต้องการเปลี่ยนแปลงหลัก เป็นเพียงการกระจายความเป็นเจ้าของยอดโทเค็น ความเรียบง่ายในคำสั่งความปลอดภัยของพวกเขาคือเหตุผลที่ Uniswap ไม่เคยถูกแฮ็ก ความเรียบง่ายไม่ได้เป็นการดูหมิ่นสำหรับนักพัฒนาที่ยอดเยี่ยมของสัญญาอัจฉริยะของ Uniswap ตรงกันข้าม ต้องใช้วิศวกรที่ยอดเยี่ยมเพื่อค้นหาความเรียบง่าย
##ปัญหาแก๊ส
Twitter ของฉันเต็มไปด้วยเสียงกรีดร้องของผู้เพิ่มประสิทธิภาพที่แสดงความสยองขวัญและความเจ็บปวดว่าการตรวจสอบเหล่านี้ไม่จำเป็นและไม่มีประสิทธิภาพ สองสิ่งเกี่ยวกับคำถามนี้:
หากค่าใช้จ่ายเป็นสิ่งต้องห้าม ให้คิดใหม่เกี่ยวกับตัวแปรหลัก และพยายามทำให้ง่ายขึ้น
ในฐานะนักพัฒนา สิ่งสำคัญคือต้องกำหนดและแสดงค่าคงที่หลักตั้งแต่เนิ่นๆ ในกระบวนการพัฒนา ตามคำแนะนำที่เป็นรูปธรรม: ฟังก์ชันแรกในการเขียนด้วยตัวคุณเองคือ _verifyAfter เพื่อตรวจสอบค่าคงที่ของคุณหลังจากการเรียกใช้สัญญาของคุณทุกครั้ง ใส่ไว้ในสัญญาของคุณและปรับใช้ที่นั่น เสริมค่าคงที่ที่ไม่แปรเปลี่ยนนี้ (และค่าคงที่อื่นที่มีเอนทิตีเป็นศูนย์กลาง) ด้วยการทดสอบค่าคงที่ที่กว้างขึ้นซึ่งมีการตรวจสอบก่อนการใช้งาน (คู่มือ Foundry)
ร้านค้าชั่วคราวเปิดการเพิ่มประสิทธิภาพและการปรับปรุงที่น่าสนใจซึ่ง Nascent จะทำการทดลอง – ฉันขอแนะนำให้คุณพิจารณาว่าร้านค้าชั่วคราวสามารถใช้เป็นเครื่องมือเพื่อความปลอดภัยที่ดีขึ้นในบริบทการโทรได้อย่างไร
ในบทความนี้ เราใช้เวลาไม่มากในการแนะนำโมเดล FREI-PI ในการตรวจสอบอินพุต แต่ก็สำคัญมากเช่นกัน การกำหนดขอบเขตของอินพุตเป็นงานที่ท้าทายเพื่อหลีกเลี่ยงการล้นและสถานการณ์ที่คล้ายคลึงกัน ลองตรวจสอบและติดตามความคืบหน้าของเครื่องมือของเรา: ไพโรมิเตอร์ (ขณะนี้อยู่ในรุ่นเบต้า โปรดให้ดาวเรา) สามารถเจาะลึกและช่วยค้นหาตำแหน่งที่คุณอาจไม่ได้ทำการตรวจสอบอินพุต
นอกเหนือจากตัวย่อที่ติดหู (FREI-PI) หรือชื่อสคีมา บิตที่สำคัญจริงๆ คือ:
ค้นหาความเรียบง่ายในการไม่เปลี่ยนแปลงหลักของโปรโตคอลของคุณ และทำงานอย่างหนักเพื่อให้แน่ใจว่ามันจะไม่ถูกทำลาย (หรือถูกจับได้ก่อนที่มันจะถูกทำลาย)