ข้ามไปยังเนื้อหา

การถอดรหัส Calldata ในกระเป๋าเงิน

คู่มือที่เริ่มจากการตรวจสอบ calldata ของธุรกรรม คำและออฟเซ็ต ABI การชนกันของ selector พร็อกซี แบตช์ การอนุมัติ permit แบบ typed data การจำลอง และการกระทบยอดหลังธุรกรรม

อัปเดต

จัดทำขึ้นเพื่อการศึกษาเท่านั้น ไม่ใช่คำแนะนำการลงทุน การลงทุนอาจทำให้สูญเสียเงินลงทุนได้

คำตอบโดยตรง

Calldata คือชุดไบต์ที่เปลี่ยนแปลงไม่ได้ซึ่งส่งเป็นอินพุตให้ธุรกรรม Ethereum ระดับบนสุดหรือการเรียกข้อความภายใน การเรียกฟังก์ชัน Solidity ตามแบบแผนเริ่มด้วย selector ขนาด 4-byte ตามด้วยอาร์กิวเมนต์ที่เข้ารหัสด้วย ABI แต่ calldata ไม่ได้อธิบายตัวเอง ไบต์เดียวกันอาจมีความหมายต่างกันเมื่อใช้กับ runtime code การใช้งานพร็อกซี หรือสคีมาคนละชุด ฟังก์ชัน fallback, raw assembly และโปรโตคอลที่ไม่ได้เขียนด้วย Solidity ก็ไม่จำเป็นต้องทำตาม ABI ของฟังก์ชันตามแบบแผนเลย

ดังนั้นกระเป๋าเงินควรแสดงมากกว่าชื่อฟังก์ชันที่คาดเดา การตรวจสอบที่ปลอดภัยต้องผูกไบต์กับ chainId บล็อก from to value ดั้งเดิม codeHash ของ runtime การใช้งานที่ทำงานอยู่ และ ABI ที่เชื่อถือได้ ถอดรหัสทุกการเรียกซ้อนอย่างเคร่งครัด แยก calldata บนเชนออกจากลายเซ็น EIP-712 จำลองด้วยสถานะที่ระบุชัด และกระทบยอด receipt กับการเปลี่ยนแปลงสถานะจริงหลังธุรกรรมถูกรวมเข้าบล็อก

การถอดรหัส Calldata
0 / 5
0 ตรวจสอบรายการแล้ว; 5 รายการยังไม่ได้รับการแก้ไข

การเสร็จสิ้นการตรวจสอบนี้ไม่ได้พิสูจน์ว่าสินทรัพย์ ธุรกรรม หรือระบบมีความปลอดภัย

กลไกการทำงาน

  1. ตรึงซองข้อมูลที่จะลงนามและจุดสังเกต ได้แก่ chainId หมายเลขและแฮชบล็อก from to value ดั้งเดิม ไบต์อินพุต nonce และฟิลด์ค่าธรรมเนียม เก็บแหล่งข้อมูลจากกระเป๋าเงินหรือ RPC ไว้ เพราะ payload ที่ถอดจากเชนหรือบล็อกอื่นไม่ใช่ข้ออ้างเดียวกัน
  2. จำแนกวัตถุก่อนถอดรหัส ธุรกรรม คำขอ typed data แบบ EIP-712, permit แบบ ERC-2612, UserOperation แบบ ERC-4337 และข้อความ personal-sign ดิบ ใช้โดเมนและสคีมาต่างกัน อย่าบังคับให้ทั้งหมดผ่าน ABI ของธุรกรรม
  3. ระบุเป้าหมาย ณ บล็อกที่ตรึงไว้ อ่าน runtime bytecode และ codeHash ระบุพร็อกซี beacon หรือ implementation เมื่อเกี่ยวข้อง บันทึกสล็อต implementation และ admin แล้วใช้ ABI ที่ตรงกับโค้ดเวอร์ชันนั้นทุกประการ ฐานข้อมูล selector ให้ได้เพียงรายชื่อที่เป็นไปได้ ไม่ใช่หลักฐานชี้ขาด
  4. ถอดรหัสอย่างเคร่งครัด selector คือ 4 bytes แรกของ Keccak-256 จากลายเซ็นฟังก์ชันมาตรฐานโดยไม่รวมชนิดค่าที่ส่งคืน ค่าคงที่ใช้คำขนาด 32-byte ส่วน head ของค่าไดนามิกเก็บออฟเซ็ตจากจุดเริ่มต้นของบล็อกอาร์กิวเมนต์หลัง selector ปฏิเสธข้อมูลที่ขาด ออฟเซ็ตนอกขอบเขต ความยาวที่เป็นไปไม่ได้ padding ที่ไม่ถูกต้อง และไบต์ส่วนท้ายที่อธิบายไม่ได้
  5. คลี่ multicall, calldata ที่ซ้อนกัน และการทำงานแบบมอบหมายสิทธิ์ซ้ำลงไปทุกระดับ สำหรับ child call แต่ละรายการ ให้แสดงเป้าหมาย มูลค่าดั้งเดิม selector อาร์กิวเมนต์ ชนิดการเรียก และ flag allowFailure ถ้ามี เมื่อใช้ delegatecall โค้ด implementation จะทำงานในบริบทของที่อยู่ ยอดคงเหลือ และ storage ของผู้เรียก โดยคง msg.sender และ msg.value ไว้
  6. สร้างบัญชีสิทธิ์และบัญชีมูลค่าแยกกัน แล้วจึงจำลอง บันทึกผู้รับ spender ผู้ดำเนินการ NFT หน่วยโทเค็นดิบ decimals เส้นตาย ขอบเขต slippage และมูลค่าดั้งเดิม จำลองด้วยบล็อก ผู้ส่ง และมูลค่าที่ตรงกัน แต่ถือผลลัพธ์เป็นภาพสถานะแบบมีเงื่อนไข เพราะสถานะ ราคา เวลา โค้ด และลำดับธุรกรรมอาจเปลี่ยนได้
  7. ยืนยันทุกฟิลด์ที่มีสาระสำคัญก่อนลงนาม หลังธุรกรรมถูกรวมเข้าบล็อก ให้ตรวจสถานะ receipt, log, trace หากมี และส่วนต่างของยอดคงเหลือ allowance และสถานะผู้ดำเนินการ แยกความล้มเหลวของ child call ที่โค้ดดักไว้จากความสำเร็จระดับบนสุด คิดค่า gas แม้ธุรกรรม revert รอ finality ตามที่กำหนด และหยุดแทนการลงนามซ้ำโดยไม่เข้าใจความล้มเหลว

ตัวอย่างคำนวณ

  • การโอน ERC-20 แบบค่าคงที่ transfer(address,uint256) มักใช้ selector 0xa9059cbb โดย selector หนึ่งชุดบวกคำ ABI สองคำมีขนาด 4 + 2 * 32 = 68 bytes จำนวนดิบ 1,500,000 ของโทเค็นที่ตรวจสอบแยกต่างหากแล้วว่าใช้ 6 decimals จะแสดงเป็น 1.5 tokens ค่า decimals เป็นข้อมูลเมตาภายนอกของสัญญา ไม่ได้เข้ารหัสอยู่ในอาร์กิวเมนต์เหล่านั้น และ selector เพียงอย่างเดียวระบุสัญญาหรือฟังก์ชันแบบไม่ซ้ำไม่ได้
  • ออฟเซ็ตของไบต์ไดนามิก สำหรับ f(address,bytes) ที่มี payload ขนาด 3-byte ส่วน head สองคำใช้ 64 bytes ออฟเซ็ตไดนามิกคือ 0x40 โดยวัดจากจุดเริ่มต้นของบล็อกอาร์กิวเมนต์และไม่รวม selector ส่วน tail มีคำความยาว 32-byte หนึ่งคำและคำข้อมูลที่เติม padding แล้วขนาด 32-byte อีกหนึ่งคำ ดังนั้น calldata รวมมีขนาด 4 + 64 + 32 + 32 = 132 bytes หากมองออฟเซ็ตเป็นตำแหน่งสัมบูรณ์จากไบต์ศูนย์ จะคลาดไปสี่ไบต์
  • มูลค่าของแบตช์ขึ้นกับ implementation การเรียกภายนอกส่ง 1.00 ETH ส่วน child call ที่ถอดรหัสแล้วสามรายการขอ 0.20 ETH, 0.30 ETH และ 0.10 ETH รวมเป็น 0.60 ETH โค้ดแบตช์อาจคืน เก็บ ส่งต่อ 0.40 ETH ที่เหลือ หรือ revert ขึ้นกับ implementation หาก child รายการที่สามล้มเหลวโดยมี allowFailure=true รายการก่อนหน้าอาจยังคงถูกบันทึก ส่วน implementation แบบอะตอมิกอาจ revert ทั้งหมด
  • Permit ไม่ใช่ calldata ของ relayer ณ เวลาลงนาม เจ้าของที่มี 1,000 USDC ลงนาม permit แบบ ERC-2612 มูลค่า 300 USDC ที่ nonce 41 ลายเซ็นอย่างเดียวไม่เปลี่ยนยอดคงเหลือหรือ allowance หลัง relayer ส่งสำเร็จ nonce จะเป็น 42 และ allowance เป็น 300 เมื่อ spender ใช้ 180 ยอดคงเหลือจะเป็น 820 และ allowance ที่เหลือเป็น 120 การตัดการเชื่อมต่อเว็บไซต์ไม่ได้เพิกถอนสิทธิ์นี้

ความเสี่ยง

  • ถอดรหัสโดยอ้างอิงเชน fork, block tag หรือซองธุรกรรมที่ผิด
  • ลงนามให้โดเมน ที่อยู่เป้าหมาย หรือผู้รับที่ปลอมแปลง
  • ถือว่า selector 4-byte ไม่ซ้ำทั้งที่อาจชนกันได้
  • ใช้ ABI ที่คาดเดา ล้าสมัย หรือตรวจสอบไม่ถูกต้อง
  • เชื่อป้าย source ที่ยืนยันแล้วโดยไม่เทียบกับ codeHash ของ runtime ปัจจุบัน
  • พลาดการอัปเกรด implementation, beacon หรือ admin ระหว่างการตรวจสอบกับการทำงาน
  • มองข้ามฟังก์ชันพร็อกซีที่มี selector ชนกับ implementation
  • ลืมว่า delegatecall เขียนในบริบท storage ของผู้เรียก
  • ยอมรับออฟเซ็ต ความยาว padding ไดนามิก หรือไบต์ส่วนท้ายที่ผิดรูป
  • ไม่คลี่แบตช์ซ้อนที่ซ่อนเป้าหมาย มูลค่า หรือสิทธิ์
  • สมมติว่าเป็นอะตอมิกทั้งที่ implementation ดักหรือยอมให้ child call ล้มเหลว
  • มองข้าม value ดั้งเดิมระดับบนสุดเพราะอาร์กิวเมนต์โทเค็นดูไม่เป็นอันตราย
  • ใช้ decimals ผิดหรือสมมติว่าโทเค็นแบบ fee-on-transfer และ rebasing เป็น ERC-20 มาตรฐาน
  • ให้ allowance ERC-20 ไม่จำกัดหรือจัดการ race ของการเปลี่ยน allowance ผิด
  • มองข้ามว่า setApprovalForAll ของ NFT ครอบคลุมทั้งคอลเลกชัน
  • เข้าใจ typed data แบบ EIP-712 หรือ permit แบบ ERC-2612 ผิดว่าเป็น calldata ของธุรกรรม
  • พลาดขอบเขต nonce เส้นตาย verifying contract โดเมนเชน หรือการเล่นซ้ำ
  • ถือว่าการจำลองคงที่ทั้งที่ oracle, timestamp, pending state, MEV หรือโค้ดเปลี่ยนได้
  • ถือสถานะ receipt, log หรือ trace จากผู้ให้บริการเป็นหลักฐานสถานะทางเศรษฐกิจที่สมบูรณ์
  • ลงนามซ้ำโดยไม่ตรวจสอบผ่าน UI ที่ถูกเจาะ หรือมองข้ามความเสี่ยงด้าน inclusion, reorg และ finality

ความเข้าใจผิดที่พบบ่อย

  • selector ของฟังก์ชันระบุสิ่งที่สัญญาจะทำแบบไม่ซ้ำ
  • สรุปจาก front-end ที่ยืนยันแล้วตรงกับไบต์และ implementation ปัจจุบันที่กำลังลงนามทุกประการ
  • ธุรกรรมที่มี value=0 ไม่สามารถย้ายโทเค็น NFT หรือสินทรัพย์ที่มอบสิทธิ์ไว้ได้
  • การจำลองหรือ receipt ที่สำเร็จพิสูจน์ความปลอดภัยและผลลัพธ์ทางเศรษฐกิจที่ตั้งใจไว้
  • การตัดการเชื่อมต่อ dapp เพิกถอน approval, permit และสิทธิ์ผู้ดำเนินการ NFT

หัวข้อที่เกี่ยวข้อง

แหล่งข้อมูล

การนำทาง

ค้นหาในวิกิ...