接NEO智能合約之發佈和升級(一),咱們接下來講說智能合約的升級功能。git
合約的升級須要在合約內預先設置好升級接口,以方便在升級時調用。接下來咱們對NEO智能合約之發佈和升級(一)中的合約例子進行改造,添加升級接口。併發布合約獲得合約的hash(0x8c4994ccf1c123f91090d07568653e54d74f307d),調用put方法在存儲區存入值。github
using Neo.SmartContract.Framework; using Neo.SmartContract.Framework.Services.Neo; using Neo.SmartContract.Framework.Services.System; using Helper = Neo.SmartContract.Framework.Helper; using System; using System.ComponentModel; using System.Numerics; namespace NeoContract1 { public class Contract1 : SmartContract { static readonly byte[] superAdmin = Helper.ToScriptHash("ALjSnMZidJqd18iQaoCgFun6iqWRm2cVtj");//管理員 public static object Main(string method, object[] args) { var magicstr = "NEL"; if (Runtime.Trigger == TriggerType.Verification)//取錢纔會涉及這裏 { return true; } else if (Runtime.Trigger == TriggerType.VerificationR)//取錢纔會涉及這裏 { return true; } else if (Runtime.Trigger == TriggerType.Application) { if (method == "put") { Storage.Put(Storage.CurrentContext, "put","1"); return true; } if (method == "get") { return Storage.Get(Storage.CurrentContext, "put"); } if (method == "upgrade")//合約的升級就是在合約中要添加這段代碼來實現 { //不是管理員 不能操做 if (!Runtime.CheckWitness(superAdmin)) return false; if (args.Length != 1 && args.Length != 9) return false; byte[] script = Blockchain.GetContract(ExecutionEngine.ExecutingScriptHash).Script; byte[] new_script = (byte[])args[0]; //若是傳入的腳本同樣 不繼續操做 if (script == new_script) return false; byte[] parameter_list = new byte[] { 0x07, 0x10 }; byte return_type = 0x05; bool need_storage = (bool)(object)05; string name = "test"; string version = "1.1"; string author = "NEL"; string email = "0"; string description = "test"; if (args.Length == 9) { parameter_list = (byte[])args[1]; return_type = (byte)args[2]; need_storage = (bool)args[3]; name = (string)args[4]; version = (string)args[5]; author = (string)args[6]; email = (string)args[7]; description = (string)args[8]; } Contract.Migrate(new_script, parameter_list, return_type, need_storage, name, version, author, email, description); return true; } } return false; } } }
代碼中咱們添加了upgrade的方法用以合約升級。在升級方法中,咱們須要驗證權限以確保安全性,而後就能夠調用升級函數進行合約的升級。合約升級以後,原合約會被銷燬,存儲區會被移到新的合約。c#
在發佈上面的合約以後,我想增長一個delete功能,來刪除某個存儲。因而我對原合約進行修改,添加了delete方法。從新編譯,獲得新的合約,hash(0x53ab4dfdae199b8d76f0eac8363fb07e652aef1f)。api
using Neo.SmartContract.Framework; using Neo.SmartContract.Framework.Services.Neo; using Neo.SmartContract.Framework.Services.System; using Helper = Neo.SmartContract.Framework.Helper; using System; using System.ComponentModel; using System.Numerics; namespace NeoContract1 { public class Contract1 : SmartContract { static readonly byte[] superAdmin = Helper.ToScriptHash("ALjSnMZidJqd18iQaoCgFun6iqWRm2cVtj");//管理員 public static object Main(string method, object[] args) { var magicstr = "NEL"; if (Runtime.Trigger == TriggerType.Verification)//取錢纔會涉及這裏 { return true; } else if (Runtime.Trigger == TriggerType.VerificationR)//取錢纔會涉及這裏 { return true; } else if (Runtime.Trigger == TriggerType.Application) { if (method == "put") { Storage.Put(Storage.CurrentContext, "put","1"); return true; } if (method == "get") { return Storage.Get(Storage.CurrentContext, "put"); } if (method == "delete") { Storage.Delete(Storage.CurrentContext, "put"); } if (method == "upgrade") { //不是管理員 不能操做 if (!Runtime.CheckWitness(superAdmin)) return false; if (args.Length != 1 && args.Length != 9) return false; byte[] script = Blockchain.GetContract(ExecutionEngine.ExecutingScriptHash).Script; byte[] new_script = (byte[])args[0]; //若是傳入的腳本同樣 不繼續操做 if (script == new_script) return false; byte[] parameter_list = new byte[] { 0x07, 0x10 }; byte return_type = 0x05; bool need_storage = (bool)(object)05; string name = "test"; string version = "1.1"; string author = "NEL"; string email = "0"; string description = "test"; if (args.Length == 9) { parameter_list = (byte[])args[1]; return_type = (byte)args[2]; need_storage = (bool)args[3]; name = (string)args[4]; version = (string)args[5]; author = (string)args[6]; email = (string)args[7]; description = (string)args[8]; } Contract.Migrate(new_script, parameter_list, return_type, need_storage, name, version, author, email, description); return true; } } return false; } } }
由於我想保留原合約的存儲區,因此我用升級功能來升級合約。安全
打開thinwallet,點擊Upgrade Sc(升級合約按鈕),出現以下頁面,填入相關數據。併發
點擊確認回到主頁面,點擊test按鈕。你會發現test以後返回的狀態是Fault,執行失敗。那是由於在升級合約裏,咱們驗證了調用者的權限,invoke調用並無簽名這一步。因此咱們須要本身在output裏本身添加一條丟棄掉。費用本身估算。app
點擊發送交易獲得交易id,等待交易確認。函數
交易確認後咱們AppCall原合約(0x8c4994ccf1c123f91090d07568653e54d74f307d),發現合約不存在。post
接下來咱們AppCall新合約(0x53ab4dfdae199b8d76f0eac8363fb07e652aef1f),發現合約存在,不調用put,直接調用get看是否能獲取到值。測試
以下圖,咱們在沒有調用put設置值的狀況下仍是get到了數據。說明存儲區被新的合約所繼承。
升級合約本質上就是調用原合約的升級函數來進行升級。調用Contract.Migrate方法,參數和create相同。這裏就不重複介紹了,詳情見NEO智能合約之發佈和升級(一)。
升級合約的構造代碼以下
ThinNeo.ScriptBuilder sb = new ThinNeo.ScriptBuilder(); //倒敘插入數據 var array = new MyJson.JsonNode_Array(); array.AddArrayValue("(bytes)" + str_script);//新的合約代碼 array.AddArrayValue("(bytes)0710"); array.AddArrayValue("(bytes)05"); array.AddArrayValue("(int)"+ 5); array.AddArrayValue("(str)合約測試");//name array.AddArrayValue("(str)1");//version array.AddArrayValue("(str)ss");//author array.AddArrayValue("(str)1");//email array.AddArrayValue("(str)sssss");//desc sb.EmitParamJson(array);//參數倒序入 sb.EmitParamJson(new MyJson.JsonNode_ValueString("(str)upgrade")); var shash = Config.dapp_sgas; //原合約hash sb.EmitAppCall(shash);
最後構造交易數據 下圖中的makeTran是對tran的inputs和outputs進行構造
ThinNeo.InvokeTransData extdata = new ThinNeo.InvokeTransData(); extdata.gas = 500;// Math.Ceiling(gas_consumed - 10); extdata.script = sb.ToArray(); //拼裝交易體 ThinNeo.Transaction tran = Helper.makeTran(dir[Config.id_GAS], null, new ThinNeo.Hash256(Config.id_GAS), extdata.gas); tran.version = 1; tran.extdata = extdata; tran.type = ThinNeo.TransactionType.InvocationTransaction; byte[] msg = tran.GetMessage(); byte[] signdata = ThinNeo.Helper.Sign(msg, prikey); tran.AddWitness(signdata, pubkey, address); string txid = tran.GetHash().ToString(); byte[] data = tran.GetRawData(); string rawdata = ThinNeo.Helper.Bytes2HexString(data); url = Helper.MakeRpcUrlPost(Config.api_local, "sendrawtransaction", out postdata, new MyJson.JsonNode_ValueString(rawdata)); result = await Helper.HttpPost(url, postdata);
ThinNeo.Transaction makeTran(Dictionary<string, List<Utxo>> dir_utxos, string targetaddr, ThinNeo.Hash256 assetid, decimal sendcount) { if (!dir_utxos.ContainsKey(assetid.ToString())) throw new Exception("no enough money."); List<Utxo> utxos = dir_utxos[assetid.ToString()]; var tran = new ThinNeo.Transaction(); tran.type = ThinNeo.TransactionType.ContractTransaction; tran.version = 0;//0 or 1 tran.extdata = null; tran.attributes = new ThinNeo.Attribute[0]; var scraddr = ""; utxos.Sort((a, b) => { if (a.value > b.value) return 1; else if (a.value < b.value) return -1; else return 0; }); decimal count = decimal.Zero; List<ThinNeo.TransactionInput> list_inputs = new List<ThinNeo.TransactionInput>(); for (var i = 0; i < utxos.Count; i++) { ThinNeo.TransactionInput input = new ThinNeo.TransactionInput(); input.hash = utxos[i].txid; input.index = (ushort)utxos[i].n; list_inputs.Add(input); count += utxos[i].value; scraddr = utxos[i].addr; if (count >= sendcount) { break; } } tran.inputs = list_inputs.ToArray(); if (count >= sendcount)//輸入大於等於輸出 { List<ThinNeo.TransactionOutput> list_outputs = new List<ThinNeo.TransactionOutput>(); //輸出 if (sendcount > decimal.Zero && targetaddr != null) { ThinNeo.TransactionOutput output = new ThinNeo.TransactionOutput(); output.assetId = assetid; output.value = sendcount; output.toAddress = ThinNeo.Helper.GetPublicKeyHashFromAddress(targetaddr); list_outputs.Add(output); } //找零 var change = count - sendcount; if (change > decimal.Zero) { ThinNeo.TransactionOutput outputchange = new ThinNeo.TransactionOutput(); outputchange.toAddress = ThinNeo.Helper.GetPublicKeyHashFromAddress(scraddr); outputchange.value = change; outputchange.assetId = assetid; list_outputs.Add(outputchange); } tran.outputs = list_outputs.ToArray(); } else { throw new Exception("no enough money."); } return tran; }
其中所用到的構造腳本的scriptBuilder和構造交易的Transaction都是用了李總寫的sdk。若是你要用原生的Transaction和ScriptBuilder,這裏就不附上代碼了。原理同樣,本身對比。
thinwallet https://github.com/NewEconoLab/neo-thinsdk-cs thinWallet工程
sdk for neo (c#) https://github.com/NewEconoLab/neo-thinsdk-cs thinSDK工程