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

接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#

二    利用thinwallet升級合約

    在發佈上面的合約以後,我想增長一個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工程

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

相關文章
相關標籤/搜索