หมายเหตุเกี่ยวกับโครงการริเริ่ม Stateless Ethereum:
กิจกรรมการวิจัยชะลอตัวลง (อย่างเข้าใจได้) ในช่วงครึ่งหลังของปี 2020 เนื่องจากผู้มีส่วนร่วมทุกคนได้ปรับตัวให้เข้ากับชีวิตบนไทม์ไลน์ที่แปลกประหลาด แต่เมื่อระบบนิเวศเคลื่อนตัวเข้าใกล้ Serenity มากขึ้นเรื่อยๆ และ Eth1/Eth2 รวมกัน งานของ Ethereum ไร้สัญชาติก็จะมีความเกี่ยวข้องและมีผลกระทบมากขึ้น คาดว่าจะมีรายงานย้อนหลัง Stateless Ethereum สิ้นปีที่มีนัยสำคัญมากขึ้นในอีกไม่กี่สัปดาห์ข้างหน้า
มาดู Re-cap กันอีกครั้ง: เป้าหมายสูงสุดของ Stateless Ethereum คือการลบออก ความต้องการ ของโหนด Ethereum เพื่อเก็บสำเนาเต็มรูปแบบของสถานะที่อัปเดตไว้ตลอดเวลา และอนุญาตให้มีการเปลี่ยนแปลงสถานะโดยอาศัยข้อมูล (เล็กกว่ามาก) ที่พิสูจน์ว่าธุรกรรมใดธุรกรรมหนึ่งกำลังทำการเปลี่ยนแปลงที่ถูกต้อง การทำเช่นนี้ช่วยแก้ปัญหาสำคัญสำหรับ Ethereum; ปัญหาที่ได้รับการผลักดันออกไปโดยซอฟต์แวร์ไคลเอนต์ที่ได้รับการปรับปรุงเท่านั้น: การเติบโตของรัฐ–
หลักฐาน Merkle ที่จำเป็นสำหรับ Stateless Ethereum เรียกว่า ‘พยาน’ และเป็นการพิสูจน์ถึงการเปลี่ยนแปลงสถานะโดยการจัดหาทั้งหมด ไม่เปลี่ยนแปลง แฮชระดับกลางที่จำเป็นเพื่อให้ได้รูตสถานะที่ถูกต้องใหม่ ตามทฤษฎีแล้ว พยานมีขนาดเล็กกว่าสถานะ Ethereum เต็มรูปแบบมาก (ซึ่งใช้เวลาในการซิงค์อย่างดีที่สุด 6 ชั่วโมง) แต่พวกเขาก็ยังคงอยู่ ใหญ่กว่ามาก กว่าบล็อก (ซึ่งจำเป็นต้องเผยแพร่ไปยังเครือข่ายทั้งหมดภายในเวลาเพียงไม่กี่วินาที) การโน้มตัวขนาดของพยานจึงเป็นสิ่งสำคัญยิ่งในการทำให้ Ethereum ไร้สัญชาติไปสู่ยูทิลิตี้ขั้นต่ำสุด
เช่นเดียวกับสถานะ Ethereum พยานที่มีน้ำหนักเพิ่ม (ดิจิทัล) จำนวนมากมาจากรหัสสัญญาอัจฉริยะ หากธุรกรรมทำการเรียกสัญญาใดสัญญาหนึ่ง พยานจะต้องรวมรหัสไบต์ของสัญญาโดยค่าเริ่มต้น อย่างครบถ้วน พร้อมด้วยพยาน Code Merkelization เป็นเทคนิคทั่วไปในการลดภาระของรหัสสัญญาอัจฉริยะในตัวพยาน ดังนั้นการเรียกสัญญาจำเป็นต้องรวมบิตของรหัสที่พวกเขา ‘สัมผัส’ เพื่อพิสูจน์ความถูกต้องเท่านั้น ด้วยเทคนิคนี้เพียงอย่างเดียว เราอาจเห็นว่าพยานลดลงอย่างมาก แต่มีรายละเอียดมากมายที่ต้องพิจารณาเมื่อแบ่งรหัสสัญญาอัจฉริยะออกเป็นชิ้นขนาดไบต์
Bytecode คืออะไร?
มีข้อควรพิจารณาบางประการเมื่อแยกรหัสไบต์สัญญา คำถามที่เราจะต้องถามในที่สุดคือ “ชิ้นส่วนโค้ดจะใหญ่แค่ไหน” – แต่สำหรับตอนนี้ เรามาดูโค้ดไบต์จริงบางส่วนในสัญญาอัจฉริยะง่ายๆ กันดีกว่า เพื่อทำความเข้าใจว่ามันคืออะไร:
pragma solidity >=0.4.22 <0.7.0; contract Storage { uint256 quantity; operate retailer(uint256 num) public { quantity = num; } operate retrieve() public view returns (uint256){ return quantity; } }
เมื่อรวบรวมสัญญาการจัดเก็บข้อมูลแบบง่ายนี้ จะกลายเป็นรหัสเครื่องที่เรียกใช้ ‘ภายใน’ EVM ที่นี่ คุณสามารถดูสัญญาพื้นที่จัดเก็บข้อมูลแบบเดียวกับที่แสดงด้านบน แต่ปฏิบัติตามคำสั่ง EVM แต่ละรายการ (opcodes):
PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH1 0xF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x4 CALLDATASIZE LT PUSH1 0x32 JUMPI PUSH1 0x0 CALLDATALOAD PUSH1 0xE0 SHR DUP1 PUSH4 0x2E64CEC1 EQ PUSH1 0x37 JUMPI DUP1 PUSH4 0x6057361D EQ PUSH1 0x53 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x3D PUSH1 0x7E JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 DUP3 DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST PUSH1 0x7C PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH1 0x67 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 ADD SWAP1 DUP1 DUP1 CALLDATALOAD SWAP1 PUSH1 0x20 ADD SWAP1 SWAP3 SWAP2 SWAP1 POP POP POP PUSH1 0x87 JUMP JUMPDEST STOP JUMPDEST PUSH1 0x0 DUP1 SLOAD SWAP1 POP SWAP1 JUMP JUMPDEST DUP1 PUSH1 0x0 DUP2 SWAP1 SSTORE POP POP JUMP INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 DUP13 PUSH7 0x1368BFFE1FF61A 0x29 0x4C CALLER 0x1F 0x5C DUP8 PUSH18 0xA3F10C9539C716CF2DF6E04FC192E3906473 PUSH16 0x6C634300060600330000000000000000
ตามที่ได้อธิบายไว้ใน โพสต์ก่อนหน้าคำสั่ง opcode เหล่านี้เป็นการดำเนินการพื้นฐานของสถาปัตยกรรมสแต็กของ EVM พวกเขากำหนดสัญญาการจัดเก็บแบบง่ายและฟังก์ชันทั้งหมดที่มี คุณจะพบสัญญานี้เป็นหนึ่งในตัวอย่างสัญญาความมั่นคงใน รีมิกซ์ IDE (โปรดทราบว่ารหัสเครื่องด้านบนเป็นตัวอย่างของ storage.sol หลังจากที่ได้ใช้งานไปแล้วและไม่ใช่เอาต์พุตของคอมไพเลอร์ Solidity ซึ่งจะมี opcodes ‘bootstrapping’ พิเศษบางอย่าง) หากคุณละสายตาจากสายตาและจินตนาการถึงเครื่องสแต็กทางกายภาพที่สับไปพร้อมกับการคำนวณทีละขั้นตอนบนการ์ด opcode ท่ามกลางภาพเบลอของสแต็กที่เคลื่อนไหว คุณแทบจะมองเห็นโครงร่างของฟังก์ชันที่วางอยู่ในสัญญา Solidity
เมื่อใดก็ตามที่สัญญาได้รับการเรียกข้อความ รหัสนี้จะทำงานภายในโหนด Ethereum ทุกอันที่ตรวจสอบความถูกต้องของบล็อกใหม่บนเครือข่าย ในการส่งธุรกรรมที่ถูกต้องบน Ethereum วันนี้ เราจำเป็นต้องมีสำเนาไบต์โค้ดของสัญญาฉบับเต็ม เนื่องจากการเรียกใช้โค้ดนั้นตั้งแต่ต้นจนจบเป็นวิธีเดียวที่จะได้รับสถานะเอาต์พุต (ตามที่กำหนด) และแฮชที่เกี่ยวข้อง
อย่าลืมว่า Ethereum ไร้สัญชาติมีเป้าหมายที่จะเปลี่ยนแปลงข้อกำหนดนี้ สมมติว่าสิ่งที่คุณต้องการทำคือเรียกใช้ฟังก์ชัน ดึงข้อมูล() และไม่มีอะไรเพิ่มเติม ตรรกะที่อธิบายว่าฟังก์ชันนั้นเป็นเพียงส่วนย่อยของสัญญาทั้งหมด และในกรณีนี้ EVM ต้องการเพียงสองรายการเท่านั้น บล็อกพื้นฐาน ของคำสั่ง opcode เพื่อส่งคืนค่าที่ต้องการ:
PUSH1 0x0 DUP1 SLOAD SWAP1 POP SWAP1 JUMP, JUMPDEST PUSH1 0x40 MLOAD DUP1 DUP3 DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN
ในกระบวนทัศน์ไร้สัญชาติ เช่นเดียวกับที่พยานระบุแฮชที่หายไปของสถานะที่ไม่ถูกแตะต้อง พยานก็ควรจัดเตรียมแฮชที่หายไปสำหรับชิ้นส่วนของรหัสเครื่องที่ยังไม่ได้ดำเนินการ เพื่อให้ไคลเอนต์ไร้สัญชาติต้องการเพียงส่วนหนึ่งของสัญญาที่ดำเนินการอยู่ .
พยานของเดอะโค้ด
สัญญาอัจฉริยะใน Ethereum อยู่ในที่เดียวกับบัญชีที่เป็นเจ้าของภายนอก: เช่นเดียวกับโหนดปลายสุดในสถานะที่มีรูทเดี่ยวขนาดมหึมา สัญญามีหลายวิธีที่ไม่แตกต่างจากบัญชีที่มนุษย์เป็นเจ้าของจากภายนอก พวกเขามีที่อยู่ สามารถส่งธุรกรรม และถือยอดคงเหลือของ Ether และโทเค็นอื่น ๆ แต่บัญชีสัญญามีความพิเศษเนื่องจากต้องมีตรรกะโปรแกรม (โค้ด) ของตัวเองหรือมีแฮช แมร์เคิล-แพทริเซีย ทรี อีกกลุ่มหนึ่งที่เกี่ยวข้องกัน เรียกว่า ที่เก็บข้อมูลTrie เก็บตัวแปรหรือสถานะคงอยู่ซึ่งสัญญาที่ใช้งานอยู่ใช้เพื่อดำเนินธุรกิจในระหว่างการดำเนินการ
การสร้างภาพพยานนี้ให้ความรู้สึกที่ดีว่าการรวมโค้ดเข้าด้วยกันมีความสำคัญเพียงใดในการลดขนาดของพยาน เห็นก้อนสี่เหลี่ยมสีขนาดยักษ์นั้นไหม และมันใหญ่กว่าองค์ประกอบอื่นๆ ทั้งหมดในไตรลักษณ์มากแค่ไหน? นั่นคือการให้บริการไบต์โค้ดสัญญาอัจฉริยะเต็มรูปแบบเพียงครั้งเดียว
ถัดจากนั้นและด้านล่างเล็กน้อยคือชิ้นส่วนของสถานะถาวรใน ที่เก็บข้อมูลTrieเช่น การแมปยอดคงเหลือ ERC20 หรือรายการความเป็นเจ้าของรายการดิจิทัล ERC721 เนื่องจากนี่คือตัวอย่างของพยาน ไม่ใช่สแน็ปช็อตสถานะทั้งหมด สิ่งเหล่านี้ก็ทำจากแฮชระดับกลางเป็นส่วนใหญ่ และรวมเฉพาะการเปลี่ยนแปลงที่ไคลเอนต์ไร้สัญชาติต้องการเพื่อพิสูจน์บล็อกถัดไป
การรวมโค้ดมีจุดมุ่งหมายเพื่อแยกโค้ดอันใหญ่โตนั้นออกและแทนที่ฟิลด์ รหัสHash ในบัญชี Ethereum ที่มีรากของ Merkle Trie อื่นซึ่งมีชื่อว่า aptly รหัสTrie–
คุ้มค่ากับน้ำหนักในหน่วยแฮช
ลองดูตัวอย่างจาก วิดีโอ Ethereum Engineering Group นี้ซึ่งวิเคราะห์วิธีการบางอย่างของการรวมโค้ดโดยใช้ โทเค็น ERC20 สัญญา. เนื่องจากโทเค็นจำนวนมากที่คุณเคยได้ยินถูกสร้างขึ้นตามมาตรฐาน ERC-20 นี่เป็นบริบทในโลกแห่งความเป็นจริงที่ดีในการทำความเข้าใจการรวมโค้ด
เนื่องจาก bytecode นั้นยาวและไม่เกะกะ ลองใช้ชวเลขง่ายๆ ในการแทนที่โค้ดสี่ไบต์ (อักขระฐานสิบหก 8 ตัว) ด้วย – หรือ เอ็กซ์ อักขระ โดยตัวหลังแทนรหัสไบต์ที่จำเป็นสำหรับการดำเนินการของฟังก์ชันเฉพาะ (ในตัวอย่าง ERC20.โอน() มีการใช้ฟังก์ชันตลอด)
ในตัวอย่าง ERC20 การเรียก โอนย้าย() ฟังก์ชั่นใช้น้อยกว่าครึ่งหนึ่งของสัญญาอัจฉริยะทั้งหมด:
XXX.XXXXXXXXXXXXXXXXXX.......................................... .....................XXXXXX..................................... ............XXXXXXXXXXXX........................................ ........................XXX.................................XX.. ......................................................XXXXXXXXXX XXXXXXXXXXXXXXXXXX...............XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.................................. .......................................................XXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXX..................................X XXXXXXXX........................................................ ....
หากเราต้องการแยกโค้ดนั้นออกเป็นชิ้น ๆ ขนาด 64 ไบต์ มีเพียง 19 ชิ้นจาก 41 ชิ้นเท่านั้นที่จะต้องดำเนินการแบบไร้สัญชาติ โอนย้าย() การทำธุรกรรมโดยข้อมูลที่จำเป็นส่วนที่เหลือมาจากพยาน
|XXX.XXXXXXXXXXXX|XXXXXX..........|................|................ |................|.....XXXXXX.....|................|................ |............XXXX|XXXXXXXX........|................|................ |................|........XXX.....|................|............XX.. |................|................|................|......XXXXXXXXXX |XXXXXXXXXXXXXXXX|XX..............|.XXXXXXXXXXXXXXX|XXXXXXXXXXXXXXXX |XXXXXXXXXXXXXXXX|XXXXXXXXXXXXXX..|................|................ |................|................|................|.......XXXXXXXXX |XXXXXXXXXXXXXXXX|XXXXXXXXXXXXX...|................|...............X |XXXXXXXX........|................|................|................ |....
เปรียบเทียบกับ 31 ชิ้นจาก 81 ชิ้นในรูปแบบชิ้น 32 ไบต์:
|XXX.XXXX|XXXXXXXX|XXXXXX..|........|........|........|........|........ |........|........|.....XXX|XXX.....|........|........|........|........ |........|....XXXX|XXXXXXXX|........|........|........|........|........ |........|........|........|XXX.....|........|........|........|....XX.. |........|........|........|........|........|........|......XX|XXXXXXXX |XXXXXXXX|XXXXXXXX|XX......|........|.XXXXXXX|XXXXXXXX|XXXXXXXX|XXXXXXXX |XXXXXXXX|XXXXXXXX|XXXXXXXX|XXXXXX..|........|........|........|........ |........|........|........|........|........|........|.......X|XXXXXXXX |XXXXXXXX|XXXXXXXX|XXXXXXXX|XXXXX...|........|........|........|.......X |XXXXXXXX|........|........|........|........|........|........|........ |....
เมื่อดูเผินๆ ดูเหมือนว่าชิ้นเล็กๆ จะมีประสิทธิภาพมากกว่าชิ้นใหญ่ เพราะว่า ส่วนใหญ่ว่างเปล่า ชิ้นไม่บ่อยนัก แต่ที่นี่ เราต้องจำไว้ว่าโค้ดที่ไม่ได้ใช้ก็มีค่าใช้จ่ายเช่นกัน แต่ละส่วนของโค้ดที่ยังไม่ได้ดำเนินการจะถูกแทนที่ด้วยแฮชของ ขนาดคงที่– ชิ้นโค้ดที่เล็กลงหมายถึงจำนวนแฮชที่มากขึ้นสำหรับโค้ดที่ไม่ได้ใช้ และแฮชเหล่านั้นอาจมีขนาดใหญ่ได้ถึง 32 ไบต์ต่อชิ้น (หรือเล็กถึง 8 ไบต์) ณ จุดนี้คุณอาจอุทานว่า “Hol’ up! ถ้าแฮชของโค้ดชิ้นมีขนาดมาตรฐาน 32 ไบต์ จะช่วยแทนที่โค้ด 32 ไบต์ด้วยแฮช 32 ไบต์ได้อย่างไร?”
จำได้ว่ารหัสสัญญาคือ เมอร์เคิลไลซ์ซึ่งหมายความว่าแฮชทั้งหมดจะเชื่อมโยงเข้าด้วยกันใน รหัสTrie — แฮชรูตที่เราต้องตรวจสอบบล็อก ในโครงสร้างนั้นแต่อย่างใด ตามลำดับ ชิ้นที่ยังไม่ได้ดำเนินการต้องการเพียงแฮชเดียวเท่านั้น ไม่ว่าจะมีกี่ชิ้นก็ตาม กล่าวคือ แฮชหนึ่งรายการสามารถยืนแทนกิ่งแฮชขนาดใหญ่ที่อาจเต็มไปด้วยแฮชอันต่อเนื่องกันบนโค้ดที่ Merkleized ลอง ตราบใดที่ไม่มีสิ่งใดที่จำเป็นสำหรับการดำเนินการโค้ด
เราต้องรวบรวมข้อมูลเพิ่มเติม
ข้อสรุปที่เราสร้างขึ้นมานั้นค่อนข้างเป็นการต่อต้านจุดไคลแม็กซ์ กล่าวคือ ไม่มีรูปแบบที่ ‘ดีที่สุด’ ในทางทฤษฎีสำหรับการรวมโค้ด ตัวเลือกการออกแบบ เช่น การแก้ไขขนาดของชิ้นส่วนโค้ดและแฮช ขึ้นอยู่กับข้อมูลที่รวบรวมเกี่ยวกับ ‘โลกแห่งความเป็นจริง’– สัญญาอัจฉริยะทุกฉบับจะผสานต่างกัน ดังนั้นนักวิจัยจึงต้องเลือกรูปแบบที่ให้ประสิทธิภาพสูงสุดแก่กิจกรรมเมนเน็ตที่สังเกตได้ นั่นหมายความว่าอย่างไรกันแน่?
สิ่งหนึ่งที่สามารถระบุได้ว่าโครงการ Merkleization ของโค้ดมีประสิทธิภาพเพียงใด ค่าใช้จ่าย Merkleizationซึ่งตอบคำถาม “มีข้อมูลพิเศษที่นอกเหนือจากโค้ดที่ดำเนินการมากน้อยเพียงใดที่รวมอยู่ในพยานนี้”
เรามีแล้ว ผลลัพธ์ที่น่าหวังบางอย่างรวบรวมโดยใช้ เครื่องมือที่สร้างขึ้นโดยเฉพาะ พัฒนาโดย Horacio Mijail จากทีมวิจัย TeamX ของ Consensys ซึ่งแสดงค่าโสหุ้ยเพียงเล็กน้อยเพียง 25% — ไม่เลวเลย!
กล่าวโดยย่อ ข้อมูลแสดงให้เห็นว่าขนาดชิ้นเล็ก ๆ ทีละขนาดใหญ่มีประสิทธิภาพมากกว่าชิ้นใหญ่ โดยเฉพาะอย่างยิ่งหากใช้แฮชที่เล็กกว่า (8 ไบต์) แต่ตัวเลขเริ่มต้นเหล่านี้ไม่ได้ครอบคลุมทั้งหมด เนื่องจากเป็นเพียงประมาณ 100 บล็อกล่าสุดเท่านั้น หากคุณกำลังอ่านข้อความนี้และสนใจที่จะสนับสนุนโครงการริเริ่ม Stateless Ethereum โดยรวบรวมข้อมูลการรวมโค้ดจำนวนมากขึ้น โปรดแนะนำตัวเองในฟอรัม ethresear.ch หรือช่องทาง #code-merkleization บนข้อขัดแย้งด้านการวิจัย Eth1x/2!
และเช่นเคย หากคุณมีคำถาม ข้อเสนอแนะ หรือคำขอที่เกี่ยวข้องกับ “ไฟล์ 1.X” และ Ethereum ไร้สัญชาติ โปรด DM หรือ @gichiba บน Twitter