NEO的智能合約部署與調(diào)用

1.智能合約的部署

  1. 首先在gui里加載已經(jīng)編寫好的合約avm睡蟋,然后填入相關(guān)信息以及參數(shù)列表和返回值踏幻。這里我們的合約輸入是兩個(gè)string,輸出為一個(gè)string,所以參數(shù)列表填入0707,返回值07戳杀。
    snipaste_20181018_182245.png
  2. 調(diào)用GetTransaction()该面,其作用為從gui里讀出合約相關(guān)信息,然后根據(jù)信息創(chuàng)建一個(gè)合約腳本信卡。
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后面會(huì)在加載虛擬機(jī)時(shí)執(zhí)行下面的方法構(gòu)造一個(gè)智能合約隔缀。

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);
        }

最后返回了一個(gè)InvocationTransaction,其Script包含合約的信息傍菇。

textBox9.Text = textBox8.Text.HexToBytes().ToScriptHash().ToString();

即Script Hash的值為智能合約代碼的hash值猾瘸,后面合約調(diào)用也是根據(jù)這個(gè)hash區(qū)尋找指定的腳本。這里可能會(huì)造成一個(gè)問題丢习,如果你和別人的智能合約代碼完全相同牵触,則這兩個(gè)腳本會(huì)指向同一個(gè)地址,可能會(huì)出現(xiàn)異常咐低。

  1. 點(diǎn)擊部署完成后會(huì)自動(dòng)彈出調(diào)用合約的界面,之前生成的腳本會(huì)自動(dòng)顯示在上方的文本框中揽思。


    snipaste_20181019_121437.png

    這里必須先點(diǎn)擊試運(yùn)行,當(dāng)試運(yùn)行通過之后才可以點(diǎn)擊調(diào)用渊鞋。
    點(diǎn)擊試運(yùn)行會(huì)調(diào)用一下代碼:

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);此時(shí)會(huì)將tx放入虛擬機(jī)中進(jìn)行運(yùn)行绰更。

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,
                    PrevHash = Blockchain.Default.CurrentBlockHash,
                    MerkleRoot = new UInt256(),
                    Timestamp = Blockchain.Default.GetHeader(Blockchain.Default.Height).Timestamp + Blockchain.SecondsPerBlock,
                    Index = Blockchain.Default.Height + 1,
                    ConsensusData = 0,
                    NextConsensus = Blockchain.Default.GetHeader(Blockchain.Default.Height).NextConsensus,
                    Script = new Witness
                    {
                        InvocationScript = new byte[0],
                        VerificationScript = new byte[0]
                    },
                    Transactions = new Transaction[0]
                };
            DataCache<UInt160, AccountState> accounts = Blockchain.Default.GetStates<UInt160, AccountState>();
            DataCache<UInt256, AssetState> assets = Blockchain.Default.GetStates<UInt256, AssetState>();
            DataCache<UInt160, ContractState> contracts = Blockchain.Default.GetStates<UInt160, ContractState>();
            DataCache<StorageKey, StorageItem> storages = Blockchain.Default.GetStates<StorageKey, StorageItem>();
            CachedScriptTable script_table = new CachedScriptTable(contracts);
            using (StateMachine service = new StateMachine(persisting_block, accounts, assets, contracts, storages))
            {
                ApplicationEngine engine = new ApplicationEngine(TriggerType.Application, container, script_table, service, Fixed8.Zero, true);
                engine.LoadScript(script, false);
                engine.Execute();
                return TR.Exit(engine);
            }
        }

首先會(huì)注冊service,然后加載腳本至虛擬機(jī)瞧挤,運(yùn)行虛擬機(jī)。

public new bool Execute()
        {
            TR.Enter();
            try
            {
                while (!State.HasFlag(VMState.HALT) && !State.HasFlag(VMState.FAULT))
                {
                    if (CurrentContext.InstructionPointer < CurrentContext.Script.Length)
                    {
                        OpCode nextOpcode = CurrentContext.NextInstruction;

                        gas_consumed = checked(gas_consumed + GetPrice(nextOpcode) * ratio);
                        if (!testMode && gas_consumed > gas_amount)
                        {
                            State |= VMState.FAULT;
                            return TR.Exit(false);
                        }

                        if (!CheckItemSize(nextOpcode) ||
                            !CheckStackSize(nextOpcode) ||
                            !CheckArraySize(nextOpcode) ||
                            !CheckInvocationStack(nextOpcode) ||
                            !CheckBigIntegers(nextOpcode) ||
                            !CheckDynamicInvoke(nextOpcode))
                        {
                            State |= VMState.FAULT;
                            return TR.Exit(false);
                        }
                    }
                    StepInto();
                }
            }
            catch
            {
                State |= VMState.FAULT;
                return TR.Exit(false);
            }
            return TR.Exit(!State.HasFlag(VMState.FAULT));
        }

這部分運(yùn)行和虛擬機(jī)的運(yùn)行基本類似儡湾,只是多了一步計(jì)算gas消耗的操作特恬,GetPrice(),官方規(guī)定了不同操作收取不同的費(fèi)用 http://docs.neo.org/zh-cn/sc/systemfees.html徐钠。
其中大部分操作有固定的費(fèi)用癌刽,其他操作根據(jù)指令有不同情況。對于多簽驗(yàn)證尝丐,每個(gè)簽名收取0.1gas(應(yīng)該是一個(gè)公鑰0.1gas?)显拜。

case OpCode.CHECKMULTISIG:
                    {
                        if (EvaluationStack.Count == 0) return TR.Exit(1);
                        int n = (int)EvaluationStack.Peek().GetBigInteger();
                        if (n < 1) return TR.Exit(1);
                        return TR.Exit(100 * n);
                    }

對于Contract.Create和Contract.Migrate類型,ContractProperties位于contract第四的位置爹袁,所以將contract_properties找出來远荠,看是否需要存儲和動(dòng)態(tài)調(diào)用, 創(chuàng)建智能合約與遷移智能合約目前是根據(jù)合約所需功能進(jìn)行收費(fèi)失息。其中基礎(chǔ)的費(fèi)用為 100GAS譬淳,需要存儲區(qū) +400GAS,需要?jiǎng)討B(tài)調(diào)用 +500GAS盹兢。

 case "AntShares.Contract.Migrate":
                    long fee = 100L;

                    ContractPropertyState contract_properties = (ContractPropertyState)(byte)EvaluationStack.Peek(3).GetBigInteger();

                    if (contract_properties.HasFlag(ContractPropertyState.HasStorage))
                    {
                        fee += 400L;
                    }
                    if (contract_properties.HasFlag(ContractPropertyState.HasDynamicInvoke))
                    {
                        fee += 500L;
                    }
                    return TR.Exit(fee * 100000000L / ratio);

運(yùn)行完成后會(huì)返回engine,然后將相關(guān)信息顯示

            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;
            }

tx的gas消耗為所有操作的gas消耗綜合減去10gas的免費(fèi)額度后的值邻梆。并將調(diào)用按鈕變?yōu)榭捎谩?/p>

snipaste_20181019_141410.png

點(diǎn)擊調(diào)用后,由tx構(gòu)建一個(gè)InvocationTransaction绎秒,調(diào)用了MakeTransaction<T>浦妄。

public T MakeTransaction<T>(T tx, UInt160 from = null, UInt160 change_address = null, Fixed8 fee = default(Fixed8)) where T : Transaction
        {
            TR.Enter();
            if (tx.Outputs == null) tx.Outputs = new TransactionOutput[0];
            if (tx.Attributes == null) tx.Attributes = new TransactionAttribute[0];
            fee += tx.SystemFee;   // tx.SystemFee = tx.Gas
            var pay_total = (typeof(T) == typeof(IssueTransaction) ? new TransactionOutput[0] : tx.Outputs).GroupBy(p => p.AssetId, (k, g) => new
            {
                AssetId = k,
                Value = g.Sum(p => p.Value)
            }).ToDictionary(p => p.AssetId);
            if (fee > Fixed8.Zero)
            {
                if (pay_total.ContainsKey(Blockchain.UtilityToken.Hash))
                {
                    pay_total[Blockchain.UtilityToken.Hash] = new
                    {
                        AssetId = Blockchain.UtilityToken.Hash,
                        Value = pay_total[Blockchain.UtilityToken.Hash].Value + fee
                    };
                }
                else
                {
                    pay_total.Add(Blockchain.UtilityToken.Hash, new
                    {
                        AssetId = Blockchain.UtilityToken.Hash,
                        Value = fee
                    });
                }
            }
            var pay_coins = pay_total.Select(p => new
            {
                AssetId = p.Key,
                Unspents = from == null ? FindUnspentCoins(p.Key, p.Value.Value) : FindUnspentCoins(p.Key, p.Value.Value, from)
            }).ToDictionary(p => p.AssetId);
            if (pay_coins.Any(p => p.Value.Unspents == null)) return null;
            var input_sum = pay_coins.Values.ToDictionary(p => p.AssetId, p => new
            {
                p.AssetId,
                Value = p.Unspents.Sum(q => q.Output.Value)
            });
            if (change_address == null) change_address = GetChangeAddress();
            List<TransactionOutput> outputs_new = new List<TransactionOutput>(tx.Outputs);
            foreach (UInt256 asset_id in input_sum.Keys)
            {
                if (input_sum[asset_id].Value > pay_total[asset_id].Value)
                {
                    outputs_new.Add(new TransactionOutput
                    {
                        AssetId = asset_id,
                        Value = input_sum[asset_id].Value - pay_total[asset_id].Value,
                        ScriptHash = change_address
                    });
                }
            }
            tx.Inputs = pay_coins.Values.SelectMany(p => p.Unspents).Select(p => p.Reference).ToArray();
            tx.Outputs = outputs_new.ToArray();
            return TR.Exit(tx);
        }

paytotal = gas,會(huì)根據(jù)這個(gè)新建一個(gè)outputs.之后得到一個(gè)完整的tx见芹。

  1. SignAndShowInformation(tx) 這部分與之前講的一樣剂娄。
public static void SignAndShowInformation(Transaction tx)
        {
            if (tx == null)
            {
                MessageBox.Show(Strings.InsufficientFunds);
                return;
            }
            ContractParametersContext context;
            try
            {
                context = new ContractParametersContext(tx);
            }
            catch (InvalidOperationException)
            {
                MessageBox.Show(Strings.UnsynchronizedBlock);
                return;
            }
            Program.CurrentWallet.Sign(context); //簽名
            if (context.Completed) //如果簽名完成
            {
                context.Verifiable.Scripts = context.GetScripts();
                Program.CurrentWallet.ApplyTransaction(tx);
                Program.LocalNode.Relay(tx); //廣播至其他節(jié)點(diǎn)
                InformationBox.Show(tx.Hash.ToString(), Strings.SendTxSucceedMessage, Strings.SendTxSucceedTitle);
            }
            else
            {
                InformationBox.Show(context.ToString(), Strings.IncompletedSignatureMessage, Strings.IncompletedSignatureTitle);
            }
        }

首先是簽名部分,

public bool Sign(ContractParametersContext context)
        {
            TR.Enter();
            bool fSuccess = false;
            foreach (UInt160 scriptHash in context.ScriptHashes) // 找到交易所有輸入對應(yīng)的地址
            {
                WalletAccount account = GetAccount(scriptHash); // 查看錢包是否有對應(yīng)的賬戶地址
                if (account?.HasKey != true) continue;
                KeyPair key = account.GetKey(); //獲取賬戶秘鑰對
                byte[] signature = context.Verifiable.Sign(key); //簽名
                fSuccess |= context.AddSignature(account.Contract, key.PublicKey, signature); //將簽名添加到參數(shù)表中
            }
            return TR.Exit(fSuccess);
        }

主要是AddSignature()

public bool AddSignature(Contract contract, ECPoint pubkey, byte[] signature)
        {
            TR.Enter();
            if (contract.IsMultiSigContract()) //判斷多簽
            {
                ContextItem item = CreateItem(contract);
                if (item == null) return TR.Exit(false);
                if (item.Parameters.All(p => p.Value != null)) return TR.Exit(false);
                if (item.Signatures == null)
                    item.Signatures = new Dictionary<ECPoint, byte[]>();
                else if (item.Signatures.ContainsKey(pubkey))
                    return TR.Exit(false);
                List<ECPoint> points = new List<ECPoint>(); //需要簽名的地址列表
                {
                    int i = 0;
                    switch (contract.Script[i++])
                    {
                        case 1:
                            ++i;
                            break;
                        case 2:
                            i += 2;
                            break;
                    }
                    while (contract.Script[i++] == 33)
                    {
                        points.Add(ECPoint.DecodePoint(contract.Script.Skip(i).Take(33).ToArray(), ECCurve.Secp256r1));
                        i += 33;
                    }
                }
                if (!points.Contains(pubkey)) return TR.Exit(false); //檢測是不是該這個(gè)用戶簽這個(gè)簽名
                item.Signatures.Add(pubkey, signature);
                if (item.Signatures.Count == contract.ParameterList.Length)
                {
                    Dictionary<ECPoint, int> dic = points.Select((p, i) => new
                    {
                        PublicKey = p,
                        Index = i
                    }).ToDictionary(p => p.PublicKey, p => p.Index);
                    byte[][] sigs = item.Signatures.Select(p => new
                    {
                        Signature = p.Value,
                        Index = dic[p.Key]
                    }).OrderByDescending(p => p.Index).Select(p => p.Signature).ToArray();
                    for (int i = 0; i < sigs.Length; i++) //按照順序依次插入簽名玄呛。
                        if (!Add(contract, i, sigs[i]))
                            throw new InvalidOperationException();
                    item.Signatures = null;
                }
                return TR.Exit(true);
            }
            else
            {
                int index = -1;
                for (int i = 0; i < contract.ParameterList.Length; i++)
                    if (contract.ParameterList[i] == ContractParameterType.Signature)
                        if (index >= 0)
                            throw new NotSupportedException();
                        else
                            index = i;

                if(index == -1) {
                    // unable to find ContractParameterType.Signature in contract.ParameterList 
                    // return now to prevent array index out of bounds exception
                    return TR.Exit(false);
                }
                return TR.Exit(Add(contract, index, signature));
            }
        }

首先判斷是否是多簽:


snipaste_20181022_183129.png
public virtual bool IsMultiSigContract()
        {
            TR.Enter();
            int m, n = 0;
            int i = 0;
            if (Script.Length < 37) return TR.Exit(false); //m一個(gè)字節(jié)+push公鑰一個(gè)字節(jié)+最少一個(gè)公鑰+n一個(gè)字節(jié)+CHECKMULTISIG一個(gè)字節(jié)宜咒,最少是37字節(jié)。
            if (Script[i] > (byte)OpCode.PUSH16) return TR.Exit(false);
            if (Script[i] < (byte)OpCode.PUSH1 && Script[i] != 1 && Script[i] != 2) return TR.Exit(false);
            switch (Script[i])
            {
                case 1:
                    m = Script[++i];
                    ++i;
                    break;
                case 2:
                    m = Script.ToUInt16(++i);
                    i += 2;
                    break;
                default:
                    m = Script[i++] - 80;
                    break;
            }
            if (m < 1 || m > 1024) return TR.Exit(false);
            while (Script[i] == 33)
            {
                i += 34;
                if (Script.Length <= i) return TR.Exit(false);
                ++n;
            }
            if (n < m || n > 1024) return TR.Exit(false);
            switch (Script[i])
            {
                case 1:
                    if (n != Script[++i]) return TR.Exit(false);
                    ++i;
                    break;
                case 2:
                    if (n != Script.ToUInt16(++i)) return TR.Exit(false);
                    i += 2;
                    break;
                default:
                    if (n != Script[i++] - 80) return TR.Exit(false);
                    break;
            }
            if (Script[i++] != (byte)OpCode.CHECKMULTISIG) return TR.Exit(false);
            if (Script.Length != i) return TR.Exit(false);
            return TR.Exit(true);
        }

如果是多簽把鉴,則首先獲取所有需要簽名的地址列表,然后檢測是否有需要該用戶簽名的儿咱,如果是庭砍,則把簽名添加到簽名列表中。當(dāng)所有簽名完畢時(shí)混埠,對所有簽名排序怠缸。
如果是單簽,則找到參數(shù)列表中簽名參數(shù)所在的下標(biāo)钳宪,將簽名 signature 加入到合約的參數(shù)變量列表里面揭北。

隨后會(huì)判斷簽名是否已經(jīng)完成扳炬,如果多簽簽名數(shù)不滿足條件,則需要其他賬戶繼續(xù)簽名搔体;如果滿足條件恨樟,則根據(jù)參數(shù)和腳本構(gòu)建witness,將交易發(fā)送至其他節(jié)點(diǎn)疚俱。到此便完成了一個(gè)智能合約的部署劝术。


snipaste_20181024_154912.png

2.智能合約的調(diào)用

這里主要介紹利用Script hash進(jìn)行函數(shù)調(diào)用。在gui中打開函數(shù)調(diào)用呆奕,如下圖养晋,輸入之前我們生成合約時(shí)的Script hash,然后點(diǎn)擊搜索的按鈕梁钾。


snipaste_20181019_161158.png

之后的代碼如下:

private void button1_Click(object sender, EventArgs e)
        {
            script_hash = UInt160.Parse(textBox1.Text);
            ContractState contract = Blockchain.Default.GetContract(script_hash);
            if (contract == null) return;
            parameters = contract.ParameterList.Select(p => new ContractParameter(p)).ToArray();
            textBox2.Text = contract.Name;
            textBox3.Text = contract.CodeVersion;
            textBox4.Text = contract.Author;
            textBox5.Text = string.Join(", ", contract.ParameterList);
            button2.Enabled = parameters.Length > 0;
            UpdateScript();
        }

這里調(diào)用Blockchain.Default.GetContract(script_hash)绳泉,通過合約的哈希去數(shù)據(jù)庫中尋找對應(yīng)的合約。

public override ContractState GetContract(UInt160 hash)
        {
            TR.Enter();
            return TR.Exit(db.TryGet<ContractState>(ReadOptions.Default, DataEntryPrefix.ST_Contract, hash));
        }

返回一個(gè)ContractState 姆泻,然后將部分信息顯示出來零酪。這里可以給參數(shù)列表賦值。例如


snipaste_20181019_163640.png

每次更新參數(shù)的值都會(huì)執(zhí)行下面的代碼:

private void button1_Click(object sender, EventArgs e)
        {
            if (listView1.SelectedIndices.Count == 0) return;
            ContractParameter parameter = (ContractParameter)listView1.SelectedItems[0].Tag;
            try
            {
                parameter.SetValue(textBox2.Text);
                listView1.SelectedItems[0].SubItems["value"].Text = parameter.ToString();
                textBox1.Text = listView1.SelectedItems[0].SubItems["value"].Text;
                textBox2.Clear();
            }
            catch(Exception err)
            {
                MessageBox.Show(err.Message);
            }
        }

重點(diǎn)是SetValue:

 public void SetValue(string text)
        {
            TR.Enter();
            switch (Type)
            {
                case ContractParameterType.Signature:
                    byte[] signature = text.HexToBytes();
                    if (signature.Length != 64) throw new FormatException();
                    Value = signature;
                    break;
                case ContractParameterType.Boolean:
                    Value = string.Equals(text, bool.TrueString, StringComparison.OrdinalIgnoreCase);
                    break;
                case ContractParameterType.Integer:
                    Value = BigInteger.Parse(text);
                    break;
                case ContractParameterType.Hash160:
                    Value = UInt160.Parse(text);
                    break;
                case ContractParameterType.Hash256:
                    Value = UInt256.Parse(text);
                    break;
                case ContractParameterType.ByteArray:
                    Value = text.HexToBytes();
                    break;
                case ContractParameterType.PublicKey:
                    Value = ECPoint.Parse(text, ECCurve.Secp256r1);
                    break;
                case ContractParameterType.String:
                    Value = text;
                    break;
                default:
                    throw new ArgumentException();
            }
            TR.Exit();
        }

之后合約的字節(jié)碼會(huì)變成這樣:


snipaste_20181022_135128.png

分開看如下圖:


snipaste_20181022_141256.png

點(diǎn)擊試運(yùn)行后過程與部署合約部分相同麦射,最后可以看到試運(yùn)行結(jié)果是一個(gè)ByteArray,值為helloworld蛾娶。
snipaste_20181022_144533.png

3.智能合約消耗GAS的處理

部署和調(diào)用智能合約都是作為一筆交易發(fā)送到其他節(jié)點(diǎn)的,其中交易的SystemFee值為試運(yùn)行過程中計(jì)算出的gas消耗量潜秋。Gas的值最終會(huì)寫入?yún)^(qū)塊當(dāng)中蛔琅,并且在生成gas的claimgas中會(huì)計(jì)算指定高度區(qū)間的系統(tǒng)費(fèi)用總量。

public T MakeTransaction<T>(T tx, UInt160 from = null, UInt160 change_address = null, Fixed8 fee = default(Fixed8)) where T : Transaction
        {
            TR.Enter();
            if (tx.Outputs == null) tx.Outputs = new TransactionOutput[0];
            if (tx.Attributes == null) tx.Attributes = new TransactionAttribute[0];
            fee += tx.SystemFee;  // SystemFee在合約交易中等于gas消耗費(fèi)用
            var pay_total = (typeof(T) == typeof(IssueTransaction) ? new TransactionOutput[0] : tx.Outputs).GroupBy(p => p.AssetId, (k, g) => new
            {
                AssetId = k,
                Value = g.Sum(p => p.Value)
            }).ToDictionary(p => p.AssetId);
            if (fee > Fixed8.Zero) // gas消耗大于0峻呛,則添加進(jìn)pay_total
            {
                if (pay_total.ContainsKey(Blockchain.UtilityToken.Hash))
                {
                    pay_total[Blockchain.UtilityToken.Hash] = new
                    {
                        AssetId = Blockchain.UtilityToken.Hash,
                        Value = pay_total[Blockchain.UtilityToken.Hash].Value + fee
                    };
                }
                else
                {
                    pay_total.Add(Blockchain.UtilityToken.Hash, new
                    {
                        AssetId = Blockchain.UtilityToken.Hash,
                        Value = fee
                    });
                }
            }
            var pay_coins = pay_total.Select(p => new
            {
                AssetId = p.Key,
                Unspents = from == null ? FindUnspentCoins(p.Key, p.Value.Value) : FindUnspentCoins(p.Key, p.Value.Value, from)
            }).ToDictionary(p => p.AssetId);
            if (pay_coins.Any(p => p.Value.Unspents == null)) return null;
            var input_sum = pay_coins.Values.ToDictionary(p => p.AssetId, p => new
            {
                p.AssetId,
                Value = p.Unspents.Sum(q => q.Output.Value)
            });
            if (change_address == null) change_address = GetChangeAddress();
            List<TransactionOutput> outputs_new = new List<TransactionOutput>(tx.Outputs);
            foreach (UInt256 asset_id in input_sum.Keys)
            {
                if (input_sum[asset_id].Value > pay_total[asset_id].Value)
                {
                    outputs_new.Add(new TransactionOutput
                    {
                        AssetId = asset_id,
                        Value = input_sum[asset_id].Value - pay_total[asset_id].Value,
                        ScriptHash = change_address
                    });
                }
            }
            tx.Inputs = pay_coins.Values.SelectMany(p => p.Unspents).Select(p => p.Reference).ToArray();
            tx.Outputs = outputs_new.ToArray();
            return TR.Exit(tx);
        }

共識節(jié)點(diǎn)處理交易時(shí)

private void FillContext()
        {
            TR.Enter();
            IEnumerable<Transaction> mem_pool = LocalNode.GetMemoryPool().Where(p => CheckPolicy(p));
            foreach (PolicyPlugin plugin in PolicyPlugin.Instances)
                mem_pool = plugin.Filter(mem_pool);
            List<Transaction> transactions = mem_pool.ToList();
            Fixed8 amount_netfee = Block.CalculateNetFee(transactions);
            TransactionOutput[] outputs = amount_netfee == Fixed8.Zero ? new TransactionOutput[0] : new[] { new TransactionOutput
            {
                AssetId = Blockchain.UtilityToken.Hash,
                Value = amount_netfee,
                ScriptHash = wallet.GetChangeAddress()
            } };
            while (true)
            {
                ulong nonce = GetNonce();
                MinerTransaction tx = new MinerTransaction
                {
                    Nonce = (uint)(nonce % (uint.MaxValue + 1ul)),
                    Attributes = new TransactionAttribute[0],
                    Inputs = new CoinReference[0],
                    Outputs = outputs,
                    Scripts = new Witness[0]
                };
                if (Blockchain.Default.GetTransaction(tx.Hash) == null)
                {
                    context.Nonce = nonce;
                    transactions.Insert(0, tx);
                    break;
                }
            }
            context.TransactionHashes = transactions.Select(p => p.Hash).ToArray();
            context.Transactions = transactions.ToDictionary(p => p.Hash);
            context.NextConsensus = Blockchain.GetConsensusAddress(Blockchain.Default.GetValidators(transactions).ToArray());
            TR.Exit();
        }

這里的amount_netfee是網(wǎng)絡(luò)費(fèi)用的數(shù)量罗售,計(jì)算方法為amount_in - amount_out - amount_sysfee:

public static Fixed8 CalculateNetFee(IEnumerable<Transaction> transactions)
        {
            TR.Enter();
            Transaction[] ts = transactions.Where(p => p.Type != TransactionType.MinerTransaction && p.Type != TransactionType.ClaimTransaction).ToArray();
            Fixed8 amount_in = ts.SelectMany(p => p.References.Values.Where(o => o.AssetId == Blockchain.UtilityToken.Hash)).Sum(p => p.Value);
            Fixed8 amount_out = ts.SelectMany(p => p.Outputs.Where(o => o.AssetId == Blockchain.UtilityToken.Hash)).Sum(p => p.Value);
            Fixed8 amount_sysfee = ts.Sum(p => p.SystemFee);
            return TR.Exit(amount_in - amount_out - amount_sysfee);
        }

在claimgas當(dāng)中,最后一步會(huì)加上系統(tǒng)費(fèi)用钩述,相當(dāng)于最后把系統(tǒng)費(fèi)用分給了所有neo持有者寨躁。

private static Fixed8 CalculateBonusInternal(IEnumerable<SpentCoin> unclaimed)
        {
            TR.Enter();
            Fixed8 amount_claimed = Fixed8.Zero;
            foreach (var group in unclaimed.GroupBy(p => new { p.StartHeight, p.EndHeight }))
            {
                uint amount = 0;
                uint ustart = group.Key.StartHeight / DecrementInterval;
                if (ustart < GenerationAmount.Length)
                {
                    uint istart = group.Key.StartHeight % DecrementInterval;
                    uint uend = group.Key.EndHeight / DecrementInterval;
                    uint iend = group.Key.EndHeight % DecrementInterval;
                    if (uend >= GenerationAmount.Length)
                    {
                        uend = (uint)GenerationAmount.Length;
                        iend = 0;
                    }
                    if (iend == 0)
                    {
                        uend--;
                        iend = DecrementInterval;
                    }
                    while (ustart < uend)
                    {
                        amount += (DecrementInterval - istart) * GenerationAmount[ustart];
                        ustart++;
                        istart = 0;
                    }
                    amount += (iend - istart) * GenerationAmount[ustart];
                }
                amount += (uint)(Default.GetSysFeeAmount(group.Key.EndHeight - 1) - (group.Key.StartHeight == 0 ? 0 : Default.GetSysFeeAmount(group.Key.StartHeight - 1)));
                amount_claimed += group.Sum(p => p.Value) / 100000000 * amount;
            }
            return TR.Exit(amount_claimed);
        }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市牙勘,隨后出現(xiàn)的幾起案子职恳,更是在濱河造成了極大的恐慌,老刑警劉巖方面,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件放钦,死亡現(xiàn)場離奇詭異,居然都是意外死亡恭金,警方通過查閱死者的電腦和手機(jī)操禀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來横腿,“玉大人颓屑,你說我怎么就攤上這事斤寂。” “怎么了揪惦?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵遍搞,是天一觀的道長。 經(jīng)常有香客問我丹擎,道長尾抑,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任蒂培,我火速辦了婚禮再愈,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘护戳。我一直安慰自己翎冲,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布媳荒。 她就那樣靜靜地躺著抗悍,像睡著了一般。 火紅的嫁衣襯著肌膚如雪钳枕。 梳的紋絲不亂的頭發(fā)上缴渊,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天,我揣著相機(jī)與錄音鱼炒,去河邊找鬼衔沼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛昔瞧,可吹牛的內(nèi)容都是我干的指蚁。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼自晰,長吁一口氣:“原來是場噩夢啊……” “哼凝化!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起酬荞,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤搓劫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后混巧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體糟把,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年牲剃,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片雄可。...
    茶點(diǎn)故事閱讀 38,577評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡凿傅,死狀恐怖缠犀,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情聪舒,我是刑警寧澤辨液,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站箱残,受9級特大地震影響滔迈,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜被辑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一燎悍、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧盼理,春花似錦谈山、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至臊诊,卻和暖如春鸽粉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背抓艳。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工触机, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人壶硅。 一個(gè)月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓威兜,卻偏偏與公主長得像,于是被迫代替她去往敵國和親庐椒。 傳聞我的和親對象是個(gè)殘疾皇子椒舵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評論 2 348

推薦閱讀更多精彩內(nèi)容