nep7协议的发布,新增长了VerificationR和ApplicationR两种合约触发器。使合约可以拒绝接受资产,或者在接受资产的同时可以作一些操做。同时nep5协议也更新了transfer时须要验证目标帐户是否是容许得到资产。这要求咱们在发布合约时要对合约是否可收钱进行设置。而这个功能NeoGui的支持更新不是很到位,因此在本文中咱们将利用Nel的轻钱包(thinwallet)来发布和升级合约。html
在本文中会分为两部分,首先会介绍利用thinwallet发布和升级合约的操做流程,而后会介绍相关例子代码。例子的实现利用了nel大佬李总写的sdk来构造和解析数据。最下方会附上本文中涉及工具以及资讯的地址。git
首先咱们编写一个智能合约方便用如下面的流程,编译出对应的avm。github
using Neo.SmartContract.Framework; using Neo.SmartContract.Framework.Services.Neo; using System; using System.Numerics; namespace NeoContract1 { public class Contract1 : SmartContract { 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"); } } return false; } } }
首先咱们打开thinwallet主页面,并导入私钥。你能够在AccountInfo框中看到你全部的资产。因为发布合约是须要花费必定gas的,因此你须要在input框中拖入必定数量的utxo。c#
点击发布合约按钮打开以下图页面,导入咱们刚才生成的avm格式的合约文件,并设置相关参数。api
其中Parameter List和Return Type 是智能合约参数和返回值。具体的需求能够参考http://docs.neo.org/zh-cn/sc/Parameter.html函数
在咱们的例子中咱们的参数是(string method, object[] args)返回值是object 因此咱们这里的参数设置为0710返回值为05工具
Need Storage ,Need DynCall,Can Charge 这三个确认按键表明的是合约是否须要存储区,是否须要动态调用,可否收钱post
合约的hash是0x297c78a028bc387b71801be0156f9952ca5925ad测试
点击ok键回到主页面,点击test得到发布合约所需gas,因为例子合约中使用了存储空间,因此花费是490ui
点击Publish中的键进行签名和发送交易,等待交易确认。
· 等交易确认后,咱们点击AppCall键,来到下图的页面,输入咱们刚才合约的hash(0x297c78a028bc387b71801be0156f9952ca5925ad),点击LoadScript。若是参数框出现参数,则表明合约发布成功。
接下来咱们测试一下合约的put和get方法,首先将下方的textParam改为put发送交易,而后重复流程改为get,点击test,能够获得值。这里只附上调用get的图,能够看到获取到了存入的“1”
首先合约的发布是调用Neo.Contract.Create(byte[] script, byte[] parameter_list, byte return_type, bool need_storage, string name, string version, string author, string email, string description)函数来执行的。其中script是合约代码,parameter_list是参数列表,return_type是合约的返回值,name是合约的名称,version是版本号,author是做者,email是做者邮箱,description是合约的描述。
其中need_storage这个参数单独拉出来说,是由于need_storage从字面意思和官方介绍中都为是否须要持久化存储区。但随着nep4和nep7协议的发布,这个字段也增长了对是否动态调用以及是否为可收钱合约的兼容。如今的need_storage=need_storage | need_nep4 | need_canCharge; 其中的need_storage为是否须要存储区(0false,1true),need_nep4为是否须要动态调用(0false,2true),need_canCharge为是否为可收钱合约(0false,4true)。也就是说若是你的合约须要存储区也须要动态调用而且为可收钱合约,那么这个字段的值应该为7.
接下来附上代码片断,例子详情可见https://github.com/NewEconoLab/neo-thinsdk-cs/blob/master/smartContractDemo/tests/others/PubScDemo.cs 。
发布合约的构造代码以下
ThinNeo.ScriptBuilder sb = new ThinNeo.ScriptBuilder() //倒叙插入数据 sb.EmitPushString(description); sb.EmitPushString(email); sb.EmitPushString(auther); sb.EmitPushString(version); sb.EmitPushString(name); sb.EmitPushNumber(need_storage|need_nep4| need_canCharge); sb.EmitPushBytes(return_type); sb.EmitPushBytes(parameter__list); sb.EmitPushBytes(script); sb.EmitSysCall("Neo.Contract.Create"); string scriptPublish = ThinNeo.Helper.Bytes2HexString(sb.ToArray());
而后调用invokeScript来得到虚拟机执行这段脚本所需花费的gas(你能够调用本地cli或者nelApi的invoke来得到花费)
byte[] postdata; var url = Helper.MakeRpcUrlPost(api, "invokescript", out postdata, new MyJson.JsonNode_ValueString(scriptPublish)); var result = await Helper.HttpPost(url, postdata); //string result = http.Post(api, "invokescript", new MyJson.JsonNode_Array() { new MyJson.JsonNode_ValueString(scriptPublish) },Encoding.UTF8); var consume =((( MyJson.Parse(result) as MyJson.JsonNode_Object)["result"] as MyJson.JsonNode_Array)[0] as MyJson.JsonNode_Object)["gas_consumed"].ToString(); decimal gas_consumed = decimal.Parse(consume);
最后构造交易数据 下图中的makeTran是对tran的inputs和outputs进行构造
ThinNeo.InvokeTransData extdata = new ThinNeo.InvokeTransData(); extdata.script = sb.ToArray(); //Console.WriteLine(ThinNeo.Helper.Bytes2HexString(extdata.script)); extdata.gas = Math.Ceiling(gas_consumed-10); //拼装交易体 ThinNeo.Transaction tran = makeTran(dir,null, new ThinNeo.Hash256(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);
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,这里就不附上代码了。原理同样,本身对比。
升级的相关内容会在NEO智能合约之发布和升级(二)继续。
thinwallet https://github.com/NewEconoLab/neo-thinsdk-cs thinWallet工程
sdk for neo (c#) https://github.com/NewEconoLab/neo-thinsdk-cs thinSDK工程