UTXO global assets for NEO source code analysis

Author: Liao Jinghui

Original link: https://mp.weixin.qq.com/s?__biz=MzUzNDQwNDQ0Mw==&mid=2247483941&idx=1&sn=4ad5cca89a750127139bea96f7153001&chksm=fa940e11cde387077edfdefb5bda7f57301cd7ac5540354beebd2094209da6f78b6cda3d0359&scene=21#wechat_redirect

0x00 Introduction

Is Bitcoin a Bubble? Maybe. After all, except for frying, this thing feels painful. But it is very annoying to compare the Bitcoin bubble with the tulip bubble. What the heck is a tulip? It’s been so long for a year and a few days, and the bubble hasn’t burst yet. Bitcoin is different. Not only is the coin produced by each block unique, but each coin also has its own unique history. There will be more than 20 million bitcoins in the world, but the 50 coins in the genesis block of Satoshi Nakamoto cannot replace anything. Having said that, if the tulip flowers that opened during the tulip bubble period can be preserved to the present, the value will be absolutely leveraged. But the question is, what exactly defines the total amount of cryptocurrencies, and what exactly is the "coin" we have? As the third blog of NEO source code analysis Hillie, this article will analyze the source code of NEO asset part from the perspective of source code. Links to the first two articles:

Looking at the DBFT consensus protocol from the analysis of NEO source code

Network communication for NEO source code analysis


Note:  In the following article, the English abbreviation "NEO" refers to the management token "NEO Coin" used in the NEO network, and the English abbreviation "GAS" refers to the fuel token "NEOGas" in the NEO network.

0x01 Total Assets

Before explaining the specific assets in the NEO network, we need to explain the class RegisterTransaction used to register new assets in the NEO network. This class is used to register new assets, which means that anyone can publish new assets based on the NEO network. RegisterTransaction inherits from Transaction, which means that the process of publishing assets is also a transaction process, and the transaction information will be recorded in the block to ensure that the data cannot be tampered with. The key fields in RegisterTransaction are as follows:

  • AssetType // asset class

  • Name // asset name

  • Amount // total amount of tokens

  • Precision //Token precision

  • Owner // Issuer's public key

  • Admin // Asset manager's contract hash

  • Attributes // Transaction characteristics: usage and its data In addition, it is very expensive to release a new asset to the NEO network, requiring 5000 GAS, and according to the current market price, it requires about 1.5 million RMB. Even in the test network, the GAS given to me by the official handout is only 5000 GAS.

There are two official assets in the NEO network, one is the management token NEO, which serves as a certificate for managing the NEO network, and the other is the fuel currency GAS, which has a function similar to that of BitCoin in the Bitcoin network. Because the consensus strategy of the NEO network adopts a voting mechanism, the more people who hold NEO, the greater the voting power, and the more chance they have to become members of the NEO network. Members preside over the daily operation of the NEO network, generate new blocks, and receive newly generated GAS as rewards. Other than that, NEO has no other use. GAS is used to pay for daily transactions and contract execution fees in the blockchain network. At the beginning of the creation of the NEO network, the total amount of NEO is determined and written into the blockchain and cannot be changed. The code for creating NEO management tokens is in the BlockChain.cs file: Source code location: neo/Core/BlockChain.cs

public static readonly RegisterTransaction GoverningToken = new RegisterTransaction
        {
            AssetType = AssetType.GoverningToken,
            Name = "[{\"lang\":\"zh-CN\",\"name\":\"小蚁股\"},{\"lang\":\"en\",\"name\":\"AntShare\"}]",
            Amount = Fixed8.FromDecimal(100000000),  /* NEO管理代币总量一亿份 */
            Precision = 0,   /* 小数点精度为0,意味着NEO最小单位为1, 不可再分 */
            Owner = ECCurve.Secp256r1.Infinity,
            Admin = (new[] { (byte)OpCode.PUSHT }).ToScriptHash(),
            Attributes = new TransactionAttribute[0],
            Inputs = new CoinReference[0],
            Outputs = new TransactionOutput[0],
            Scripts = new Witness[0]
        };

It can be seen from the code that at the beginning, the total amount of NEO is hard-coded into the blockchain, and does not involve complex calculations. In the same way, below the code for registering NEO assets is the code for registering GAS assets:

        public static readonly RegisterTransaction UtilityToken = new RegisterTransaction
        {
            AssetType = AssetType.UtilityToken,
            Name = "[{\"lang\":\"zh-CN\",\"name\":\"小蚁币\"},{\"lang\":\"en\",\"name\":\"AntCoin\"}]",
            Amount = Fixed8.FromDecimal(GenerationAmount.Sum(p => p) * DecrementInterval), 
            Precision = 8, //精度为小数点后8位
            Owner = ECCurve.Secp256r1.Infinity,
            Admin = (new[] { (byte)OpCode.PUSHF }).ToScriptHash(),
            Attributes = new TransactionAttribute[0],
            Inputs = new CoinReference[0],
            Outputs = new TransactionOutput[0],
            Scripts = new Witness[0]
        };

It can be seen that the total amount of GAS is calculated. The GenerationAmount array defines the amount of GAS that is rewarded for each block generated over time, and the DecrementInterval is the decay rate of the amount of generated GAS: every 2 million blocks are generated, The newly generated block reward GAS is attenuated by the value in the GenerationAmount array. I did a quick calculation with a calculator, and the total amount is also 100 million, which is consistent with the definition in the white paper.

But here comes the problem. To release a new asset, it needs to consume 5000 GAS, but if GAS is not released, it is impossible for GAS to exist in the NEO network. Publishing GAS requires GAS, which is a paradox. Of course, this is a paradox in my eyes, not in the eyes of Core developers. The registration of NEO and GAS assets is directly hard-coded in the genesis block as part of the genesis block transaction. Then, with the nodes of the new network, they are synchronized to all parts of the world. The hard-coded transactions in the Genesis Block are as follows: Source address: neo/Core/BlockChain.cs/GenesisBlock

        Transactions = new Transaction[]
            {                new MinerTransaction // 创建矿工交易
                {
                    Nonce = 2083236893,
                    Attributes = new TransactionAttribute[0],
                    Inputs = new CoinReference[0],
                    Outputs = new TransactionOutput[0],
                    Scripts = new Witness[0]
                },
                GoverningToken,  // 发布NEO
                UtilityToken,         // 发布GAS
                new IssueTransaction // 用于分发资产的特殊交易
                {                    // 代码省略
                }
            }

0x02 Asset distribution

After new asset types are created, where do those assets go? How did you get the assets you created yourself? In section 0x01, I omitted the details of the IssueTransaction transaction in the genesis block generation code, because this part needs to be explained in detail, and the detailed code is pasted below:

Source address: neo/Core/BlockChain.cs/GenesisBlock

    new IssueTransaction
                {
                    Attributes = new TransactionAttribute[0],  // 交易属性
                    Inputs = new CoinReference[0],  
                    Outputs = new[]  //
                    {                        new TransactionOutput
                        {
                            AssetId = GoverningToken.Hash,
                            Value = GoverningToken.Amount, // 直接分发全部NEO
                            ScriptHash = Contract.CreateMultiSigRedeemScript(StandbyValidators.Length / 2 + 1, StandbyValidators).ToScriptHash() 
                        }
                    },
                    Scripts = new[]
                    {                       // 代码省略
                    }
                }

IssueTransaction inherits from Transaction and is a special transaction for distributing assets. The biggest peculiarity of this kind of transaction is that you need to pay a system transaction fee, which is defined in the protocol.json file:

Source code location: neo/protocol.json

       "SystemFee": {              "EnrollmentTransaction": 1000,              "IssueTransaction": 500,              "PublishTransaction": 500,              "RegisterTransaction": 10000
    }

In the IssueTransaction transaction in the genesis block, all NEO is directly distributed. What does this mean? Meaning, if you're one of the StandbyValidators, you've now achieved dozens of small goals in life.

GAS的分发就相对比较复杂,因为GAS是需要挖掘的,而且还有一个衰减期。挖掘GAS涉及到NEO网络的共识过程,对NEO网络共识算法感兴趣的同学可以看我的另一篇博文《NEO从源码分析看共识协议》。在每个视图周期开始的时候,议长添加矿工交易并将本地缓存的交易信息签名后广播给议员,议员进行验证,在验证通过的议员数量合法之后,议长创建新的区块。每个区块奖励GAS数的计算在创建矿工交易的时候进行:

源码位置:neo/Consensus/ConsensusService.cs/CreateMinerTransaction

Fixed8 amount_netfee = Block.CalculateNetFee(transactions); // 获取手续费(in-out-sysfee)TransactionOutput[] outputs = amount_netfee == Fixed8.Zero ? new TransactionOutput[0] : new[] { new TransactionOutput
{
          AssetId = Blockchain.UtilityToken.Hash,
          Value = amount_netfee,
          ScriptHash = wallet.GetChangeAddress()
 } };

可以看到这里调用了Block的CalculateNetFee方法来计算当前区块应该获取的手续费,当前区块的奖励也自然归属于生当前区块的账户。

0x03 账户余额

前面讲了那么多,但是还是没有把一个概念讲清楚----"{'CH':'币','EN':'Coin'}" ,币到底是什么呢?我们NEO钱包中显示的余额究竟是什么呢? 在NEO网络世界里,“币”流通的唯一途径就是交易,币的整个生命周期都在交易中度过。注册一种薪资产的RegisterTransaction方法是交易,资产分发的IssueTransaction 也是一种特殊交易,向矿工支付手续费的MinerTransaction也是交易,甚至每个区块的奖励分发ClaimTransaction方法也是一个交易。所以我们就先看看这个所有交易类型之父----交易基类Transaction。 Transaction关键字段如下:

源码位置:neo/Core/Transaction.cs

        /// <summary>
        /// 交易类型
        /// </summary>
        public readonly TransactionType Type;        /// <summary>
        /// 版本
        /// </summary>
        public byte Version;        /// <summary>
        /// 该交易所具备的额外特性
        /// </summary>
        public TransactionAttribute[] Attributes;        /// <summary>
        /// 输入列表
        /// </summary>
        public CoinReference[] Inputs;        /// <summary>
        /// 输出列表
        /// </summary>
        public TransactionOutput[] Outputs;        /// <summary>
        /// 用于验证该交易的脚本列表
        /// </summary>
        public Witness[] Scripts { get; set; }

可以看出,对于每个交易,需要明确指定交易资产的来源Inputs以及交易资产的去向Outputs。每个钱包在组网同步区块链时候,会对区块链上面的每一笔交易进行检查,如果这笔交易有Outputs指向自己的账户,就会新建CoinReference对象来记录这个转账,然后尝试在本地记录的资产列表里查找,如果这笔转账已经被记录过,则将这笔资产状态修改为已确认。如果当前转账未被记录过,则将reference对象作为KEY,新建Coin对象作为Value保存在自己的资产列表中: 源码位置:neo/Wallets/WalletIndexer.cs/ProcessBlock

                for (ushort index = 0; index < tx.Outputs.Length; index++)
                {
                    TransactionOutput output = tx.Outputs[index];
                    if (accounts_tracked.ContainsKey(output.ScriptHash))
                    {
                        CoinReference reference = new CoinReference
                        {
                            PrevHash = tx.Hash,
                            PrevIndex = index
                        };
                        if (coins_tracked.TryGetValue(reference, out Coin coin))
                        {
                            coin.State |= CoinState.Confirmed;
                        }
                        else
                        {
                            accounts_tracked[output.ScriptHash].Add(reference);
                            coins_tracked.Add(reference, coin = new Coin
                            {
                                Reference = reference,
                                Output = output,
                                State = CoinState.Confirmed
                            });
                        }
                        batch.Put(SliceBuilder.Begin(DataEntryPrefix.ST_Coin).Add(reference), SliceBuilder.Begin().Add(output).Add((byte)coin.State));
                        accounts_changed.Add(output.ScriptHash);
                    }

而每笔交易的资产来源也就来自于这个资产列表中记录的数据。由于每一笔资产的都会记录prehash,这也就意味着每笔资产都是可以在区块链中进行溯源的,同时,我们也可以知道了另一个问题的答案,就是在NEO网络中,“币”只是个数字概念,并没有实体。 资产在用户之间流通的示意图如下:


可以看到资产在被挖掘出来之后,整个流通的过程随着交易的过程是个树状的结构。但是对于每一份资产来说,它的结构是这样的:


从示意图中可以看出,针对每一份资产,其来源可以一直追随到其最初被开采出来的那个区块。

0x04 发布新资产

NEO网络是支持用户发布属于自己的资产的,前文也已经提到过,NEO和GAS都是在创世区块中通过特殊交易的形式发布的资产。那用户如何发布自己的资产呢? 这部分代码我从neo-gui-nel项目的源码中找到的入口: 源码位置:neo-gui-nel/neo-gui/UI/AssetRegisterDialog.cs

            using (ScriptBuilder sb = new ScriptBuilder())
            {
                sb.EmitSysCall("Neo.Asset.Create", asset_type, name, amount, precision, owner, admin, issuer);                return new InvocationTransaction
                {
                    Attributes = new[]
                    {                        new TransactionAttribute
                        {
                            Usage = TransactionAttributeUsage.Script,
                            Data = Contract.CreateSignatureRedeemScript(owner).ToScriptHash().ToArray()
                        }
                    },
                    Script = sb.ToArray()
                };
            }

可以看到这里是进行了系统调用"Neo.Asset.Create",这个命令会触发StateMachine.cs中的Asset_Create方法:

源码位置:neo/SmartContract/StateMachie.cs/StateMachine

Register("Neo.Asset.Create", Asset_Create);

在Asset_Create方法中,根据传入的新资产的属性信息来构造合约。智能合约部分的讲解将在接下来的博客中进行,此处不再详细解释。

最后: 本人正在进行NEO轻钱包微信小程序的开发,主要使用wepy框架,欢迎感兴趣的朋友参与进来。NEOThinWallet for Wechat Miniprogram


本文由NEL内容奖励计划支持


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324605688&siteId=291194637