ที่ ลอง/จับไวยากรณ์แนะนำใน 0.6.0 การก้าวกระโดดที่ยิ่งใหญ่ที่สุดในการจัดการความผิดพลาดในความแข็งแกร่งเนื่องจากเหตุผลสำหรับสตริงสำหรับ ย้อนกลับ และ จำเป็นต้อง ได้รับการปล่อยตัวใน v0.4.22 ทั้งคู่ พยายาม และ จับ ได้รับการจองคำหลัก ตั้งแต่ v0.5.9 และตอนนี้เราสามารถใช้พวกเขาเพื่อจัดการกับความล้มเหลวใน ภายนอก การเรียกใช้ฟังก์ชั่นโดยไม่ย้อนกลับธุรกรรมที่สมบูรณ์ (การเปลี่ยนแปลงสถานะในฟังก์ชั่นที่เรียกว่ายังคงย้อนกลับไป แต่สิ่งที่อยู่ในฟังก์ชั่นการโทรไม่ได้)
เรากำลังก้าวไปอีกขั้นหนึ่งจากวิธีการที่ไร้เดียงสา “ทั้งหมดหรือไม่มีอะไร” ในวงจรชีวิตการทำธุรกรรมซึ่งขาดพฤติกรรมการปฏิบัติที่เราต้องการบ่อยครั้ง
การจัดการความล้มเหลวของการโทรภายนอก
คำสั่ง Attempt/Catch ช่วยให้คุณสามารถตอบสนองต่อความล้มเหลวได้ ภายนอก การโทรและ การสร้างสัญญา โทรหาคุณไม่สามารถใช้งานได้ ภายใน การเรียกใช้ฟังก์ชัน โปรดทราบว่าในการปิดการเรียกใช้ฟังก์ชันสาธารณะภายในสัญญาเดียวกันกับ Attempt/Catch มันสามารถทำได้ภายนอกได้โดยเรียกฟังก์ชั่นด้วย นี้.–
ตัวอย่างด้านล่างแสดงให้เห็นว่าการลอง/จับถูกใช้ในรูปแบบโรงงานที่การสร้างสัญญาอาจล้มเหลว ต่อไปนี้ องค์กรการกุศล สัญญาต้องใช้ทรัพย์สินที่อยู่บังคับ _เจ้าของ ในตัวสร้าง
pragma solidity ^0.6.1; contract CharitySplitter { handle public proprietor; constructor (handle _owner) public { require(_owner != handle(0), "no-owner-provided"); proprietor = _owner; } }
มีสัญญาจากโรงงาน – องค์กรการกุศล ซึ่งใช้ในการสร้างและจัดการอินสแตนซ์ของ องค์กรการกุศล– ในโรงงานเราสามารถห่อไฟล์ ใหม่ Charitysplitter (องค์กรการกุศล) ในการลอง/จับเป็น failsafe สำหรับเมื่อตัวสร้างนั้นอาจล้มเหลวเนื่องจากว่างเปล่า เจ้าขององค์กรการกุศล ถูกส่งผ่าน
pragma solidity ^0.6.1; import "./CharitySplitter.sol"; contract CharitySplitterFactory { mapping (handle => CharitySplitter) public charitySplitters; uint public errorCount; occasion ErrorHandled(string motive); occasion ErrorNotHandled(bytes motive); perform createCharitySplitter(handle charityOwner) public { strive new CharitySplitter(charityOwner) returns (CharitySplitter newCharitySplitter) { charitySplitters(msg.sender) = newCharitySplitter; } catch { errorCount++; } } }
โปรดทราบว่าด้วยการลอง/จับมีเพียงข้อยกเว้นที่เกิดขึ้นภายในการโทรภายนอกเอง ข้อผิดพลาดภายในนิพจน์จะไม่ถูกจับตัวอย่างเช่นหากพารามิเตอร์อินพุตสำหรับ องค์กรการกุศลใหม่ เป็นส่วนหนึ่งของการโทรภายในข้อผิดพลาดใด ๆ ที่เกิดขึ้นจะไม่ถูกจับ ตัวอย่างที่แสดงให้เห็นถึงพฤติกรรมนี้คือการแก้ไข createcharitysplitter การทำงาน. นี่คือ องค์กรการกุศล พารามิเตอร์อินพุตคอนสตรัคเตอร์จะถูกดึงออกมาจากฟังก์ชั่นอื่น – แบบไดนามิก – GetCharityOwner– หากฟังก์ชั่นนั้นเปลี่ยนกลับในตัวอย่างนี้ด้วย “rethert-required-testing”นั่นจะไม่ถูกจับในคำสั่งลอง/จับ
perform createCharitySplitter(handle _charityOwner) public { strive new CharitySplitter(getCharityOwner(_charityOwner, false)) returns (CharitySplitter newCharitySplitter) { charitySplitters(msg.sender) = newCharitySplitter; } catch (bytes reminiscence motive) { ... } } perform getCharityOwner(handle _charityOwner, bool _toPass) inner returns (handle) { require(_toPass, "revert-required-for-testing"); return _charityOwner; }
การดึงข้อความแสดงข้อผิดพลาด
เราสามารถขยายตรรกะการลอง/จับได้เพิ่มเติมในไฟล์ createcharitysplitter ฟังก์ชั่นเพื่อดึงข้อความแสดงข้อผิดพลาดหากมีการปล่อยออกมาโดยความล้มเหลว ย้อนกลับ หรือ จำเป็นต้อง และปล่อยมันในเหตุการณ์ มีสองวิธีในการบรรลุเป้าหมายนี้:
1. ใช้ ข้อผิดพลาดจับ (เหตุผลหน่วยความจำสตริง)
perform createCharitySplitter(handle _charityOwner) public { strive new CharitySplitter(_charityOwner) returns (CharitySplitter newCharitySplitter) { charitySplitters(msg.sender) = newCharitySplitter; } catch Error(string reminiscence motive) { errorCount++; CharitySplitter newCharitySplitter = new CharitySplitter(msg.sender); charitySplitters(msg.sender) = newCharitySplitter; // Emitting the error in occasion emit ErrorHandled(motive); } catch { errorCount++; } }
ซึ่งปล่อยเหตุการณ์ต่อไปนี้ในตัวสร้างที่ล้มเหลวต้องมีข้อผิดพลาด:
CharitySplitterFactory.ErrorHandled( motive: 'no-owner-provided' (sort: string) )
2. ใช้ จับ (เหตุผลหน่วยความจำไบต์)
perform createCharitySplitter(handle charityOwner) public { strive new CharitySplitter(charityOwner) returns (CharitySplitter newCharitySplitter) { charitySplitters(msg.sender) = newCharitySplitter; } catch (bytes reminiscence motive) { errorCount++; emit ErrorNotHandled(motive); } }
ซึ่งปล่อยเหตุการณ์ต่อไปนี้ในตัวสร้างที่ล้มเหลวต้องมีข้อผิดพลาด:
CharitySplitterFactory.ErrorNotHandled( motive: hex'08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000116e6f2d6f776e65722d70726f7669646564000000000000000000000000000000' (sort: bytes)
สองวิธีข้างต้นสำหรับการดึงสตริงข้อผิดพลาดสร้างผลลัพธ์ที่คล้ายกัน ความแตกต่างคือวิธีที่สองไม่ได้บันทึกสตริงข้อผิดพลาด ข้อได้เปรียบของวิธีที่สองคือมันจะดำเนินการหาก ABI ถอดรหัสสตริงข้อผิดพลาดล้มเหลวหรือหากไม่มีเหตุผล
แผนการในอนาคต
มีแผนที่จะปล่อยการสนับสนุนประเภทข้อผิดพลาดซึ่งหมายความว่าเราจะสามารถประกาศข้อผิดพลาดในลักษณะเดียวกันกับเหตุการณ์ที่ช่วยให้เราสามารถจับข้อผิดพลาดประเภทต่าง ๆ ได้เช่น:
catch CustomErrorA(uint data1) { … } catch CustomErrorB(uint() reminiscence data2) { … } catch {}