介 紹
在寫智能合約時,我傾向于采取引導方式。即使它們旨在用于生產(chǎn)環(huán)境,我也使它們盡可能易于理解。我寫的智能合約是可重用的,但是通常會針對每個特定的業(yè)務案例重新編寫智能合約。
在本文中,我將討論solidity智能合約中的三種許可方法。討論這些方法的復雜性從高到低,這是您在項目中應考慮的順序。 我提供了可用于每種方法的代碼。
本文假定您可以輕松地編寫智能合約,并使用繼承和傳遞合約地址等功能作為參數(shù)。
簡單方法— Ownable.sol
OpenZeppelin的Ownable.sol合同必須是最重用的合同之一。它在77行中實現(xiàn):
1. 斷言某人是智能合約所有者的邏輯。
2. 限制函數(shù)調(diào)用以繼承智能合約的所有者的邏輯。
3. 將所有權(quán)轉(zhuǎn)移到其他地址的邏輯。
在編寫智能合約時,您會經(jīng)常從Ownable繼承。讓我們來看一個如何使用Ownable的示例。想象一下,您想在智能合約中保留一個地址列表,但是您想成為唯一可以添加更多地址的列表??梢詫⑵湟暈槟湃蔚娜说淖员?。您可以執(zhí)行以下操作:
contract Whitelist is Ownable {
mapping (address =》 bool) members;
constructor() public Ownable() {
}
function addMember(address _member)
public
onlyOwner
{
members[_member] = true;
}
}
從Ownable繼承并在您的Ownable上調(diào)用其構(gòu)造函數(shù)可確保將部署智能合約的地址注冊為所有者。如果未通過注冊為所有者的地址調(diào)用,則onlyOwner修飾符會使函數(shù)恢復。
部署此智能合約后,只有您或您指定的人可以將新成員添加到其中的列表中。
盡管有用,但很多時候Ownable還不夠。 在給定時間只有一個地址可以成為所有者,只有所有者可以決定誰可以成為新所有者,您只能檢查您是否是所有者,而不是其他人。
中級復雜方法— Whitelist.sol
Whitelist.sol保留一個地址列表,然后可用于限制功能或任何其他目的。它在功能上與OpenZeppelin的Roles.sol非常相似,盡管有一些重要差異。
Whitelist.sol僅具有三個功能:
funcTIon isMember(address _member) public view returns(bool);
funcTIon addMember(address _member) public onlyOwner;
funcTIon removeMember(address _member) public onlyOwner;
例如通過該智能合約,您可以保留一份已批準的利益相關(guān)者列表,這些利益相關(guān)者可能是令牌轉(zhuǎn)移的唯一接收者。 您可以執(zhí)行以下操作:
pragma solidity ^0.5.0;
import “@openzeppelin/contracts/token/ERC20/ERC20.sol”;
import “。./access/Whitelist.sol”;
contract ERC20Whitelisted is ERC20 {
Whitelist whitelist;
constructor(address _whitelistAddress) public {
whitelist = Whitelist(_whitelistAddress);
}
funcTIon transfer(address account, uint256 amount) public {
require(whitelist.isMember(account), “Account not whitelisted.”);
super._transfer(account, amount);
}
}
在上面的示例中,您還可以使ERC20Whitelisted繼承自ERC20和Whitelist。 我很樂意討論一些折衷方案。
簡單的白名單功能可能非常強大。OpenZeppelin使用它們實現(xiàn)了許多ERC20和ERC721變體,并設法提供了超出我們大多數(shù)人所需的更多功能。在TechHQ,我們也僅使用白名單實施了CementDAO。
但是有時候,白名單也會落空。您可能需要有多個所有者才能擁有白名單?;蛘吣赡苄枰芾碓S多重疊的白名單。對于這些情況,我們具有分層的角色合約。
高級復雜方法-RBAC.sol
我們開發(fā)了RBAC.sol,旨在提供與現(xiàn)代共享系統(tǒng)一樣的多用戶功能。
1. 有些角色不過是地址組。
2. 組成員資格只能由某些管理員角色的成員修改。
3. 可以在運行時創(chuàng)建新角色。
4. 可以驗證角色成員身份。
在低層,我們使用用戶選擇的bytes32參數(shù)來標識角色。 通常,這些是可識別的短字符串,但是您也可以使用加密的值或地址。
角色本身是一組成員地址和admin角色的標識符。 有趣的是,我們不需要將角色的標識符存儲在其自己的結(jié)構(gòu)中。
struct Role {
bytes32 adminRoleId;
mapping (address =》 bool) members;
}
現(xiàn)在有兩種方法可以添加新角色并驗證角色是否存在:
function roleExists(bytes32 _roleId) public view returns(bool);
function addRole(bytes32 _roleId, bytes32 _adminRoleId) public;
并且管理成員的功能是相同的,只是現(xiàn)在必須指定相關(guān)角色:
function isMember(address _member, bytes32 _roleId) public view returns(bool);
function addMember(address _member, bytes32 _roleId) public;
function removeMember(address _member, bytes32 _roleId) public;
僅當調(diào)用者屬于我們要添加成員的角色的管理員角色時,addMember和removeMember才會成功。
僅當調(diào)用者屬于將管理所創(chuàng)建角色的角色時,addRole才會成功。
這些簡單的規(guī)則將允許創(chuàng)建角色的層次結(jié)構(gòu),然后可將其用于實現(xiàn)具有不同權(quán)限級別或區(qū)域的復雜多用戶平臺。
進階學習
為了進一步深入兔子洞,我建議從OpenZeppelin的這個問題開始。他們的代碼庫與我們的代碼庫沒有什么不同,即使在我們選擇采用其他方法的情況下,您也會發(fā)現(xiàn)大多數(shù)設計決策的透徹推理。他們在諸如ERC20Mintable之類的合約中使用Roles是一個很好的例子,可以替代Whitelist。
勇敢者的另一個資源是AragonOS ACL合約。界面一眼就可以看出他們決定走得更遠:
function hasPermission(address who, address where, bytes32 what,
bytes how) public view returns (bool);
對于我們自己的@ hq20 / contracts包中的示例,我們使用本文中描述的三個級別的訪問控制,因此,您也應該注意這一點。
結(jié) 論
對于智能合約的實現(xiàn),最好僅實現(xiàn)所需的復雜性,而無需再實現(xiàn)任何復雜性。 在許可方面,存在三種不同的復雜性級別:
· 單用戶
· 用戶群
· 用戶組的層次結(jié)構(gòu)
您可以將Ownable.sol用于單個用戶允許的系統(tǒng)。 您可以將@ openzeppelin/Roles.sol或@ hq20/Whitelist.sol用于需要組中權(quán)限用戶的系統(tǒng)。對于需要組層次結(jié)構(gòu)的系統(tǒng),我們過去已成功使用@ hq20/RBAC.sol。
您將有自己的要求,并且需要權(quán)衡取舍。了解每個實現(xiàn)背后的設計決策將使您可以使用現(xiàn)有合約,也可以修改合約以供自己使用。