1.智能合约的部署
- 首先在gui里加载已经编写好的合约avm,然后填入相关信息以及参数列表和返回值。这里我们的合约输入是两个string,输出为一个string,所以参数列表填入0707,返回值07。
snipaste_20181018_182245.png - 调用GetTransaction(),其作用为从gui里读出合约相关信息,然后根据信息创建一个合约脚本。
public InvocationTransaction GetTransaction()
{
byte[] script = textBox8.Text.HexToBytes();
byte[] parameter_list = textBox6.Text.HexToBytes();
ContractParameterType return_type = textBox7.Text.HexToBytes().Select(p => (ContractParameterType?)p).FirstOrDefault() ?? ContractParameterType.Void;
ContractPropertyState properties = ContractPropertyState.NoProperty;
if (checkBox1.Checked) properties |= ContractPropertyState.HasStorage;
if (checkBox2.Checked) properties |= ContractPropertyState.HasDynamicInvoke;
string name = textBox1.Text;
string version = textBox2.Text;
string author = textBox3.Text;
string email = textBox4.Text;
string description = textBox5.Text;
using (ScriptBuilder sb = new ScriptBuilder())
{
sb.EmitSysCall("Neo.Contract.Create", script, parameter_list, return_type, properties, name, version, author, email, description);
return new InvocationTransaction
{
Script = sb.ToArray()
};
}
EmitSysCall后面会在加载虚拟机时执行下面的方法构造一个智能合约。
private bool Contract_Create(ExecutionEngine engine)
{
TR.Enter();
byte[] script = engine.EvaluationStack.Pop().GetByteArray();
if (script.Length > 1024 * 1024) return TR.Exit(false);
ContractParameterType[] parameter_list = engine.EvaluationStack.Pop().GetByteArray().Select(p => (ContractParameterType)p).ToArray();
if (parameter_list.Length > 252) return TR.Exit(false);
ContractParameterType return_type = (ContractParameterType)(byte)engine.EvaluationStack.Pop().GetBigInteger();
ContractPropertyState contract_properties = (ContractPropertyState)(byte)engine.EvaluationStack.Pop().GetBigInteger();
if (engine.EvaluationStack.Peek().GetByteArray().Length > 252) return TR.Exit(false);
string name = Encoding.UTF8.GetString(engine.EvaluationStack.Pop().GetByteArray());
if (engine.EvaluationStack.Peek().GetByteArray().Length > 252) return TR.Exit(false);
string version = Encoding.UTF8.GetString(engine.EvaluationStack.Pop().GetByteArray());
if (engine.EvaluationStack.Peek().GetByteArray().Length > 252) return TR.Exit(false);
string author = Encoding.UTF8.GetString(engine.EvaluationStack.Pop().GetByteArray());
if (engine.EvaluationStack.Peek().GetByteArray().Length > 252) return TR.Exit(false);
string email = Encoding.UTF8.GetString(engine.EvaluationStack.Pop().GetByteArray());
if (engine.EvaluationStack.Peek().GetByteArray().Length > 65536) return TR.Exit(false);
string description = Encoding.UTF8.GetString(engine.EvaluationStack.Pop().GetByteArray());
UInt160 hash = script.ToScriptHash();
ContractState contract = contracts.TryGet(hash);
if (contract == null)
{
contract = new ContractState
{
Script = script,
ParameterList = parameter_list,
ReturnType = return_type,
ContractProperties = contract_properties,
Name = name,
CodeVersion = version,
Author = author,
Email = email,
Description = description
};
contracts.Add(hash, contract);
contracts_created.Add(hash, new UInt160(engine.CurrentContext.ScriptHash));
}
engine.EvaluationStack.Push(StackItem.FromInterface(contract));
return TR.Exit(true);
}
最后返回了一个InvocationTransaction,其Script包含合约的信息。
textBox9.Text = textBox8.Text.HexToBytes().ToScriptHash().ToString();
即Script Hash的值为智能合约代码的hash值,后面合约调用也是根据这个hash区寻找指定的脚本。这里可能会造成一个问题,如果你和别人的智能合约代码完全相同,则这两个脚本会指向同一个地址,可能会出现异常。
-
点击部署完成后会自动弹出调用合约的界面,之前生成的脚本会自动显示在上方的文本框中。
snipaste_20181019_121437.png这里必须先点击试运行,当试运行通过之后才可以点击调用。
点击试运行会调用一下代码:
private void button5_Click(object sender, EventArgs e)
{
byte[] script;
try
{
script = textBox6.Text.Trim().HexToBytes();
}
catch (FormatException ex)
{
MessageBox.Show(ex.Message);
return;
}
if (tx == null) tx = new InvocationTransaction();
tx.Version = 1;
tx.Script = script;
if (tx.Attributes == null) tx.Attributes = new TransactionAttribute[0];
if (tx.Inputs == null) tx.Inputs = new CoinReference[0];
if (tx.Outputs == null) tx.Outputs = new TransactionOutput[0];
if (tx.Scripts == null) tx.Scripts = new Witness[0];
ApplicationEngine engine = ApplicationEngine.Run(tx.Script, tx);
StringBuilder sb = new StringBuilder();
sb.AppendLine($"VM State: {engine.State}");
sb.AppendLine($"Gas Consumed: {engine.GasConsumed}");
sb.AppendLine($"Evaluation Stack: {new JArray(engine.EvaluationStack.Select(p => p.ToParameter().ToJson()))}");
textBox7.Text = sb.ToString();
if (!engine.State.HasFlag(VMState.FAULT))
{
tx.Gas = engine.GasConsumed - Fixed8.FromDecimal(10);
if (tx.Gas < Fixed8.Zero) tx.Gas = Fixed8.Zero;
tx.Gas = tx.Gas.Ceiling();
Fixed8 fee = tx.Gas.Equals(Fixed8.Zero) ? net_fee : tx.Gas;
label7.Text = fee + " gas";
button3.Enabled = true;
}
else
{
MessageBox.Show(Strings.ExecutionFailed);
}
}
ApplicationEngine.Run(tx.Script, tx);此时会将tx放入虚拟机中进行运行。
public static ApplicationEngine Run(byte[] script, IScriptContainer container = null, Block persisting_block = null)
{
TR.Enter();
if (persisting_block == null)
persisting_block = new Block
{
Version = 0,