您的位置:首页 > 其它

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

2018-06-25 02:04 197 查看

接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

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  EAC