เจาะลึกโครงสร้างข้อมูล EVM ใบเสร็จธุรกรรม และบันทึกเหตุการณ์

การทำความเข้าใจโครงสร้างข้อมูลที่ประกอบกันเป็นบล็อกเชนช่วยให้เราคิดวิธีที่สร้างสรรค์ในการแยกวิเคราะห์ข้อมูลนี้

**เขียนโดย:**NOXX

** รวบรวม: ล้าง **

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

บันทึกทำไม

ก่อนที่เราจะเริ่ม เรามาคุยกันสั้น ๆ เกี่ยวกับสาเหตุที่เราต้องใช้บันทึกเหตุการณ์ในฐานะผู้พัฒนาที่มั่นคง:

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

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

Ethereum บล็อก Merkle Root

ในตอนที่ 4 เราได้เจาะลึกถึงกรอบการทำงานของ Ethereum โดยเฉพาะส่วนราก Merkle ของรัฐ State Root เป็นหนึ่งในสามของ Merkle root ที่รวมอยู่ในส่วนหัวของบล็อก อีกสองรายการคือรูทธุรกรรมและรูทใบเสร็จ

สำหรับข้อมูลในการสร้างเฟรมเวิร์กนี้ เราจะอ้างอิงถึงบล็อก 15001871 บน Ethereum ซึ่งมีธุรกรรม 5 รายการพร้อมใบเสร็จรับเงินที่เกี่ยวข้องและบันทึกเหตุการณ์ที่ส่งไป

ส่วนหัวของบล็อก

เราจะเริ่มต้นด้วย 3 ส่วนในส่วนหัวของบล็อก ได้แก่ Transaction Root, Receipt Root และ Logs Bloom (สามารถดูข้อมูลเบื้องต้นเกี่ยวกับส่วนหัวของบล็อกได้ในตอนที่ 4)

แหล่งที่มา:

ในไคลเอ็นต์ Ethereum ภายใต้ Transaction Root และ Receipt Root นั้น Merkle Patricia Tries มีข้อมูลธุรกรรมและข้อมูลการรับทั้งหมดในบล็อก บทความนี้จะมุ่งเน้นไปที่ธุรกรรมและใบเสร็จรับเงินทั้งหมดที่โหนดสามารถเข้าถึงได้เท่านั้น

ข้อมูลส่วนหัวบล็อกของบล็อก 15001871 ที่พบผ่านโหนด Ethereum มีดังนี้:

logsBloom ในส่วนหัวของบล็อกเป็นโครงสร้างข้อมูลหลัก ซึ่งจะกล่าวถึงในบทความนี้ ขั้นแรกให้เริ่มต้นด้วยข้อมูลที่อยู่ภายใต้ Transaction Root, Transaction Trie

** ลำดับการทำธุรกรรมทรี **

Trie ของธุรกรรมคือชุดข้อมูลที่สร้างธุรกรรมรูทและบันทึกเวกเตอร์คำขอธุรกรรม ธุรกรรมคำขอเวกเตอร์คือชิ้นส่วนของข้อมูลที่จำเป็นในการดำเนินการธุรกรรม ฟิลด์ข้อมูลที่มีอยู่ในธุรกรรมมีดังนี้:

  • ประเภท - ประเภทธุรกรรม (ธุรกรรมดั้งเดิม LegacyTxType, การแนะนำ AccessListTxType EIP-2930, การแนะนำ DynamicFeeTxType EIP-1559)
  • ChainId - รหัสเชน EIP155 ของธุรกรรม
  • ข้อมูล - ข้อมูลการป้อนข้อมูลของการทำธุรกรรม
  • AccessList - รายการเข้าถึงสำหรับการทำธุรกรรม
  • แก๊ส - ขีด จำกัด แก๊สของการทำธุรกรรม
  • GasPrice - ราคาก๊าซของการทำธุรกรรม
  • GasTipCap - เบี้ยประกันจูงใจสำหรับนักขุดที่มีหน่วยธุรกรรมก๊าซเกินค่าธรรมเนียมฐานเพื่อบรรจุก่อน maxPriorityFeePerGas ใน Geth ถูกกำหนดโดย EIP1559
  • GasFeeCap - ขีดจำกัดสูงสุดของค่าธรรมเนียมก๊าซต่อหน่วยของการทำธุรกรรม maxFeePerGas ใน Geth (GasFeeCap ≥ baseFee + GasTipCap)
  • มูลค่า - จำนวนของ Ethereum ที่ซื้อขาย
  • ไม่มี - ไม่มีตัวตนของผู้ริเริ่มบัญชีซื้อขาย
  • ถึง - ที่อยู่ผู้รับของธุรกรรม สำหรับธุรกรรมการสร้างสัญญา หากต้องการส่งคืนค่าศูนย์
  • RawSignature - ค่าลายเซ็น V, R, S ของข้อมูลธุรกรรม

หลังจากทำความเข้าใจช่องข้อมูลด้านบนแล้ว มาดูธุรกรรมแรกของบล็อก 15001871 กัน

จากการสอบถาม ethclient ของ Geth คุณจะเห็นว่าทั้ง ChainId และ AccessList มี "omitempty" ซึ่งหมายความว่าหากฟิลด์นี้ว่างเปล่า ฟิลด์นั้นจะถูกละไว้ในการตอบกลับเพื่อลดหรือย่อขนาดของข้อมูลซีเรียลไลซ์

ที่มาของรหัส:

ธุรกรรมนี้แสดงถึงการโอนโทเค็น USDT ไปยังที่อยู่ 0xec23e787ea25230f74a3da0f515825c1d820f47a ที่อยู่ To คือที่อยู่สัญญา ERC20 USDT 0xdac17f958d2ee523a2206206994597c13d831ec7 ผ่าน INPUT DATA เราจะเห็นว่าลายเซ็นของฟังก์ชัน 0xa9059cbb สอดคล้องกับฟังก์ชัน Transfer (Address, UINT256) และ 42.251 USDT (ความแม่นยำ 6) ถึง 0x2b279b8 (45251000) ถูกโอนไปยัง 0xEC23E787EA25230F ถึง 0xEC23E787EA25230F 74 ที่อยู่ A3DA0F515825C1D820F47A

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

ใบเสร็จรับเงิน

เช่นเดียวกับใบเสร็จรับเงินที่บันทึกผลลัพธ์ของธุรกรรม วัตถุในใบเสร็จ Trie จะทำเช่นเดียวกันกับธุรกรรม Ethereum แต่ยังบันทึกรายละเอียดเพิ่มเติมบางอย่างด้วย ย้อนกลับไปที่คำถามเกี่ยวกับการรับธุรกรรมด้านบน เราจะมุ่งเน้นไปที่บันทึกที่เรียกเหตุการณ์ต่อไปนี้

สืบค้นข้อมูลออนไลน์ของ 0x311b อีกครั้งและรับใบเสร็จการทำธุรกรรม ในขณะนี้ จะได้รับฟิลด์ต่อไปนี้:

ที่มาของรหัส:

  • ประเภท - ประเภทธุรกรรม (LegacyTxType, AccessListTxType, DynamicFeeTxType)
  • PostState(root) - StateRoot โหนดรูทของ state tree ที่สร้างขึ้นหลังจากดำเนินการธุรกรรม ค่าที่สอดคล้องกันที่พบในรูปคือ 0x อาจเป็นเพราะ EIP98
  • CumulativeGasUsed - แก๊สรวมสะสมที่ใช้โดยธุรกรรมนี้และธุรกรรมก่อนหน้าทั้งหมดในบล็อกเดียวกัน
  • Bloom(logsBloom) - ตัวกรอง Bloom สำหรับบันทึกเหตุการณ์ ใช้เพื่อค้นหาและเข้าถึงบันทึกเหตุการณ์สัญญาบนบล็อกเชนได้อย่างมีประสิทธิภาพ ช่วยให้โหนดดึงข้อมูลได้อย่างรวดเร็วว่ามีเหตุการณ์บางอย่างเกิดขึ้นในบล็อกหรือไม่ โดยไม่ต้องแยกวิเคราะห์บล็อกทั้งหมด ธุรกรรมทั้งหมดในบล็อก
  • บันทึก - อาร์เรย์ของวัตถุบันทึกที่มีรายการบันทึกที่สร้างขึ้นโดยเหตุการณ์สัญญาที่ทริกเกอร์ระหว่างการดำเนินการธุรกรรม
  • TxHash - แฮชการทำธุรกรรมที่เกี่ยวข้องกับใบเสร็จรับเงิน
  • ที่อยู่สัญญา - หากการทำธุรกรรมคือการสร้างสัญญา ที่อยู่ที่มีการปรับใช้สัญญา หากธุรกรรมไม่ใช่การสร้างสัญญา แต่เช่น การโอนหรือการโต้ตอบกับสัญญาอัจฉริยะที่ปรับใช้ ช่องที่อยู่ของสัญญาจะว่างเปล่า
  • GasUsed - ก๊าซที่ใช้โดยธุรกรรมนี้
  • BlockNumber - หมายเลขบล็อกของบล็อกที่เกิดธุรกรรมนี้
  • TransactionIndex - ดัชนีธุรกรรมภายในบล็อก ดัชนีจะกำหนดว่าธุรกรรมใดจะดำเนินการก่อน ธุรกรรมนี้อยู่ที่ด้านบนสุดของบล็อก ดังนั้นดัชนีจึงเป็น 0

ตอนนี้เราทราบองค์ประกอบของการรับธุรกรรมแล้ว เรามาดูรายละเอียดเพิ่มเติมที่ logsBloom และล็อกอาร์เรย์ในบันทึกธุรกรรม

บันทึกเหตุการณ์

ผ่านรหัสสัญญา USDT บน Ethereum mainnet เราจะเห็นว่ามีการประกาศเหตุการณ์การโอนในบรรทัดที่ 86 ของสัญญา และพารามิเตอร์อินพุตสองตัวมีคำหลัก "จัดทำดัชนี"

(ที่มาของรหัส:

เมื่ออินพุตเหตุการณ์ถูก "จัดทำดัชนี" จะช่วยให้เราสามารถค้นหาบันทึกผ่านอินพุตนั้นได้อย่างรวดเร็ว ตัวอย่างเช่น เมื่อใช้ดัชนี "จาก" ด้านบน เป็นไปได้ที่จะได้รับบันทึกเหตุการณ์ประเภทการถ่ายโอนทั้งหมดที่มีที่อยู่ "จาก" 0x5041ed759dd4afc3a72b8192c143f72f4724081a ระหว่างบล็อก X และ Y เรายังเห็นได้ว่าเมื่อฟังก์ชันการถ่ายโอนถูกเรียกใช้ที่บรรทัด 138 บันทึกเหตุการณ์จะเริ่มทำงาน เป็นที่น่าสังเกตว่าสัญญาปัจจุบันใช้ solidity เวอร์ชันก่อนหน้า ดังนั้นคีย์เวิร์ด emit จึงหายไป

กลับไปที่ข้อมูลออนไลน์ที่ได้รับ:

ที่มาของรหัส:

เรามาเจาะลึกลงไปในที่อยู่ หัวข้อ และช่องข้อมูลกันสักหน่อย

หัวข้อกระทู้

หัวข้อเป็นค่าดัชนี จากรูปด้านบน เราจะเห็นว่ามีพารามิเตอร์ดัชนี 3 ตัวของหัวข้อในข้อมูลการสืบค้นบนเชน ในขณะที่เหตุการณ์ Transfer มีพารามิเตอร์ดัชนีเพียง 2 ตัว (จากและถึง) เนื่องจากหัวข้อแรกเป็นแฮชลายเซ็นของฟังก์ชันของเหตุการณ์เสมอ ลายเซ็นของฟังก์ชันเหตุการณ์ในตัวอย่างปัจจุบันคือ Transfer(address, address, uint256) โดยการแฮชด้วย keccak256 เราจะได้ผลลัพธ์

(เครื่องมือออนไลน์:

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

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

ข้อมูลข้อมูล

ส่วนข้อมูลประกอบด้วยพารามิเตอร์ที่เหลือ (ไม่ได้จัดทำดัชนี) จากบันทึกเหตุการณ์ ในตัวอย่างข้างต้น มีค่า 0x00000000000000000000000000000000000000000000000000000002b279b8 ซึ่งเป็น 45251000 ในรูปทศนิยม ซึ่งเป็นจำนวนเงินที่กล่าวถึงข้างต้นคือ $45.251 หากมีพารามิเตอร์ดังกล่าวมากกว่านี้ พารามิเตอร์เหล่านี้จะถูกผนวกเข้ากับรายการข้อมูล ตัวอย่างด้านล่างจะแสดงกรณีของพารามิเตอร์ที่ไม่ได้จัดทำดัชนีมากกว่า 1 รายการ

ตัวอย่างปัจจุบันเพิ่มช่อง "ภาษี" พิเศษให้กับเหตุการณ์การโอน สมมติว่าภาษีที่ตั้งไว้คือ 20% ค่าภาษีควรเป็น 45251000 * 20% = 9050200 ค่าเลขฐานสิบหกของมันคือ 0x8a1858 เนื่องจากประเภทของตัวเลขนี้คือ uint256 และประเภทของข้อมูลคือ 32 ไบต์ คุณต้อง ค่าเลขฐานสิบหกเต็มไปด้วย 32 ไบต์ และผลลัพธ์ของรายการข้อมูลคือ 0000 00000000000000000000008a1858.

ที่อยู่

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

ออปโค้ด ออปโค้ด

ในที่สุดก็มีรหัส LOG โดยมีตั้งแต่ LOG0 เมื่อไม่มีหัวข้อไปจนถึง LOG4 เมื่อมี 4 หัวข้อ LOG3 คือสิ่งที่เราใช้ในตัวอย่างของเรา ประกอบด้วยสิ่งต่อไปนี้:

  • ออฟเซ็ต - ออฟเซ็ตหน่วยความจำ ระบุตำแหน่งเริ่มต้นของอินพุตฟิลด์ข้อมูล
  • ความยาว - ความยาวของข้อมูลที่จะอ่านจากหน่วยความจำ
  • หัวข้อ x(0 - 4) - ค่าของหัวข้อ x

(แหล่งที่มา:

ค่าชดเชยและความยาวกำหนดตำแหน่งของข้อมูลในส่วนข้อมูลในหน่วยความจำ

หลังจากทำความเข้าใจโครงสร้างของบันทึกและวิธีการจัดทำดัชนีหัวข้อแล้ว เรามาทำความเข้าใจวิธีการค้นหารายการดัชนี

ฟิลเตอร์บลูม ฟิลเตอร์บลูม

เคล็ดลับในการจัดทำดัชนีรายการที่ค้นหาได้เร็วขึ้นคือตัวกรอง Bloom

บทความ Llimllib มีคำจำกัดความและคำอธิบายที่ดีเกี่ยวกับโครงสร้างข้อมูลนี้

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

ด้านล่างนี้เป็นตัวอย่างของเวกเตอร์บิต เซลล์สีขาวแสดงบิตด้วยค่า 0 และเซลล์สีเขียวแสดงบิตด้วยค่า 1

บิตเหล่านี้ถูกกำหนดเป็น 1 โดยการป้อนข้อมูลและแฮช ค่าแฮชที่ได้จะถูกใช้เป็นดัชนีบิตที่ควรอัปเดตบิต เวกเตอร์บิตด้านบนเป็นผลจากการใช้แฮช 2 ค่ากับค่า "ethereum" เพื่อให้ได้ดัชนี 2 บิต แฮชแทนเลขฐานสิบหก และเพื่อให้ได้ดัชนี คุณต้องนำตัวเลขนั้นมาแปลงเป็นค่าระหว่าง 0 ถึง 14 มีหลายวิธีในการทำเช่นนี้ เช่น mod 14

ทบทวน

ด้วยตัวกรอง Bloom สำหรับธุรกรรม ซึ่งก็คือบิตเวกเตอร์ จึงสามารถแฮชใน Ethereum เพื่อกำหนดว่าบิตใดในเวกเตอร์บิตที่จะอัปเดต อินพุตคือฟิลด์ที่อยู่และหัวข้อของบันทึกเหตุการณ์ มาตรวจสอบ logsBloom ในใบเสร็จธุรกรรม ซึ่งเป็นตัวกรอง Bloom เฉพาะธุรกรรม การทำธุรกรรมสามารถมีบันทึกได้หลายรายการ ซึ่งมีที่อยู่ / หัวข้อของบันทึกทั้งหมด

หากคุณมองย้อนกลับไปที่ส่วนหัวของบล็อก คุณจะพบ logsBloom อีกอันหนึ่ง นี่คือตัวกรอง Bloom สำหรับธุรกรรมทั้งหมดภายในบล็อก ซึ่งมีที่อยู่/หัวข้อทั้งหมดในแต่ละบันทึกสำหรับแต่ละธุรกรรม

ตัวกรอง Bloom เหล่านี้แสดงเป็นเลขฐานสิบหกไม่ใช่เลขฐานสอง พวกมันมีความยาว 256 ไบต์และเป็นตัวแทนของเวกเตอร์ 2048 บิต ถ้าเราอ้างถึงตัวอย่าง Llimllib ด้านบน ความยาวเวกเตอร์บิตของเราคือ 15 และดัชนีบิต 2 และ 13 จะพลิกกลับเป็น 1 มาดูกันว่าเราจะได้อะไรเมื่อเราแปลงมันเป็นเลขฐานสิบหก

แม้ว่าการแสดงเลขฐานสิบหกจะดูไม่เหมือนเวกเตอร์บิต แต่ใน logsBloom

**แบบสอบถาม **

มีการกล่าวถึงข้อความค้นหาก่อนหน้านี้ "ค้นหาบันทึกเหตุการณ์ทั้งหมดของประเภทการถ่ายโอนซึ่งมีที่อยู่ "จาก" คือ 0x5041ed759dd4afc3a72b8192c143f72f4724081a ระหว่างบล็อก X และ Y" เราสามารถรับหัวข้อลายเซ็นเหตุการณ์ซึ่งแสดงถึงหัวข้อประเภท Transfer และจากค่า (0x5041…) และกำหนดว่าดัชนีบิตใดในตัวกรอง Bloom ควรตั้งค่าเป็น 1

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

ดำเนินการข้างต้นในบล็อก X ถึง Y เพื่อค้นหาและดึงบันทึกทั้งหมดที่ตรงตามเกณฑ์อย่างรวดเร็ว นี่คือแนวคิดการทำงานของตัวกรอง Bloom

ทีนี้มาดูการใช้งานที่ใช้ใน Ethereum กัน

ใช้งาน Geth - ตัวกรอง Bloom

ตอนนี้เรารู้แล้วว่าตัวกรอง Bloom ทำงานอย่างไร เรามาเรียนรู้วิธีที่ตัวกรอง Bloom ทำการตรวจคัดกรองทีละขั้นตอนตั้งแต่ที่อยู่ / หัวข้อไปจนถึง logsBloom ในบล็อกจริง

ก่อนอื่น จากคำจำกัดความของ Ethereum Yellow Paper:

แหล่งที่มา:

"เรากำหนดฟังก์ชันตัวกรอง Bloom M ที่ลดรายการบันทึกลงในแฮช 256 ไบต์เดียว:

ใน เป็นตัวกรอง Bloom แบบพิเศษที่ตั้งค่าสามบิตในปี 2048 โดยกำหนดลำดับไบต์ตามอำเภอใจ สิ่งนี้ทำได้โดยการรับ 11 บิตที่ต่ำกว่าของแต่ละไบต์สามคู่แรกในแฮช Keccak-256 ของลำดับไบต์ "

ตัวอย่างและการอ้างอิงถึงการนำไคลเอนต์ Geth ไปใช้ด้านล่างเพื่อลดความซับซ้อนของความเข้าใจในคำจำกัดความข้างต้น

นี่คือบันทึกการทำธุรกรรมที่เราดูบน Etherscan

หัวข้อแรกคือลายเซ็นเหตุการณ์ 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef และแปลงค่านี้เป็นดัชนีบิตที่ควรอัปเดต

ด้านล่างนี้คือฟังก์ชัน BloomValues จากฐานรหัส Geth

ฟังก์ชันนี้รับหัวข้อลายเซ็นเหตุการณ์ เช่น: 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef และข้อมูลอื่นๆ และส่งคืนดัชนีบิตที่จำเป็นต้องอัปเดตในตัวกรอง Bloom

ที่มาของรหัส:

  1. ฟังก์ชัน BloomValues รับเป็นอินพุตหัวข้อ (ลายเซ็นเหตุการณ์ในตัวอย่าง) และ hashbuf (อาร์เรย์ไบต์ว่างที่มีความยาว 6)
  1. อ้างถึงตัวอย่างข้อมูลของ Yellow Paper "ไบต์สามคู่แรกในแฮช Keccak-256 ของลำดับไบต์" ไบต์ทั้งสามคู่นี้มีขนาด 6 ไบต์ ซึ่งเป็นความยาวของแฮชบัฟ

  2. ข้อมูลตัวอย่าง: 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef

  1. คำสั่ง sha ระหว่างบรรทัด 140 - 144 hashes ข้อมูลอินพุตและโหลดเอาต์พุตลงใน hashbuf
  1. ผลลัพธ์เลขฐานสิบหกของเอาต์พุต sha โดยใช้ keccak256 คือ (เมื่อใช้ keccak 256 เป็นลายเซ็นของฟังก์ชัน อินพุตจะเป็นประเภทข้อความ แต่นี่คือประเภทเลขฐานสิบหก): ada389e1fc24a8587c776340efb91b36e675792ab631816100d55df0b5cf3cbc

  1. เนื้อหาปัจจุบันของ hasbuf [ad, a3, 89, e1, fc, 24] (เลขฐานสิบหก) อักขระเลขฐานสิบหกแต่ละตัวแทน 4 บิต

3 คำนวณ v1.

1)แฮชบัฟ [1] = 0xa3 = 10100011 สำหรับระดับบิตและด้วย 0x7 0x7 = 00000111.

  1. ไบต์ประกอบด้วย 8 บิต หากคุณต้องการรับดัชนีบิตคุณต้องแน่ใจว่าค่าที่ได้รับอยู่ระหว่าง 0 ถึง 7 ของอาร์เรย์ดัชนีศูนย์ ใช้บิตและเพื่อ hashbuf [1] จำกัด ค่าระหว่าง 0 และ 7 คำนวณในตัวอย่าง 10100011 & 00000111 = 00000011 = 3

  2. ค่าดัชนีบิตนี้ใช้กับตัวดำเนินการเปลี่ยนบิต เช่น เลื่อนไปทางซ้าย 3 บิต ทำให้เกิดดัชนีไบต์ 8 บิต 00001000 เพื่อสร้างบิตพลิกกลับ

  3. v1 เป็นไบต์ทั้งหมดแทนที่จะเป็นดัชนีบิตจริง เนื่องจากค่านี้จะเป็น ORed ระดับบิตในตัวกรอง Bloom ในภายหลัง การดำเนินการ OR จะทำให้แน่ใจว่ามีการพลิกบิตที่เกี่ยวข้องทั้งหมดในตัวกรอง Bloom ด้วย

  1. ตอนนี้เรามีค่าไบต์แล้ว แต่เรายังต้องการดัชนีไบต์ ตัวกรอง Bloom มีความยาว 256 ไบต์ (2048 บิต) ดังนั้นเราจึงจำเป็นต้องรู้ว่าจะใช้ไบต์ใดในบิตหรือบิต ค่า i1 แสดงถึงดัชนีไบต์นี้
  1. ใส่ hashbuf ตามลำดับไบต์ big-endian uint16 ซึ่งทำให้จำกัด 2 ไบต์แรกของอาร์เรย์บิต ซึ่งก็คือ 0xada3 = 1010110110100011 ในตัวอย่าง

  2. Bitwise และค่านี้ด้วย 0x7ff = 0000011111111111 มี 11 บิตโดยตั้งค่า 0x7ff เป็น 1 ตามที่กล่าวไว้ในกระดาษสีเหลือง "ทำสิ่งนี้โดยนำ 11 บิตล่างของแต่ละคู่จากสามคู่แรก" ซึ่งจะส่งผลให้มีค่า 0000010110100011 ซึ่งก็คือ 1010110110100011 & 0000011111111111

  3. จากนั้นเลื่อนค่าไปทางขวา 3 บิต สิ่งนี้จะแปลงตัวเลข 11 หลักเป็นตัวเลข 8 หลัก เราต้องการดัชนีไบต์ และความยาวของไบต์ของตัวกรอง Bloom คือ 256 ดังนั้นค่าดัชนีไบต์ต้องอยู่ในช่วงนี้ และตัวเลข 8 บิตสามารถเป็นค่าใดก็ได้ระหว่าง 0 ถึง 255 ในตัวอย่างของเรา ค่านี้คือ 0000010110100011 เลื่อนไปทางขวา 3 บิต 10110100 = 180

  4. คำนวณดัชนีไบต์ของเราโดย BloomByteLength โดยรู้ว่ามันคือ 256 ลบด้วย 180 ที่คำนวณได้ ลบ 1 ลบ 1 เพื่อให้ผลลัพธ์อยู่ระหว่าง 0 ถึง 255 สิ่งนี้ให้ดัชนีไบต์แก่เราเพื่ออัปเดต ซึ่งในกรณีนี้กลายเป็นไบต์ 75 ซึ่งเป็นวิธีที่เราคำนวณ i1

  1. อัปเดตดัชนีบิต 3 ในไบต์ที่ 75 ของตัวกรอง Bloom (0 คือดัชนีของบิตที่ 4) ซึ่งทำได้โดยดำเนินการตามบิตหรือการดำเนินการ v1 บนไบต์ที่ 75 ในตัวกรอง Bloom
  1. เราครอบคลุมเฉพาะคู่ไบต์แรก 0xada3 ซึ่งทำอีกครั้งสำหรับคู่ไบต์ 2 และ 3 แต่ละที่อยู่ / หัวข้อจะอัปเดต 3 บิตในเวกเตอร์ 2048 บิต ตามที่กล่าวไว้ใน Yellow Paper "ตัวกรอง Bloom ตั้งค่าสามบิตในปี 2048 โดยกำหนดลำดับไบต์โดยพลการ"

  2. Byte pair 2 status update bit index 1 in byte 195 (ดำเนินการตามขั้นตอนที่ 3 และ 4 ผลลัพธ์จะแสดงในรูป)

  3. ไบต์คู่ 3 อัพเดตสถานะบิตดัชนี 4 ในไบต์ 123

  4. หากบิตที่จะอัปเดตถูกพลิกโดยหัวข้ออื่นแล้ว บิตนั้นจะยังคงเหมือนเดิม จะพลิกเป็น 1 ถ้าไม่

จากขั้นตอนการดำเนินการข้างต้น สามารถระบุได้ว่าหัวข้อลายเซ็นเหตุการณ์จะพลิกบิตต่อไปนี้ในตัวกรอง Bloom:

  • ดัชนีบิต 3 ในไบต์ 75
  • ดัชนีบิต 1 ในไบต์ 195
  • ดัชนีบิต 4 ในไบต์ 123

ดูที่ logBlooms ในใบเสร็จธุรกรรมที่แปลงเป็นไบนารี ตรวจสอบว่าตั้งค่าดัชนีบิตเหล่านี้แล้ว

ในขณะเดียวกัน สำหรับผู้อ่านที่สนใจเรียนรู้เพิ่มเติมเกี่ยวกับการใช้งานการค้นหาบันทึกและตัวกรอง Bloom คุณสามารถดูบทความ BloomBits Trie ได้

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

ดูต้นฉบับ
เนื้อหานี้มีสำหรับการอ้างอิงเท่านั้น ไม่ใช่การชักชวนหรือข้อเสนอ ไม่มีคำแนะนำด้านการลงทุน ภาษี หรือกฎหมาย ดูข้อจำกัดความรับผิดชอบสำหรับการเปิดเผยความเสี่ยงเพิ่มเติม
  • รางวัล
  • แสดงความคิดเห็น
  • แชร์
แสดงความคิดเห็น
0/400
ไม่มีความคิดเห็น
  • ปักหมุด