作者:廖京辉

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

0x00 引言

比特币是泡沫么?也许是的。毕竟这东西除了用来炒,干什么实事都感觉肉疼。但是有人将比特币泡沫和郁金香泡沫相提并论就很气人了,郁金香什么鬼,长那么一年,开那么几天,泡沫还没破呢,郁金香已经花开花落几个春秋了。比特币就不一样了,不仅每一个区块产出的币都独一无二,而且每一枚币还拥有自己的独一无二的历史。世界上会有两千多万比特币,但是中本聪创世区块的那50枚币什么都替代不了。话说回来,如果当年郁金香泡沫时期开放的郁金香花株能被保存到现在,价值也绝对杠杠的。 但是问题来了,究竟是什么限定了加密货币的总量,我们拥有的“币”又究竟是什么呢?作为NEO源码分析希列的第三篇博客,本文将从源码的角度对NEO资产部分的源码进行解析。 前两篇文章链接:

从NEO源码分析看DBFT共识协议

NEO源码分析之网络通信

注: 在接下来的文章中,英文缩写“NEO”指代NEO网络中使用的管理代币 “NEO Coin", 英文缩写"GAS"指代NEO网络中的燃料代币"NEOGas".

0x01 资产总量

在讲解NEO网络中具体的资产之前需要讲解一下NEO网络中用来注册新资产的类RegisterTransaction,这个类用于注册新的资产,这就意味者任何人都可以基于NEO网络来发布新的资产。RegisterTransaction继承自Transaction,这意味着发布资产的过程也是一个交易的过程,交易的信息会被记录在区块中来保证数据的不可篡改性。RegisterTransaction中的关键字段如下:

  • AssetType // 资产类别

  • Name // 资产名称

  • Amount // 代币总量

  • Precision //代币精度

  • Owner // 发行者的公钥

  • Admin // 资产管理员的合约散列值

  • Attributes // 交易特性 :用途及其数据 此外,发布一种新的资产到NEO网络中是非常贵的,需要5000GAS,按照现在的市价,需要人民币大约150万。即便是测试网络中,官方施舍给我的GAS也就只有5000个GAS而已。

在NEO网络中存在两种官方资产,一种是作为管理NEO网络的凭证的管理代币NEO,另一种是功能和比特币网络中的BitCoin功能类似的燃料货币GAS。因为NEO网络的共识策略采用的是投票机制,持有NEO越多的人,投票权越大,越有机会成为NEO网络中的议员。议员主持NEO网络的日常运转,生成新的区块,领取新生成的GAS作为奖励。除此之外,NEO并没有别的用处。而GAS则是用来缴纳区块链网络中日常交易以及合约执行的手续费。 NEO在NEO网络创建之初总量就确定并写入区块链中无法再进行更改,创建NEO管理代币的代码在BlockChain.cs文件中: 源码位置: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]};

从代码中可以看出,在一开始,NEO的总量就是硬编码进区块链中的,并不涉及到复杂的计算。 同样的道理,在注册NEO资产的代码下面,就是注册GAS资产的代码:

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

可以看到这个GAS的总量是计算得到的,GenerationAmount数组中定义的是随着时间每生成一个区块奖励的GAS数量,DecrementInterval则是生成GAS数量的衰减速度:每生成200万个区块,新生成的区块奖励GAS数按GenerationAmount数组中的值衰减。我用计算器非常快速的算了一下,这个总量也是一亿,和白皮书中定义的一致。

但是问题来了,要发布一个新的资产,需要消耗5000GAS,但是如果GAS不发布则NEO网络中不可能有GAS存在。发布GAS需要GAS,这是个悖论来着。当然,这在我眼中是悖论,在Core开发者眼里不是,NEO和GAS资产的注册是直接被硬编码在了创世区块里作为创世区块交易中的一部分的。而后随着新组网的节点被同步到整个世界各地。创世区块中硬编码写入的交易如下: 源码地址: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,  // 发布NEOUtilityToken,         // 发布GASnew IssueTransaction // 用于分发资产的特殊交易{                    // 代码省略}}

0x02 资产分发

新的资产类型创建了之后,那些资产去了哪里呢?有是如何获得自己创建的资产的呢? 在0x01小节中我将创世区块生成代码中的IssueTransaction交易的详情略去了,因为这部分需要详细讲解,下面先贴上详细代码:

源码地址: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, // 直接分发全部NEOScriptHash = Contract.CreateMultiSigRedeemScript(StandbyValidators.Length / 2 + 1, StandbyValidators).ToScriptHash() }},Scripts = new[]{                       // 代码省略}}

IssueTransaction继承自Transaction,是一种用于分发资产的特殊交易。这种交易最大的特殊性就在于,你需要交一笔系统交易费,这笔费用的定义在protocol.json文件中:

源码位置:neo/protocol.json

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

在创世区块中的IssueTransaction交易中,直接将所有的NEO全部分发出去,这意味着什么呢?意味者,如果你是StandbyValidators之一,那么你现在已经实现了人生的几十个小目标。

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内容奖励计划支持

NEO源码分析之UTXO全局资产相关推荐

  1. 从NEO源码分析看DBFT共识协议

    作者:廖京辉 原文链接:https://mp.weixin.qq.com/s?__biz=MzUzNDQwNDQ0Mw==&mid=2247483919&idx=1&sn=59 ...

  2. Android wpa_supplicant源码分析--启动之全局初始化

    1. wpa_supplicant简介 wpa_supplicant是用来用来支持无线中各种加密方式的,包括WEP.WPA/WPA2和WAPI(中国特有).EAP(8021x).wpa_s通过sock ...

  3. NEO从源码分析看数字资产

    0x00 引言 比特币是泡沫么?也许是的.毕竟这东西除了用来炒,干什么实事都感觉肉疼.但是有人将比特币泡沫和郁金香泡沫相提并论就很气人了,郁金香什么鬼,长那么一年,开那么几天,泡沫还没破呢,郁金香已经 ...

  4. NEO从源码分析看NEOVM

    2019独角兽企业重金招聘Python工程师标准>>> 0x00 前言 这篇文章是为下一篇<NEO从源码分析看UTXO转账交易>打前站,为交易的构造及执行的一些技术基础做 ...

  5. NEO从源码分析看网络通信

    2019独角兽企业重金招聘Python工程师标准>>> 0x00 前言 NEO被称为中国版的Ethereum,支持C#和java开发,并且在社区的努力下已经把SDK拓展到了js,py ...

  6. NEO从源码分析看共识协议

    2019独角兽企业重金招聘Python工程师标准>>> 0x00 概论 不同于比特币使用的工作量证明(PoW)来实现共识,NEO提出了DBFT共识算法.DBFT改良自股权证明算法(P ...

  7. Golang|区块链UTXO集源码分析

    区块链UTXO集源码分析 资源 go实现区块链 前提 在未实现UTXO集之前,假设系统需要查询某个钱包地址的余额,系统需要遍历区块链的所有区块,当区块链非常长时,这种做法的成本太高了. UTXO集是未 ...

  8. EOS智能合约:system系统合约源码分析

    链客,专为开发者而生,有问必答! 此文章来自区块链技术社区,未经允许拒绝转载. eosio.system 概览 笔者使用的IDE是VScode,首先来看eosio.system的源码结构.如下图所示. ...

  9. 【精】EOS智能合约:system系统合约源码分析

    系统合约在链启动阶段就会被部署,是因为系统合约赋予了EOS链资源.命名拍卖.基础数据准备.生产者信息.投票等能力.本篇文章将会从源码角度详细研究system合约. 关键字:EOS,eosio.syst ...

最新文章

  1. pc端WINCE的安装包
  2. Zxing 竖屏切换 android
  3. 记录gulp报错The following tasks did not complete: cssmin或类似任务
  4. Linux glibc内存管理:用户态内存分配器——ptmalloc实现原理
  5. ios 不被遮挡 阴影_解决ios10导航栏底部阴影线条隐藏失效问题
  6. [css] 使用css实现彩虹的效果
  7. python3 生成器_python3基础之生成器
  8. 老杨和驳客都在胡言乱语——千条道理不如一个事实
  9. 使用未初始化的内存是什么意思_活动板房是什么意思?怎么装修?使用年限+价格知识点合集...
  10. 改进的冒泡排序算法一
  11. oracle 物料属性批次过期,物料批次特性值
  12. oracle将列名进行拼接,请教关于用拼接的字符串作为表的列名进行操作的问题
  13. andpods授权码订单号分享_Axure 9.0学生免费授权申请详细步骤
  14. 局部特征提取-LBP算法
  15. 阿里Java开发规范IDEA开发插件
  16. 嵌入式Linux系统uart串口编程详解及实例分析
  17. 那些悄悄变厉害的人,都在偷偷对自己下狠手
  18. PTB-XL大型的心电图数据集
  19. 定时任务监控服务Healthchecks
  20. EPW的输入文件浅析

热门文章

  1. C++语言篇 第五章 一维数组
  2. Excel2007版的常用功能(17):Excel数学函数
  3. 10.24 每日学习打卡
  4. 【高等数学笔记】彻底弄懂最小二乘法(Least Squares Method)
  5. PYTHON学习之旅1:linux操作系统学习
  6. CentOS之—— CentOS-8-x86_64-1905-boot.iso网络安装教程
  7. Brave浏览器推出首个基于隐私的广告平台
  8. 动手学深度学习 - 3. 深度学习基础
  9. rabiitmq卸载_RabbitMQ 的安装与卸载 以及各种踩坑
  10. 使用frp实现内网穿透(含SSH、远程桌面、文件访问等)