NEO智能合約之發佈和升級(一)

前言

        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發佈合約

        首先咱們打開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工程

文中的例子  https://github.com/NewEconoLab/neo-thinsdk-cs/blob/master/smartContractDemo/tests/others/PubScDemo.cs

nep協議    https://github.com/neo-project/proposals

相關文章
相關標籤/搜索