0x00 前言vue
基於NEO進行dapp開發的過程當中,基於UTXO的GAS並不方便做爲dapp的代幣使用,因而爲了便於在DAPP中直接接入GAS,NEL顏值和技術擔當李總開發了基於NEP5的SGAS合約,用於與GAS進行一比一兌換,搭建了應用合約與UTXO之間的橋樑。git
儘管SGAS開發的初衷是爲了NNS域名競拍系統的功能實現,可是李總在設計的時候便將SGAS定義爲一種通用的NEP5合約,所以任何須要在DAPP中使用GAS做爲燃料的合約均可以直接使用SGAS。github
0x01 獲取SGAS安全
SGAS是NEP5的代幣合約,自己並無發行量,只有當用戶向SGAS合約地址轉入GAS時,SGAS合約纔會發行對應數量的SGAS代幣,並把新發行的SGAS轉給該用戶。app
接下來的內容設計應用合約腳本的構建,建議閱讀以前先了解下這方面的知識。測試
前文已經提到獲取SGAS須要用戶向SGAS合約地址充值指定金額的GAS,可是僅僅轉帳是沒有辦法實現SGAS發行的,由於GAS轉帳的過程是鑑權合約調用的過程,這個過程當中SGAS合約沒法進行數據的寫入,而SGAS發行代幣的方式就是經過數據的記錄,這也就意味着SGAS沒有辦法進行發行操做。爲了解決這個問題,在轉帳以後,還須要經過應用合約的調用過程來向用戶地址解鎖指定數量的SGAS,完成GAS兌換SGAS的過程。ui
var sb = new ThinNeo.ScriptBuilder(); sb.EmitParamJson([]); sb.EmitPushString('mintTokens'); sb.EmitAppCall(DAPP_SGAS); return sb.ToArray();
在構造交易的時候,傳入構造的腳本,這樣交易在完成了轉帳以後會直接執行交易內的腳本。兌換SGAS的指令是 mintTokens ,SGAS合約在接收到這個指令後,會經過runtime獲取交易中GAS的output,在對output進行驗證和統計後,向該用戶的帳戶解鎖對應數量的SGAS。設計
合約交易構造過程以下:code
let tran = Transfer.makeTran(target, asset, count); tran.type = ThinNeo.TransactionType.InvocationTransaction; tran.extdata = new ThinNeo.InvokeTransData(); //塞入腳本 (tran.extdata as ThinNeo.InvokeTransData).script = script; return await Transfer.signAndSend(tran);
target是SGAS合約地址,makeTran是合約交易中向交易中添加input和output的標準過程。 由於經過GAS兌換SGAS大部分仍是UTXO的操做,因此合約構造和操做起來並非很複雜,可是當經過SGAS兌換GAS的時候,因爲NEP5的諸多限制,操做起來就複雜多了。接口
0x02 兌換GAS
經過SGAS兌換GAS這個過程,若是真正理解了每一步的原理和緣由,對於NEO合約的理解應該算是絕對的一大進步,我這麼理解。我第一次看的時候懵懵懂懂,代碼量也不大,本身爲就看懂了,還跑了測試。可是當我用TS本身去寫這塊的調用代碼的時候,才發現舉步維艱,其操做之複雜,設計之精巧遠超我想象。這也是爲何我想把SGAS的調用單獨寫成一篇博客來介紹的緣由。
首先咱們須要明確幾件事:
以上四條是沒法違背的,在兌換GAS的過程當中,必須遵循。那如今就須要思考如下幾個問題:
首先咱們分析第一個問題,SGAS合約如何轉GAS。爲了帳戶的安全,NEO合約沒法進行主動的UTXO轉帳,必須由用戶進行鑑權調用,執行轉帳操做。也就是說,SGAS沒法主動退回GAS給用戶,必須由用戶來從SGAS帳戶轉出GAS。
第二個問題如何控制轉帳金額。有NEO帳戶而且進行過NEO或者GAS轉帳的童鞋應該知道,只要你有轉帳權限,那麼轉帳的金額徹底是由你本身指定的,也就是說,只要你願意,那你徹底能夠轉出一個帳戶裏的全部資產。可是對於SGAS合約來講,這樣就不行,一旦給了用戶轉帳的權限,咱們徹底沒法依靠上帝保佑全部用戶只會轉出他但願兌換的金額的GAS。
也就是說咱們如今咱們必須給用戶轉帳的權限,可是又須要限制用戶轉帳的金額。這怎麼辦?
同時,因爲GAS自己是藝UTXO的形式存在,而UTXO自己又是沒法分割,一次使用的,咱們沒法保證對於每個用戶但願兌換的GAS額度都恰好有對應金額的GAS的UTXO存在,那咱們如何分割UTXO來構造出用戶須要的UTXO呢?
固然,這些問題都是我在看到李總的解決方案以後,又經過幾乎一行代碼一行代碼向印煒大神請教以後才整理出的。
接下來就開始分析李總對於這一系列幾乎使得SGAS方案陷於不可行問題的解決方案。
第一步: 獲取可用UTXO。
因爲轉帳操做不管如何都須要用戶主動發起,因此用戶必須首先獲取SGAS合約地址的GAS的UTXO。
第二步:查詢UTXO可用性。
多出這一步的緣由是因爲兌換GAS操做的複雜性,在一個共識週期內沒法完成全部操做,因此本輪共識極可能會有以前共識週期中已經被標記的UTXO存在,咱們須要在使用以前檢測如下這個UTXO是否被標記過。
var sb = new ThinNeo.ScriptBuilder(); sb.EmitParamJson([output_hash]); sb.EmitPushString('getRefundTarget'); sb.EmitAppCall(DAPP_SGAS); return sb.ToArray();
第三步:拆分UTXO。
因爲UTXO只能使用一次,每次被使用事後就會報廢,所以咱們只能經過構造才能得到咱們須要數額的UTXO,經過SGAS合約給SGAS合約本身的地址轉帳構造出用戶須要的UTXO。同時用戶須要記錄下這個UTXO。
該步驟主體就是普通的轉帳操做,直接構造ContractTransaction就能夠,須要注意的地方是,這裏用的input和指向的output地址都須要是SGAS合約地址。此外,在添加見證人的時候,除了須要添加用戶簽名以外,還須要添加sgas合約腳本爲見證腳本。
let sb = new ThinNeo.ScriptBuilder(); sb.EmitPushString("whatever") sb.EmitPushNumber(new Neo.BigInteger(250)); tran.AddWitnessScript(SGASScript, sb.ToArray());
第四步:標記UTXO。
因爲轉帳GAS是個鑑權操做,因此咱們還須要在轉帳以後進行一個合約調用來標記用戶生成的對應UTXO,將這個UTXO標記爲只能該用戶領取。
標記UTXO自己是GSAS合約的工做,可是這部分功能還須要用戶進行觸發才能執行,在用戶拆分UTXO的時候,須要構造一個腳原本進行SGAS合約的調用:
var sb = new ThinNeo.ScriptBuilder(); sb.EmitParamJson([address_hash]); sb.EmitPushString('refund'); sb.EmitAppCall(DAPP_SGAS); return sb.ToArray();
第五步:領取GAS。
用戶在通過一個共識週期確認UTXO已經拆分完成而且標記完成以後就能夠經過記錄的UTXO來進行SGAS轉帳,因爲在SGAS應用合約執行期間用戶地址就已經和該UTXO進行了綁定所以,在轉帳的時候,只有該用戶能夠轉走這個UTXO。
至此,SGAS兌換GAS完成。
光是看描述原理的篇幅都知道這個SGAS兌換GAS是多麼複雜的一個過程,固然這也是由於目前NEO的一些限制致使的,若是將來NEO對DAPP開放更多的接口和權限,這個過程確定會大大簡化。
不知道看到這裏的人有沒有對李總的清奇腦洞震撼到,這鑑權調用和應用調用翻來倒去,對此稍有一點不清楚都會雲裏霧裏不知所云。尤爲是當全部的代碼都寫在同一個文件裏的時候,感受都是兩種人格在戰鬥。
參考資料:
SGAS:https://github.com/NewEconoLab/neo-ns/blob/master/dapp_sgas/sgas.cs
ThinSDK-cs:https://github.com/NewEconoLab/neo-thinsdk-cs/blob/master/smartContractDemo/tests/nns/sgas.cs
nel-wallet-vue: https://github.com/NewEconoLab/nel-wallet-vue