Ownable Contracts
Tự học Solidity bài 26: Ownable Contracts - Chủ sở hữu Hợp đồng có các quyền đặc biệt
Trong ví dụ ở bài trước (bài 25), bạn có phát hiện ra lỗ hổng bảo mật không?
setKittyContractAddress được khai báo là external, tức là bất kỳ ai cũng có thể gọi nó! Điều đó có nghĩa là bất kỳ ai cũng có thể thay đổi địa chỉ của hợp đồng CryptoKitties trong hợp đồng và phá vỡ ứng dụng của chúng ta!
Chúng ta muốn có chức năng để cập nhật địa chỉ này trong hợp đồng của mình, nhưng chúng ta lại không muốn tất cả mọi người có thể cập nhật nó.
Để xử lý những trường hợp như thế này thì chúng ta tạo các hợp đồng Có thể sở hữu Ownable Contracts - nghĩa là hợp đồng này có một chủ sở hữu (là bạn) có các quyền đặc biệt.
Hợp đồng có thể sở hữu của OpenZeppelin
Dưới đây là hợp đồng có thể sở hữu được lấy từ thư viện OpenZeppelin Solidity. OpenZeppelin là một thư viện các hợp đồng thông minh an toàn và được cộng đồng kiểm duyệt mà bạn có thể sử dụng trong DApp của riêng mình. Sau bài học này, tôi khuyên bạn nên xem trang web của họ để học hỏi thêm!
Hãy đọc hợp đồng bên dưới. Bạn sẽ thấy một số điều chúng ta chưa được học, nhưng đừng lo lắng, chúng ta sẽ nói về chúng sau.
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address private _owner;
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
constructor() internal {
_owner = msg.sender;
emit OwnershipTransferred(address(0), _owner);
}
/**
* @return the address of the owner.
*/
function owner() public view returns(address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner());
_;
}
/**
* @return true if `msg.sender` is the owner of the contract.
*/
function isOwner() public view returns(bool) {
return msg.sender == _owner;
}
/**
* @dev Allows the current owner to relinquish control of the contract.
* @notice Renouncing to ownership will leave the contract without an owner.
* It will not be possible to call the functions with the `onlyOwner`
* modifier anymore.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}
/**
* @dev Transfers control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0));
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
Một số điều mới mà chúng ta chưa từng thấy trước đây:
- Constructors: constructor () là một phương thức khởi tạo, là một hàm đặc biệt tùy chọn có cùng tên với hợp đồng. Nó sẽ chỉ được thực thi một lần, khi hợp đồng được tạo lần đầu tiên.
- Function Modifiers: modifier onlyOwner() Modifier là một loại half-functions được sử dụng để sửa đổi các hàm khác, thường là để kiểm tra một số yêu cầu trước khi thực thi. Trong trường hợp này, onlyOwner có thể được sử dụng để giới hạn quyền truy cập, do đó chỉ chủ sở hữu của hợp đồng mới có thể chạy chức năng này. Chúng ta sẽ nói thêm về các công cụ sửa đổi hàm trong các bài tiếp theo.
- Từ khóa indexed: đừng lo lắng về cái này, chúng tôi chưa cần nó.
Vì vậy, hợp đồng có thể sở hữu Ownable Contracts này cơ bản thực hiện những điều sau:
- Khi hợp đồng được tạo, phương thức khởi tạo sẽ đặt msg.sender = chủ sở hữu (người đã triển khai nó)
- Nó thêm một công cụ sửa đổi onlyOwner, có thể hạn chế quyền truy cập vào các chức năng nhất định cho chỉ dành cho chủ sở hữu.
- Nó cho phép bạn chuyển hợp đồng sang chủ sở hữu mới.
onlyOwner là một nhu cầu phổ biến đến nỗi hầu hết các DApp Solidity đều bắt đầu bằng bản sao của Ownable contract này, và sau đó khi code hợp đồng đầu tiên lập trình viên sẽ kế thừa từ nó.
Vì chúng ta muốn giới hạn setKittyContractAddress thành onlyOwner, chúng ta sẽ làm tương tự cho hợp đồng của mình.
Thực hành luôn cho nhớ lâu bạn nhé
Copy mã của Ownable Contracts vào một tệp mới đặt tên là ownable.sol. Và set cho ZombieFactory kế thừa từ nó.
Import ownable.sol
Sửa đổi hợp đồng ZombieFactory để kế thừa từ Ownable.
Nếu bạn chưa biết cách TẠO FILE, COMPILER và DEPLOY thì hãy xem lại trong bài giới thiệu nhé. Có hướng dẫn chi tiết ở đó.