คำเตือนความเสี่ยง: ระวังความเสี่ยงจากการระดมทุนที่ผิดกฎหมายในนาม 'สกุลเงินเสมือน' 'บล็อกเชน' — จากห้าหน่วยงานรวมถึงคณะกรรมการกำกับดูแลการธนาคารและการประกันภัย
ข่าวสาร
ค้นพบ
ค้นหา
เข้าสู่ระบบ
简中
繁中
English
日本語
한국어
ภาษาไทย
Tiếng Việt
BTC
ETH
HTX
SOL
BNB
ดูตลาด
นอกจาก Solidity แล้ว ภาษา EVM อื่น ๆ ที่ควรค่าแก่การให้ความสนใจคืออะไร?
Foresight News
特邀专栏作者
2023-03-18 09:30
บทความนี้มีประมาณ 7632 คำ การอ่านทั้งหมดใช้เวลาประมาณ 11 นาที
ศึกษาเทคโนโลยีล่าสุดของการออกแบบ EVM DSL วิธีการออกแบบภาษาที่ยอดเยี่ยม?

การรวบรวมต้นฉบับ: 0x11, Foresight News

การรวบรวมต้นฉบับ: 0x11, Foresight News

Ethereum Virtual Machine (EVM) เป็นเครื่องทัวริงแบบ 256 บิตแบบสแต็กและเข้าถึงได้ทั่วโลก เนื่องจากสถาปัตยกรรมแตกต่างอย่างมากจากเครื่องเสมือนและเครื่องจริงอื่น ๆ EVM จึงต้องการ DSL ภาษาเฉพาะโดเมน (หมายเหตุ: ภาษาเฉพาะโดเมนหมายถึงภาษาคอมพิวเตอร์ที่มุ่งเน้นไปที่โดเมนแอปพลิเคชันบางตัว)

ในบทความนี้ เราจะตรวจสอบความล้ำสมัยในการออกแบบ EVM DSL โดยแนะนำหกภาษา ได้แก่ Solidity, Vyper, Fe, Huff, Yul และ ETK

รุ่นภาษา

  • Solidity: 0.8.19 

  • Vyper: 0.3.7 

  • Fe: 0.21.0 

  • Huff: 0.3.1 

  • ETK: 0.2.1 

  • Yul: 0.8.19 

การอ่านบทความนี้ คุณต้องมีความเข้าใจพื้นฐานเกี่ยวกับ EVM สแต็ก และการเขียนโปรแกรม

ภาพรวมของเครื่องเสมือน Ethereum

EVM เป็นเครื่องทัวริงแบบสแต็ก 256 บิต อย่างไรก็ตาม ก่อนที่จะดำดิ่งลงไปในคอมไพเลอร์ ควรแนะนำคุณสมบัติการทำงานบางอย่างก่อน

เนื่องจาก EVM "ทัวริงสมบูรณ์" จึงประสบปัญหา "หยุดทำงาน" กล่าวโดยย่อคือ ก่อนที่โปรแกรมจะทำงาน ไม่มีทางที่จะตัดสินได้ว่าโปรแกรมจะหยุดทำงานในอนาคตหรือไม่ วิธีที่ EVM แก้ปัญหานี้คือผ่านหน่วยการคำนวณ "แก๊ส" ซึ่งโดยทั่วไปจะเป็นสัดส่วนกับทรัพยากรทางกายภาพที่จำเป็นในการดำเนินการตามคำสั่ง ปริมาณแก๊สของแต่ละธุรกรรมมีจำกัด และผู้เริ่มธุรกรรมต้องจ่าย ETH ตามสัดส่วนของแก๊สที่ใช้โดยธุรกรรม ความหมายประการหนึ่งของกลยุทธ์นี้คือหากมีสัญญาอัจฉริยะที่ทำหน้าที่เหมือนกันสองสัญญา สัญญาอัจฉริยะที่กินน้ำมันน้อยกว่าจะถูกนำมาใช้มากขึ้น ซึ่งส่งผลให้มีโปรโตคอลที่แข่งขันกันเพื่อประสิทธิภาพการใช้ก๊าซสูงสุด โดยวิศวกรพยายามลดการใช้ก๊าซให้น้อยที่สุดสำหรับงานเฉพาะด้าน

นอกจากนี้ เมื่อมีการเรียกใช้สัญญา จะสร้างบริบทการดำเนินการ ในบริบทนี้ สัญญามีสแต็กสำหรับการดำเนินการและการประมวลผล อินสแตนซ์หน่วยความจำเชิงเส้นสำหรับการอ่านและเขียน ที่เก็บข้อมูลถาวรในเครื่องสำหรับการอ่านและเขียนสัญญา และข้อมูล "calldata" ที่แนบมากับการโทรสามารถอ่านได้แต่ไม่สามารถบันทึกได้ .

ข้อควรทราบที่สำคัญเกี่ยวกับหน่วยความจำคือ แม้ว่าจะไม่มี "ขีดจำกัดบน" ที่แน่นอนสำหรับขนาดของหน่วยความจำ แต่ก็ยังมีขอบเขตจำกัด ต้นทุนแก๊สของการขยายหน่วยความจำเป็นแบบไดนามิก: เมื่อถึงเกณฑ์ ต้นทุนของการขยายหน่วยความจำจะเพิ่มขึ้นเป็นกำลังสอง นั่นคือ ต้นทุนแก๊สจะเป็นสัดส่วนกับกำลังสองของการจัดสรรหน่วยความจำเพิ่มเติม

สัญญายังสามารถใช้คำสั่งต่าง ๆ เพื่อเรียกสัญญาอื่น ๆ คำสั่ง "โทร" จะส่งข้อมูลและเลือก ETH ไปยังสัญญาเป้าหมาย จากนั้นสร้างบริบทการดำเนินการของมันเองจนกว่าการดำเนินการของสัญญาเป้าหมายจะหยุดลง คำสั่ง "staticcall" นั้นเหมือนกับ "การโทร" แต่เพิ่มการตรวจสอบที่ยืนยันว่าไม่มีส่วนใดของสถานะส่วนกลางได้รับการปรับปรุงจนกว่าการโทรแบบคงที่จะเสร็จสมบูรณ์ สุดท้าย คำสั่ง "delegatecall" จะทำงานเหมือน "call" ยกเว้นว่าจะเก็บข้อมูลสภาพแวดล้อมบางอย่างจากบริบทก่อนหน้า โดยทั่วไปจะใช้กับไลบรารีภายนอกและสัญญาพร็อกซี

เหตุใดการออกแบบภาษาจึงมีความสำคัญ

ภาษาเฉพาะโดเมน (DSL) จำเป็นเมื่อโต้ตอบกับสถาปัตยกรรมที่ผิดปกติ ในขณะที่คอมไพเลอร์ toolchains เช่น LLVM มีอยู่ การพึ่งพาพวกมันเพื่อจัดการกับสัญญาอัจฉริยะนั้นน้อยกว่าอุดมคติที่ความถูกต้องของโปรแกรมและประสิทธิภาพการคำนวณเป็นสิ่งสำคัญ

ความถูกต้องของขั้นตอนมีความสำคัญเนื่องจากสัญญาอัจฉริยะไม่สามารถเปลี่ยนแปลงได้โดยค่าเริ่มต้น และด้วยคุณสมบัติของเครื่องเสมือนบล็อกเชน (VMs) สัญญาอัจฉริยะจึงเป็นตัวเลือกยอดนิยมสำหรับแอปพลิเคชันทางการเงิน แม้ว่าจะมีโซลูชันที่สามารถอัปเกรดได้สำหรับ EVM แต่ก็เป็นแพตช์ที่ดีที่สุดและช่องโหว่ในการเรียกใช้โค้ดโดยอำเภอใจที่แย่ที่สุด

ประสิทธิภาพการคำนวณก็มีความสำคัญเช่นกัน เนื่องจากการย่อขนาดการคำนวณให้เล็กที่สุดมีข้อได้เปรียบทางเศรษฐกิจ แต่ไม่ต้องเสียค่าใช้จ่ายด้านความปลอดภัย

กล่าวโดยสรุปคือ EVM DSL ต้องสร้างความสมดุลระหว่างความถูกต้องของโปรแกรมและประสิทธิภาพของแก๊ส และบรรลุหนึ่งในนั้นด้วยการแลกเปลี่ยนความแตกต่างโดยไม่สูญเสียความยืดหยุ่นมากเกินไป

ภาพรวมของภาษา

สำหรับแต่ละภาษา เราจะอธิบายคุณลักษณะเด่นและตัวเลือกการออกแบบ และรวมถึงสัญญาอัจฉริยะของฟังก์ชันการนับอย่างง่าย ความนิยมของ Word จะพิจารณาจากข้อมูล Total Value Locked (TVL) บน Defi Llama

Solidity

Solidity เป็นภาษาระดับสูงที่มีไวยากรณ์คล้ายกับ C, Java และ Javascript เป็นภาษาที่ได้รับความนิยมมากที่สุดโดย TVL โดยเป็นสิบเท่าของ TVL ของภาษาที่ดีที่สุดรองลงมา สำหรับการใช้โค้ดซ้ำ จะใช้รูปแบบเชิงวัตถุ โดยที่สัญญาอัจฉริยะจะถือว่าเป็นออบเจกต์คลาส โดยใช้การสืบทอดหลายรายการ คอมไพเลอร์เขียนด้วย C++ โดยมีแผนที่จะย้ายไปยัง Rust ในอนาคต

ฟิลด์สัญญาที่ไม่แน่นอนจะถูกจัดเก็บไว้ในที่จัดเก็บถาวร เว้นแต่จะทราบค่าในเวลาคอมไพล์ (คงที่) หรือเวลาปรับใช้ (ไม่เปลี่ยนรูป) วิธีการที่ประกาศในสัญญาสามารถประกาศเป็น Pure, View, Payable หรือ Non-Payable ตามค่าเริ่มต้น แต่สถานะสามารถแก้ไขได้ เมธอดแท้ไม่ได้อ่านจากสภาพแวดล้อมการดำเนินการ และไม่สามารถอ่านหรือเขียนไปยังที่เก็บข้อมูลถาวรได้ นั่นคือ เมธอดแท้จะส่งคืนเอาต์พุตเดียวกันเสมอเมื่อได้รับอินพุตเดียวกัน และไม่มีผลข้างเคียง วิธีการดูสามารถอ่านข้อมูลจากที่เก็บข้อมูลถาวรหรือสภาพแวดล้อมการดำเนินการ แต่ไม่สามารถเขียนไปยังที่เก็บข้อมูลถาวร และไม่สามารถสร้างผลข้างเคียง เช่น การต่อท้ายบันทึกธุรกรรม วิธีการชำระเงินสามารถอ่านและเขียนที่เก็บข้อมูลถาวร อ่านข้อมูลจากสภาพแวดล้อมการดำเนินการ สร้างผลข้างเคียง และสามารถรับ ETH ที่แนบมากับการโทร วิธีการที่ไม่ต้องชำระเงินจะเหมือนกับวิธีการที่ชำระเงิน แต่มีการตรวจสอบรันไทม์เพื่อยืนยันว่าไม่มี ETH แนบมากับบริบทการดำเนินการปัจจุบัน

หมายเหตุ: การแนบ ETH กับธุรกรรมนั้นแยกจากการชำระค่าก๊าซ สัญญาได้รับ ETH ที่แนบมาซึ่งสามารถยอมรับหรือปฏิเสธได้โดยการกู้คืนบริบท

เมื่อประกาศภายในขอบเขตของสัญญา เมธอดสามารถระบุตัวแก้ไขการมองเห็นหนึ่งในสี่ตัว: ส่วนตัว ภายใน สาธารณะ หรือภายนอก วิธีการส่วนตัวสามารถเข้าถึงได้ภายในผ่านคำสั่ง "ข้าม" ภายในสัญญาปัจจุบัน สัญญาที่สืบทอดมาไม่สามารถเข้าถึงวิธีการส่วนตัวได้โดยตรง วิธีการภายในยังสามารถเข้าถึงได้ภายในผ่านคำสั่ง "ข้าม" แต่สัญญาที่สืบทอดมาสามารถใช้วิธีการภายในได้โดยตรง เมธอดสาธารณะสามารถเข้าถึงได้โดยสัญญาภายนอกผ่านคำสั่ง "โทร" ซึ่งสร้างบริบทการดำเนินการใหม่ และภายในผ่านการข้ามเมื่อเรียกใช้เมธอดโดยตรง นอกจากนี้ยังสามารถเข้าถึงวิธีการสาธารณะได้จากภายในสัญญาเดียวกันในบริบทการดำเนินการใหม่โดยนำหน้าการเรียกใช้เมธอดด้วย "สิ่งนี้" วิธีการภายนอกสามารถเข้าถึงได้ผ่านคำสั่ง "โทร" เท่านั้น ไม่ว่าจะมาจากสัญญาอื่นหรือภายในสัญญาเดียวกัน คุณต้องเพิ่ม "สิ่งนี้" ก่อนการเรียกใช้เมธอด

หมายเหตุ: คำสั่ง "jump" จัดการตัวนับโปรแกรม และคำสั่ง "call" จะสร้างบริบทการดำเนินการใหม่สำหรับระยะเวลาการดำเนินการของสัญญาเป้าหมาย หากเป็นไปได้ การใช้ "jump" แทน "call" จะมีประสิทธิภาพมากกว่า

Solidity ยังมีสามวิธีในการกำหนดไลบรารี อย่างแรกคือไลบรารีภายนอก ซึ่งเป็นสัญญาแบบไร้สถานะที่ปรับใช้บนเชนแยกกัน เชื่อมโยงแบบไดนามิกเมื่อมีการเรียกใช้สัญญา และเข้าถึงได้ผ่านคำสั่ง "delegatecall" นี่เป็นวิธีการที่ใช้กันน้อยที่สุดเนื่องจากการสนับสนุนเครื่องมือที่ไม่ดีสำหรับไลบรารีภายนอก "การเรียกผู้รับมอบสิทธิ์" มีราคาแพง ต้องโหลดโค้ดเพิ่มเติมจากที่เก็บข้อมูลถาวร และต้องใช้ธุรกรรมหลายรายการเพื่อปรับใช้ ไลบรารีภายในถูกกำหนดในลักษณะเดียวกับไลบรารีภายนอก ยกเว้นว่าแต่ละเมธอดต้องถูกกำหนดเป็นเมธอดภายใน ในเวลาคอมไพล์ ไลบรารีภายในจะถูกฝังลงในสัญญาขั้นสุดท้าย และในระหว่างขั้นตอนการวิเคราะห์โค้ดที่ไม่ทำงาน วิธีการที่ไม่ได้ใช้ในไลบรารีจะถูกลบออก วิธีที่สามคล้ายกับไลบรารีภายใน แต่แทนที่จะกำหนดโครงสร้างข้อมูลและฟังก์ชันภายในไลบรารี จะกำหนดไว้ที่ระดับไฟล์และสามารถนำเข้าและใช้ในสัญญาขั้นสุดท้ายได้โดยตรง วิธีที่สามให้การโต้ตอบระหว่างมนุษย์และคอมพิวเตอร์ที่ดีขึ้น คุณสามารถใช้โครงสร้างข้อมูลแบบกำหนดเอง ใช้ฟังก์ชันในขอบเขตส่วนกลาง และใช้ตัวดำเนินการนามแฝงกับบางฟังก์ชันได้ในระดับหนึ่ง

คอมไพเลอร์ให้การเพิ่มประสิทธิภาพสองรอบ อย่างแรกคือเครื่องมือเพิ่มประสิทธิภาพระดับคำสั่งที่ดำเนินการปรับให้เหมาะสมในรหัสไบต์สุดท้าย ประการที่สองคือการเพิ่มขึ้นของการใช้ภาษา Yul (อธิบายในรายละเอียดในภายหลัง) ในฐานะตัวแทนระดับกลาง (IR) ในกระบวนการรวบรวม จากนั้นปรับรหัส Yul ที่สร้างขึ้นให้เหมาะสม

ในการโต้ตอบกับวิธีการสาธารณะและภายนอกในสัญญา Solidity ระบุมาตรฐาน Application Binary Interface (ABI) สำหรับการโต้ตอบกับสัญญา ปัจจุบัน Solidity ABI ถือเป็นมาตรฐานโดยพฤตินัยสำหรับ EVM DSL มาตรฐาน Ethereum ERC ที่ระบุอินเทอร์เฟซภายนอกนั้นถูกนำมาใช้ตามข้อกำหนด ABI และแนวทางสไตล์ของ Solidity ภาษาอื่น ๆ ยังเป็นไปตามข้อกำหนด ABI ของ Solidity โดยมีความเบี่ยงเบนเล็กน้อย

Solidity ยังมีบล็อก Yul แบบอินไลน์ซึ่งอนุญาตให้เข้าถึงชุดคำสั่ง EVM ในระดับต่ำ บล็อก Yul มีชุดย่อยของฟังก์ชัน Yul โปรดดูรายละเอียดในส่วน Yul ซึ่งมักใช้สำหรับการปรับให้เหมาะสม เพื่อใช้ประโยชน์จากคุณสมบัติที่ไม่รองรับโดยไวยากรณ์ระดับสูง และเพื่อปรับแต่งที่เก็บข้อมูล หน่วยความจำ และข้อมูลการโทร

เนื่องจากความนิยมของ Solidity เครื่องมือสำหรับนักพัฒนาจึงมีความเป็นผู้ใหญ่และได้รับการออกแบบมาอย่างดี Foundry จึงเป็นตัวแทนที่โดดเด่นในเรื่องนี้

นี่คือสัญญาอย่างง่ายที่เขียนด้วย Solidity:

Vyper

Vyper เป็นภาษาระดับสูงที่มีไวยากรณ์คล้ายกับ Python เป็นส่วนย่อยของ Python ที่มีความแตกต่างเล็กน้อย เป็น EVM DSL ที่ได้รับความนิยมเป็นอันดับสอง Vyper ได้รับการปรับแต่งเพื่อความปลอดภัย อ่านง่าย ตรวจสอบได้ และประหยัดน้ำมัน ไม่ใช้รูปแบบเชิงวัตถุ แอสเซมบลีแบบอินไลน์ และไม่สนับสนุนการใช้โค้ดซ้ำ คอมไพเลอร์เขียนด้วย Python

ตัวแปรที่เก็บในที่เก็บข้อมูลถาวรจะถูกประกาศที่ระดับไฟล์ หากทราบค่า ณ เวลาคอมไพล์ สามารถประกาศเป็น "ค่าคงที่" หากทราบค่า ณ เวลาปรับใช้ สามารถประกาศเป็น "ไม่เปลี่ยนรูป" หากทำเครื่องหมายเป็นสาธารณะ สัญญาขั้นสุดท้ายจะเปิดเผยการอ่าน- เฉพาะฟังก์ชันสำหรับตัวแปรนั้น ค่าของค่าคงที่และค่าคงที่สามารถเข้าถึงได้ภายในโดยใช้ชื่อ แต่ตัวแปรที่ไม่แน่นอนในที่เก็บข้อมูลถาวรสามารถเข้าถึงได้โดยนำหน้าชื่อด้วย "self" สิ่งนี้มีประโยชน์ในการป้องกันความขัดแย้งของเนมสเปซระหว่างตัวแปรที่เก็บไว้ พารามิเตอร์ของฟังก์ชัน และตัวแปรภายในเครื่อง

เช่นเดียวกับ Solidity Vyper ยังใช้แอตทริบิวต์ของฟังก์ชันเพื่อแสดงถึงการมองเห็นและความไม่แน่นอนของฟังก์ชัน ฟังก์ชันที่มีเครื่องหมาย "@external" สามารถเข้าถึงได้จากสัญญาภายนอกผ่านคำสั่ง "call" ฟังก์ชันที่มีเครื่องหมาย "@internal" สามารถเข้าถึงได้ภายในสัญญาเดียวกันเท่านั้น และต้องขึ้นต้นด้วย "self" ฟังก์ชันที่มีเครื่องหมาย "@pure" ไม่สามารถอ่านได้จากสภาพแวดล้อมการดำเนินการหรือที่เก็บข้อมูลถาวร และไม่สามารถเขียนไปยังที่เก็บข้อมูลถาวรหรือสร้างผลข้างเคียงใดๆ ฟังก์ชันที่มีเครื่องหมาย "@view" สามารถอ่านข้อมูลจากสภาพแวดล้อมการดำเนินการหรือที่เก็บข้อมูลถาวร แต่ไม่สามารถเขียนไปยังที่เก็บข้อมูลถาวรหรือสร้างผลข้างเคียงได้ ฟังก์ชันที่มีเครื่องหมาย "@payable" สามารถอ่านหรือเขียนไปยังที่เก็บข้อมูลถาวร สร้างผลข้างเคียง และยอมรับหรือรับ ETH ฟังก์ชันที่ไม่ประกาศแอตทริบิวต์ที่ไม่แน่นอนนี้มีค่าเริ่มต้นเป็น non-payable นั่นคือทำงานเหมือนฟังก์ชัน payable แต่ไม่สามารถรับ ETH ได้

คอมไพเลอร์ Vyper ยังเลือกที่จะเก็บตัวแปรโลคัลไว้ในหน่วยความจำมากกว่าในสแต็ก สิ่งนี้ทำให้สัญญาง่ายขึ้นและมีประสิทธิภาพมากขึ้น และแก้ปัญหา "too deep stack" ที่พบได้ทั่วไปในภาษาระดับสูงอื่นๆ อย่างไรก็ตาม สิ่งนี้ยังมาพร้อมกับการแลกเปลี่ยนบางอย่าง

นอกจากนี้ เนื่องจากต้องทราบโครงร่างหน่วยความจำในเวลาคอมไพล์ จึงต้องทราบความจุสูงสุดของประเภทไดนามิกในเวลาคอมไพล์ด้วย ซึ่งเป็นข้อจำกัด นอกจากนี้ การจัดสรรหน่วยความจำจำนวนมากอาจทำให้สิ้นเปลืองก๊าซแบบไม่เชิงเส้น ดังที่กล่าวไว้ในส่วนภาพรวม EVM อย่างไรก็ตาม สำหรับการใช้งานหลายๆ กรณี ค่าน้ำมันนี้ถือว่าเล็กน้อย

แม้ว่า Vyper จะไม่รองรับการประกอบแบบอินไลน์ แต่ก็มีฟังก์ชันในตัวเพิ่มเติมเพื่อให้แน่ใจว่าเกือบทุกฟังก์ชันใน Solidity และ Yul สามารถนำไปใช้ใน Vyper ได้ การดำเนินการบิตระดับต่ำ การเรียกภายนอก และการดำเนินการตามสัญญาพร็อกซีสามารถเข้าถึงได้ผ่านฟังก์ชันในตัว และเค้าโครงพื้นที่เก็บข้อมูลแบบกำหนดเองสามารถทำได้โดยการให้ไฟล์ซ้อนทับในเวลาคอมไพล์

Vyper ไม่มีชุดเครื่องมือการพัฒนาที่หลากหลาย แต่มีเครื่องมือที่ผสานรวมแน่นกว่าและยังสามารถเชื่อมต่อกับเครื่องมือพัฒนา Solidity เครื่องมือ Vyper ที่โดดเด่น ได้แก่ ตัวแปล Titanaboa ซึ่งมีเครื่องมือในตัวมากมายที่เกี่ยวข้องกับ EVM และ Vyper สำหรับการทดลองและการพัฒนา และ Dasy ซึ่งเป็น Lisp ที่ใช้ Vyper พร้อมการประมวลผลรหัสเวลาคอมไพล์

นี่คือสัญญาง่ายๆ ที่เขียนด้วย Vyper:

Fe

Fe เป็นภาษาคล้ายสนิมระดับสูงซึ่งกำลังอยู่ในระหว่างการพัฒนาอย่างแข็งขัน โดยฟีเจอร์ส่วนใหญ่ยังไม่พร้อมใช้งาน คอมไพเลอร์เขียนด้วยภาษา Rust เป็นหลัก แต่ใช้ Yul เป็นตัวแทนระดับกลาง (IR) โดยอาศัยตัวเพิ่มประสิทธิภาพ Yul ที่เขียนด้วยภาษา C++ สิ่งนี้คาดว่าจะเปลี่ยนแปลงด้วยการเพิ่ม Sonatina ซึ่งเป็นแบ็กเอนด์แบบ Rust-native Fe ใช้โมดูลสำหรับการแบ่งปันโค้ด ดังนั้นแทนที่จะใช้รูปแบบเชิงวัตถุ โค้ดจะถูกใช้ซ้ำผ่านระบบที่ใช้โมดูลซึ่งมีการประกาศตัวแปร ประเภท และฟังก์ชันภายในโมดูล ซึ่งสามารถนำเข้าได้ในลักษณะที่เหมือนสนิม

ตัวแปรหน่วยเก็บข้อมูลแบบถาวรได้รับการประกาศที่ระดับสัญญาและไม่สามารถเข้าถึงได้แบบสาธารณะหากไม่มีฟังก์ชัน getter ที่กำหนดด้วยตนเอง ค่าคงที่สามารถประกาศได้ที่ระดับไฟล์หรือโมดูล และสามารถเข้าถึงได้ภายในสัญญา ไม่รองรับตัวแปรเวลาปรับใช้ที่ไม่เปลี่ยนรูปในขณะนี้

วิธีการสามารถประกาศได้ที่ระดับโมดูลหรือภายในสัญญา ค่าเริ่มต้นนั้นบริสุทธิ์และเป็นส่วนตัว ในการทำให้วิธีการทำสัญญาเป็นแบบสาธารณะ คำจำกัดความต้องนำหน้าด้วยคีย์เวิร์ด "pub" ซึ่งทำให้สามารถเข้าถึงได้จากภายนอก หากต้องการอ่านจากตัวแปรหน่วยเก็บข้อมูลถาวร พารามิเตอร์แรกของเมธอดต้องเป็น "self" นำหน้าชื่อตัวแปรด้วย "self" ทำให้เมธอดเข้าถึงตัวแปรหน่วยเก็บข้อมูลในเครื่องแบบอ่านอย่างเดียว หากต้องการอ่านและเขียนไปยังที่เก็บข้อมูลถาวร อาร์กิวเมนต์แรกต้องเป็น "ปิดเสียงตัวเอง" คีย์เวิร์ด "mut" ระบุว่าที่เก็บข้อมูลของสัญญาไม่สามารถใช้งานได้ระหว่างการดำเนินการเมธอด การเข้าถึงตัวแปรสภาพแวดล้อมทำได้โดยการส่งพารามิเตอร์ "บริบท" ไปยังเมธอด ซึ่งปกติจะเรียกว่า "ctx"

ฟังก์ชันและประเภทที่กำหนดเองสามารถประกาศได้ที่ระดับโมดูล ตามค่าเริ่มต้น รายการโมดูลเป็นแบบส่วนตัวและไม่สามารถเข้าถึงได้เว้นแต่จะเพิ่มคีย์เวิร์ด "pub" อย่างไรก็ตาม อย่าสับสนกับคีย์เวิร์ด "pub" ที่ระดับสัญญา สมาชิกสาธารณะของโมดูลสามารถเข้าถึงได้ภายในสัญญาขั้นสุดท้ายหรือโมดูลอื่นๆ เท่านั้น

ปัจจุบัน Fe ไม่รองรับการประกอบแบบอินไลน์ แต่คำแนะนำจะถูกห่อด้วยส่วนประกอบภายในของคอมไพเลอร์หรือฟังก์ชันพิเศษที่แก้ไขคำแนะนำในเวลาคอมไพล์

Fe เป็นไปตามระบบไวยากรณ์และประเภทของ Rust รองรับนามแฝงประเภท enums ที่มีประเภทย่อย ลักษณะ และชื่อสามัญ ขณะนี้การสนับสนุนนี้มีจำกัด แต่อยู่ระหว่างดำเนินการ สามารถกำหนดและใช้งานลักษณะเฉพาะสำหรับประเภทต่างๆ ได้ แต่ไม่รองรับข้อจำกัดทั่วไปหรือลักษณะเฉพาะ Enums รองรับการพิมพ์ย่อยและเมธอดได้ แต่ไม่สามารถเข้ารหัสในฟังก์ชันภายนอกได้ แม้ว่าระบบประเภทของ Fe จะยังอยู่ในระหว่างดำเนินการ แต่ก็แสดงให้เห็นถึงศักยภาพมากมายสำหรับนักพัฒนาในการเขียนโค้ดที่ปลอดภัยและตรวจสอบเวลาในการคอมไพล์

นี่คือสัญญาง่ายๆ ที่เขียนด้วยภาษา Fe:

Huff

Huff เป็นภาษาแอสเซมบลีที่มีการควบคุมสแต็กแบบแมนนวลและชุดคำสั่ง EVM ที่เป็นนามธรรมน้อยที่สุด ผ่านคำสั่ง "#include" ไฟล์ Huff ที่รวมอยู่สามารถแยกวิเคราะห์ในเวลาคอมไพล์เพื่อให้โค้ดกลับมาใช้ใหม่ได้ เดิมเขียนโดยทีมงาน Aztec สำหรับอัลกอริธึมเส้นโค้งวงรีที่ได้รับการปรับปรุงให้เหมาะสมที่สุด คอมไพเลอร์ถูกเขียนใหม่ใน TypeScript และใน Rust

ต้องกำหนดค่าคงที่ ณ เวลาคอมไพล์ ปัจจุบันยังไม่รองรับค่าคงที่ และตัวแปรหน่วยเก็บข้อมูลถาวรไม่ได้กำหนดไว้อย่างชัดเจนในภาษา เนื่องจากตัวแปรสตอเรจที่มีชื่อเป็นนามธรรมระดับสูง การเขียนไปยังสตอเรจถาวรใน Huff จึงทำผ่าน opcodes "sstore" สำหรับการเขียน และ "sload" สำหรับการอ่าน เลย์เอาต์พื้นที่เก็บข้อมูลแบบกำหนดเองสามารถกำหนดโดยผู้ใช้ หรือเริ่มต้นจากศูนย์และเพิ่มตัวแปรแต่ละตัวโดยใช้ "FREE_STORAGE_POINTER" ในตัวของคอมไพเลอร์ การทำให้ตัวแปรที่เก็บไว้สามารถเข้าถึงได้จากภายนอกจำเป็นต้องกำหนดเส้นทางรหัสด้วยตนเองที่สามารถอ่านและส่งคืนตัวแปรไปยังผู้เรียก

ฟังก์ชันภายนอกยังเป็นนามธรรมที่นำเสนอโดยภาษาระดับสูง ดังนั้นจึงไม่มีแนวคิดเกี่ยวกับฟังก์ชันภายนอกใน Huff อย่างไรก็ตาม โครงการส่วนใหญ่ปฏิบัติตามข้อกำหนด ABI ของภาษาระดับสูงอื่น ๆ ในระดับที่แตกต่างกัน ซึ่งส่วนใหญ่มักเป็น Solidity รูปแบบทั่วไปคือการกำหนด "ตัวกำหนดตารางเวลา" ที่โหลดข้อมูลการโทรดิบและใช้เพื่อตรวจสอบการจับคู่กับตัวเลือกฟังก์ชัน หากตรงกัน รหัสที่ตามมาจะถูกดำเนินการ เนื่องจากตัวกำหนดตารางเวลาถูกกำหนดโดยผู้ใช้ พวกเขาอาจทำตามรูปแบบการจัดกำหนดการที่แตกต่างกัน Solidity จัดเรียงตัวเลือกในตัวกำหนดตารางเวลาตามตัวอักษรตามชื่อ Vyper จัดเรียงตามตัวเลขและทำการค้นหาแบบไบนารีเมื่อรันไทม์ และตัวกำหนดตารางเวลา Huff ส่วนใหญ่จะจัดเรียงตามความถี่ที่คาดไว้ของการใช้ฟังก์ชัน โดยแทบไม่ใช้ตารางกระโดด ปัจจุบัน EVM ไม่รองรับตารางกระโดด ดังนั้นจำเป็นต้องมีคำแนะนำในการทบทวน เช่น "codecopy" เพื่อนำไปใช้

ฟังก์ชันที่แท้จริงถูกกำหนดโดยใช้คำสั่ง "#define fn" ซึ่งสามารถยอมรับพารามิเตอร์เทมเพลตเพื่อความยืดหยุ่นและระบุความลึกของสแต็กที่คาดไว้เมื่อเริ่มต้นและสิ้นสุดของฟังก์ชัน เนื่องจากฟังก์ชันเหล่านี้เป็นฟังก์ชันภายใน จึงไม่สามารถเข้าถึงได้จากภายนอก และการเข้าถึงภายในต้องใช้คำสั่ง "กระโดด"

โฟลว์การควบคุมอื่นๆ เช่น คำสั่งเงื่อนไขและคำสั่งวนซ้ำสามารถกำหนดได้โดยใช้เป้าหมายกระโดด เป้าหมายกระโดดถูกกำหนดโดยตัวระบุตามด้วยเครื่องหมายทวิภาค การข้ามไปยังเป้าหมายเหล่านี้สามารถทำได้โดยการกดตัวระบุลงบนสแต็กและดำเนินการตามคำสั่งกระโดด วิธีนี้จะแก้ไขเป็นการชดเชย bytecode ณ เวลาคอมไพล์

มาโครถูกกำหนดโดย "#define macro" มิฉะนั้นจะเหมือนกับฟังก์ชันภายใน ข้อแตกต่างที่สำคัญคือมาโครไม่ได้สร้างคำสั่ง "กระโดด" ในเวลาคอมไพล์ แต่จะคัดลอกเนื้อความของมาโครโดยตรงในการเรียกแต่ละครั้งในไฟล์

การออกแบบนี้ให้น้ำหนักกับความสัมพันธ์ระหว่างการลดการกระโดดโดยพลการและต้นทุนแก๊สรันไทม์ โดยแลกกับขนาดโค้ดที่เพิ่มขึ้นเมื่อเรียกหลายๆ ครั้ง แมโคร "หลัก" ถือเป็นจุดเริ่มต้นของสัญญา และคำสั่งแรกในเนื้อหาจะเป็นคำสั่งแรกในรันไทม์ไบต์โค้ด

คุณลักษณะอื่นๆ ในตัวคอมไพเลอร์ ได้แก่ การสร้างแฮชเหตุการณ์สำหรับการบันทึก ตัวเลือกฟังก์ชันสำหรับการจัดส่ง ตัวเลือกข้อผิดพลาดสำหรับการจัดการข้อผิดพลาด ตัวตรวจสอบขนาดโค้ดสำหรับฟังก์ชันและมาโครภายใน และอื่นๆ

หมายเหตุ: ไม่จำเป็นต้องใช้ความคิดเห็นสแต็กเช่น "// [นับ]" ใช้เพื่อระบุสถานะของสแต็กเมื่อสิ้นสุดการดำเนินการของบรรทัด

นี่คือสัญญาง่ายๆ ที่เขียนด้วยภาษา Huff:

ETK

EVM Toolkit (ETK) เป็นภาษาแอสเซมบลีที่มีการจัดการสแต็กด้วยตนเองและสิ่งที่เป็นนามธรรมน้อยที่สุด สามารถใช้โค้ดซ้ำได้ผ่านคำสั่ง "%include" และ "%import" และคอมไพเลอร์เขียนด้วยภาษา Rust

ความแตกต่างอย่างหนึ่งระหว่าง Huff และ ETK คือ Huff เพิ่มสิ่งที่เป็นนามธรรมเล็กน้อยให้กับ initcode หรือที่เรียกว่ารหัสตัวสร้าง ซึ่งสามารถลบล้างได้โดยการกำหนดมาโคร "CONSTRUCTOR" แบบพิเศษ ใน ETK สิ่งเหล่านี้จะไม่ถูกแยกออกไป ต้องกำหนด initcode และ runtime code ร่วมกัน

เช่นเดียวกับ Huff ETK อ่านและเขียนไปยังที่เก็บข้อมูลถาวรผ่านคำสั่ง "sload" และ "sstore" อย่างไรก็ตาม ไม่มีคำหลักที่คงที่หรือเปลี่ยนรูปไม่ได้ แต่สามารถจำลองค่าคงที่ได้โดยใช้มาโครประเภทใดประเภทหนึ่งจากสองประเภทใน ETK นั่นคือมาโครนิพจน์ มาโครนิพจน์ไม่ได้แยกวิเคราะห์เป็นคำสั่ง แต่สร้างค่าตัวเลขที่สามารถใช้ในคำสั่งอื่นแทน ตัวอย่างเช่น อาจไม่สร้างคำสั่ง "push" ทุกประการ แต่อาจสร้างตัวเลขเพื่อรวมไว้ในคำสั่ง "push"

ดังที่ได้กล่าวไว้ก่อนหน้านี้ ฟังก์ชันต่างประเทศเป็นแนวคิดภาษาระดับสูง ดังนั้นการเปิดเผยโค้ดพาธภายนอกจำเป็นต้องสร้างโปรแกรมเลือกฟังก์ชัน

ฟังก์ชันภายในไม่ได้กำหนดไว้อย่างชัดเจนเหมือนในภาษาอื่นๆ แต่คุณสามารถระบุนามแฝงที่ผู้ใช้กำหนดสำหรับ Jump Target และข้ามไปที่พวกมันด้วยชื่อ สิ่งนี้ยังอนุญาตให้มีโฟลว์การควบคุมอื่นๆ เช่น ลูปและข้อความแสดงเงื่อนไข

ETK รองรับมาโครสองประเภท อย่างแรกคือมาโครนิพจน์ที่สามารถยอมรับอาร์กิวเมนต์จำนวนเท่าใดก็ได้ และส่งกลับค่าตัวเลขที่สามารถใช้ในคำสั่งอื่นๆ มาโครนิพจน์ไม่ได้สร้างคำสั่ง แต่สร้างค่าหรือค่าคงที่ในทันที อย่างไรก็ตาม มาโครคำสั่งจะยอมรับอาร์กิวเมนต์จำนวนเท่าใดก็ได้ และสร้างคำสั่งจำนวนเท่าใดก็ได้ในเวลาคอมไพล์ มาโครคำสั่งใน ETK คล้ายกับมาโคร Huff

นี่คือสัญญาอย่างง่ายที่เขียนด้วย ETK:

Yul

Yul เป็นภาษาแอสเซมบลีที่มีโฟลว์การควบคุมระดับสูงและสิ่งที่เป็นนามธรรมมากมาย เป็นส่วนหนึ่งของ Solidity toolchain และสามารถเลือกใช้ใน Solidity build ไปป์ไลน์ Yul ไม่สนับสนุนการใช้รหัสซ้ำ เนื่องจากมีวัตถุประสงค์เพื่อเป็นเป้าหมายในการคอมไพล์แทนที่จะเป็นภาษาแบบสแตนด์อโลน คอมไพเลอร์เขียนด้วยภาษา C++ และมีแผนจะโอนย้ายไปยัง Rust พร้อมกับไปป์ไลน์ Solidity ที่เหลือ

ใน Yul โค้ดจะแบ่งออกเป็นอ็อบเจกต์ ซึ่งสามารถมีโค้ด ข้อมูล และออบเจกต์ซ้อนกันได้ ดังนั้นจึงไม่มีค่าคงที่หรือฟังก์ชันภายนอกใน Yul จำเป็นต้องกำหนดตัวระบุตัวเลือกฟังก์ชันเพื่อแสดงเส้นทางรหัสสู่โลกภายนอก

ยกเว้นคำสั่งสแต็กและการควบคุมโฟลว์ คำสั่งส่วนใหญ่จะแสดงเป็นฟังก์ชันใน Yul คำสั่งสามารถซ้อนกันเพื่อลดขนาดรหัส และยังสามารถกำหนดให้กับตัวแปรชั่วคราวแล้วส่งผ่านไปยังคำสั่งอื่นเพื่อใช้งาน แบรนช์แบบมีเงื่อนไขสามารถใช้บล็อก "if" ซึ่งจะดำเนินการหากค่าไม่ใช่ศูนย์ แต่ไม่มีบล็อก "else" ดังนั้นการจัดการพาธโค้ดหลายรายการจึงจำเป็นต้องใช้ "สวิตช์" เพื่อจัดการกรณีและปัญหาจำนวนเท่าใดก็ได้และ " เริ่มต้น" ตัวเลือกสำรอง การวนซ้ำสามารถทำได้โดยใช้การวนซ้ำ "for" แม้ว่าไวยากรณ์จะแตกต่างจากภาษาระดับสูงอื่น ๆ แต่ก็มีฟังก์ชันพื้นฐานที่เหมือนกัน ฟังก์ชันที่แท้จริงสามารถกำหนดได้โดยใช้คีย์เวิร์ด "ฟังก์ชัน" และคล้ายกับคำจำกัดความของฟังก์ชันในภาษาระดับสูง

ฟังก์ชันส่วนใหญ่ใน Yul แสดงใน Solidity โดยใช้บล็อกแอสเซมบลีแบบอินไลน์ ซึ่งช่วยให้นักพัฒนาสามารถแยกสิ่งที่เป็นนามธรรม เขียนฟังก์ชันแบบกำหนดเอง หรือใช้ Yul ในฟังก์ชันที่ไม่มีในไวยากรณ์ระดับสูง อย่างไรก็ตาม การใช้คุณสมบัตินี้จำเป็นต้องมีความเข้าใจอย่างลึกซึ้งเกี่ยวกับพฤติกรรมของ Solidity ในแง่ของข้อมูลการโทร หน่วยความจำ และพื้นที่เก็บข้อมูล

นอกจากนี้ยังมีฟังก์ชั่นพิเศษบางอย่าง ฟังก์ชัน "datasize", "dataoffset" และ "datacopy" จัดการวัตถุ Yul ผ่านนามแฝงของสตริง ฟังก์ชัน "setimmutable" และ "loadimmutable" อนุญาตให้ตั้งค่าและโหลดพารามิเตอร์ที่ไม่เปลี่ยนรูปในตัวสร้าง แม้ว่าการใช้งานจะถูกจำกัดก็ตาม ฟังก์ชัน "memoryguard" หมายความว่ามีการจัดสรรเฉพาะช่วงหน่วยความจำที่กำหนด ทำให้คอมไพเลอร์สามารถใช้หน่วยความจำเกินช่วงที่ได้รับการป้องกันเพื่อเพิ่มประสิทธิภาพเพิ่มเติม สุดท้าย "คำต่อคำ" อนุญาตให้ใช้คำสั่งที่คอมไพเลอร์ Yul ไม่รู้

นี่คือสัญญาง่ายๆ ที่เขียนด้วยภาษา Yul:

คุณลักษณะของ EVM DSL ที่ดี

EVM DSL ที่ดีควรเรียนรู้จากจุดแข็งและจุดอ่อนของแต่ละภาษาที่ระบุไว้ที่นี่ และควรครอบคลุมพื้นฐานในภาษาสมัยใหม่เกือบทั้งหมด เช่น ข้อความแสดงเงื่อนไข การจับคู่รูปแบบ การวนซ้ำ ฟังก์ชัน และอื่นๆ โค้ดควรไม่คลุมเครือ เพิ่มสิ่งที่เป็นนามธรรมโดยนัยให้น้อยที่สุดเพื่อความสวยงามของโค้ดหรือความสามารถในการอ่าน ในสภาพแวดล้อมที่มีเดิมพันสูง ถูกต้อง-สำคัญ โค้ดทุกบรรทัดควรอธิบายได้อย่างชัดเจน นอกจากนี้ ระบบโมดูลที่กำหนดไว้อย่างดีควรเป็นหัวใจของภาษาที่ยอดเยี่ยม ควรระบุให้ชัดเจนว่ารายการใดถูกกำหนดในขอบเขตใด และรายการใดสามารถเข้าถึงได้ ทุกรายการในโมดูลควรเป็นส่วนตัวตามค่าเริ่มต้น โดยมีเฉพาะรายการสาธารณะอย่างชัดเจนที่สามารถเข้าถึงได้จากภายนอก

ในสภาพแวดล้อมที่มีทรัพยากรจำกัด เช่น EVM ประสิทธิภาพมีความสำคัญ ประสิทธิภาพมักทำได้โดยการจัดหานามธรรมที่มีต้นทุนต่ำ เช่น การประมวลผลรหัสเวลาคอมไพล์ผ่านมาโคร ระบบประเภทที่หลากหลายเพื่อสร้างไลบรารีที่นำกลับมาใช้ใหม่ได้ซึ่งออกแบบมาอย่างดี และตัวห่อหุ้มสำหรับการโต้ตอบบนเครือข่ายทั่วไป มาโครสร้างโค้ดในเวลาคอมไพล์ ซึ่งยอดเยี่ยมสำหรับการลดโค้ดสำเร็จรูปสำหรับการดำเนินการทั่วไป และในกรณีเช่น Huff สามารถใช้เพื่อแลกเปลี่ยนขนาดโค้ดกับประสิทธิภาพรันไทม์ได้ ระบบประเภทที่หลากหลายช่วยให้โค้ดแสดงอารมณ์ได้มากขึ้น การตรวจสอบเวลาคอมไพล์มากขึ้นเพื่อตรวจจับข้อผิดพลาดก่อนรันไทม์ และเมื่อรวมเข้ากับองค์ประกอบภายในของคอมไพเลอร์ที่มีการตรวจสอบประเภท อาจช่วยลดความจำเป็นในการประกอบอินไลน์จำนวนมาก Generics ยังอนุญาตให้รวมค่า Nullable (เช่น โค้ดภายนอก) ไว้ในประเภท "ตัวเลือก" หรือการดำเนินการที่เกิดข้อผิดพลาดได้ง่าย (เช่น การโทรภายนอก) ที่จะรวมไว้ในประเภท "ผลลัพธ์" ทั้งสองประเภทนี้เป็นตัวอย่างของวิธีที่ผู้เขียนห้องสมุดสามารถบังคับให้นักพัฒนาจัดการกับผลลัพธ์แต่ละรายการโดยกำหนดเส้นทางรหัสหรือธุรกรรมที่กู้คืนผลลัพธ์ที่ล้มเหลว อย่างไรก็ตาม โปรดทราบว่าสิ่งเหล่านี้เป็นนามธรรมของเวลาคอมไพล์ที่แก้ไขการกระโดดแบบมีเงื่อนไขอย่างง่ายที่รันไทม์ การบังคับให้นักพัฒนาจัดการทุกผลลัพธ์ในเวลาคอมไพล์จะเพิ่มเวลาในการพัฒนาเริ่มต้น แต่ได้ประโยชน์จากความประหลาดใจที่น้อยลงเมื่อรันไทม์

ความยืดหยุ่นยังเป็นสิ่งสำคัญสำหรับนักพัฒนา ดังนั้นในขณะที่ค่าเริ่มต้นสำหรับการดำเนินการที่ซับซ้อนควรเป็นเส้นทางที่ปลอดภัยและอาจมีประสิทธิภาพน้อยกว่า แต่ก็มีบางครั้งที่ต้องใช้เส้นทางโค้ดที่มีประสิทธิภาพมากขึ้นหรือคุณสมบัติที่ไม่รองรับ สำหรับสิ่งนี้ การประกอบแบบอินไลน์ควรเปิดสำหรับนักพัฒนาและไม่มีรั้วกั้น แอสเซมบลีแบบอินไลน์ของ Solidity ทำให้มีการป้องกันบางอย่างเพื่อความเรียบง่ายและการส่งมอบเครื่องมือเพิ่มประสิทธิภาพที่ดีขึ้น แต่นักพัฒนาควรได้รับสิทธิ์เหล่านี้เมื่อพวกเขาต้องการควบคุมสภาพแวดล้อมการดำเนินการอย่างเต็มรูปแบบ

สรุปแล้ว

สรุปแล้ว

ลิงค์ต้นฉบับ

ลิงค์ต้นฉบับ

ETH
สัญญาที่ชาญฉลาด
ความปลอดภัย
ยินดีต้อนรับเข้าร่วมชุมชนทางการของ Odaily
กลุ่มสมาชิก
https://t.me/Odaily_News
กลุ่มสนทนา
https://t.me/Odaily_CryptoPunk
บัญชีทางการ
https://twitter.com/OdailyChina
กลุ่มสนทนา
https://t.me/Odaily_CryptoPunk
สรุปโดย AI
กลับไปด้านบน
ศึกษาเทคโนโลยีล่าสุดของการออกแบบ EVM DSL วิธีการออกแบบภาษาที่ยอดเยี่ยม?
ดาวน์โหลดแอพ Odaily พลาเน็ตเดลี่
ให้คนบางกลุ่มเข้าใจ Web3.0 ก่อน
IOS
Android