0x00 引言

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

  • NEO从源码分析看共识协议
  • 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

群交流:795681763

原文:https://my.oschina.net/u/2276921/blog/1622364

NEO从源码分析看数字资产相关推荐

  1. NEO从源码分析看NEOVM

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

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

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

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

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

  4. NEO从源码分析看nep2与nep6

    0x00 前言 混社区的时候(QQ群)总是听到大佬们聊到nep,好奇心驱使下就去neo官网找资料,然鹅,什么都没找到.后来就请教大佬,才知道nep是neo一系列提案,文档并不在neo官网,在这里.但是 ...

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

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

  6. NEO源码分析之UTXO全局资产

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

  7. Netty源码分析系列之常用解码器(下)——LengthFieldBasedFrameDecoder

    扫描下方二维码或者微信搜索公众号菜鸟飞呀飞,即可关注微信公众号,Spring源码分析和Java并发编程文章. 前言 在上一篇文章中分析了三个比较简单的解码器,今天接着分析最后一个常用的解码器:Leng ...

  8. Swin Transformer源码分析

    swin transformer是什么这里就不在说明了,会点进来肯定是知道这个模型是做什么的. 直接看论文有些地方看的一知半解.这里直接从源码分析看下模型的具体实现 论文地址:https://arxi ...

  9. 从vuex源码分析module与namespaced

    使用vue已经有半年有余, 在各种正式非正式项目中用过, 开始专注于业务比较多, 用到现在也遇见不少因为理解不深导致的问题. 有问题就有找原因的勇气, 所以带着问题搞一波. 带着问题看源码 所以来整理 ...

最新文章

  1. 清理Visual Studio2010产生的垃圾调试文件
  2. 利用TcpClient TcpListener 实现发送图片
  3. 第四次作业-四则运算
  4. 微型计算机实验代码,微型计算机原理实验1-数据传送
  5. MsSql正反表达式
  6. 数据结构前缀,后缀,中缀表达式
  7. jqGrid细节备注—jqGrid中自定义格式,URL格式
  8. 学习笔记之加密解密,PKI,CA
  9. C#入门详解(10)
  10. springboot内存占用大_《SpringBoot整合redis、Scheduled/quartz定时任务》
  11. 地图下载区 哪家好用
  12. matlab动力学系统仿真 教程,MATLAB/SIMULINK动力学系统建模与仿真
  13. 假设你毕业后有两个选择:一个是在某处找工作,另一个是自己创业。你要做决定。写一篇文章解释你的决定的理由
  14. 启动root用户 银河麒麟_银河麒麟V10启用VNCServer
  15. 1.2编程基础之变量定义、赋值及转换
  16. 负载均衡实现的各种优缺点
  17. HoloLens原理分析和硬件拆解
  18. 20210215 Cobalt Strike 重定器/转发器/红队反溯源手段
  19. CVPR 2022 Oral|港中文开源PoseC3D:基于3D-CNN的骨骼动作识别框架
  20. 程序员的财务自由之路(四)- 选择大于努力

热门文章

  1. 为什么年终奖是一个彻头彻尾的职场圈套?
  2. 高数-极限-存在与连续1
  3. java小游戏:五子棋人机大战
  4. BL-HUF35A-AV-TRB 电子元器件 BRIGHT 封装SMD 批次2021
  5. LOL如何对一个英雄的技能进行测试 对一个英雄技能测试需要测试什么?
  6. 利用MSCNN实现人群密度监测
  7. 【最小生成树】P2259 Charmer--viv
  8. Qt+VLC播放多个视频的Demo
  9. 使用普通账户安装 Redis 服务
  10. x2检验(chi-square test)/ 卡方检验