目錄
一、什么是智能合約
二、智能合約的代碼結(jié)構(gòu)
????????1.Solidity語言
????????2.bid函數(shù)
????????3.fallback()函數(shù)
?二、外部賬戶如何調(diào)用智能合約
三、一個合約如何調(diào)用另一個合約中的函數(shù)
????????1.直接調(diào)用
????????2.使用address類型的call()函數(shù)
????????3.代理調(diào)用 delegatecall()
????????智能合約是以太坊的精髓,也是以太坊和比特幣一個最大的區(qū)別。
一、什么是智能合約
1.智能合約的本質(zhì)是運行在區(qū)塊鏈上的一段代碼,代碼的邏輯定義了智能合約的內(nèi)容。
2.智能合約的賬戶保存了合約當(dāng)前的運行狀態(tài)
(1)balance:當(dāng)前余額;
(2)nonce:交易次數(shù);
(3)code:合約代碼;
(4)storage:存儲,數(shù)據(jù)結(jié)構(gòu)一是一顆MPT;
3.Solidity是智能合約最常用的語言,語法上與JavaScript很接近。
二、智能合約的代碼結(jié)構(gòu)
pragma solidity ^0.4.21;
contract SimpleAuction {
address public beneficiary;//拍賣受益人
uint public auctionEnd;//結(jié)束時間
address public highestBidder;//當(dāng)前的最高出價人
mapping( address => uint) bids;//所有競拍者的出價
address[] bidders;//所有競拍者
//需要記錄的事件
event HighestBidIncreased(address bidder,uint amount);
event -Pay2Beneficiary( address - winner , uint amount);
//以受益者地址 `_beneficiary` 的名義,
//創(chuàng)建一個簡單的拍賣,拍賣時間為 `_biddingTime` 秒。
constructor(uint _biddingTime,address _beneficiary
)public {
beneficiary = _beneficiary;
auctionEnd = now + biddingTime;
}
//對拍賣進行出價,隨交易一起發(fā)送的ether與之前已經(jīng)發(fā)送的ether的和為本次出價。
function bid() public payable {…
}
//使用withdraw模式
//由投標(biāo)者自己取回出價,返回是否成功
function withdraw() public returns (bool) {…
}
//結(jié)束拍賣,把最高的出價發(fā)送給受益人
function pay2Beneficiary() public returns (bool) {…
}
}
1.Solidity語言
????????Solidity是面向?qū)ο蟮木幊陶Z言,這里的contract類似于C++當(dāng)中的類(class),定義了很多狀態(tài)變量。Solidity是強類型語言,大部分跟普通的編程語言(如C++等)比較接近,如:uint,即unsigned int(無符號整數(shù))。address類型是Solidity語言所特有的。
????????上述代碼段中的event事件,是用來記錄日志的。第一個事件是HighestBidIncreased,拍賣的最高出價增加了,代碼中是一個網(wǎng)上拍賣的例子,記錄一下最新高價的參數(shù)(address bidder),金額是amount;第二個事件是Pay2Beneficiary,參數(shù)是贏得拍賣的人的地址及最后出價amount。
????????Solidity語言跟其他普通編程語言相比,有一些特別之處。如:mapping,mapping是一個哈希表,保存了從地址到unit的一個映射。Solidity語言中哈希表不支持遍歷,如果想遍歷哈希表里的所有元素,需要想辦法記錄哈希表中有哪些元素,用bidders數(shù)組記錄下來,Solidity語言中的數(shù)組可以是固定長度的,也可以是動態(tài)改變長度的。上述代碼是一個動態(tài)改變長度的數(shù)組,如果要在數(shù)組里增加一個元素,就用push操作,即bidders.push(bidder):新增加一個出價人在數(shù)組的末尾;想知道這個數(shù)組有多少個元素,可以用bidders.length;如果是固定長度的數(shù)組,就要寫明數(shù)組的長度,比如address[1024],這個就是長度為1024的數(shù)組。
????????Solidity語言中定義構(gòu)造函數(shù)有兩種方法,構(gòu)造函數(shù)只能有一個。一種方法就是像c++構(gòu)造函數(shù)一樣,定一個與contract同名的函數(shù),這個函數(shù)可以有參數(shù),但是不能有返回值。實際上新版本Solidity語言更推薦用這里使用的方法:用一個constructor來定義一個構(gòu)造函數(shù),這個構(gòu)造函數(shù)只有在合約創(chuàng)建的時候會被調(diào)用一次。
????????最后是三個成員函數(shù),三個函數(shù)都是public,說明其他賬戶可以調(diào)用這些函數(shù),bid這個函數(shù),這里標(biāo)志有一個payable,這個后面會解釋是什么意思。
![北大肖臻老師《區(qū)塊鏈技術(shù)與應(yīng)用》系列課程學(xué)習(xí)筆記[21]以太坊-智能合約-1](https://imgs.yssmx.com/Uploads/2023/05/449844-1.png)
2.bid函數(shù)
????????在bid函數(shù)中,有一個payable(另外兩個函數(shù)都沒有),以太坊中規(guī)定如果這個合約賬戶要能接收外部轉(zhuǎn)賬的話,那么必須標(biāo)注成payable,這個例子中bid函數(shù)是什么意思?這是網(wǎng)上拍賣的合約,bid函數(shù)是用來進行競拍出價的。比如說你要參與拍賣,你說你出100個以太幣,那么就調(diào)用合約當(dāng)中的bid函數(shù),所以拍賣規(guī)則是,調(diào)用bid函數(shù)時要把拍賣的出價100個以太幣也發(fā)送過去,存儲到這個合約里,鎖定到拍賣結(jié)束。避免有人憑空出價,說出1萬個以太幣,實際上你沒那么多錢,所以要拍賣的時候,要把你發(fā)的價錢放到合約里鎖定起來,所以bid函數(shù)要有能夠接收外部轉(zhuǎn)賬的能力,所以才標(biāo)注一個payable。withdraw函數(shù)就沒有payable,withdraw就是拍賣結(jié)束了,出價最高的那個人贏得了拍賣,其他人沒有拍到想要的東西,可以調(diào)用withdraw把自己當(dāng)初出的價錢,就是原來bid的時候鎖定在智能合約里的以太幣再取回來,因為這個的目的不是為了真的轉(zhuǎn)賬,不是要把錢轉(zhuǎn)給智能合約,而僅僅是調(diào)用withdraw函數(shù)把當(dāng)初鎖定在智能合約里的那一部分錢取回來,所以沒必要弄payable。圖2-1中的交易就屬于不需要payable。
3.fallback()函數(shù)
function()public [payable]{
……
}
????????這個函數(shù)既沒有參數(shù)也沒有返回值,而且也沒有函數(shù)名,是個匿名函數(shù),fallback這個關(guān)鍵字也沒有出現(xiàn)在這個函數(shù)名里。調(diào)用這個合約的時候,A調(diào)用B這個合約,然后要在轉(zhuǎn)賬交易的data域說明你調(diào)用的是B當(dāng)中的哪個函數(shù)。如果A給合約B轉(zhuǎn)賬了一筆錢,沒有說明調(diào)用的是哪個函數(shù),data域是空的,那么這個時候就是調(diào)用這個fallback()函數(shù),沒有別的函數(shù)可調(diào)了,就調(diào)他。還有一種情況是要調(diào)的函數(shù)不存在,在那個data域里,你說要調(diào)這個函數(shù),而實際這個合約當(dāng)中沒有這個函數(shù),那也是調(diào)用這個fallback()函數(shù),這就是為什么這個函數(shù)沒有參數(shù)也沒有返回值,因為他沒法提供參數(shù)。
????????對于fallback()函數(shù)來說,也可能需要標(biāo)注payable關(guān)鍵字,如果fallback()函數(shù)需要有接收轉(zhuǎn)賬的能力的話,也需要寫成是payable,一般情況下,都是寫上payable的,如果合約賬戶沒有任何函數(shù)標(biāo)識為payable,包括fallback()函數(shù)也沒有標(biāo)識成payable,那么這個合約沒有任何能力接受外部的轉(zhuǎn)賬。就是如果這個合約沒有fallback()函數(shù)或者是有fallback()函數(shù) 但是沒有寫payable,那么其他人往這個合約里轉(zhuǎn)一筆錢,別的都不說,data域是空的就會引發(fā)異常。
????????fallback()函數(shù)和payable都是在合約定義的時候?qū)懙模医o你轉(zhuǎn)賬時候不用寫payable,也不用寫fallback(),如果轉(zhuǎn)賬的時候,別的什么都不寫,沒有調(diào)用任何一個函數(shù),那么就自動調(diào)用這個fallback()函數(shù)。
????????fallback()函數(shù)不是必須定義的,一個合約可以沒有fallback()函數(shù),如果沒有fallback()函數(shù)的話,出現(xiàn)前面說的幾種情況,就會拋出異常。比如給一個合約轉(zhuǎn)賬,沒有說調(diào)哪個函數(shù),那個合約也沒有定義fallback()函數(shù),那么這個轉(zhuǎn)賬就是錯誤的,就會引發(fā)錯誤處理。另外只有合約賬戶才有這些東西,外部賬戶跟這個都沒有關(guān)系,外部賬戶都沒有代碼。
????????另外,轉(zhuǎn)賬金額可以是0,但汽油費是要給的,這是兩碼事,轉(zhuǎn)賬金額是給收款人的,汽油費是給發(fā)布這個區(qū)塊的礦工的,如果汽油費不給的話,礦工不會把你這個交易打包發(fā)布到區(qū)塊鏈。
?二、外部賬戶如何調(diào)用智能合約
????????調(diào)用智能合約其實跟轉(zhuǎn)賬是類似的。如A發(fā)起一個交易轉(zhuǎn)賬給B,如果B是一個普通的賬戶,那么這就是一個普通的轉(zhuǎn)賬交易,與比特幣當(dāng)中的轉(zhuǎn)賬交易是一樣的。如果B是一個合約賬戶的話,那么這個轉(zhuǎn)賬實際上是發(fā)起一次對B這個合約的調(diào)用,那么具體是調(diào)用合約中的哪個函數(shù),是在數(shù)據(jù)域data域中說明的,如圖2-1所示。
![北大肖臻老師《區(qū)塊鏈技術(shù)與應(yīng)用》系列課程學(xué)習(xí)筆記[21]以太坊-智能合約-1](https://imgs.yssmx.com/Uploads/2023/05/449844-2.png)
三、一個合約如何調(diào)用另一個合約中的函數(shù)
1.直接調(diào)用
contract A {
event LogCallFoo(string str);
function foo(string str) returns (uint){
emit LogCallFoo( str) ;
return 123;
}
}
contract B {
uint ua;
function cal1AFooDirectly(address addr) public{
Aa = A(addr) ;
ua = a.foo("call foo directly");
}
}
????????有A,B兩個合約,A這個合約就只是寫成log,event定義事件LogCallFoo,emit LogCallFoo():用emit這個操作來調(diào)用這個事件,作用就是寫一個log,對于程序的運行邏輯是沒有影響的。在B合約中,函數(shù)參數(shù)是一個地址(A合約的地址),然后把這個地址轉(zhuǎn)換成A這個合約的一個實例,然后調(diào)用其中的foo這個函數(shù)。
????????以太坊中規(guī)定一個交易只有外部賬戶才能夠發(fā)起,合約賬戶不能自己主動發(fā)起一個交易。這個例子當(dāng)中需要有一個外部賬戶調(diào)用了合約B當(dāng)中的這個callAFooDirectly函數(shù),然后這個函數(shù)再調(diào)用合約A當(dāng)中的foo函數(shù)。
![北大肖臻老師《區(qū)塊鏈技術(shù)與應(yīng)用》系列課程學(xué)習(xí)筆記[21]以太坊-智能合約-1](https://imgs.yssmx.com/Uploads/2023/05/449844-3.png)
?2.使用address類型的call()函數(shù)
contract c {
function callAFooByCall(address addr) public returns (bool){
bytes4 funcsig = bytes4(keccak256("foo(string)"));
if ( addr.call(funcsig,"call foo by func call"))
return true;
return false;
}
}
????????funcsing:要調(diào)用函數(shù)的簽名,然后后面跟的是調(diào)用的參數(shù)。該方法與直接調(diào)用方法相比,區(qū)別是對于錯誤處理的不同:直接調(diào)用方法,如果調(diào)用的合約在執(zhí)行過程中出現(xiàn)錯誤,那么會導(dǎo)致發(fā)起調(diào)用的合約也跟著一起回滾,如果在直接調(diào)用方法中A在執(zhí)行過程出現(xiàn)異常,B這個合約也跟著一起出錯。而address.call()這種形式,如果在調(diào)用過程中,被調(diào)用的合約拋出異常,那么這個call函數(shù)會返回false,表明這個調(diào)用是失敗的,但發(fā)起調(diào)用的這個函數(shù)不會拋出異常,可以繼續(xù)執(zhí)行。
![北大肖臻老師《區(qū)塊鏈技術(shù)與應(yīng)用》系列課程學(xué)習(xí)筆記[21]以太坊-智能合約-1](https://imgs.yssmx.com/Uploads/2023/05/449844-4.png)
3.代理調(diào)用 delegatecall()
? ? ? ? 與address.call()方法基本上是一樣的,一個主要的區(qū)別是delegatecall不需要切換到被調(diào)用的合約的環(huán)境中去執(zhí)行,而是在當(dāng)前合約環(huán)境中執(zhí)行就可以了,比如就用當(dāng)前賬戶的賬戶余額存儲之類的,如圖3-3所示。文章來源:http://www.zghlxwxcb.cn/news/detail-449844.html
![北大肖臻老師《區(qū)塊鏈技術(shù)與應(yīng)用》系列課程學(xué)習(xí)筆記[21]以太坊-智能合約-1](https://imgs.yssmx.com/Uploads/2023/05/449844-5.png)
?文章來源地址http://www.zghlxwxcb.cn/news/detail-449844.html
到了這里,關(guān)于北大肖臻老師《區(qū)塊鏈技術(shù)與應(yīng)用》系列課程學(xué)習(xí)筆記[21]以太坊-智能合約-1的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!