接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工程