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

相关文章
相关标签/搜索