為了實(shí)現(xiàn)兩條EOSIO體系區(qū)塊鏈間token的跨鏈轉(zhuǎn)移,首先需要解決兩個(gè)難題,1.輕客戶端如何實(shí)現(xiàn),2.如何確??珂溄灰椎耐暾院涂煽啃?, 如何防止雙花和重放攻擊。下文均以EOS主網(wǎng)和BOS主網(wǎng)為例進(jìn)行說明,但本文檔適用于任何兩條EOSIO體系的區(qū)塊鏈。
?
一、術(shù)語
BOSIBC:是指BOSCORE技術(shù)團(tuán)隊(duì)開發(fā)的IBC合約及插件。
二、關(guān)鍵概念及數(shù)據(jù)結(jié)構(gòu)
· Simple Payment Verification (SPV)
簡單支付驗(yàn)證技術(shù)最早在中本聰?shù)?a href="http://ttokpm.com/tags/比特幣/" target="_blank">比特幣白皮書中提出Bitcoin,用于驗(yàn)證一筆交易存在于區(qū)塊鏈中。 SPV client存儲(chǔ)著連續(xù)的區(qū)塊頭,但沒有區(qū)塊體,因此只需占用很小的存儲(chǔ)空間。當(dāng)獲得一筆交易和這筆交易的Merkle path后,可以驗(yàn)證這筆交易是否 存在于區(qū)塊鏈上。
· Lightwight client (lwc)
輕客戶端,即SPV client,即由區(qū)塊頭組成的一條輕量的鏈。
· Merkle path 默克爾路徑。為驗(yàn)證一筆交易是否存在于某個(gè)區(qū)塊中,只需要提供交易原始數(shù)據(jù)和交易在所在區(qū)塊的merkle path,而無需提供整個(gè)區(qū)塊體,通過計(jì)算merkle path并和區(qū)塊頭中 記錄的merkle root對比,若相等,則說明此交易存在于此區(qū)塊中。merkle path也被稱為merkle branch。
· Block Producer Schedule
BP Schedule是基于DPOS機(jī)制的EOSIO體系公鏈用于決定生產(chǎn)區(qū)塊權(quán)利的技術(shù),新的BP Schedule是由上一批BP Schedule包含的Block Producers認(rèn)證通過后生效 以此確保嚴(yán)格的BP權(quán)利交接,在輕客戶端中跟隨對應(yīng)主網(wǎng)的BP Schedule是IBC系統(tǒng)邏輯的一項(xiàng)核心技術(shù)。
· forkdb
EOSIO節(jié)點(diǎn)在運(yùn)行時(shí),有兩個(gè)底層的db用于存儲(chǔ)區(qū)塊信息,一個(gè)是blog即block log,用于存儲(chǔ)不可逆區(qū)塊,一個(gè)是forkdb,用于存儲(chǔ)可逆區(qū)塊。 forkdb存儲(chǔ)的是當(dāng)前區(qū)塊鏈最頂端的一部分區(qū)塊信息,一個(gè)區(qū)塊首先要被forkdb接受才能最終進(jìn)入不可逆區(qū)塊,IBC系統(tǒng)在合約實(shí)現(xiàn)的輕客戶端主要參考了forkdb的邏輯。
三、輕客戶端
為了解決跨鏈問題,首先要解決的是輕客戶端如何實(shí)現(xiàn)的問題。
1. 輕客戶端運(yùn)行在哪里合理,合約中還是合約外,例如插件中;
2. 若運(yùn)行在合約中,是實(shí)時(shí)同步對方鏈的全部區(qū)塊頭數(shù)據(jù),還是根據(jù)需要同步一部分區(qū)塊數(shù)據(jù)來驗(yàn)證交易,因?yàn)槿绻饺繀^(qū)塊數(shù)據(jù)會(huì)消耗兩條鏈大量cpu資源。
3. 若運(yùn)行在合約中,如何確保輕客戶端的可信性,如何防止惡意攻擊,如何做到完全去中心化,不依賴對任何中繼節(jié)點(diǎn)的信任;
3.1 輕客戶端是否運(yùn)行在合約中
比特幣的輕客戶端最早是運(yùn)行在單個(gè)節(jié)點(diǎn)上(如個(gè)人電腦或去中心化比特幣手機(jī)錢包),用于驗(yàn)證交易是否存,并查看交易所在區(qū)塊深度。
IBC和去中心化錢包對輕客戶端的需求是不同的,去中心化錢包一般運(yùn)行在個(gè)人的手機(jī)App上,為用戶個(gè)人提供交易驗(yàn)證服務(wù),而IBC系統(tǒng)需要的輕客戶端 要對所有人公開可查可信,從這個(gè)角度看,一個(gè)能夠獲得大眾信任的輕客戶端只能運(yùn)行在合約中,因?yàn)橹挥泻霞s的數(shù)據(jù)是全局一致不可篡改的, 運(yùn)行在合約外則無法實(shí)現(xiàn)一個(gè)可信的輕客戶端,因此BOSIBC將輕客戶端運(yùn)行在合約中。
3.2 是否同步全量區(qū)塊頭信息
在比特幣和以太坊的輕客戶端中,會(huì)尋找一個(gè)起點(diǎn)和后續(xù)的一些驗(yàn)證點(diǎn),輕客戶端會(huì)同步起點(diǎn)后的全量的區(qū)塊頭信息。比特幣每年產(chǎn)生的所有區(qū)塊的區(qū)塊頭體積僅有4Mb, 按現(xiàn)在移動(dòng)設(shè)備的存儲(chǔ)能力,是完全可以容納的,并且同步這些區(qū)塊頭也不會(huì)消耗移動(dòng)設(shè)備大量計(jì)算資源。然而EOSIO的情況卻很不同, EOSIO每0.5秒一個(gè)區(qū)塊,實(shí)際測試可知,每添加一個(gè)區(qū)塊頭到合約中需要消耗0.5毫秒cpu,每刪除一個(gè)區(qū)塊頭需要0.2毫秒,因此每處理一個(gè)區(qū)塊頭需要0.7ms的cpu。 假設(shè)要同步對方EOSIO公鏈的全量區(qū)塊頭信息,按現(xiàn)在每個(gè)區(qū)塊總的cpu時(shí)間200ms計(jì)算,也就是需要一條鏈全部計(jì)算的0.7ms / 200ms = 0.35%才能實(shí)時(shí)全量同步 另一條鏈的所有區(qū)塊頭, 按實(shí)際全網(wǎng)抵押總量為4億token計(jì)算,如果再cpu繁忙時(shí)保證IBC系統(tǒng)正常工作,需要為push區(qū)塊信息的賬戶抵押 4億 * 0.35% = 140萬token,這是個(gè)很大的數(shù)目。又因?yàn)镋OSIO倡導(dǎo)多側(cè)鏈的生態(tài),假設(shè)未來有多條側(cè)鏈和EOSIO主網(wǎng)實(shí)現(xiàn)跨鏈,并且側(cè)鏈與側(cè)鏈間 也實(shí)現(xiàn)了一對多的跨鏈,按1對10計(jì)算,每條鏈需要維護(hù)10個(gè)輕客戶端,則只為了維護(hù)這些輕客戶端就需要消耗3.5%的單條鏈全網(wǎng)cpu,這個(gè)比例實(shí)在是太高了, 因此需要尋找更合理的方案。
設(shè)計(jì)跨鏈通信的過程是一種尋找可信證據(jù)的過程,有沒有一種方案即不需要同步全量區(qū)塊信息,又可以保證輕客戶端的可信性,EOSIO底層已經(jīng)為實(shí)現(xiàn)這一目的有所準(zhǔn)備。 我們先假設(shè),如果BP schedule自始至終不會(huì)變化,那么任何時(shí)候,當(dāng)ibc.chain合約中獲得一連串的簽名驗(yàn)證通過的區(qū)塊頭,比如第n ~ n+336個(gè), 并且有2/3以上的活躍bp在出塊,就可以確信第n個(gè)區(qū)塊已經(jīng)是不可逆的,可以用于驗(yàn)證跨鏈交易。 然后,就是需要考慮有BP schedule更換的情況了,當(dāng)出現(xiàn)BP schedule更換時(shí),不在接受交易驗(yàn)證,直到更換完成,處理BP更換時(shí)相對復(fù)雜的過程,后續(xù)會(huì)更詳細(xì)介紹, 因此使用這個(gè)方案就可以大大降低需要同步的區(qū)塊頭數(shù)量,只有在BP列表更換或有跨鏈交易時(shí)才需要同步區(qū)塊。
為了實(shí)現(xiàn)這一目的,在ibc.chain中引入了概念section,一個(gè)section記錄的是一段連續(xù)的區(qū)塊頭信息,section結(jié)構(gòu)不存儲(chǔ)具體的區(qū)塊頭信息,而是記錄這一段 區(qū)塊頭的第一個(gè)區(qū)塊編號(first)和最后一個(gè)區(qū)塊編號(last),具體區(qū)塊頭信息在chaindb中存儲(chǔ),每個(gè)section都有一個(gè)valid值,在沒有bp schedule更替的時(shí)候, 只要有2/3的活躍BP在出塊,并且last - first 》 lib_depth則認(rèn)為first ~ last - lib_depth的區(qū)塊是不可逆的,可以用于驗(yàn)證跨鏈交易, 當(dāng)遇到BP schedule 更替,section的valid變?yōu)閒alse,不再接受交易驗(yàn)證,直到schedule更替完成,valid重新變?yōu)閠rue之后,繼續(xù)驗(yàn)證跨鏈交易。
3.3 如何確保輕客戶端的可信性
3.3.1 forkdb
1.一個(gè)新的區(qū)塊是如何追加到forkdb的
一個(gè)運(yùn)行的nodeos節(jié)點(diǎn)維護(hù)著兩個(gè)底層數(shù)據(jù)結(jié)構(gòu)blog 和forkdb, blog用于存儲(chǔ)不可逆的區(qū)塊信息,其存儲(chǔ)的數(shù)據(jù)是序列化的signed_block,forkdb用于存儲(chǔ)可逆區(qū)塊信息,其存儲(chǔ)的數(shù)據(jù)是block_state, block_state比signed_block包含更多區(qū)塊相關(guān)信息。一個(gè)區(qū)塊首先要被追加到forkdb,才可能最終變?yōu)椴豢赡鎱^(qū)塊而移除forkdb進(jìn)入blog, 一個(gè)區(qū)塊的block_state信息是如何獲得的呢,并非生產(chǎn)區(qū)塊的BP將所生產(chǎn)區(qū)塊的block_state通過p2p網(wǎng)絡(luò)傳遞給其他bp和全節(jié)點(diǎn),p2p網(wǎng)絡(luò) 只傳遞signed_block, 當(dāng)一個(gè)節(jié)點(diǎn)通過p2p網(wǎng)絡(luò)接收到一個(gè)signed_block后,它會(huì)使用此signed_block構(gòu)建block_state并驗(yàn)證簽名 相關(guān)函數(shù), 其中需要說明的幾個(gè)關(guān)鍵點(diǎn)是,1.blockroot_merkle,2.get_scheduled_producer(),3.verify_signee()。
blockroot_merkle
EOSIO在block_state::block_header_state結(jié)構(gòu)中維護(hù)了一個(gè)blockroot_merkle的incremental_merkle數(shù)據(jù),incremental_merkle實(shí)際是 一個(gè)完整的merkle樹的活躍節(jié)點(diǎn),使用incremental_merkle只需維護(hù)極少的活躍節(jié)點(diǎn)信息即可不斷累加并獲得merkle_root,是block_state 中使用的一個(gè)關(guān)鍵技術(shù)。BOSIBC的ibc.chain合約同樣使用了此數(shù)據(jù)結(jié)構(gòu)。
blockroot_merkle從創(chuàng)世區(qū)塊id不斷累加,但是signed_block和blog中并沒有這個(gè)數(shù)據(jù),只有forkdb的每個(gè)block_state中記錄著當(dāng)前block的 blockroot_merkle,并且此值被用于計(jì)算區(qū)塊簽名。
get_scheduled_producer()
此函數(shù)根據(jù)一個(gè)區(qū)塊的header.timestamp計(jì)算出應(yīng)該生成此區(qū)塊的producer_key(見block_header_state::next()),為后續(xù)驗(yàn)證簽名做準(zhǔn)備。
驗(yàn)證簽名相關(guān)函數(shù)如下
digest_type block_header_state::sig_digest()const {
auto header_bmroot = digest_type::hash( std::make_pair( header.digest(), blockroot_merkle.get_root() ) );
return digest_type::hash( std::make_pair(header_bmroot, pending_schedule_hash) );
}
public_key_type block_header_state::signee()const {
return fc::crypto::public_key( header.producer_signature, sig_digest(), true );
}
void block_header_state::verify_signee( const public_key_type& signee )const {
EOS_ASSERT( block_signing_key == signee, wrong_signing_key, “block not signed by expected key”,
(“block_signing_key”, block_signing_key)( “signee”, signee ) );
}
驗(yàn)證簽名的第一步是獲得區(qū)塊摘要,即sig_digest(),此函數(shù)中用到了header.digest(),blockroot_merkle.get_root()和pending_schedule_hash; 第二步是獲得簽名公鑰,即signee(),通過區(qū)塊的producer_signature和sig_digest()計(jì)算BP公鑰; 第三步是驗(yàn)證公鑰是否正確,即verify_signee(),此函數(shù)在block_header_state::next()被調(diào)用;驗(yàn)證通過后,一個(gè)區(qū)塊被追加的forkdb中的分支中。
所以在forkdb中每添加一個(gè)區(qū)塊都經(jīng)過了非常嚴(yán)格全面的效驗(yàn),核心是包括blockroot_merkle,get_scheduled_producer()和verify_signee(), 在ibc.chain合約完全繼承了forkdb嚴(yán)格的效驗(yàn)。
2.forkdb如何處理分叉
當(dāng)添加一個(gè)新的區(qū)塊導(dǎo)致fordb的head.id和controller_impl的head.id不同時(shí),則重新選擇分支。 源碼參考eosio::chain::controller_impl的push_block()和maybe_switch_forks();
3.LIB如何確定
EOSIO目前使用的共識方式是dpos,當(dāng)構(gòu)造一個(gè)區(qū)塊的block_header_state時(shí)會(huì)設(shè)定required_confs,此值為當(dāng)前活躍BP數(shù)量的2/3+1, 在21個(gè)BP的情況下,required_confs為15。每個(gè)區(qū)塊頭中都有header.confirmed,用于對前面的區(qū)塊進(jìn)行確認(rèn),每個(gè)區(qū)塊得到一個(gè)確認(rèn), 其required_confs會(huì)減1,當(dāng)某個(gè)區(qū)塊的required_confs減少到零時(shí),此區(qū)塊會(huì)被最新區(qū)塊(即forkdb的head)提名為dpos_proposed_irreversible_blocknum, 當(dāng)某個(gè)區(qū)塊獲得了2/3的BP提名后,其變?yōu)椴豢赡鎱^(qū)塊,即進(jìn)入LIB。由于確認(rèn)的信息是在header中傳遞的,因此一個(gè)區(qū)塊從產(chǎn)生到進(jìn)入LIB總共需要 兩個(gè)2/3輪,也就是 12 *( 14 * 2 ) = 336才會(huì)進(jìn)入LIB,考錄到BP每次都是連續(xù)出12個(gè)區(qū)塊,只有第一個(gè)區(qū)塊的header.confirmed為非零, 因此當(dāng)一個(gè)BP開始出塊時(shí),只有第一個(gè)區(qū)塊會(huì)提升LIB,因此實(shí)際head和LIB的差距在325至336之間,但是在有BP丟塊的情況下,head和LIB的差距可能出現(xiàn)小于 325至336。
4.BP列表是如何更換的
在pow的區(qū)塊鏈中,比如比特幣和以太坊,是選擇算力累加最大的分叉作為主鏈,一個(gè)區(qū)塊只有包含一定的算力才有可能被認(rèn)可,最終變?yōu)椴豢赡妗?而在以dpos為共識算法的EOS中,區(qū)塊被認(rèn)可的標(biāo)記是BP簽名,因此BP列表在EOSIO中具有至關(guān)重要的地位。IBC的輕客戶端同樣需要維護(hù)BP列表和BP列表的更換, 因此需要透徹分析BP列表的更換邏輯。
第一步,在系統(tǒng)合約eosio.system的onblock()函數(shù)中,系統(tǒng)會(huì)每分鐘一次嘗試更新bp列表update_elected_producers( timestamp ),此函數(shù)最終調(diào)用 wasm接口set_proposed_producers(),通過一系列檢查后,會(huì)將新的schedule和當(dāng)前區(qū)塊編號存到global_property_object對象中。
第二部,當(dāng)此區(qū)塊變?yōu)椴豢赡嬷?,?huì)在當(dāng)前的pending區(qū)塊中設(shè)置新的名單header.new_producers,并重置global_property_object對象,當(dāng)前 pending區(qū)塊編號會(huì)被記錄到pending_schedule_lib_num,此時(shí)在nodeos日志中可以看到新的名單;具體邏輯參考controller::start_block() // Promote proposed schedule to pending schedule.。 也就是說新的名單從proposed schedule變?yōu)閜ending schedule大約需要經(jīng)歷325至336個(gè)區(qū)塊。從這里開始, 后面區(qū)塊block_header_state的pending_schedule.version會(huì)比active_schedule.version大1.
第三步,當(dāng)pending_schedule_lib_num變?yōu)椴豢赡婧?,active_schedule會(huì)被pending schedule替換,整個(gè)的BP更換過程完成。 從pending schedule出現(xiàn)到其變?yōu)閍ctive_schedule同樣需要經(jīng)歷約325至336區(qū)塊。
IBC系統(tǒng)的輕客戶端同樣需要繼承forkdb的這些邏輯,才能實(shí)現(xiàn)可信的輕客戶端。然而,輕客戶端是在合約中實(shí)現(xiàn),需要充分考慮合約的特性和限制, 因此在實(shí)現(xiàn)細(xì)節(jié)上,需要做諸多調(diào)整。
3.3.2 eosio::table(“chaindb”), ibc.chain合約中的forkdb
1.輕客戶端(lwc)的LIB如何確定
有兩種方案,一種是完全按forkdb的邏輯,維護(hù)一整套confirm_count和confirmations等block_header_state相關(guān)信息,每添加一個(gè)區(qū)塊 計(jì)算一次LIB,這樣做的優(yōu)點(diǎn)是可以準(zhǔn)確獲得實(shí)時(shí)LIB值,然而對于輕客戶端來說,其關(guān)心的是區(qū)塊已經(jīng)不可逆,而并非實(shí)時(shí)的精確LIB值。有沒有更簡單的方案呢, 根據(jù)上述的邏輯,如果有活躍的2/3以上的BP在出塊,并且某個(gè)區(qū)塊的深度超過336,則此區(qū)塊一定是不可逆的,可以用于驗(yàn)證跨鏈交易;使用這種方案, 可以簡化合約中forkdb的復(fù)雜度。
ibc.chain合約中表global的lib_depth是一個(gè)深度值,當(dāng)在一段連續(xù)的區(qū)塊頭中,某個(gè)區(qū)塊頭的深度超過此值時(shí),則認(rèn)為不可逆,可以用于驗(yàn)證交易了。 此值應(yīng)該設(shè)置多少合適呢,可以設(shè)置成336,當(dāng)輕客戶端檢查到有2/3以上的BP在出塊,則認(rèn)為深度超過了336的區(qū)塊是不可逆的。然而在合約中添加和刪除區(qū)塊頭 是非常耗cpu的,實(shí)際測試可知,每添加一個(gè)區(qū)塊頭需要消耗0.5毫秒cpu,每刪除一個(gè)區(qū)塊頭需要0.2毫秒,因此每個(gè)區(qū)塊頭需要0.7ms的cpu。 是否會(huì)出現(xiàn)主網(wǎng)巨大波動(dòng),導(dǎo)致沒有進(jìn)lib的區(qū)塊全部回滾呢,這是有可能的,實(shí)際也發(fā)生過,因此要必須確保一筆跨鏈交易所在區(qū)塊進(jìn)入lib,再開始處理。 插件在此時(shí)可以起到一定的作用,在BOSCORE技術(shù)團(tuán)隊(duì)研發(fā)的ibc_plugin中,設(shè)置了參數(shù),只處理一定深度內(nèi)的跨鏈交易,這樣,插件中的深度和合約中的深度相加 超過336即可。這只是BOSIBC初期的做法,目的是避免消耗大量cpu,后續(xù)可能會(huì)考慮將合約中的深度設(shè)置為336,從而完全不依賴中繼的深度,然而無論是插件 還是合約中增加深度值,都會(huì)直接延長跨鏈交易到賬時(shí)間,從而影響用戶體驗(yàn),因此需要根據(jù)實(shí)際情況確定一個(gè)合理的值,從而即保證足夠安全又不失良好的用戶體驗(yàn)。
2.表chaindb
表chaindb是ibc.chain合約中的forkdb,ibc.chain合約中沒有blog結(jié)構(gòu),和eosio中forkdb使用的block_header_state不同,chaindb進(jìn)行了大量精簡,只保留了 block_num、block_id、header、active_schedule、pending_schedule、blockroot_merkle、block_signing_key7個(gè)數(shù)據(jù),又因?yàn)?bp schedule需要占用大量空間,因此在另外的表prodsches中存儲(chǔ)實(shí)際schedule信息,在chaindb中用id引用,以節(jié)省內(nèi)存占用和wasm cpu消耗。
3.輕客戶端的創(chuàng)世區(qū)塊
輕客戶端需要一個(gè)可信的起點(diǎn),此起點(diǎn)是輕客戶端的創(chuàng)世區(qū)塊頭,后面所有區(qū)塊頭的驗(yàn)證都是基于創(chuàng)世區(qū)塊頭的信息。 創(chuàng)世區(qū)塊頭需要block_header_state中的blockroot_merkle,active_schedule和header信息,才能驗(yàn)證區(qū)塊簽名。 源碼為chain::chaininit(),最重要一個(gè)限制是,創(chuàng)世區(qū)塊頭的pending_schedule必須和active_schedule相同,因?yàn)槠洳煌馕吨?此區(qū)塊是bp列表更替過程中的一個(gè)區(qū)塊,如果使用更替過程中的區(qū)塊,需要同步后續(xù)的區(qū)塊,直到active_schedule被pending_schedule替換, 增加了復(fù)雜度,因此這樣的區(qū)塊不適合作為創(chuàng)世區(qū)塊。
4.輕客戶端是如何添加新區(qū)塊的
輕客戶端添加header的方式和和eosio的forkdb非常類似。源碼見ibc.chain的chain::pushheader()。 第一步,通過區(qū)塊編號驗(yàn)證是否能夠連接到最新的section 第二部,是否需要處理分叉,刪除舊數(shù)據(jù)。在ibc.chainz中不會(huì)同時(shí)保存多個(gè)分支,而是以后者替代前者的方式實(shí)現(xiàn)對分叉的處理。 第三步,通過區(qū)塊id驗(yàn)證是否能夠連接到最新的section 第四部,構(gòu)造block_header_state,并驗(yàn)證BP簽名
其中最核心的是構(gòu)造block_header_state,在這個(gè)過程中處理的BP schedule的更換,確保有2/3以上活躍BP(見section_type::add())。
5. Section Section是chaindb的核心概念和創(chuàng)新,意思是一段連續(xù)的區(qū)塊頭,Section的管理也是最ibc.chain的核心的邏輯。 使用section的目的是降低cpu消耗,只有在BP schedule有變化或有跨鏈交易時(shí)才需要同步一部分區(qū)塊頭。 任何section的起始區(qū)塊不能是 bp schedule 更換過程中的區(qū)塊,也就是說,任何一個(gè)section的起始區(qū)塊的pending_schedule.version必須等于 active_schedule.version,并且一個(gè)section的起始區(qū)塊頭的active_schedule必須和前一個(gè)section最后區(qū)塊的active_schedule相同, 這樣就保證了在任意兩個(gè)section之間一定不存在BP schedule的更換,每一此BP schedule更換的完整過程必須在某個(gè)section中完成,從而確保 section數(shù)據(jù)的可信性。
四、跨鏈交易
1. 跨鏈交易三部曲
一筆跨鏈交易分為三個(gè)過程,下面以將EOS從EOS主網(wǎng)跨鏈轉(zhuǎn)賬到BOS主網(wǎng)為例說明,首先用戶在EOS主網(wǎng)發(fā)起一筆跨鏈交易o(hù)rig_trx,在EOS側(cè)ibc.token合約的 origtrx表中會(huì)記錄此交易信息,當(dāng)這筆交易所在區(qū)塊進(jìn)入lib后,EOS側(cè)IBC中繼(relay_eos)將此交易和交易相關(guān)信息(區(qū)塊信息及Merkle路徑) 傳遞到BOS側(cè)中繼(relay_bos);relay_bos構(gòu)造cash交易并調(diào)用BOS側(cè)ibc.token合約的cash接口,如果調(diào)用成功, cash函數(shù)中會(huì)給目標(biāo)用戶發(fā)行對應(yīng)的token;等cash交易所在的區(qū)塊進(jìn)入lib后,relay_bos會(huì)將cash_trx和此交易相關(guān)信息(區(qū)塊信息及Merkle路徑) 傳遞到relay_eos,relay_eos構(gòu)造cashconfirm交易并調(diào)用EOS側(cè)ibc.token合約的cashconfirm接口,cashconfirm會(huì)刪除EOS側(cè)ibc.token合約 中對orig_trx的記錄,至此一筆完整的跨鏈交易完成。
2. 跨鏈?zhǔn)〉慕灰?/p>
跨鏈交易是可能失敗的,比如指定的賬戶在對方鏈上不存在,或者由于網(wǎng)絡(luò)環(huán)境惡劣,導(dǎo)致調(diào)用cash接口失敗,未能成功跨鏈的交易會(huì)被回滾,即原路退還用戶的資產(chǎn), 然而現(xiàn)在的IBC系統(tǒng)是交易驅(qū)動(dòng)的,失敗的IBC交易需要等到有成功的IBC交易完成后才會(huì)被回滾。(注:后續(xù)版本升級會(huì)讓失敗的交易盡快回滾)
3. 如何防止replay攻擊,即雙花攻擊
防止雙花攻擊分為兩個(gè)階段:
1,一筆成功的跨鏈交易只能執(zhí)行一次cash,否則會(huì)造成重復(fù)cash。
2,對于每一個(gè)cash交易,必須將其相關(guān)信息傳回原鏈執(zhí)行cashconfirm,以消除合約中記錄的原始交易信息,否則會(huì)出現(xiàn)即在目的鏈上給用戶發(fā)行了token, 又將原鏈的token退還給了用戶。
cash函數(shù)是ibc.token的核心邏輯,ibc.token合約中記錄著最近執(zhí)行cash的原始交易id,即orig_trx_id,并且新的cash的orig_trx的區(qū)塊編號必須 大于或等于所有orig_trx所在的區(qū)塊編號,也就是說必須按原始交易在原鏈按區(qū)塊的順序進(jìn)行cash,(執(zhí)行cash時(shí),原鏈某個(gè)區(qū)塊內(nèi)的跨鏈交易順序是無關(guān)緊要的) ,再結(jié)合trx_id檢查,可以確保一筆跨鏈交易只能執(zhí)行一次cash。
同樣,cashconfirm接口會(huì)檢查cash交易的編號seq_num,此編號必須逐一遞增,以確保所有在目的鏈上的cash交易都會(huì)刪除在原鏈上的原始交易記錄, 從而確保不會(huì)出現(xiàn)雙花的情況。
五、插件
插件的作用分為兩部分:1,輕客戶端同步;2,跨鏈交易傳遞。
核心邏輯請參考ibc_plugin_impl::ibc_core_checker()
ibc_plugin主要參考了net_plugin的框架。
六、問答
問:IBC合約的多個(gè)action中用到了relay的權(quán)限,那么,本IBC系統(tǒng)是否依賴對中繼的信任。
答:目前出于安全以及快速功能迭代的考量,特意添加了中繼權(quán)限,隨著功能逐漸完善 BOS IBC 方案會(huì)支持多中繼機(jī)制,以避免單點(diǎn)風(fēng)險(xiǎn)。
驗(yàn)證relay權(quán)限處于兩種考慮:1,ibc.chain合約使用了section的機(jī)制,現(xiàn)在的邏輯不允許為舊的section添加區(qū)塊,也不允許在一個(gè)section前面 添加區(qū)塊頭,如果任何人都可以調(diào)用pushsection接口,假設(shè)應(yīng)該push的區(qū)塊范圍是1000-1300,故意搗亂的人可能會(huì)搶先push 1100-1300, 從而導(dǎo)致1000-1100無法被push,進(jìn)而導(dǎo)致一些跨鏈交易無法成功,(注,此問題會(huì)在后續(xù)版本中考慮優(yōu)化);2,考慮到IBC系統(tǒng)承載著 大量用戶資產(chǎn),并且本系統(tǒng)還未經(jīng)過長期市場考驗(yàn),因此增加了relay權(quán)限,以降低安全風(fēng)險(xiǎn)。
七、升級計(jì)劃
1. 兼容pbft
2. 以更優(yōu)雅的方式支持多條側(cè)鏈&EOS主鏈互相跨鏈
3. 支持token以外其他類型數(shù)據(jù)的跨鏈
評論
查看更多