Các tính năng của Solidity 0.6.x: câu lệnh try / catch

Rate this post

Các cú pháp try / catch được giới thiệu trong 0.6.0 được cho là bước nhảy vọt lớn nhất về khả năng xử lý lỗi trong Solidity, vì các chuỗi lý do cho hoàn nguyênyêu cầu đã được phát hành trong v0.4.22. Cả hai thửchụp lấy đã được bảo lưu từ khóa kể từ v0.5.9 và bây giờ chúng tôi có thể sử dụng chúng để xử lý các lỗi trong bên ngoài các lệnh gọi hàm mà không khôi phục giao dịch hoàn chỉnh (các thay đổi trạng thái trong hàm được gọi vẫn được khôi phục, nhưng các thay đổi trong hàm gọi thì không).

Chúng ta đang tiến một bước khỏi cách tiếp cận “tất cả hoặc không có gì” thuần túy nhất trong vòng đời giao dịch, phương pháp này không phù hợp với hành vi thực tế mà chúng ta thường mong muốn.

Xử lý lỗi cuộc gọi bên ngoài

Câu lệnh try / catch cho phép bạn phản ứng khi không thành công bên ngoài cuộc gọi và tạo hợp đồng cuộc gọi, vì vậy bạn không thể sử dụng nó cho nội bộ các cuộc gọi chức năng. Lưu ý rằng để kết thúc một lệnh gọi hàm công khai trong cùng một hợp đồng với try / catch, nó có thể được thực hiện bên ngoài bằng cách gọi hàm với đây..

Ví dụ dưới đây cho thấy cách try / catch được sử dụng trong một mô hình nhà máy mà việc tạo hợp đồng có thể không thành công. Sau CharitySplitter hợp đồng yêu cầu thuộc tính địa chỉ bắt buộc _chủ nhân trong hàm tạo của nó.

pragma solidity ^0.6.1;

contract CharitySplitter {
    address public owner;
    constructor (address _owner) public {
        require(_owner != address(0), "no-owner-provided");
        owner = _owner;
    }
}

Có hợp đồng xuất xưởng – CharitySplitterFactory được sử dụng để tạo và quản lý các phiên bản của CharitySplitter. Trong nhà máy, chúng tôi có thể bọc mới CharitySplitter (Chủ từ thiện) trong một lần thử / nắm bắt như một dự phòng an toàn khi phương thức khởi tạo đó có thể không thành công do trống từ thiện Được thông qua.

pragma solidity ^0.6.1;
import "./CharitySplitter.sol";
contract CharitySplitterFactory {
    mapping (address => CharitySplitter) public charitySplitters;
    uint public errorCount;
    event ErrorHandled(string reason);
    event ErrorNotHandled(bytes reason);
    function createCharitySplitter(address charityOwner) public {
        try new CharitySplitter(charityOwner)
            returns (CharitySplitter newCharitySplitter)
        {
            charitySplitters[msg.sender] = newCharitySplitter;
        } catch {
            errorCount++;
        }
    }
}

Lưu ý rằng với try / catch, chỉ các ngoại lệ xảy ra bên trong bản thân lệnh gọi bên ngoài mới bị bắt. Lỗi bên trong biểu thức không được phát hiện, ví dụ: nếu tham số đầu vào cho CharitySplitter mới bản thân nó là một phần của cuộc gọi nội bộ, bất kỳ lỗi nào mà nó phát sinh sẽ không bị bắt. Mẫu chứng minh hành vi này là mẫu đã được sửa đổi createCharitySplitter hàm số. Đây CharitySplitter tham số đầu vào của hàm tạo được truy xuất động từ một hàm khác – getCharityOwner. Nếu hàm đó hoàn nguyên, trong ví dụ này với “hoàn nguyên-yêu cầu-để kiểm tra”sẽ không bị mắc kẹt trong câu lệnh try / catch.

function createCharitySplitter(address _charityOwner) public {
    try new CharitySplitter(getCharityOwner(_charityOwner, false))
        returns (CharitySplitter newCharitySplitter)
    {
        charitySplitters[msg.sender] = newCharitySplitter;
    } catch (bytes memory reason) {
        ...
    }
}
function getCharityOwner(address _charityOwner, bool _toPass)
        internal returns (address) {
    require(_toPass, "revert-required-for-testing");
    return _charityOwner;
}

Truy xuất thông báo lỗi

Chúng tôi có thể mở rộng thêm logic try / catch trong createCharitySplitter chức năng để truy xuất thông báo lỗi nếu một thông báo được phát ra do lỗi hoàn nguyên hoặc yêu cầu và phát ra nó trong một sự kiện. Có hai cách để đạt được điều này:

1. Sử dụng bắt lỗi (lý do bộ nhớ chuỗi)

function createCharitySplitter(address _charityOwner) public {
    try new CharitySplitter(_charityOwner) returns (CharitySplitter newCharitySplitter)
    {
        charitySplitters[msg.sender] = newCharitySplitter;
    }
    catch Error(string memory reason)
    {
        errorCount++;
        CharitySplitter newCharitySplitter = new
            CharitySplitter(msg.sender);
        charitySplitters[msg.sender] = newCharitySplitter;
        // Emitting the error in event
        emit ErrorHandled(reason);
    }
    catch
    {
        errorCount++;
    }
}

Sự kiện nào tạo ra sự kiện sau trên lỗi yêu cầu hàm tạo không thành công:

CharitySplitterFactory.ErrorHandled(
    reason: 'no-owner-provided' (type: string)
)

2. Sử dụng bắt (lý do bộ nhớ byte)

function createCharitySplitter(address charityOwner) public {
    try new CharitySplitter(charityOwner)
        returns (CharitySplitter newCharitySplitter)
    {
        charitySplitters[msg.sender] = newCharitySplitter;
    }
    catch (bytes memory reason) {
        errorCount++;
        emit ErrorNotHandled(reason);
    }
}

Sự kiện nào tạo ra sự kiện sau trên lỗi yêu cầu hàm tạo không thành công:

CharitySplitterFactory.ErrorNotHandled(
  reason: hex'08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000116e6f2d6f776e65722d70726f7669646564000000000000000000000000000000' (type: bytes)

Hai phương pháp trên để lấy chuỗi lỗi tạo ra một kết quả tương tự. Sự khác biệt là phương pháp thứ hai không giải mã chuỗi lỗi ABI. Ưu điểm của phương pháp thứ hai là nó cũng được thực thi nếu giải mã ABI chuỗi lỗi không thành công hoặc nếu không có lý do nào được cung cấp.

Các kế hoạch trong tương lai

Có kế hoạch phát hành hỗ trợ cho các loại lỗi, nghĩa là chúng tôi sẽ có thể khai báo lỗi theo cách tương tự với các sự kiện cho phép chúng tôi bắt các loại lỗi khác nhau, ví dụ:

catch CustomErrorA(uint data1) {}
catch CustomErrorB(uint[] memory data2) {}
catch {}

Thuc Quyen

Leave a Reply

Your email address will not be published. Required fields are marked *