您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“solidity智能合約的經(jīng)典設(shè)計模式有哪些”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
合約自毀模式用于終止一個合約,這意味著將從區(qū)塊鏈上永久刪除這個合約。 一旦被銷毀,就不可能 調(diào)用合約的功能,也不會在賬本中記錄交易。
現(xiàn)在的問題是:“為什么我要銷毀合約?”。
有很多原因,比如某些定時合約,或者那些一旦達(dá)到里程碑就必須終止的合約。 一個典型的案例 是貸款合約,它應(yīng)當(dāng)在貸款還清后自動銷毀;另一個案例是基于時間的拍賣合約,它應(yīng)當(dāng)在拍賣結(jié)束后 終止 —— 假設(shè)我們不需要在鏈上保存拍賣的歷史記錄。
在處理一個被銷毀的合約時,有一些需要注意的問題:
合約銷毀后,發(fā)送給該合約的交易將失敗
任何發(fā)送給被銷毀合約的資金,都將永遠(yuǎn)丟失
為避免資金損失,應(yīng)當(dāng)在發(fā)送資金前確保目標(biāo)合約仍然存在,移除所有對已銷毀合約的引用。 現(xiàn)在我們來看看代碼:
contract SelfDesctructionContract { public address owner; public string someValue; modifier ownerRestricted { require(owner == msg.sender); _; } // constructor function SelfDesctructionContract() { owner = msg.sender; } // a simple setter function function setSomeValue(string value){ someValue = value; } // you can call it anything you want function destroyContract() ownerRestricted { suicide(owner); } }
正如你所看到的, destroyContract()
方法負(fù)責(zé)銷毀合約。
請注意,我們使用自定義的ownerRestricted
修飾符來顯示該方法的調(diào)用者,即僅允許合約的擁有者 銷毀合約。
工廠合約用于創(chuàng)建和部署“子”合約。 這些子合約可以被稱為“資產(chǎn)”,可以表示現(xiàn)實生活中的房子或汽車。
工廠用于存儲子合約的地址,以便在必要時提取使用。 你可能會問,為什么不把它們存在Web應(yīng)用數(shù)據(jù)庫里? 這是因為將這些地址數(shù)據(jù)存在工廠合約里,就意味著是存在區(qū)塊鏈上,因此更加安全,而數(shù)據(jù)庫的損壞 可能會造成資產(chǎn)地址的丟失,從而導(dǎo)致丟失對這些資產(chǎn)合約的引用。 除此之外,你還需要跟蹤所有新 創(chuàng)建的子合約以便同步更新數(shù)據(jù)庫。
工廠合約的一個常見用例是銷售資產(chǎn)并跟蹤這些資產(chǎn)(例如,誰是資產(chǎn)的所有者)。 需要向負(fù)責(zé)部署資產(chǎn)的 函數(shù)添加payable修飾符以便銷售資產(chǎn)。 代碼如下:
contract CarShop { address[] carAssets; function createChildContract(string brand, string model) public payable { // insert check if the sent ether is enough to cover the car asset ... address newCarAsset = new CarAsset(brand, model, msg.sender); carAssets.push(newCarAsset); } function getDeployedChildContracts() public view returns (address[]) { return carAssets; } } contract CarAsset { string public brand; string public model; address public owner; function CarAsset(string _brand, string _model, address _owner) public { brand = _brand; model = _model; owner = _owner; } }
代碼address newCarAsset = new CarAsset(...)
將觸發(fā)一個交易來部署子合約并返回該合約的地址。 由于工廠合約和資產(chǎn)合約之間唯一的聯(lián)系是變量address[] carAssets
,所以一定要正確保存子合約的地址。
假設(shè)你正在構(gòu)建一個依賴與多個合約的DApp,例如一個基于區(qū)塊鏈的在線商城,這個DApp使用了 ClothesFactoryContract、GamesFactoryContract、BooksFactoryContract等多個合約。
現(xiàn)在想象一下,將所有這些合約的地址寫在你的應(yīng)用代碼中。 如果這些合約的地址隨著時間的推移而變化,那該怎么辦?
這就是名稱注冊表的作用,這個模式允許你只在代碼中固定一個合約的地址,而不是數(shù)十、數(shù)百甚至數(shù)千個 地址。它的原理是使用一個合約名稱 => 合約地址的映射表,因此可以通過調(diào)用getAddress("ClothesFactory")
從DApp內(nèi)查找每個合約的地址。 使用名稱注冊表的好處是,即使更新那些合約,DApp也不會受到任何影響,因為 我們只需要修改映射表中合約的地址。
代碼如下:
contract NameRegistry { struct ContractDetails { address owner; address contractAddress; uint16 version; } mapping(string => ContractDetails) registry; function registerName(string name, address addr, uint16 ver) returns (bool) { // versions should start from 1 require(ver >= 1); ContractDetails memory info = registry[name]; require(info.owner == msg.sender); // create info if it doesn't exist in the registry if (info.contractAddress == address(0)) { info = ContractDetails({ owner: msg.sender, contractAddress: addr, version: ver }); } else { info.version = ver; info.contractAddress = addr; } // update record in the registry registry[name] = info; return true; } function getContractDetails(string name) constant returns(address, uint16) { return (registry[name].contractAddress, registry[name].version); } }
你的DApp將使用getContractDetails(name)
來獲取指定合約的地址和版本。
很多時候我們需要對一個映射表進(jìn)行迭代操作 ,但由于Solidity中的映射表只能存儲值, 并不支持迭代,因此映射表迭代器模式非常有用。 需要指出的是,隨著成員數(shù)量的增加, 迭代操作的復(fù)雜性會增加,存儲成本也會增加,因此請盡可能地避免迭代。
實現(xiàn)代碼如下:
contract MappingIterator { mapping(string => address) elements; string[] keys; function put(string key, address addr) returns (bool) { bool exists = elements[key] == address(0) if (!exists) { keys.push(key); } elements[key] = addr; return true; } function getKeyCount() constant returns (uint) { return keys.length; } function getElementAtIndex(uint index) returns (address) { return elements[keys[index]]; } function getElement(string name) returns (address) { return elements[name]; } }
實現(xiàn)put()
函數(shù)的一個常見錯誤,是通過遍歷來檢查指定的鍵是否存在。正確的做法是 elements[key] == address(0)
。雖然遍歷檢查的做法不完全是一個錯誤,但它并不可取, 因為隨著keys數(shù)組的增長,迭代成本越來越高,因此應(yīng)該盡可能避免迭代。
假設(shè)你銷售汽車輪胎,不幸的是賣出的所有輪胎出問題了,于是你決定向所有的買家退款。
假設(shè)你跟蹤記錄了合約中的所有買家,并且合約有一個refund()函數(shù),該函數(shù)會遍歷所有買家 并將錢一一返還。
你可以選擇 - 使用buyerAddress.transfer()或buyerAddress.send() 。 這兩個函數(shù)的區(qū)別在于, 在交易異常時,send()不會拋出異常,而只是返回布爾值false ,而transfer()則會拋出異常。
為什么這一點很重要?
假設(shè)大多數(shù)買家是外部賬戶(即個人),但一些買家是其他合約(也許是商業(yè))。 假設(shè)在 這些買方合約中,有一個合約,其開發(fā)者在其fallback函數(shù)中犯了一個錯誤,并且在被調(diào)用時拋出一個異常, fallback()函數(shù)是合約中的默認(rèn)函數(shù),如果將交易發(fā)送到合同但沒有指定任何方法,將調(diào)用合約 的fallback()函數(shù)。 現(xiàn)在,只要我們在refund函數(shù)中調(diào)用contractWithError.transfer() ,就會拋出 異常并停止迭代遍歷。 因此,任何一個買家合約的fallback()異常都將導(dǎo)致整個退款交易被回滾, 導(dǎo)致沒有一個買家可以得到退款。
雖然在一次調(diào)用中退款所有買家可以使用send()來實現(xiàn),但是更好的方式是提供withdrawFunds()方法,它 將單獨(dú)按需要退款給調(diào)用者。 因此,錯誤的合約不會應(yīng)用其他買家拿到退款。
實現(xiàn)代碼如下:
contract WithdrawalContract { mapping(address => uint) buyers; function buy() payable { require(msg.value > 0); buyers[msg.sender] = msg.value; } function withdraw() { uint amount = buyers[msg.sender]; require(amount > 0); buyers[msg.sender] = 0; require(msg.sender.send(amount)); } }
“solidity智能合約的經(jīng)典設(shè)計模式有哪些”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。