文档章节

NEO智能合约之发布和升级(二)

红烧飞鱼
 红烧飞鱼
发布于 06/25 02:04
字数 1594
阅读 50
收藏 0
EAC

接NEO智能合约之发布和升级(一),我们接下来说说智能合约的升级功能。

一    准备工作

        合约的升级需要在合约内预先设置好升级接口,以方便在升级时调用。接下来我们对NEO智能合约之发布和升级(一)中的合约例子进行改造,添加升级接口。并发布合约得到合约的hash(0x8c4994ccf1c123f91090d07568653e54d74f307d),调用put方法在存储区存入值。

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的方法用以合约升级。在升级方法中,我们需要验证权限以确保安全性,然后就可以调用升级函数进行合约的升级。合约升级之后,原合约会被销毁,存储区会被移到新的合约。

二    利用thinwallet升级合约

    在发布上面的合约之后,我想增加一个delete功能,来删除某个存储。于是我对原合约进行修改,添加了delete方法。重新编译,得到新的合约,hash(0x53ab4dfdae199b8d76f0eac8363fb07e652aef1f)。

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里自己添加一条丢弃掉。费用自己估算。

点击发送交易得到交易id,等待交易确认。

交易确认后我们AppCall原合约(0x8c4994ccf1c123f91090d07568653e54d74f307d),发现合约不存在。

接下来我们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

© 著作权归作者所有

共有 人打赏支持
红烧飞鱼
粉丝 2
博文 4
码字总数 5340
作品 0
私信 提问
深入浅出区块链教程——21.国内区块链项目技术一览

国内区块链项目技术一览 小蚁 NEO 简介 我们以前讲到过,NEO 的前身是小蚁,小蚁最早在 2015 年发起,它在 2017 年中正式更名为 NEO。 NEO 项目一共经历过两次 ICO,第一次 ICO 是项目创立,...

纳兰少
08/17
0
0
让分叉成为历史?一个区块链世界的新物种

区块链技术如今正在经历快速变革,涌现出了像以太坊、EOS、NEO、Ripple、IOTA等热点项目。不过目前圈内很多讨论仍然围绕在如何在不妨碍创新和去中心化的前提下减少波动性。作为小蚁(NEO)的...

雪花又一年
04/23
0
0
NEO游戏大赛与Cocos达成战略合作 共建开发者生态圈

NEO游戏大赛与Cocos达成战略合作 共建开发者生态圈 2018-08-20 11:32编辑: yyuuzhu分类:区块链来源: NEO游戏大赛 招聘信息: iOS开发 iOS开发 iOS开发 app开发上架H5技术 app开发技术 图像...

yyuuzhu
08/20
0
0
区块链周刊(2018-08-12)

新闻 MetaMask发布4.9.0版本 在8月6日的发布会上,MetaMask的研发团队发布了最新版本:4.9.0。一些主要的更新包括对Trezor硬件钱包的支持以及与Ethereum Name Service (ENS)域的集成。区块链D...

区块链周刊
08/12
0
0
《Netkiller Blockchain 手札》智能合约收发 ETH 实例详解

智能合约收发 ETH 实例详解 本文节选自电子书《Netkiller Blockchain 手札》 Netkiller Blockchain 手札 Mr. Neo Chan, 陈景峯(BG7NYT) 中国广东省深圳市龙华新区民治街道溪山美地 518131 +...

netkiller-
07/24
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Confluence 6 教程:在 Confluence 中导航

当你对 Confluence 有所了解后,你会发现 Confluence 使用起来非常简单。这个教程主要是针对你使用的 Confluence 界面进行一些说明,同时向你展示在那里可以进行一些通用的任务和操作。 空间...

honeymose
今天
2
0
sed, awk 练习

1. sed打印某行到某行之间的内容 2. sed 转换大小写 将单词首字母转化大写 将所有小写转化大写 3. sed 在某一行最后面添加一个数字 4. 删除某行到最后一行 解析: {:a;N;$!ba;d} :a : 是...

Fc丶
今天
2
0
babel6升级到7,jest-babel报错:Requires Babel "^7.0.0-0", but was loaded with "6.26.3".

自从将前端环境更新到babel7,jest-babel之前是基于babel6的,执行时候就会报:Requires Babel "^7.0.0-0", but was loaded with "6.26.3". 很烦,因为连续帮好几台电脑修复这个问题,所以记...

曾建凯
今天
1
0
探索802.11ax

802.11ax承诺在真实条件下改善峰值性能和最差情况。 如何改善今天的Wi-Fi? 在决定如何改进当前版本以外的Wi-Fi时,802.11ac,IEEE和Wi-Fi联盟调查了Wi-Fi部署和行为,以确定更广泛使用的障碍...

linuxprobe16
今天
2
0
使用linux将64G的SDCARD格式化为FAT32

一、命令如下: sudo fdisk -lsudo mkfs.vfat /dev/sda -Isudo fdisk /dev/sda Welcome to fdisk (util-linux 2.29.2). Changes will remain in memory only, until you decide to wri......

mbzhong
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部