1. 合約A中引用合約B典唇,是根據(jù) import和路徑 引用馋吗。一旦合約A編譯完成焕盟,那么意味著合約B此時此刻的abi“骨架”也已經在合約A中“定型”,因而合約A后續(xù)對合約B的使用都是基于其內已經“定型的骨架”宏粤。合約A對合約B使用之前需要給合約A傳入合約B(某個版本的)address 以構造出合約B的實例對象脚翘。
這里考慮一種意外情景:公司A的合約A編譯完成后,而公司B未告知公司A的情況下臨時升級合約B代碼并部署然后將 合約B的address告訴公司A绍哎。公司A在部署完合約A后初始化了這個合約B的地址来农。
"臨時升級合約B代碼",有下面情況:
? ? 1)沒有涉及到? 合約ABI骨架 的改動:如 a. 僅僅修改了function中的邏輯而沒有修改到function 的名字崇堰、返回值沃于、可見性、modifier等海诲。b. 合約B增加了function繁莹、modifier、變量特幔,這些是兼容 之前版本骨架的
? ? ? ? 合約A引用合約B的function時咨演,不會拋異常。
? ? 2)涉及到? 合約ABI骨架 的改動蚯斯。
? ? ? ? 合約A引用合約B的function時薄风,會拋異常。
? ? 其實合約之間的引用調用拍嵌,本質也是跟我們手動發(fā)起交易調用合約函數(shù)是一樣的:只要骨架未變或者升級后骨架兼容之前版本的骨架遭赂,那么一般不會拋異常。
由上述可見:合約abi骨架尤其重要横辆,否則會導致大量合約不得不升級撇他,這是非常折騰的。這個“骨架”,就猶如 java中的接口逆粹,我們應該如何才能駕馭好募疮?
2.? 常見的中心化系統(tǒng),“業(yè)務邏輯和數(shù)據(jù)存儲隔離” 以及 “可重啟來升級系統(tǒng)”僻弹,這非常方便系統(tǒng)升級阿浓。
? ? ? 而dapp,拿以太坊solidity合約編程做例子蹋绽,是將數(shù)據(jù)放在合約中存儲芭毙,即 業(yè)務邏輯和數(shù)據(jù)存儲一體化。而且合約一旦部署則不可更改卸耘。這樣子為后續(xù)合約系統(tǒng)升級增加了好多麻煩退敦,如不得不設計復雜的架構模式以達到方便系統(tǒng)升級的目的,可是設計復雜起來蚣抗,又產生 性能侈百、調用鏈過長、gas消耗過多以及 代碼冗余龐大 諸如此類的與業(yè)務無關的顧慮翰铡。
solidity中有沒有一種成熟點的架構模式或框架使用下钝域?可以讓我們更專注業(yè)務開發(fā)?
3.? 合約A中引用其他合約B锭魔,我應該保留的是 合約B地址例证,還是 合約B引用? 哪種好?
其實現(xiàn)在的我覺得迷捧,沒區(qū)別织咧。因為對于 已經編譯部署的 合約A 而言,合約B的“abi骨架”已經定型的了漠秋。
4.? 合約之間的函數(shù)外部調用笙蒙,目前EVM不支持 外部函數(shù)調用接收 變長數(shù)組。
? ? 如上圖庆锦,this.f()屬于 外部函數(shù)調用捅位,而f()返回的是 變長數(shù)組uint[] (Runtime才知道), 所以無法通過編譯肥荔。沒辦法,EVM目前能力有限朝群。但是燕耿,只要不是 合約之間的外部調用,而是 web3j之類的調用f()姜胖,可以返回變長數(shù)組uint[]誉帅。
因此,涉及到外部調用時候,如果可以提前確定數(shù)組長度蚜锨,則應該寫死档插。可是如果真有變長數(shù)組的情況亚再,那么我們應該怎么辦郭膛?有些前輩的做法是: 構造一個超長的數(shù)組并寫死長度 作為返回值。這樣就可以包含我們想要返回的數(shù)組的所有元素了氛悬≡蛱辏可是這樣有點笨.........
? ? 5.? array\mapping等集合類的聲明,默認采取的是storage類型如捅,即指針引用棍现。然而對于重要敏感的集合類型變量,我們不可以暴露指針給外部合約镜遣,防止作惡己肮。用memory則意味著復制一份集合類型變量再返回。
6.? 對于public變量悲关,默認有getter方法谎僻,我們可以直接獲取其值。
那么坚洽,合約a 在function中可否 修改合約b的 public變量戈稿??
如: function afunc(){
? ? contractB.publicParam = x ;? // 和java一樣讶舰,直接修改了 合約B的public成員變量
}
答:不會鞍盗,如下:
因為getter的使用是要 以function的形式。所以無法被第三方賦值跳昼。
7.? solidity中不可以在struct結構體中直接定義自己作為一個類型般甲。
//Error: Recursive struct definition.
原因如下: 如果將 自定義類型 又作為 自己的成員變量,那么此結構體的大小是無限的鹅颊。
由上圖敷存,可以在 array\mapping 中引用自己類型,這是因為堪伍,array\mapping 作為 結構體的成員變量锚烦,數(shù)量是用限的,不會出現(xiàn)遞歸帝雇。
8.? solidity中不允許直接暴露struct結構體給外部合約使用涮俄。
9. solidity中沒有null的概念,那么我們如何針對 結構體尸闸、字符串彻亲、數(shù)組孕锄、mapping進行“判空”?
答案在此:https://ethereum.stackexchange.com/questions/11618/how-to-test-if-a-struct-state-variable-is-set
https://ethereum.stackexchange.com/questions/5683/what-is-the-zero-value-for-a-string
總結如下:
總得來說苞尝,“空”在 solidity中指各類型的各自的zero-value畸肆,如 uint 類型的zero-value為0 。
? ? 1.? 如何對 string 判空宙址,通常是將其字節(jié)化然后判斷其length是否為0轴脐;
其實,是不是說 string類型的 zero-value 為 “” 曼氛?豁辉?
? ? string x ;
? ? if( bytes(x).length == 0 ){......}
? ? 另外也可以如此:
if (sha3(myVariable) != sha3("")) {
// is not empty string
? ? }
不知道可否:
if( x.length == 0 ){...}
? ? 2. 對于結構體,solidity中不需要new 也可以直接訪問其成員變量舀患,可是其成員變量均為空徽级。
? ? 如:
Transaction private tx;
struct Transaction {
? ? address to;
? ? uint value;
? ? bytes data;
}
即使 tx 從沒有賦值,我們也可以直接 訪問其成員變量: tx.to ,不過值為 本類型的zero-value;
? ? 3. 我們知道聊浅,在solidity中 array, mapping似乎是 聲明了就可以直接使用餐抢。
mapping(string =>Transaction )? txMapping ;
? ? ...
//key 為 “xx” 對應的 value (結構體Transaction,見上)其實從來沒有賦值低匙。然而我們依然可以直接訪問 這個“虛無的”value結構體 的成員旷痕。
if (txMapping["xx"].value == 0) {...}
10.? 在solidity中,結構體顽冶、array欺抗、mapping 似乎都是只需要聲明完就可以直接訪問其屬性了?强重?
在 function 內绞呈,不可以聲明 mapping , 原因是 memory空間是 數(shù)組,而mapping是storage空間的專屬類型间景。佃声??倘要?圾亏??封拧?
生成一個 struct時志鹃,入?yún)雎蕴^ mapping成員。因為mapping成員默認是自動創(chuàng)建和初始化的泽西?曹铃??尝苇?铛只??
evm的存儲模型是怎么樣的糠溜? 在代碼中要如何結合思考淳玩?
11. 什么時候用 memory ,什么時候用storage ???
左右為難啊.......
解決方案:不知其解非竿?蜕着?
12.? 入?yún)⑻啵幾g出錯红柱?承匣?
13.? struct 不可見,如何返回批量(如數(shù)組)锤悄?韧骗?
? ? 我本想返回
14.library 一旦部署,可以被任何組織機構零聚、項目的合約delegateCall而引用袍暴。這樣可以避免大量同質的lib部署,一來節(jié)省大量同類重復部署需要的gas隶症,二來減少evm中合約的泛濫污染政模,三是增強安全性:事實上,多個合約依賴于一個現(xiàn)存的代碼蚂会,可以讓營造一個更安全的環(huán)境淋样。因為一方面這些方面有良好的代碼審查(比如,Zepelin所做的偉大的工作)胁住,以及這些代碼經過線上實際運行的驗證趁猴。比如在這個案例中,一個打算最大融資五千萬的項目措嵌,ERC20代幣的問題被查出來了躲叼。
1)library 中的struct 可以被引用它的合約引用,可以作為 function的入?yún)ⅰ?/p>
困惑企巢?枫慷?
? 如果說是因為庫被delegateCall方式調用而允許 庫中的struct 被引用在 合約中的話,那么 庫中的struct 用在 function做入?yún)ⅲㄒ?庫incremented(Counter storage self ))不會有問題嗎浪规?或听?
? 不會有問題。因為 合約調用庫function笋婿,都是在 合約本身的function中調用誉裆。庫function不會直接暴露。庫被引用缸濒,可以理解為 運行時合約自身的代碼足丢,這也是為何庫struct 也可以在 合約中直接訪問的原因粱腻。也因為這個原因 庫incremented(Counter storage self ) 聲明internal的意義不大。
庫中的 this斩跌,都是指 delegateCall的合約實例绍些。雖然說 using 庫 for xType, 可是xType 沒有實例的概念耀鸦,因此也沒有this的概念柬批,所以 this 就指 所在的合約實例。
2)library 直接沒有繼承概念袖订,不過可以 類似“合約引用庫”這樣 互相引用氮帐。"庫引用庫"
3) 我想引用 建設銀行在evm部署好的一個 library,請問我應該怎么做洛姑?上沐?
猜想??:整個以太坊看做一臺電腦,那么它也是有統(tǒng)一的文件系統(tǒng)楞艾。這樣子的話奄容, 那么在Evm上的部署的項目都是在這個文件系統(tǒng)中。類比pc的文件系統(tǒng)产徊,其實evm的項目目錄也是有統(tǒng)一的根目錄這樣子昂勒。如果真是這樣的話,library的共享delegateCall就合理了舟铜。我們只需要將library開源代碼目錄結構按照 路徑放在我們自己的項目中戈盈,這樣目錄就和 evm中的對上了∽慌伲可是還是有很多問題:既然同目錄為何沒有覆蓋替換的概念塘娶?
? ? 4) 庫的設計,是為了項目代碼復用痊夭、甚至整個Evm共用同一套庫代碼刁岸。
? ? ? ? 調用library庫函數(shù)有兩個方式:
? ? ? ? a. lib.func(**)?
? ? ? ? ? ? 類似java中的靜態(tài)方法的調用格式:類.靜態(tài)方法(***)
? ? ? ? b. using lib for xType ;? 或? using lib for *;?
? ? ? ? ? ? 將lib的function附著到 xType類型中,從而達到代碼復用的目的她我。
注意:這種“附著”方式虹曙,默認原則是 將? 調用類型實例對象(基本類型則是自己) 作為 function的第一個參數(shù)。如果參數(shù)不匹配番舆,都會報錯酝碳。因而這里有一個盲區(qū):下圖中,i.toUint() 是 調用了 toUint(uint a):薇贰疏哗! 因為 i 是會默認作為第一個參數(shù)傳入的,這是必須的禾怠。如果 Utils中沒有 toUint(uint a) 返奉,那么就無法 將 i 作為第一個參數(shù)傳入 了贝搁,就會報錯!芽偏!
? ? ? ? 也是因為這個語法糖的特點徘公,所以建議對 庫函數(shù)的 第一個參數(shù) 命名為 self 。
15.? storage? 和memory
復雜類型變量占空間大哮针,占用的空間通常超過256位, 拷貝時開銷很大坦袍,同時需要考慮及時回收的問題十厢。因此evm設計了 兩種存儲:memory 和 storage機制。
? ? 1)? storage 是永久存儲捂齐,memory是臨時存儲蛮放。
? ? 2)? evm兩種類型:值類型 和引用類型。值類型變量之間賦值奠宜,都是 值傳遞(拷貝出一份獨立)包颁。而復雜類型變量,如array\struct\mapping變量之間賦值則有兩種:值傳遞(拷貝出一份獨立) 和 引用傳遞压真。
3)所有的復雜類型如數(shù)組(arrays)和結構體(struct)有一個額外的屬性:數(shù)據(jù)的存儲位置(data location)娩嚼。可為memory和storage滴肿≡牢颍基本類型則類似java等語言的機制。
a. 函數(shù)入?yún)⒑头祬⑵貌睿瑥碗s類型默認是memory贵少。
b. 局部復雜類型變量(local variables)和 狀態(tài)變量(state variables) 默認是storage。
堆缘?滔灶?不明白為何 局部復雜類型變量(local variables) 會設計成 storage ?吼肥?
? ? 4)? storage 變量 之間賦值:引用傳遞录平;
? ? ? ? memory 變量 之間賦值:引用傳遞;
? ? ? ? storage 變量 和 memory 變量 之間賦值:傳值缀皱,就是拷貝出獨立一份萄涯,之后就各不相關;
memory 變量 不可以賦值給局部復雜類型變量(storage local variables) K艏Α@杂啊?争占?
這點真是令我感覺不解燃逻?P蚰俊!為何? memory變量 可以賦值給? 狀態(tài)復雜類型變量(storage)伯襟,卻不可以賦值給 局部復雜類型變量(storage)
? ? 我的嘗試猿涨,針對 復雜類型(array\struct):
a.? 復雜類型 作為函數(shù)入?yún)⒑头祬?時,復雜類型默認也是memory姆怪,可以顯式聲明為storage 葱色。
b.? 復雜類型 作為函數(shù)體內的局部變量 時坚芜,復雜類型默認也是storage 。
c.? memory復雜類型變量 ,不可以賦值給局部復雜類型變量(storage) 和 作為函數(shù)入?yún)⒌膕torage 復雜類型變量英融。
d.? 也就是說但校,memory復雜類型變量光稼,僅僅可以賦值給狀態(tài)復雜類型變量(storage)? @ご巍!
這些坑爹的限制揪胃,會經常在 復雜類型賦值環(huán)節(jié) 絆倒我們璃哟,因為我們不僅僅要考慮是否可以成功賦值,還要顧慮 調用棧里的各函數(shù)在入?yún)r會拷貝過多而導致的性能喊递、浪費問題K嫔痢!骚勘!我覺得蕴掏,只要一個 memory復雜類型 最終是要 永久存儲到evm的 (如 新增一條業(yè)務數(shù)據(jù)最終是需要存儲的 ),那么我們很應該一開始就將之 “storage化”后再進入 調用棧中调鲸。這樣“一開始就將之storage化”其實更方便了我們編程盛杰,因為storage變量 是可以無限制地 賦值給 memory變量的。因此藐石,大膽storage化 吧<垂!
? ? 當然也有人問:函數(shù)返參struct于微,我們應該顯式聲明storage逗嫡,還是直接默認memory?
? ? 顧慮這問題的開發(fā)們,往往是擔心返回 storage變量 會被惡意修改存儲株依。這是因為開發(fā)的思維還停留在java語言驱证。
? ? 其實這不用顧慮,因為 既然 struct作為返參恋腕,那么 此函數(shù)肯定是 internal了抹锄,即別的合約無法external訪問的。
? ? 注意:struct中含有mapping時,當storage 的struct變量賦值給memory struct變量伙单,其mapping是不會拷貝的获高。因此當訪問 memory struct變量 中的mapping 是會編譯出錯的。
5)? 我們在聲明 復雜類型變量時吻育,有些情形可以修改 其默認數(shù)據(jù)位置念秧。如下圖,
1)函數(shù)入?yún)⒒貐?默認是memory 布疼,可是我們可以通過聲明 storage 改變其默認位置摊趾。
2)復雜類型的局部變量,默認是 storage游两,可是我們可以通過 memory 聲明改變其默認位置砾层。這樣,就可以將? memory復雜類型入?yún)?賦值給 memory復雜類型局部變量 了 器罐。
詳細介紹如下:
memory賦值給局部變量
綜上,storage復雜類型變量渐行,只能被storage復雜類型變量賦值 轰坊。
16.? 定長字節(jié)數(shù)組,非常好用哦祟印!
? ? 1)? byte 是基本數(shù)據(jù)類型肴沫,bytesN 是數(shù)組,復雜類型蕴忆。
? ? ? ? byte? == bytes1?
? ? 2)? 任何基本類型 本質都是 二進制字節(jié)組合颤芬。所以 任何基本類型 都可以 賦值給 bytesN,只要N足夠大(即字節(jié)數(shù)組空間足夠大)套鹅。
? ? 3)? 定長字節(jié)數(shù)組 和 其他基本數(shù)據(jù)類型 之間交互案例:
? ? 可以看到站蝠,bytesN 的設計 和 其他語言中的字節(jié)數(shù)組不一樣的地方:基本數(shù)據(jù)類型 可以直接賦值? 給 字節(jié)數(shù)組!
? ? 4)
18.Solidity封裝了兩種函數(shù)的調用方式internal和external卓鹿。
Solidity 中函數(shù) 提供了 external \ public \ internal \ private 這四種可見性聲明菱魔。
注意:1.? 別弄混了 調用方式(internal和external) 和 可見性聲明(internal和external)。
? ? 2.? 可見性聲明的函數(shù)中吟孙,external? 和 public 函數(shù)是采用消息調用的方式澜倦;public \ internal \ private 函數(shù) 是 internal調用方式。
3. internal調用杰妓,實現(xiàn)時轉為簡單的EVM跳轉藻治,所以它能直接使用上下文環(huán)境中的數(shù)據(jù),對于引用傳遞時將會變得非常高效(不用拷貝數(shù)據(jù))巷挥。
4. external調用桩卵,實現(xiàn)為合約的外部消息調用。所以在合約初始化時不能external的方式調用自身函數(shù),因為合約還未初始化完成吸占。
疑問晴叨??為何external不可以讓 當前合約內的函數(shù)直接訪問矾屯?兼蕊?
19.? 數(shù)組:
? ? ? ? a.編譯期定長數(shù)組 ,如uint[12], bytes3 等等件蚕;
? ? ? ? b.變長數(shù)組孙技,如uint[]\bytes\string 等等;
20.
疑問排作?牵啦?
stateVar[stateVar.length++] = a;
是否是拆解成:
stateVar[stateVar.length] = a;
stateVar.length++ ;
21.? 對于memory的變長數(shù)組,不支持修改length屬性妄痪,來調整數(shù)組大小哈雏。memory的變長數(shù)組雖然可以通過參數(shù)靈活指定大小,但一旦創(chuàng)建衫生,大小不可調整裳瘪。
22. 多維數(shù)據(jù)的定義與非區(qū)塊鏈語言類似。如罪针,我們要創(chuàng)建一個長度為5的uint數(shù)組彭羹,每個元素又是一個變長數(shù)組。將被聲明為uint[][5](注意泪酱,定義方式對比大多數(shù)語言來說是反的派殷,使用下標訪問元素時與其它語言一致)。
//一個變長的數(shù)組墓阀,里面的每個元素是一個長度為2的數(shù)組毡惜。
? ? //定義方式與常規(guī)語言相反
? ? bool[2][] flags;
23.? bytes 等同于 byte[], length自增\push()使用 和 byte[]無異。
? ? string 雖然是變長數(shù)組斯撮,可是 無法通過 length自增虱黄、push() 來擴容。
24.? 由于bytes與string吮成,可以自由轉換橱乱,你可以將字符串s通過bytes(s)轉為一個bytes。但需要注意的是通過這種方式訪問到的是UTF-8編碼的碼流粱甫,并不是獨立的一個個字符泳叠。比如中文編碼是多字節(jié),變長的茶宵,所以你訪問到的很有可能只是其中的一個代碼點危纫。
25.? 狀態(tài)變量:struct數(shù)組,為何就會出錯?种蝶?
? ? 難道
26.? struct 類型變量契耿,目前還不可以跨合約傳遞,所以折中方案是拆解到 一個Bean性質的合約中螃征。
27. 我對 Mapping 非常無語搪桂!
mapping 只需要是 狀態(tài)變量!盯滚!
由上踢械,mapping 僅僅被允許storage聲明!魄藕! 如果顯式聲明為memory 則肯定錯内列!
然而這樣為何又報錯?
storage 聲明只應用在 數(shù)組和struct 類型的變量上背率,不允許顯式應用在 mapping變量话瞧。但是局部變量mapping 的的確確又是 storage ,為何就不允許顯式聲明呢寝姿?真是奇怪
交排??死記先: mapping 只能是狀態(tài)變量会油,且不可以顯式聲明storage个粱、memory !!
28. 我覺得應該是:
Type is required tobe able tolive outside storage.
29. 簡單總結下 :(采坑死記古毛,原因不解翻翩?)
a. solidity語言中“容器”性質的類型,就三個:array稻薇、struct嫂冻、mapping。由于 數(shù)據(jù)位置特性 導致的轉換問題 都是發(fā)生在它們身上塞椎。
b. mapping 只能聲明為 狀態(tài)變量 或 局部變量桨仿,不可以聲明在 函數(shù)入?yún)⒑头祬?上。且當 局部變量mapping 還不可以 顯式聲明為 storage !(原因不解案狠?服傍?)
c. 從b可以總結出,
? ? 1.? storage顯式聲明骂铁,只能用在 array吹零、struct 上了。
? ? 2.? 函數(shù)入?yún)⒑头祬?拉庵,只允許是 基本數(shù)據(jù)類型 和 array灿椅、struct 。struct作為 入?yún)⒑头祬?還強制需要將函數(shù)定義為 internal 。
對于傳值的基本數(shù)據(jù)類型茫蛹,愛咋搞就咋搞操刀,反正是傳值。
30.? solidity中變量聲明后都會有初始值婴洼,就是“零態(tài)”骨坑。solidity中沒有 null 或 undefined 的語義。下面是各基本類型的初始值窃蹋,就是“零態(tài)":
在上述例子中卡啰,
對于基本數(shù)據(jù)類型,bool的默認值為false警没,bytes32的默認值為32字節(jié)長的0匈辱。
疑惑:那么 uint8[5] x ; x[0] 默認是0 嗎?杀迹?
對于引用類型亡脸,動態(tài)數(shù)組bytes類型默認值為空字節(jié)數(shù)組,string為默認值為空串树酪,動態(tài)數(shù)組uint8[] memory arr為空數(shù)組浅碾。都是空字節(jié)!運行時異常:Exception during execution. (invalid opcode)
31.引用類型變量初始化:
1. 動態(tài)數(shù)組:需要new 分配空間才可使用续语,否則報錯:Exception during execution. (invalid opcode)
2.? mapping \ enum\ stuct不需要顯式通過 new 分配空間垂谢,可以直接使用!
? ? ? ? 其中疮茄,
? ? ? ? mapping聲明即可用滥朱,沒有任何k-v; 訪問m[k],只會返回 v類型的 ”零值“
enum 聲明即可用力试,默認值將為0徙邻。即順位第一個值。
struct 聲明即可用畸裳,默認值將為基本類型的默認值缰犁。
32.delete 的用法
? ? 使用delete操作符會將對象重置為默認值。
1)對于基本類型怖糊,使用delete會設置為對應的初始值
2)刪除枚舉類型時帅容,會將其值重置為序號為0的值。
3)刪除一個結構體伍伤,會將其中的所有成員變量一一置為初值
? ? 4)刪除 mapping并徘,無效果!? 刪除 mapping中的一個k-v嚷缭,有效果R鳌耍贾!
? ? 5)刪除 struct中的 mapping,也是無效果路幸!刪除動作會直接跳過mapping成員荐开。
? ? 6)刪除 定長數(shù)組:會將各數(shù)組元素 重置為 零值。
7)刪除 變長數(shù)組:則是將長度變 0 <螂取(所有元素都砍掉晃听!不要了)
? ? ? ? 這里有一個性能優(yōu)化的方案:定長數(shù)組重用 可以通過delete后直接重用,因為空間不變砰识;而變長數(shù)組能扒,delete后長度是置0 的,也就是說 又要重新new了辫狼,干脆就不重用變長數(shù)組了初斑。
? ? 8) 刪除數(shù)組的一個元素,會留個坑那里膨处。
刪除的注意事項:
就是說见秤,deletestorage局部變量(動態(tài)),需要先判空咯真椿!
??? storage局部引用鹃答,是什么一個存在?突硝? 和 傳統(tǒng)語言中的 局部引用的理解 完全不同啊??
33.函數(shù)作用域
變量無論在函數(shù)內什么位置定義测摔,其作用域均為整個函數(shù),而非大多數(shù)據(jù)語言常見的塊級作用域解恰。下面的例子會報錯Identifier already declared锋八。
34.var 可以 被賦值? 變長類型;變長類型 無法接收 var修噪。
可是查库, var 和 基本
這也可以看出 solidity的不靈活路媚。
35.? solidity中沒有null/undefined 之類的概念黄琼,所以都是返回默認值(零態(tài)值)。例如 uint , 函數(shù)返回0整慎。這種情況下脏款,我們要注意 零態(tài)值 和 業(yè)務值 重合時的區(qū)分。
36.? 函數(shù)修改器 使用一覽
? ? 注意點:1.? 第一個 Testx1 , 由于 false, 所以永遠無法進入函數(shù)體裤园。這種情況下撤师,solidity 是默認返回 所調用函數(shù)的 return 值類型 的 “零態(tài)值”的。
? ? ? ? ? ? 2.? _;? 意思是:向下傳遞 的意思拧揽。當只有一個 modifier 時剃盾,則是指函數(shù)體腺占;當有多個時,則是指按順序的下一個modifier痒谴。
? ? ? ? ? ? 3.? 子合約可以重寫父合約的modifier衰伯。
37.? Event
event luoEvent(string indexed idcard, string indexed name,? uint indexed age, string blogContent);
1.? event , 在執(zhí)行過程中,被稱為:事件积蔚∫饩ǎ可是事件發(fā)生時,就作為 日志 永久存在區(qū)塊鏈上尽爆。
2.? 一個合約發(fā)生過的所有event 是跟合約一起storage在evm上的怎顾。
3. event 聲明中的 indexed, 是指 為修飾的字段建立索引,以太坊中稱其為: topic漱贱。因為Topic是用于快速查找的槐雾,不能存任意長度的數(shù)據(jù)。
solidity中event最多只有3個indexed 字段幅狮,即 最多3個索引字段蚜退,或? 最多3個主題topic。
4.? 日志, 分為兩部分存儲: topic主題部分? 和 data彪笼。
topic 部分钻注,對于固定大小的類型數(shù)據(jù)字段,是直接存儲的配猫》担可是對于 string, bytes,數(shù)組 這些非固定大小的類型字段泵肄,則是存儲其字段字節(jié)的hash值(固定大小了)捆交。如上面案例中的 idcard 和 name 字段,都是存hash值而已腐巢。業(yè)內案例:https://ethereum.stackexchange.com/questions/6840/indexed-event-with-string-not-getting-logged
data部分品追, 可以直接存儲任意長度的類型。如案例中的 blogContent冯丙,一遍博文都可以肉瓦。可是注意的是胃惜,記錄日志 也是消耗gas的泞莉,只是極小量而已:日志每字節(jié)花費8gas,然而合約存儲每32字節(jié)花費20000gas船殉。
也由于 非固定大小的類型字段在topic存儲hash鲫趁,所以,當我們在 web3.js 中過濾 非固定大小的類型字段 時利虫,是要以其 hash 值作為過濾條件的挨厚;否則無法過濾堡僻。
5. event的應用場景:1) 充當異步的數(shù)據(jù)觸發(fā)器:用戶提交了transaction后,界面UI的腳本一直監(jiān)測合約的事件日志疫剃,一旦監(jiān)測到(交易被打包入塊了)苦始,則UI提示成功。如(當用戶存款后慌申,UI進行對應的更新)
2)將事件記錄為日志陌选,以一種低成本的存儲的形式使用。去中心化交易所的用戶登錄時蹄溉,通過檢索過濾出用戶的所有歷史操作日志咨油,比 “將用戶操作日志記錄存放到智能合約” 節(jié)省成本。
38.繼承
繼承中柒爵,容易忽略一種情況: 狀態(tài)變量的getter 函數(shù)役电,剛好 和 某個函數(shù) 同名了。這是不允許的棉胀。
39.多繼承
? ? 注意:1)出現(xiàn)重名函數(shù)法瑟,按照“最遠繼承原則”
2)指定調用父合約方法
? ? ? ? ? ? 雖然存在最遠繼承原則,但是我們仍可以在子合約中主動調用父合約被重寫的方法來觸發(fā)被覆蓋的函數(shù)唁奢。
3)super 關鍵字
和 java中的不同霎挟。super.func(), 意味著:將 多繼承中的所有父合約中的 和 func 函數(shù)同名的 都執(zhí)行一遍。具體執(zhí)行順序不清楚麻掸?酥夭?需要謹慎使用!如果出現(xiàn)不可把握的情況脊奋,可以直接? “指定調用父合約方法”? 的方式熬北。
40.? 如何判斷一個address上是否部署了合約?
41.? interface 在 合約設計 環(huán)節(jié)是非常重要的诚隙。
? ? 1.? 使用方式:Interface(address).doSth();?
2.當合約設計出現(xiàn)“交叉引用”時會無法編譯和部署讶隐,而接口 可以很好地幫我們解決這個問題【糜郑“ContractA引用ContractB”的設計巫延,轉化成? “ContractA引用ContractB的接口”,然后通過傳入ContractB的地址來按照:Interface(address).doSth() 來間接調用ContractB籽孙。
? ? ? ? 其實這個和java的接口使用有點類似烈评,只是java中只需要往 “主引用類中注入被引用類的對象一次即可”火俄,而合約中則是每次都要以Interface(address).doSth() 的方式 調用 “被引用合約”犯建。
醬紫設計, 主引用合約 不需要寫死 被引用合約瓜客,因此 被引用合約 的升級時适瓦,我們只需要往? 主引用合約 中設置 被引用合約 的新地址即可竿开。
案例:https://ethereum.stackexchange.com/questions/9189/contract-design-interfaces
設計可升級合約項目:https://blog.imaginea.com/interfaces-make-your-solidity-contracts-upgradeable/
https://ethereum.stackexchange.com/questions/2404/upgradeable-smart-contracts
3.? interface往往會定義好 event。這就猶如 java接口中我們往往會定義好常量 一般玻熙。 ?
42.? 合約public函數(shù)否彩,無法返回struct;
? ? 合約函數(shù)嗦随,可以返回動態(tài)數(shù)組列荔;
? ? 合約調用合約,無法接收動態(tài)數(shù)組枚尼;
? ? js調用合約,可以接收到動態(tài)數(shù)組贴浙;
那么,對于需要批量返回struct(數(shù)組)的場景署恍,我們如何處理崎溃??
? ? 1)返回一個struct時盯质,可以將struct字段作為函數(shù)返參袁串;
? ? ? ? 返回批量struct時,可以將struct字段數(shù)組作為函數(shù)返參呼巷;
? ? 2)將 struct字段字節(jié)化囱修,以特殊字符分隔,存入一個大字節(jié)數(shù)組中王悍;
43.? solidity中蔚袍,字符串拼接 不是用 + !配名!
https://ethereum.stackexchange.com/questions/729/how-to-concatenate-strings-in-solidity
? ? 先將各個string轉化成字節(jié)數(shù)組啤咽,再合并到一個更大的字節(jié)數(shù)組,然后轉會string 渠脉。
44. 可以考慮用js宇整、java寫一個工具,方便將struct 的字段拼接成json格式芋膘。
? ? 按照網上說法鳞青,不建議在 evm 中進行 struct->json之類的操作,因為非常expensive !!
? ? 不過为朋,在 bcos平臺臂拓,沒有gas成本負擔,可以隨便搞习寸!
https://ethereum.stackexchange.com/questions/6121/parse-json-in-solidity
45.? json字符串 小軍刀:
https://github.com/eBusinessMan/jsmnSol
Usage: 對 json格式的string 進行操作胶惰。
在 evm里面對string json操作非常費gas!不過對于一小段json的操作 ,還是可以接受的霞溪!
46.? solidity中孵滞,抽象合約 和 普通合約 的聲明是一樣的中捆,不跟java般得顯式聲明 abstract。不過坊饶,當我們錯誤地new 一個抽象合約時泄伪,就會出現(xiàn)編譯錯誤。
47.? solidity中匿级,interface的用法蟋滴,跟java中的很類似:控制反轉。合約A 引用 合約B的接口痘绎,而不是 合約B本身: ContractBInterface( contractBAddress )脓杉,我們只需要傳入 contractBAddress 即可 。
這種用法的好處明顯:1) ContractBInterface( contractBAddress )? 的方式可以方便 具體合約 的升級简逮; 2)如果不借用接口球散,當 合約之間出現(xiàn)交叉引用時,是無法部署的散庶。3)接口的出現(xiàn)蕉堰,也方便了 分工開發(fā)嘛!(定義了接口悲龟,就各自開發(fā)即可屋讶。)
48.? 定長數(shù)組 為何不可以賦值給 不定長數(shù)組變量?须教?
49.? solidity 沒有 += 皿渗??轻腺?
50.? 對于默認生成的Getter乐疆,在合約交互時,我們要留意返回值中是否有變長類型贬养。
這種情況尤其出現(xiàn)在 對struct的getter訪問時要考慮的摄欲。
上面的struct 如下:
下面是我的探索:
就是說幅慌,默認的getter中辫秧,struct中的string\string[]\mapping 是無法返回給調用合約的担锤!而且 即使是struct中的定長數(shù)組都是沒法直接返回的。這非常尷尬儿礼。
面對這種情況咖杂,我們能解決的是,在原合約中再寫一個Getter的Function 將原始getter封裝并返回需要的數(shù)據(jù)蚊夫。(我也很無奈诉字。。。)