สัญญาพร็อกซีเป็นเครื่องมือสำคัญสำหรับนักพัฒนาสัญญาอัจฉริยะ ปัจจุบัน มีโหมดพร็อกซีและกฎการใช้งานที่สอดคล้องกันมากมายในระบบสัญญา ก่อนหน้านี้เราได้สรุปแนวทางปฏิบัติที่ดีที่สุดด้านความปลอดภัยของสัญญาพร็อกซีที่อัปเกรดได้ในบทความนี้ เราจะแนะนำโมเดลพร็อกซีอีกรูปแบบหนึ่งที่ได้รับความนิยมในชุมชนนักพัฒนา นั่นคือโมเดลไดมอนด์พร็อกซีสัญญาเพชรพร็อกซี่หรือที่เรียกว่า "เพชร" เป็นรูปแบบการออกแบบสำหรับ Ethereum smart contract ที่นำเสนอโดย Ethereum Improvement Proposal (EIP) 2535โหมดไดมอนด์ช่วยให้สัญญามีฟังก์ชันไม่จำกัดโดยแบ่งฟังก์ชันออกเป็นสัญญาเล็กๆ (เรียกอีกอย่างว่า "ลักษณะ") ไดมอนด์ทำหน้าที่เป็นพร็อกซี ฟังก์ชันการกำหนดเส้นทางจะเรียกไปยังลักษณะที่เหมาะสมการออกแบบโมเดลเพชรสามารถแก้ปัญหาการจำกัดขนาดสัญญาสูงสุดของเครือข่าย Ethereum การแบ่งสัญญาขนาดใหญ่ออกเป็นส่วนย่อยๆ รูปแบบข้าวหลามตัดช่วยให้นักพัฒนาสามารถสร้างสัญญาอัจฉริยะที่ซับซ้อนและเต็มไปด้วยฟีเจอร์ได้มากขึ้นโดยไม่ได้รับผลกระทบจากข้อจำกัดด้านขนาดDiamond Brokerage มอบความยืดหยุ่นอย่างมากเมื่อเทียบกับสัญญาที่อัปเกรดได้แบบดั้งเดิม ช่วยให้ชิ้นส่วนสัญญาสามารถอัพเกรด เพิ่ม แทนที่ หรือลบส่วนของฟังก์ชันที่เลือกโดยไม่ต้องแตะต้องส่วนอื่นบทความนี้แสดงภาพรวมของ EIP-2535 รวมถึงการเปรียบเทียบกับโหมดพร็อกซีโปร่งใสที่ใช้กันอย่างแพร่หลายและโหมดพร็อกซี UUPS และข้อควรพิจารณาด้านความปลอดภัยสำหรับชุมชนนักพัฒนาในบริบทของ EIP-2535 "ไดมอนด์" เป็นสัญญาพร็อกซีที่มีการใช้งานตามหน้าที่โดยสัญญาตรรกะที่แตกต่างกัน ซึ่งเรียกว่า "ลักษณะ"ลองนึกภาพว่าเพชรแท้มีด้านต่างๆ ที่เรียกว่า facets และสัญญาเพชร Ethereum ที่สอดคล้องกันก็มีด้านต่างๆ กัน ฟังก์ชั่นการยืมเพชรแต่ละสัญญานั้นมีด้านหรือแง่มุมที่แตกต่างกันมาตรฐานเพชรใช้การเปรียบเทียบเพื่อขยายขีดความสามารถของ "การเจียระไนเพชร" เพื่อเพิ่ม แทนที่ หรือลบเหลี่ยมเพชรพลอยและคุณลักษณะต่างๆนอกจากนี้ Diamond Standard ยังมีคุณสมบัติที่เรียกว่า "Diamond Loupe" ซึ่งจะส่งคืนข้อมูลเกี่ยวกับเหลี่ยมเพชรพลอยและการมีอยู่ของเพชรเมื่อเทียบกับรูปแบบพร็อกซีแบบดั้งเดิม "ไดมอนด์" เทียบเท่ากับสัญญาตัวแทน และ "แง่มุม" ที่แตกต่างกันสอดคล้องกับการทำให้สัญญาเป็นจริง ลักษณะต่างๆ ของไดมอนด์เอเจนต์สามารถแชร์ฟังก์ชันภายใน ไลบรารี และตัวแปรสถานะได้ ส่วนประกอบที่สำคัญของเพชรมีดังนี้สัญญากลางที่ทำหน้าที่เป็นพร็อกซี ฟังก์ชันการกำหนดเส้นทางเรียกไปยังลักษณะที่เหมาะสม ประกอบด้วยการแมปตัวเลือกฟังก์ชันกับที่อยู่ "ด้าน"สัญญาเดียวที่ใช้ฟังก์ชันเฉพาะ แต่ละด้านประกอบด้วยชุดของฟังก์ชันที่เพชรเรียกได้เป็นชุดของฟังก์ชันมาตรฐานที่กำหนดใน EIP-2535 ซึ่งให้ข้อมูลเกี่ยวกับตัวเลือกด้านและฟังก์ชันที่ใช้ในเพชร Diamond Loupe ช่วยให้นักพัฒนาและผู้ใช้ตรวจสอบและทำความเข้าใจโครงสร้างของเพชรได้ฟังก์ชันสำหรับเพิ่ม แทนที่ หรือลบเหลี่ยมเพชรและตัวเลือกคุณลักษณะที่เกี่ยวข้อง เฉพาะที่อยู่ที่ได้รับอนุญาต (เช่น เจ้าของเพชรหรือสัญญาหลายลายเซ็น) เท่านั้นที่สามารถทำการเจียระไนเพชรได้คล้ายกับตัวแทนแบบดั้งเดิม เมื่อมีการเรียกใช้ฟังก์ชันบนตัวแทนเพชร ฟังก์ชันสำรอง (ฟังก์ชันทางเลือก) ของตัวแทนจะถูกเรียกใช้ ข้อแตกต่างหลักจากพร็อกซีไดมอนด์คือในฟังก์ชันสำรอง มีการแมป SelectorToFacet ซึ่งจัดเก็บและกำหนดว่าที่อยู่สัญญาแบบลอจิคัลใดมีการใช้งานฟังก์ชันที่เรียกว่า จากนั้นจะใช้การเรียกผู้รับมอบสิทธิ์เพื่อดำเนินการฟังก์ชัน เช่นเดียวกับพร็อกซีแบบดั้งเดิมพร็อกซีทั้งหมดใช้ฟังก์ชัน fallback() เพื่อมอบหมายการเรียกใช้ฟังก์ชันไปยังที่อยู่ภายนอก ด้านล่างนี้คือการใช้งานพร็อกซีไดมอนด์และการใช้งานพร็อกซีแบบดั้งเดิมเป็นที่น่าสังเกตว่าบล็อกโค้ดแอสเซมบลีของพวกเขามีความคล้ายคลึงกันมาก ดังนั้นข้อแตกต่างเพียงอย่างเดียวคือแอดเดรสลักษณะในการเรียกตัวแทนไดมอนด์พร็อกซีและแอดเดรสโดยนัยในการเรียกตัวแทนพร็อกซีแบบดั้งเดิมข้อแตกต่างที่สำคัญคือในพร็อกซีรูปเพชร ที่อยู่ของลักษณะจะถูกกำหนดโดยแฮชแมปจาก msg.sig ของผู้โทร (ตัวเลือกฟังก์ชัน) ไปยังที่อยู่ของลักษณะ ขณะที่ในพร็อกซีแบบดั้งเดิม ที่อยู่โดยนัยจะไม่ขึ้นอยู่กับ ของผู้โทรเข้ามาฟังก์ชั่นสำรองตัวแทนเพชรฟังก์ชันสำรองพร็อกซีแบบดั้งเดิมการแมป SelectorToFacet กำหนดว่าสัญญาใดมีการใช้งานตัวเลือกฟังก์ชันแต่ละตัว ผู้ปฏิบัติงานโครงการมักจำเป็นต้องเพิ่ม แทนที่ หรือลบการแมปสัญญาตัวเลือกการนำไปใช้งานของฟังก์ชันนี้ EIP-2535 กำหนด: เพื่อให้บรรลุวัตถุประสงค์นี้ ต้องมีฟังก์ชัน diamondCut() ด้านล่างนี้คืออินเทอร์เฟซตัวอย่างโครงสร้าง FacetCut แต่ละโครงสร้างมีที่อยู่ของ facet และอาร์เรย์ตัวเลือกคุณลักษณะสี่ไบต์ที่จะอัปเดตในสัญญาพร็อกซีรูปเพชร FaceCutAction ช่วยให้สามารถเพิ่ม แทนที่ และลบตัวเลือกคุณสมบัติได้ การใช้ฟังก์ชัน diamondCut() ควรรวมถึงการควบคุมการเข้าถึงที่เพียงพอ ป้องกันการชนกันของช่อง การกู้คืนเมื่อล้มเหลว เป็นต้นเพื่อสอบถามว่าตัวแทนเพชรมีหน้าที่และลักษณะอย่างไร เราจึงใช้ "แว่นขยายเพชร" "Diamond Loupe" มีลักษณะพิเศษที่ใช้อินเทอร์เฟซต่อไปนี้ที่กำหนดไว้ใน EIP-2535:ฟังก์ชัน facets() ควรส่งคืนที่อยู่ของ facets ทั้งหมดและตัวเลือกฟังก์ชันสี่ไบต์ ฟังก์ชัน facetFunctionSelectors() ควรส่งคืนตัวเลือกฟังก์ชันทั้งหมดที่รองรับโดยลักษณะเฉพาะ ฟังก์ชัน facetAddresses() ควรส่งคืนที่อยู่ facet ทั้งหมดที่ใช้โดยเพชรฟังก์ชัน facetAddress() ควรส่งคืนส่วนที่สนับสนุนตัวเลือกที่กำหนด หรือที่อยู่ (0) หากไม่พบ โปรดทราบว่าไม่ควรมีที่อยู่ด้านกว้างมากกว่าหนึ่งรายการที่มีตัวเลือกคุณลักษณะเดียวกันเนื่องจากพร็อกซี่ไดมอนด์มอบหมายการเรียกใช้ฟังก์ชันที่แตกต่างกันให้กับสัญญาการใช้งานที่แตกต่างกัน จึงจำเป็นอย่างยิ่งที่จะต้องจัดการช่องจัดเก็บข้อมูลอย่างเหมาะสมเพื่อป้องกันความขัดแย้ง EIP-2535 กล่าวถึงวิธีการจัดการช่องเก็บข้อมูลหลายวิธีด้านนี้สามารถประกาศตัวแปรสถานะในโครงสร้าง ลักษณะนี้สามารถใช้โครงสร้างจำนวนเท่าใดก็ได้ โดยแต่ละส่วนมีตำแหน่งจัดเก็บต่างกัน แต่ละโครงสร้างมีตำแหน่งเฉพาะในการจัดเก็บสัญญา Aspects สามารถประกาศตัวแปรสถานะของตัวเองได้ แต่ไม่สามารถขัดแย้งกับตำแหน่งที่เก็บข้อมูลของตัวแปรสถานะที่ประกาศโดยด้านอื่นๆ ห้องสมุดตัวอย่างและสัญญาการจัดเก็บเพชรมีอยู่ใน EIP-2535 ดังแสดงในรูปต่อไปนี้:ที่เก็บข้อมูลแอปเป็นที่เก็บเพชรเวอร์ชันพิเศษกว่า รูปแบบนี้ใช้เพื่อแบ่งปันตัวแปรสถานะของลักษณะต่างๆ ได้สะดวกและง่ายดายยิ่งขึ้น โครงสร้าง App Store ถูกกำหนดให้มีจำนวนและประเภทของตัวแปรสถานะที่แอปพลิเคชันต้องการ ลักษณะจะประกาศโครงสร้าง AppStorage เป็นตัวแปรสถานะตัวแรกและตัวเดียวเสมอ ที่ตำแหน่ง 0 ของสล็อตที่เก็บข้อมูล ด้านต่างๆจึงสามารถเข้าถึงตัวแปรจากโครงสร้างนี้ได้นอกจากนี้ยังมีกลยุทธ์การจัดการสล็อตพื้นที่เก็บข้อมูลอื่นๆ รวมถึงการผสมผสานระหว่าง Diamond Storage และ AppStorage ตัวอย่างเช่น โครงสร้างบางอย่างใช้ร่วมกันระหว่างลักษณะต่างๆ และบางโครงสร้างมีความเฉพาะเจาะจงสำหรับลักษณะเฉพาะ ในทุกกรณี การป้องกันการชนกันของสล็อตโดยไม่ตั้งใจเป็นสิ่งสำคัญมาก**เปรียบเทียบกับ Transparent Proxy และ UUPS Proxy**โหมดพร็อกซีหลักสองโหมดที่ใช้ในปัจจุบันโดยชุมชนนักพัฒนา Web3 คือโหมดพร็อกซีแบบโปร่งใสและโหมดพร็อกซี UUPS ในส่วนนี้ เราจะเปรียบเทียบโหมดไดมอนด์พร็อกซีกับโหมดพร็อกซีแบบโปร่งใสและโหมดพร็อกซี UUPS โดยสังเขป**1.EPI-2535:****2.**EPI-1967:******3.การดำเนินการอ้างอิงพร็อกซีไดมอนด์:****4.การใช้งาน OpenZeppelin:**พร็อกซีและโซลูชันที่ปรับขนาดได้เป็นระบบที่ซับซ้อนมากขึ้น และ OpenZeppelin จัดเตรียมฐานรหัสและเอกสารที่ครอบคลุมสำหรับพร็อกซีที่ปรับขนาดได้ของ UUPS, Transparent และ Beacon อย่างไรก็ตาม สำหรับโหมดพร็อกซีเพชร แม้ว่า OpenZeppelin จะยืนยันถึงประโยชน์ของมัน แต่พวกเขาก็ยังตัดสินใจที่จะไม่รวมการใช้งานเพชร EIP-2535 ไว้ในไลบรารีของพวกเขาดังนั้น นักพัฒนาที่ใช้ไลบรารีของบุคคลที่สามที่มีอยู่หรือนำโซลูชันนี้ไปใช้เองจะต้องดำเนินการด้วยความระมัดระวังเป็นอย่างยิ่ง ที่นี่เราได้รวบรวมรายการตรวจสอบแนวทางปฏิบัติที่ดีที่สุดด้านความปลอดภัยสำหรับชุมชนนักพัฒนาซอฟต์แวร์เพื่อพิจารณาการแบ่งตรรกะของสัญญาออกเป็นโมดูลที่เล็กลงและสามารถจัดการได้มากขึ้น นักพัฒนาสามารถทดสอบและตรวจสอบรหัสของตนได้ง่ายขึ้นนอกจากนี้ วิธีการนี้ช่วยให้นักพัฒนาสามารถมุ่งเน้นไปที่ลักษณะเฉพาะของการสร้างและการรักษาสัญญา แทนที่จะจัดการ codebase ขนาดใหญ่ที่ซับซ้อน ผลลัพธ์ที่ได้คือฐานโค้ดแบบโมดูลาร์ที่ยืดหยุ่นมากขึ้นซึ่งสามารถอัปเดตและแก้ไขได้ง่ายโดยไม่กระทบกับส่วนอื่นๆ ของสัญญาที่มา: Aavegotchi Githubเมื่อมีการปรับใช้สัญญาพร็อกซีเพชร จะต้องเพิ่มที่อยู่ของสัญญา DiamondCutFacet ลงในสัญญาพร็อกซีเพชรและใช้ฟังก์ชัน diamondCut() ฟังก์ชัน diamondCut() ใช้เพื่อเพิ่ม ลบ หรือแทนที่ facets และฟังก์ชันต่างๆ หากไม่มี DiamondCutFacet และ diamondCut() ตัวแทนเพชรจะไม่สามารถทำงานได้อย่างถูกต้องที่มา: Mugen's Diamond-3-Hardhatเมื่อเพิ่มตัวแปรสถานะใหม่ให้กับโครงสร้างหน่วยเก็บข้อมูลในสัญญาอัจฉริยะ จะต้องเพิ่มตัวแปรดังกล่าวที่ส่วนท้ายของโครงสร้าง การเพิ่มตัวแปรสถานะใหม่ที่จุดเริ่มต้นหรือตรงกลางของโครงสร้างจะทำให้ตัวแปรสถานะใหม่เขียนทับข้อมูลตัวแปรสถานะที่มีอยู่ และตัวแปรสถานะใดๆ หลังจากตัวแปรสถานะใหม่อาจอ้างถึงตำแหน่งหน่วยความจำที่ไม่ถูกต้องรูปแบบ AppStorage กำหนดให้มีการประกาศโครงสร้างเดียวและโครงสร้างเดียวสำหรับไดมอนด์พร็อกซี และโครงสร้างนี้ต้องแชร์โดยทุกด้าน หากจำเป็นต้องมีโครงสร้างหลายส่วน ควรใช้รูปแบบ DiamondStorageอย่าใส่ struct ลงใน struct อื่นโดยตรง เว้นแต่คุณจะแน่ใจว่าคุณไม่ต้องการเพิ่มตัวแปร state ให้กับ struct ภายใน ไม่สามารถเพิ่มตัวแปรสถานะใหม่ให้กับโครงสร้างภายในในการอัพเกรดโดยไม่ต้องเขียนทับช่องเก็บตัวแปรที่ประกาศหลังโครงสร้างวิธีแก้ปัญหาคือเพิ่มตัวแปรสถานะใหม่ให้กับโครงสร้างที่แมปหน่วยความจำแทนการวาง "struct" โดยตรงใน "struct" ช่องจัดเก็บตัวแปรในแผนที่มีการคำนวณต่างกันและไม่ได้อยู่ติดกันในที่จัดเก็บขนาดของอาร์เรย์จะได้รับผลกระทบจากขนาดของโครงสร้าง เมื่อมีการเพิ่มตัวแปรสถานะใหม่ลงในโครงสร้าง ตัวแปรดังกล่าวจะเปลี่ยนขนาดและเค้าโครงของโครงสร้างนั้นสิ่งนี้อาจทำให้เกิดปัญหาหากใช้โครงสร้างเป็นองค์ประกอบในอาร์เรย์ ถ้าขนาดและเค้าโครงของโครงสร้างเปลี่ยนไป ขนาดและเค้าโครงของอาร์เรย์ก็จะเปลี่ยนไปด้วย ซึ่งอาจทำให้เกิดปัญหากับการจัดทำดัชนีหรือการดำเนินการอื่นๆ ที่ต้องใช้ขนาดและเค้าโครงที่สอดคล้องกันของโครงสร้างเช่นเดียวกับรูปแบบพร็อกซีอื่นๆ ตัวแปรแต่ละตัวควรมีช่องจัดเก็บที่ไม่ซ้ำกัน มิฉะนั้น โครงสร้างที่แตกต่างกันสองแห่งในตำแหน่งเดียวกันจะเขียนทับซึ่งกันและกันฟังก์ชัน initialize() มักจะใช้เพื่อตั้งค่าตัวแปรที่สำคัญ เช่น ที่อยู่ของบทบาทที่มีสิทธิพิเศษ หากไม่ได้เริ่มต้นเมื่อใช้งานสัญญา ผู้ประสงค์ร้ายสามารถโทรและควบคุมสัญญาได้ขอแนะนำให้เพิ่มการควบคุมการเข้าถึงที่เหมาะสมให้กับฟังก์ชันการเริ่มต้น/การตั้งค่า หรือตรวจสอบให้แน่ใจว่ามีการเรียกใช้ฟังก์ชันเมื่อสัญญาถูกนำไปใช้ และไม่สามารถเรียกใช้ได้อีกหากส่วนใดของสัญญาสามารถเรียกฟังก์ชัน selfdestruct() ได้ ก็อาจทำลายทั้งสัญญาได้ ส่งผลให้สูญเสียเงินทุนหรือข้อมูล สิ่งนี้เป็นอันตรายอย่างยิ่งในโหมดพร็อกซีไดมอนด์ เนื่องจากมีหลายแง่มุมที่สามารถเข้าถึงพื้นที่เก็บข้อมูลและข้อมูลของสัญญาพร็อกซีได้ปัจจุบัน เราเห็นโครงการจำนวนมากขึ้นเรื่อยๆ ที่ใช้โมเดลพร็อกซีเพชรในสัญญาอัจฉริยะของพวกเขา ให้ความยืดหยุ่นและข้อดีอื่น ๆ ที่เหนือกว่าผู้รับมอบฉันทะแบบดั้งเดิมอย่างไรก็ตาม ความยืดหยุ่นที่เพิ่มขึ้นอาจหมายถึงพื้นผิวการโจมตีที่กว้างขึ้นสำหรับผู้โจมตี เราหวังว่าบทความนี้จะช่วยให้ชุมชนนักพัฒนาเข้าใจกลไกของโมเดลพร็อกซีไดมอนด์และข้อควรพิจารณาด้านความปลอดภัยในขณะเดียวกัน ทีมงานโครงการควรทำการทดสอบอย่างเข้มงวดและการตรวจสอบจากบุคคลที่สามเพื่อลดความเสี่ยงของช่องโหว่ที่เกี่ยวข้องกับการดำเนินการตามสัญญาตัวแทนเพชร
แนวทางปฏิบัติด้านความปลอดภัยที่ดีที่สุดสำหรับสัญญาตัวแทนของ Diamond
สัญญาพร็อกซีเป็นเครื่องมือสำคัญสำหรับนักพัฒนาสัญญาอัจฉริยะ ปัจจุบัน มีโหมดพร็อกซีและกฎการใช้งานที่สอดคล้องกันมากมายในระบบสัญญา ก่อนหน้านี้เราได้สรุปแนวทางปฏิบัติที่ดีที่สุดด้านความปลอดภัยของสัญญาพร็อกซีที่อัปเกรดได้
ในบทความนี้ เราจะแนะนำโมเดลพร็อกซีอีกรูปแบบหนึ่งที่ได้รับความนิยมในชุมชนนักพัฒนา นั่นคือโมเดลไดมอนด์พร็อกซี
สัญญาเพชรพร็อกซี่หรือที่เรียกว่า "เพชร" เป็นรูปแบบการออกแบบสำหรับ Ethereum smart contract ที่นำเสนอโดย Ethereum Improvement Proposal (EIP) 2535
โหมดไดมอนด์ช่วยให้สัญญามีฟังก์ชันไม่จำกัดโดยแบ่งฟังก์ชันออกเป็นสัญญาเล็กๆ (เรียกอีกอย่างว่า "ลักษณะ") ไดมอนด์ทำหน้าที่เป็นพร็อกซี ฟังก์ชันการกำหนดเส้นทางจะเรียกไปยังลักษณะที่เหมาะสม
การออกแบบโมเดลเพชรสามารถแก้ปัญหาการจำกัดขนาดสัญญาสูงสุดของเครือข่าย Ethereum การแบ่งสัญญาขนาดใหญ่ออกเป็นส่วนย่อยๆ รูปแบบข้าวหลามตัดช่วยให้นักพัฒนาสามารถสร้างสัญญาอัจฉริยะที่ซับซ้อนและเต็มไปด้วยฟีเจอร์ได้มากขึ้นโดยไม่ได้รับผลกระทบจากข้อจำกัดด้านขนาด
Diamond Brokerage มอบความยืดหยุ่นอย่างมากเมื่อเทียบกับสัญญาที่อัปเกรดได้แบบดั้งเดิม ช่วยให้ชิ้นส่วนสัญญาสามารถอัพเกรด เพิ่ม แทนที่ หรือลบส่วนของฟังก์ชันที่เลือกโดยไม่ต้องแตะต้องส่วนอื่น
บทความนี้แสดงภาพรวมของ EIP-2535 รวมถึงการเปรียบเทียบกับโหมดพร็อกซีโปร่งใสที่ใช้กันอย่างแพร่หลายและโหมดพร็อกซี UUPS และข้อควรพิจารณาด้านความปลอดภัยสำหรับชุมชนนักพัฒนา
ในบริบทของ EIP-2535 "ไดมอนด์" เป็นสัญญาพร็อกซีที่มีการใช้งานตามหน้าที่โดยสัญญาตรรกะที่แตกต่างกัน ซึ่งเรียกว่า "ลักษณะ"
ลองนึกภาพว่าเพชรแท้มีด้านต่างๆ ที่เรียกว่า facets และสัญญาเพชร Ethereum ที่สอดคล้องกันก็มีด้านต่างๆ กัน ฟังก์ชั่นการยืมเพชรแต่ละสัญญานั้นมีด้านหรือแง่มุมที่แตกต่างกัน
มาตรฐานเพชรใช้การเปรียบเทียบเพื่อขยายขีดความสามารถของ "การเจียระไนเพชร" เพื่อเพิ่ม แทนที่ หรือลบเหลี่ยมเพชรพลอยและคุณลักษณะต่างๆ
นอกจากนี้ Diamond Standard ยังมีคุณสมบัติที่เรียกว่า "Diamond Loupe" ซึ่งจะส่งคืนข้อมูลเกี่ยวกับเหลี่ยมเพชรพลอยและการมีอยู่ของเพชร
เมื่อเทียบกับรูปแบบพร็อกซีแบบดั้งเดิม "ไดมอนด์" เทียบเท่ากับสัญญาตัวแทน และ "แง่มุม" ที่แตกต่างกันสอดคล้องกับการทำให้สัญญาเป็นจริง ลักษณะต่างๆ ของไดมอนด์เอเจนต์สามารถแชร์ฟังก์ชันภายใน ไลบรารี และตัวแปรสถานะได้ ส่วนประกอบที่สำคัญของเพชรมีดังนี้
สัญญากลางที่ทำหน้าที่เป็นพร็อกซี ฟังก์ชันการกำหนดเส้นทางเรียกไปยังลักษณะที่เหมาะสม ประกอบด้วยการแมปตัวเลือกฟังก์ชันกับที่อยู่ "ด้าน"
สัญญาเดียวที่ใช้ฟังก์ชันเฉพาะ แต่ละด้านประกอบด้วยชุดของฟังก์ชันที่เพชรเรียกได้
เป็นชุดของฟังก์ชันมาตรฐานที่กำหนดใน EIP-2535 ซึ่งให้ข้อมูลเกี่ยวกับตัวเลือกด้านและฟังก์ชันที่ใช้ในเพชร Diamond Loupe ช่วยให้นักพัฒนาและผู้ใช้ตรวจสอบและทำความเข้าใจโครงสร้างของเพชรได้
ฟังก์ชันสำหรับเพิ่ม แทนที่ หรือลบเหลี่ยมเพชรและตัวเลือกคุณลักษณะที่เกี่ยวข้อง เฉพาะที่อยู่ที่ได้รับอนุญาต (เช่น เจ้าของเพชรหรือสัญญาหลายลายเซ็น) เท่านั้นที่สามารถทำการเจียระไนเพชรได้
คล้ายกับตัวแทนแบบดั้งเดิม เมื่อมีการเรียกใช้ฟังก์ชันบนตัวแทนเพชร ฟังก์ชันสำรอง (ฟังก์ชันทางเลือก) ของตัวแทนจะถูกเรียกใช้ ข้อแตกต่างหลักจากพร็อกซีไดมอนด์คือในฟังก์ชันสำรอง มีการแมป SelectorToFacet ซึ่งจัดเก็บและกำหนดว่าที่อยู่สัญญาแบบลอจิคัลใดมีการใช้งานฟังก์ชันที่เรียกว่า จากนั้นจะใช้การเรียกผู้รับมอบสิทธิ์เพื่อดำเนินการฟังก์ชัน เช่นเดียวกับพร็อกซีแบบดั้งเดิม
พร็อกซีทั้งหมดใช้ฟังก์ชัน fallback() เพื่อมอบหมายการเรียกใช้ฟังก์ชันไปยังที่อยู่ภายนอก ด้านล่างนี้คือการใช้งานพร็อกซีไดมอนด์และการใช้งานพร็อกซีแบบดั้งเดิม
เป็นที่น่าสังเกตว่าบล็อกโค้ดแอสเซมบลีของพวกเขามีความคล้ายคลึงกันมาก ดังนั้นข้อแตกต่างเพียงอย่างเดียวคือแอดเดรสลักษณะในการเรียกตัวแทนไดมอนด์พร็อกซีและแอดเดรสโดยนัยในการเรียกตัวแทนพร็อกซีแบบดั้งเดิม
ข้อแตกต่างที่สำคัญคือในพร็อกซีรูปเพชร ที่อยู่ของลักษณะจะถูกกำหนดโดยแฮชแมปจาก msg.sig ของผู้โทร (ตัวเลือกฟังก์ชัน) ไปยังที่อยู่ของลักษณะ ขณะที่ในพร็อกซีแบบดั้งเดิม ที่อยู่โดยนัยจะไม่ขึ้นอยู่กับ ของผู้โทรเข้ามา
ฟังก์ชั่นสำรองตัวแทนเพชร
ฟังก์ชันสำรองพร็อกซีแบบดั้งเดิม
การแมป SelectorToFacet กำหนดว่าสัญญาใดมีการใช้งานตัวเลือกฟังก์ชันแต่ละตัว ผู้ปฏิบัติงานโครงการมักจำเป็นต้องเพิ่ม แทนที่ หรือลบการแมปสัญญาตัวเลือกการนำไปใช้งานของฟังก์ชันนี้ EIP-2535 กำหนด: เพื่อให้บรรลุวัตถุประสงค์นี้ ต้องมีฟังก์ชัน diamondCut() ด้านล่างนี้คืออินเทอร์เฟซตัวอย่าง
โครงสร้าง FacetCut แต่ละโครงสร้างมีที่อยู่ของ facet และอาร์เรย์ตัวเลือกคุณลักษณะสี่ไบต์ที่จะอัปเดตในสัญญาพร็อกซีรูปเพชร FaceCutAction ช่วยให้สามารถเพิ่ม แทนที่ และลบตัวเลือกคุณสมบัติได้ การใช้ฟังก์ชัน diamondCut() ควรรวมถึงการควบคุมการเข้าถึงที่เพียงพอ ป้องกันการชนกันของช่อง การกู้คืนเมื่อล้มเหลว เป็นต้น
เพื่อสอบถามว่าตัวแทนเพชรมีหน้าที่และลักษณะอย่างไร เราจึงใช้ "แว่นขยายเพชร" "Diamond Loupe" มีลักษณะพิเศษที่ใช้อินเทอร์เฟซต่อไปนี้ที่กำหนดไว้ใน EIP-2535:
ฟังก์ชัน facets() ควรส่งคืนที่อยู่ของ facets ทั้งหมดและตัวเลือกฟังก์ชันสี่ไบต์ ฟังก์ชัน facetFunctionSelectors() ควรส่งคืนตัวเลือกฟังก์ชันทั้งหมดที่รองรับโดยลักษณะเฉพาะ ฟังก์ชัน facetAddresses() ควรส่งคืนที่อยู่ facet ทั้งหมดที่ใช้โดยเพชร
ฟังก์ชัน facetAddress() ควรส่งคืนส่วนที่สนับสนุนตัวเลือกที่กำหนด หรือที่อยู่ (0) หากไม่พบ โปรดทราบว่าไม่ควรมีที่อยู่ด้านกว้างมากกว่าหนึ่งรายการที่มีตัวเลือกคุณลักษณะเดียวกัน
เนื่องจากพร็อกซี่ไดมอนด์มอบหมายการเรียกใช้ฟังก์ชันที่แตกต่างกันให้กับสัญญาการใช้งานที่แตกต่างกัน จึงจำเป็นอย่างยิ่งที่จะต้องจัดการช่องจัดเก็บข้อมูลอย่างเหมาะสมเพื่อป้องกันความขัดแย้ง EIP-2535 กล่าวถึงวิธีการจัดการช่องเก็บข้อมูลหลายวิธี
ด้านนี้สามารถประกาศตัวแปรสถานะในโครงสร้าง ลักษณะนี้สามารถใช้โครงสร้างจำนวนเท่าใดก็ได้ โดยแต่ละส่วนมีตำแหน่งจัดเก็บต่างกัน แต่ละโครงสร้างมีตำแหน่งเฉพาะในการจัดเก็บสัญญา Aspects สามารถประกาศตัวแปรสถานะของตัวเองได้ แต่ไม่สามารถขัดแย้งกับตำแหน่งที่เก็บข้อมูลของตัวแปรสถานะที่ประกาศโดยด้านอื่นๆ ห้องสมุดตัวอย่างและสัญญาการจัดเก็บเพชรมีอยู่ใน EIP-2535 ดังแสดงในรูปต่อไปนี้:
ที่เก็บข้อมูลแอปเป็นที่เก็บเพชรเวอร์ชันพิเศษกว่า รูปแบบนี้ใช้เพื่อแบ่งปันตัวแปรสถานะของลักษณะต่างๆ ได้สะดวกและง่ายดายยิ่งขึ้น โครงสร้าง App Store ถูกกำหนดให้มีจำนวนและประเภทของตัวแปรสถานะที่แอปพลิเคชันต้องการ ลักษณะจะประกาศโครงสร้าง AppStorage เป็นตัวแปรสถานะตัวแรกและตัวเดียวเสมอ ที่ตำแหน่ง 0 ของสล็อตที่เก็บข้อมูล ด้านต่างๆจึงสามารถเข้าถึงตัวแปรจากโครงสร้างนี้ได้
นอกจากนี้ยังมีกลยุทธ์การจัดการสล็อตพื้นที่เก็บข้อมูลอื่นๆ รวมถึงการผสมผสานระหว่าง Diamond Storage และ AppStorage ตัวอย่างเช่น โครงสร้างบางอย่างใช้ร่วมกันระหว่างลักษณะต่างๆ และบางโครงสร้างมีความเฉพาะเจาะจงสำหรับลักษณะเฉพาะ ในทุกกรณี การป้องกันการชนกันของสล็อตโดยไม่ตั้งใจเป็นสิ่งสำคัญมาก
เปรียบเทียบกับ Transparent Proxy และ UUPS Proxy
โหมดพร็อกซีหลักสองโหมดที่ใช้ในปัจจุบันโดยชุมชนนักพัฒนา Web3 คือโหมดพร็อกซีแบบโปร่งใสและโหมดพร็อกซี UUPS ในส่วนนี้ เราจะเปรียบเทียบโหมดไดมอนด์พร็อกซีกับโหมดพร็อกซีแบบโปร่งใสและโหมดพร็อกซี UUPS โดยสังเขป
1.EPI-2535:
2.EPI-1967:
3.การดำเนินการอ้างอิงพร็อกซีไดมอนด์:
4.การใช้งาน OpenZeppelin:
พร็อกซีและโซลูชันที่ปรับขนาดได้เป็นระบบที่ซับซ้อนมากขึ้น และ OpenZeppelin จัดเตรียมฐานรหัสและเอกสารที่ครอบคลุมสำหรับพร็อกซีที่ปรับขนาดได้ของ UUPS, Transparent และ Beacon อย่างไรก็ตาม สำหรับโหมดพร็อกซีเพชร แม้ว่า OpenZeppelin จะยืนยันถึงประโยชน์ของมัน แต่พวกเขาก็ยังตัดสินใจที่จะไม่รวมการใช้งานเพชร EIP-2535 ไว้ในไลบรารีของพวกเขา
ดังนั้น นักพัฒนาที่ใช้ไลบรารีของบุคคลที่สามที่มีอยู่หรือนำโซลูชันนี้ไปใช้เองจะต้องดำเนินการด้วยความระมัดระวังเป็นอย่างยิ่ง ที่นี่เราได้รวบรวมรายการตรวจสอบแนวทางปฏิบัติที่ดีที่สุดด้านความปลอดภัยสำหรับชุมชนนักพัฒนาซอฟต์แวร์เพื่อพิจารณา
การแบ่งตรรกะของสัญญาออกเป็นโมดูลที่เล็กลงและสามารถจัดการได้มากขึ้น นักพัฒนาสามารถทดสอบและตรวจสอบรหัสของตนได้ง่ายขึ้น
นอกจากนี้ วิธีการนี้ช่วยให้นักพัฒนาสามารถมุ่งเน้นไปที่ลักษณะเฉพาะของการสร้างและการรักษาสัญญา แทนที่จะจัดการ codebase ขนาดใหญ่ที่ซับซ้อน ผลลัพธ์ที่ได้คือฐานโค้ดแบบโมดูลาร์ที่ยืดหยุ่นมากขึ้นซึ่งสามารถอัปเดตและแก้ไขได้ง่ายโดยไม่กระทบกับส่วนอื่นๆ ของสัญญา
ที่มา: Aavegotchi Github
เมื่อมีการปรับใช้สัญญาพร็อกซีเพชร จะต้องเพิ่มที่อยู่ของสัญญา DiamondCutFacet ลงในสัญญาพร็อกซีเพชรและใช้ฟังก์ชัน diamondCut() ฟังก์ชัน diamondCut() ใช้เพื่อเพิ่ม ลบ หรือแทนที่ facets และฟังก์ชันต่างๆ หากไม่มี DiamondCutFacet และ diamondCut() ตัวแทนเพชรจะไม่สามารถทำงานได้อย่างถูกต้อง
ที่มา: Mugen's Diamond-3-Hardhat
เมื่อเพิ่มตัวแปรสถานะใหม่ให้กับโครงสร้างหน่วยเก็บข้อมูลในสัญญาอัจฉริยะ จะต้องเพิ่มตัวแปรดังกล่าวที่ส่วนท้ายของโครงสร้าง การเพิ่มตัวแปรสถานะใหม่ที่จุดเริ่มต้นหรือตรงกลางของโครงสร้างจะทำให้ตัวแปรสถานะใหม่เขียนทับข้อมูลตัวแปรสถานะที่มีอยู่ และตัวแปรสถานะใดๆ หลังจากตัวแปรสถานะใหม่อาจอ้างถึงตำแหน่งหน่วยความจำที่ไม่ถูกต้อง
รูปแบบ AppStorage กำหนดให้มีการประกาศโครงสร้างเดียวและโครงสร้างเดียวสำหรับไดมอนด์พร็อกซี และโครงสร้างนี้ต้องแชร์โดยทุกด้าน หากจำเป็นต้องมีโครงสร้างหลายส่วน ควรใช้รูปแบบ DiamondStorage
อย่าใส่ struct ลงใน struct อื่นโดยตรง เว้นแต่คุณจะแน่ใจว่าคุณไม่ต้องการเพิ่มตัวแปร state ให้กับ struct ภายใน ไม่สามารถเพิ่มตัวแปรสถานะใหม่ให้กับโครงสร้างภายในในการอัพเกรดโดยไม่ต้องเขียนทับช่องเก็บตัวแปรที่ประกาศหลังโครงสร้าง
วิธีแก้ปัญหาคือเพิ่มตัวแปรสถานะใหม่ให้กับโครงสร้างที่แมปหน่วยความจำแทนการวาง "struct" โดยตรงใน "struct" ช่องจัดเก็บตัวแปรในแผนที่มีการคำนวณต่างกันและไม่ได้อยู่ติดกันในที่จัดเก็บ
ขนาดของอาร์เรย์จะได้รับผลกระทบจากขนาดของโครงสร้าง เมื่อมีการเพิ่มตัวแปรสถานะใหม่ลงในโครงสร้าง ตัวแปรดังกล่าวจะเปลี่ยนขนาดและเค้าโครงของโครงสร้างนั้น
สิ่งนี้อาจทำให้เกิดปัญหาหากใช้โครงสร้างเป็นองค์ประกอบในอาร์เรย์ ถ้าขนาดและเค้าโครงของโครงสร้างเปลี่ยนไป ขนาดและเค้าโครงของอาร์เรย์ก็จะเปลี่ยนไปด้วย ซึ่งอาจทำให้เกิดปัญหากับการจัดทำดัชนีหรือการดำเนินการอื่นๆ ที่ต้องใช้ขนาดและเค้าโครงที่สอดคล้องกันของโครงสร้าง
เช่นเดียวกับรูปแบบพร็อกซีอื่นๆ ตัวแปรแต่ละตัวควรมีช่องจัดเก็บที่ไม่ซ้ำกัน มิฉะนั้น โครงสร้างที่แตกต่างกันสองแห่งในตำแหน่งเดียวกันจะเขียนทับซึ่งกันและกัน
ฟังก์ชัน initialize() มักจะใช้เพื่อตั้งค่าตัวแปรที่สำคัญ เช่น ที่อยู่ของบทบาทที่มีสิทธิพิเศษ หากไม่ได้เริ่มต้นเมื่อใช้งานสัญญา ผู้ประสงค์ร้ายสามารถโทรและควบคุมสัญญาได้
ขอแนะนำให้เพิ่มการควบคุมการเข้าถึงที่เหมาะสมให้กับฟังก์ชันการเริ่มต้น/การตั้งค่า หรือตรวจสอบให้แน่ใจว่ามีการเรียกใช้ฟังก์ชันเมื่อสัญญาถูกนำไปใช้ และไม่สามารถเรียกใช้ได้อีก
หากส่วนใดของสัญญาสามารถเรียกฟังก์ชัน selfdestruct() ได้ ก็อาจทำลายทั้งสัญญาได้ ส่งผลให้สูญเสียเงินทุนหรือข้อมูล สิ่งนี้เป็นอันตรายอย่างยิ่งในโหมดพร็อกซีไดมอนด์ เนื่องจากมีหลายแง่มุมที่สามารถเข้าถึงพื้นที่เก็บข้อมูลและข้อมูลของสัญญาพร็อกซีได้
ปัจจุบัน เราเห็นโครงการจำนวนมากขึ้นเรื่อยๆ ที่ใช้โมเดลพร็อกซีเพชรในสัญญาอัจฉริยะของพวกเขา ให้ความยืดหยุ่นและข้อดีอื่น ๆ ที่เหนือกว่าผู้รับมอบฉันทะแบบดั้งเดิม
อย่างไรก็ตาม ความยืดหยุ่นที่เพิ่มขึ้นอาจหมายถึงพื้นผิวการโจมตีที่กว้างขึ้นสำหรับผู้โจมตี เราหวังว่าบทความนี้จะช่วยให้ชุมชนนักพัฒนาเข้าใจกลไกของโมเดลพร็อกซีไดมอนด์และข้อควรพิจารณาด้านความปลอดภัย
ในขณะเดียวกัน ทีมงานโครงการควรทำการทดสอบอย่างเข้มงวดและการตรวจสอบจากบุคคลที่สามเพื่อลดความเสี่ยงของช่องโหว่ที่เกี่ยวข้องกับการดำเนินการตามสัญญาตัวแทนเพชร