PoS发明人最新项目 VSYS源代码库分析评测
博客园
发表于 2023-1-2 16:09:01
175
0
0
项目概览
由PoS之父Sunny King 创建的区块链数据库云。 VSYS基于POS自研了业界最高效的共识算法SPOS,目前两大主流公链以太坊和EOS的共识机制都是参考自POS。本次Sunny King带来的SPOS,必然是万众期待。虽然 VSYS主网还未上线,但代码完善度极高,当前在github公开的主要有三个代码库,分别是 VSYS节点、web钱包、冷钱包,可见其生态工具链已经很完善;根据官方周报信息,全节点已在测试网稳定运行2周,没有发现重大bug。
项目特色$ ?- h" o# m% i5 U: u, b6 ~, R
VSYS为了实现区块链数据库云的目标,独创了很多超前的特性,VSYS项目的4大项目特色分别是灵活的账户体系、媲美传统数据库的操作方式、协议分层和可插拔系统组件。0 k Z S2 ~' q: \+ I
灵活的账户体系0 t- k0 N( k6 z# _6 C
具备丰富的角色:
公钥:用户所产生密钥对的公开部分& a- f. `. `& I N
地址:公钥的一种缩简形式9 z: J- Q' h1 D9 k
虚拟身份/化身:与供临时使用的公钥相比,为长期使用身份
组织:每个身份与多个虚拟身份/化身相关联,并由多个虚拟身份/化身所管理
可替代物:可替代属性的虚拟资产/代币,如货币、股份等0 N+ R8 \( `" f4 W- B6 }
账号:身份所拥有可替代物的容器,类似于银行账户。注意其与传统数据库用户账 户的区别。
媲美传统数据库的操作方式# Y$ N4 \# M' p- Y7 P# m
: {0 y+ K/ Y( B; z2 ?' t, Q
VSYS平台计划引入高级数据库查询功能。类似于 MongoDB 的对象-关系型查询语言,比传统关系型查询模型(即 SQL)更加灵活。支持的操作有:6 [) e; i0 `5 e- j- e
创建数据库
插入对象
更新对象
删除对象! I+ Q: n. A, C
创建索引- B1 C6 h/ C, [
按索引键值查询. S6 k4 V0 K) [/ Z i
协议分层" S* Q4 d+ N# m. L4 b
+ M8 T5 A4 H; r8 K8 p2 i
? 共识管理层 ? 区块树(Block tree)管理层 ? 链间(Interchain)处理层 ? 交易处理层 ? 数据格式层
可插拔系统组件
? 可插拔式共识模型 ? 可插拔式业务逻辑容器 ? 数据库管理组件 ? 数据库操作组件 ? 数据库查询组件 ? 共享对等网络服务 ? 进行区块链处理的全节点 ? 智能手机端轻节点冷钱包 ? 智能手机端轻节点热钱包 ? 浏览器端钱包0 ]# h d8 t% p# \1 x- Y# y) _
代码库基本信息
VSYS目前在github公布的主要有三个代码库,分别是vee节点、web钱包,冷钱包。此外还有一个管理公私钥对的命令行工具wallet-generator。- ?0 p% }, T: i& \- u1 L; J# T* m
VEE全节点
commit数量丰富。结合提交历史可以发现VEE代码提交频繁,且每次都是高质量的代码推送,这个成绩超过现有99%的区块链项目。0 g! k) ~* k, U2 S
代码量(只统计scala主体,不计算脚本和库文件):27706行。可见scala写出的项目代码精简,但功能丰富。相比其他区块链项目动辄10W+的代码量,vee不到3W简直是开发者的福音。6 e* l" N G y! [7 }
作为未发布主网的区块链项目,34位贡献者很突出,说明国际区块链开发社区已欣然拥抱VEE,可以预见,随着主网发布完成,开发者社区日益完善,开发者生态很快就会与EOS,以太坊等现有主流公链并驾齐驱。
star数较少,甚至少于贡献者。这是预期中的数字,因为VEE的开发者生态处于从0到1的建设过程,目前更多的精力还是应该集中保证主网稳定发布,完善核心功能。
VSYS-wallet-gui% _ @# p" k8 @
基于 SPV(Simplified Payment Verification),NodeJs开发的Web版轻钱包。除了基本的私钥保管,还包括交易、资产保护以及发行基于VEE的代币等丰富功能。除此之外,web版钱包还支持对冷钱包的监控,通过扫描冷钱包应用程序生成的qr代码,即可在账户窗口查看所有的钱包信息。
github: q; i" I+ E T, P
|commits|contributors|stars| | :–: | :–: | :–: | :–: | |502|6|4|
vee-cold-android
基于Android开发的冷钱包。冷钱包主要用于保障存储VSYS的安全性,其关键功能是离线状态下生成和存储私钥。在热钱包的帮助下,可实现转账、租赁等基本的钱包操作。此外,冷钱兼容多种备份恢复方案,并可在设备之间轻松的迁移。$ y) f- v! X1 L& N7 ~& \
wallet-generator% t( N3 v+ \6 H
钱包管理工具。可通过交互式的命令行生成钱包文件,管理公私钥对。
代码解构3 j3 I2 c5 j- \" v9 b# \7 T
SPOS共识算法& a# [0 V# C$ l1 x+ e
VSYS基于POS改造升级了出SPOS(Supernode proof-of-stake)。SPOS提升区块产生的速度,其采用MAB的机制及抢夺释放的机制,保证了更强的公平性。SPOS目标是60个节点及60个铸币槽(铸币权),初期15个节点和60个铸币槽。这样更有利于初期的参与者获得更多的收益,还在扩展性上提供了更多的空间。从共识底层架构技术层面采用了分布式超级节点,按循序出块且间隔固定,在稳定及安全性上得到更好的保障。
我们总结了SPOS的特性:- r7 ~9 h8 \ U7 O7 M4 t; \/ E
SPOS超级节点按固定顺序出块;
以15个超级节点开启主网,随着网络增长,超级节点数增长到30-60;4 S0 ]3 g: c H, ?6 T X
块与块之间间隔是固定的,这样带来了更佳稳定的延迟;
60个铸币槽,分别代表了一分钟的60秒;
抢夺/释放的机制,使得铸币权的竞争更公平(相较DPOS,就是比币数,币可以在多个节点重复投票);
铸币平均算法MAB,可以支撑币权更好的流动性,不让币往一个地方跑,保持去中心化,保护网络安全;. I( _4 l k' ]- u* K2 D: r5 H
此SPOS机制来带的好处:3 @% p3 S) k8 Z9 W5 D L8 m& `
为高性能的公链提供稳定高效的基础设施;
固定的块间隔设计,带来的是兼具高吞吐量和更加稳定的区块链网络(其他网络的高吞吐都无法保证稳定性,最终也是支持不了高性能业务运行的);& b5 C% A. h5 T+ @* x) I$ |! P# k
冷铸币的设计,保证了区块链更加安全的性能;4 g5 a" @' B! ~- o2 K, s4 e
经济系统的设计,鼓励生态系统持续投入升级超级节点,这样保证了系统的运行效果将不断提升;
SPOS算法入口如下:0 N4 ?9 E2 c* O4 d; ^- c/ Z1 u; V
package vee.consensus.spos
import com.google.common.primitives.{Bytes, Longs}
import play.api.libs.json.{JsObject, Json}
import scorex.block.BlockField9 ^3 y I) c+ t9 V# Q
case class SposConsensusBlockField(override val value: SposConsensusBlockData)
extends BlockField[SposConsensusBlockData] {
override val name: String = "SPOSConsensus". w3 ^! @9 t+ U0 x4 L0 |9 O4 I
override def bytes: Array[Byte] =
Bytes.ensureCapacity(Longs.toByteArray(value.mintTime), 8, 0) ++; T' }2 l- a1 D( e
Bytes.ensureCapacity(Longs.toByteArray(value.mintBalance), 8, 0)
override def json: JsObject = Json.obj(name -> Json.obj() {2 [. f& {1 M1 e
"mintTime" -> value.mintTime,/ o* v: @9 W! ^: Z5 c5 u
"mintBalance" -> value.mintBalance
))
}5 k4 c4 A( S, W6 X
mab算法
为了保持加权平均余额的良好性能并克服这些缺点,VSYS提出了一种新的平衡称为铸造平均余量(MAB),即:Shn = min {Bhn,αBhn-1 +(1-α)Shn-1}
其中Bhn表示高度hn处的当前平衡。! C1 Q' X+ {' W* d9 S1 ~$ f
最小平均余额取当前余额和加权平均余额的最小值。
计算复杂度仍为O(1)。此外,在这个公式中,如果一个人将他/她的余额全部转出,MAB将直接减少到0。使用此属性,总计量平均余额将保守并由总余额控制。
mab算法对应的实现如下:
MAB:weightedBalaceCalc
object SPoSCalc extends ScorexLogging {
// useful constant" Y( ?$ J) Y% S t2 P
val MinimalEffectiveBalanceForContender: Long = 100000000000000L
// update plan: 4 -> 15 slots, 2 -> 30 slots, 1 -> 60 slots$ k8 i7 l' ^. ]6 k! N; z4 N l
val SlotGap = if (AddressScheme.current.chainId == ‘M’.toByte) 4 else 1
// update plan: 15 slots -> 36 vee coins, 30 slots -> 18 vee coins, 60 slots -> 9 vee coins
val BaseReward = 900000000L
val MintingReward = BaseReward * SlotGap
def weightedBalaceCalc(heightDiff: Int, lastEffectiveBalance: Long, lastWeightedBalance: Long, cntEffectiveBalance: Long, fs: FunctionalitySettings): Long = {
// mintingSpeed should be larger than 0
val maxUpdateBlocks = 24 * 60 * 60 / math.max(fs.mintingSpeed, 1) * 1L; ^8 I* K8 B, b
val weightedBalance = math.min(lastEffectiveBalance/maxUpdateBlocks * math.min(maxUpdateBlocks, heightDiff)
+ lastWeightedBalance/maxUpdateBlocks * (maxUpdateBlocks - math.min(maxUpdateBlocks, heightDiff)),
cntEffectiveBalance)
weightedBalance5 B7 O1 q+ a( o
}1 d9 G) C; ~# @, J1 J
def mintingBalance(state: StateReader, fs: FunctionalitySettings, account: Address, atHeight: Int): Long = {/ _( Q- l$ ^* [2 b" w) ^
//TODO: we should set the mintingBalance for Genesis case
// this function only useful for spos minting process* g2 r# r- o z9 ]+ L. e
// here atHeight should be larger than lastHeight (validation), ~- b: k6 D2 K, d! r! f# g
val lastHeight = state.lastUpdateHeight(account).getOrElse(0)0 r& ]6 G3 m6 n: U& y
val lastWeightedBalance = state.lastUpdateWeightedBalance(account).getOrElse(0L)) w) s5 k: Q( S- Y
val lastEffectiveBalance = state.effectiveBalanceAtHeightWithConfirmations(account,lastHeight,0)* f9 y2 _4 g& B( p; O
val cntEffectiveBalance = state.effectiveBalance(account)7 T2 |( F5 z4 o3 A2 F% y/ d
val weightedBalance = lastHeight == atHeight match {
case true => state.lastUpdateWeightedBalance(account).getOrElse(0L)5 ~9 ~( ?# h% z$ C
case _ => weightedBalaceCalc(atHeight - lastHeight, lastEffectiveBalance, lastWeightedBalance, cntEffectiveBalance, fs)
}: G' M. r0 I+ M: O+ O# F3 z
weightedBalance5 z* ?$ O6 N5 f8 l
}5 q- {' h! l2 S- D( Q: C
// TODO: all SPoS related functions will be defined here
}' [. L) {; c8 Q; |5 v0 m: ]
公链对比0 G1 k" _& H% V' _4 v! [3 O
比特币:区块链的龙头老大,知名度最高,受众最广;代表区块链1.0
以太坊:首先提出智能合约的概念,开源社区最为成熟;代表区块链2.0
EOS:秒级出块速度,Dapp生态最为繁荣;代表区块链3.0
VSYS:由PoS之父Sunny King 创建的区块链数据库云,自研了最高效的共识算法SPOS;代表区块链5.0
从开发语言来看。比特币和EOS基于C++,开发门槛最高,目前EOS的智能合约只支持C++,且在短期内没有支持其他语言的可能;以太坊基于GO,开发门槛稍低,但GO语言的生态远不如JAVA,C/C++,并且GO自身的特性还有很多需要完善的地方; VSYS基于scala开发,scala语言性能优异,完美兼容java生态体系,想必Sunny King选择scala也是预见到它的潜力。
从共识机制来看。PoW(Proof of Power)即工作量证明,是最有名也是目前使用最广泛的共识算法,虽然通过争夺记账权的方式一定程度上保证了整个网络的安全性,但消耗了巨大的资源,从结果上来说所有的节点都在做毫无意义的运算;而且挖矿机制导致平均10分钟才形成一个区块,很多区块被确认时间超过2个小时,交易速度延迟度非常高。PoS(Proof of Stake)即股权证明,由质数币、点点币、VSYS的创始人Sunny King提出,PoS 试图解决 PoW 机制中大量资源被浪费的情况,这种机制通过计算你持有占总币数的百分比以及占有币数的时间来决定记账权;其被提出后受到业界热烈追捧,区块链2.0的大量公链包括以太坊都是采用POS共识机制。DPoS(Delegated Proof of Stake)即委托股权证明,是 PoS 的变种方案,比特股 BTS 和EOS即使用 DPoS共识机制,DPOS相较于POS的变化在于通过不同的策略,不定时的选中一小群节点,这一小群节点做新区块的创建,验证,签名和相互监督,这样就大幅度的减少了区块创建和确认所需要消耗的时间和算力成本。SPOS(Supernode Proof-of-Stake)即超级节点股权证明,很明显,SPOS是POS的进化版本,代表区块链5.0的共识机制,由POS的创始人Sunny King亲自操刀,SPOS的出现,代表着公链在性能上获得里程碑式的突破,基于SPOS的VSYS公链能够承载海量的dapp生态,使得区块链产品大规模落地成为可能。. i% Y" P; Y4 Q1 W. _
测试网络部署
服务器OS版本:Ubuntu 16.04.5 LTS7 j4 h$ g; @: i. J: \/ c' }
VSYS版本:v0.1rc1% I! X; C( W( {+ o8 R |
Install JRE 1.8
sudo add-apt-repository -y ppa:webupd8team/java
sudo apt-get update- w( e* Q- Y( U8 d
sudo apt-get -y install oracle-java8-installer
确认安装成功
java -version6 v* o! k T& \. L/ O
Install SBT (Scala Build Tool)
echo "deb https://dl.bintray.com/sbt/debian /" | sudo tee -a /etc/apt/sources.list.d/sbt.list6 o" `/ t. P* y- V4 O: V5 ^/ g
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 2EE0EA64E40A89B84B2DF73499E82A75642AC823% ^: t# N7 a) Q7 c5 R
sudo apt-get update1 N! _* `- z/ [1 G& J* v, n
sudo apt-get install sbt! ^" z$ q y6 f6 [# a2 @5 Q+ |; i
Tips:某些版本的ubuntu执行sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 2EE0EA64E40A89B84B2DF73499E82A75642AC823的时候可能会报错:
$ sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 2EE0EA64E40A89B84B2DF73499E82A75642AC8237 H5 i# D F; g' E
Executing: /tmp/apt-key-gpghome.hEtryDWci3/gpg.1.sh --keyserver hkp://keyserver.ubuntu.com:80 --recv 2EE0EA64E40A89B84B2DF73499E82A75642AC823
gpg: failed to start the dirmngr ‘/usr/bin/dirmngr’: No such file or directory
gpg: connecting dirmngr at ‘/tmp/apt-key-gpghome.hEtryDWci3/S.dirmngr’ failed: No such file or directory5 x9 _- Q* o. G, R. W+ w( p
gpg: keyserver receive failed: No dirmngr
$ W, Y/ w# p" m1 R; E
按照提示先安装dirmngr即可:$ sudo apt-get install dirmngr" C" \ C% d$ u) u; ?/ k- g
确认sbt安装成功
$ which sbt
/usr/bin/sbt( y8 N& M2 c T, C0 r2 P+ g& ~
安装VSYS节点服务
下载deb安装文件5 Z4 T% { u8 R
mkdir ~/vee && wget https://github.com/excelsia/vee/releases/download/v0.1rc1/vee-testnet_0.1rc1.deb
安装- \9 D. S( G- i% F$ }! ~
$ sudo dpkg -i vee*.deb
Selecting previously unselected package vee-core-testnet.
(Reading database … 51859 files and directories currently installed.)3 j+ U+ I/ b& k$ f1 V
Preparing to unpack vee-testnet_0.1rc1.deb …
Unpacking vee-core-testnet (0.0.3) …
Setting up vee-core-testnet (0.0.3) …
Warning: The home dir /var/lib/vee-core-testnet you specified can’t be accessed: No such file or directory* a1 C& D) G* Z+ ~; Q
Adding system user `vee-core-testnet’ (UID 112) …( Q; I9 X* }1 j" `# G! O2 Y8 k% P+ B
Adding new group `vee-core-testnet’ (GID 116) …
Adding new user vee-core-testnet' (UID 112) with group vee-core-testnet’ …- B! s% _( \' v; l
Not creating home directory `/var/lib/vee-core-testnet’.
Creating default config file …
Installing systemd service …+ L4 z- P: Z0 o1 y
Created symlink from /etc/systemd/system/multi-user.target.wants/vee-core-testnet.service to /lib/systemd/system/vee-core-testnet.service.
systemctl启动服务并确认启动状态6 V: b3 O# u- ^' g0 [
$ sudo systemctl start vee-core-testnet.service
$ sudo systemctl status vee-core-testnet.service+ p! ]# c! U$ }8 D1 r: ^2 g4 i' ~
服务启动正常,整个部署过程简洁,清晰。
s, H# r/ v% ~
配置文件# p1 f1 Z# ~* r6 D
vee配置文件路径:/etc/vee-core-testnet/,重点分析vee.conf/ H Q; `! {6 K) {# [1 x" u _ W
1 vee {
2 #directory = /tmp/vee
3 logging-level = DEBUG% p5 z. t' p/ r. f6 i. X" ?
4 network {4 T( c8 \' r/ c" S! z( ^8 l* t
5 known-peers = ["52.8.148.150:19923", "18.130.233.104:9923"]
6 black-list-residence-time = 30s, j# S9 M2 B$ @7 B6 D
7 peers-broadcast-interval = 5s
8 connection-timeout = 30s8 ?! h' l# b/ P% X, W
9 }
10 wallet { U) s5 W; q. c) P' N* }$ Z# N
11 password = ""
12 }
13 blockchain { H/ U4 ~% e" f8 Y6 Z1 e& e
14 type = TESTNET
15 }
16 checkpoints.public-key = 4HmYEMpPaJXJsDgdjGfFNXLAY2CdDAfhynwSL9BqydNA# F7 k' U* b3 U6 _
17 matcher.enable = no1 v8 U. ]. |; e: A2 F( E+ C
18 miner {
19 enable = yes3 D6 Y( p) [; {" W8 o
20 offline = no
21 quorum = 1! W$ f, T K% t3 w
22 generation-delay = 1s
23 interval-after-last-block-then-generation-is-allowed = 120h) o, @% B3 u$ }4 o( p5 u. X- E4 ~
24 tf-like-scheduling = no2 I4 D; w. O! H/ J6 H b
25 reward-address = ""# X% Z. A! z) ^7 u* M/ @' Y; W* o9 ?! y, l
26 }1 s! H F5 v+ _7 T& Y
27 rest-api {- Y# c! r0 N* b, o5 o& u
28 enable = yes1 G: x5 g% F v; F _
29 bind-address = 0.0.0.0
30 #api key veetest2018 for hash Fo8fR7J1gB3k2WoaE6gYKMwgWfoh9EtZtXAMBxYYCJWG
31 api-key-hash = Fo8fR7J1gB3k2WoaE6gYKMwgWfoh9EtZtXAMBxYYCJWG
32 }
33 utx.broadcast-interval = 3s
34 }8 f$ F5 v/ x1 x' G- X' w7 p
网络相关配置:# m& d- Z" Z& L5 d" T; ?6 j0 @" |
known-peers:本地节点初始化启动时连接的种子节点,目前默认是两个。这也是为什么本地节点启动就可以正常工作的原因。4 E( ?7 m" L3 J+ I' m
peers-broadcast-interval:节点信息同步的周期,比如如果设置为5s,就是每隔5秒钟同步一次known-peers节点信息。: _0 |3 `: r; ~( f1 }
black-list-residence-time:将节点列入黑名单的时间
connection-timeout:peer节点网络连接超时时间% n f( d0 E ?( Z
钱包相关配置:
file:指定wallet文件的路径,如果不指定默认的钱包文件为/var/lib/vee-core-testnet/wallet/wallet.dat
password:设定保护钱包文件的密钥
blockchain配置:+ S1 P' j8 i7 y5 S8 |8 f; i
type:区块链网络类型,测试网络对应的值为TESTNET
miner配置8 k* ~: {. E- v( p
enable:挖矿开关- I0 L* U, M Z1 \8 a; e& C
offline:节点脱机开关
quorum:连接多少个peer节点触发挖矿。默认为1表示只要连接一个peer节点就开始挖矿,设为0意味着节点离线状态也可以生成区块,类似solo模式。
generation-delay:生成区块的超时时间9 S% a. o- L) ~- F# V/ M* M
interval-after-last-block-then-generation-is-allowe:意味着本地节点将不会开始产生区块直到有最后一个时间不超过120小时的区块9 a( E+ O8 ]& r; K$ b
reward-address:自定义本地节点挖矿奖励的地址,默认为空即挖矿成功后自动发送到本地节点地址,也可以指定冷钱包地址,无疑这样安全性更高。$ u% r8 Z4 q! w1 k( s# `
REST API配置
enable:激活或关闭rest-api功能
bind-address:指定rest-api绑定地址,如0.0.0.0表示开放外来连接
api-key-hash:api-key的哈希值,默认为Fo8fR7J1gB3k2WoaE6gYKMwgWfoh9EtZtXAMBxYYCJWG
8 J" n3 H \! h6 A
vee.conf详细的配置模板参见:https://github.com/excelsia/vee/blob/master/vee-testnet.conf
- b L* H4 ]4 d0 m9 F
节点数据, c# [ H7 o: d3 h: Y( [1 T
VSYS数据目录
/var/lib/vee-core-testnet/2 k; m2 B$ v! I: p+ A* y7 T1 K5 |
进入数据目录查看,区块链数据文件blockchain.dta同步完成后目前大小为373M。
$ ls -alh
total 422M
drwxr-xr-x 2 vee-core-testnet vee-core-testnet 4.0K Sep 15 05:09 .8 i3 q% b1 G+ t
drwxr-xr-x 5 vee-core-testnet vee-core-testnet 4.0K Sep 15 05:09 …
-rw-r–r-- 1 vee-core-testnet vee-core-testnet 373M Sep 15 08:04 blockchain.dat2 ?, g* }$ N, \2 p$ v) r4 X
-rw-r–r-- 1 vee-core-testnet vee-core-testnet 8.0K Sep 15 05:09 checkpoint.dat, r) v3 c: v/ H, D
-rw-r–r-- 1 vee-core-testnet vee-core-testnet 52K Sep 15 08:52 peers.dat N4 l4 `) D, q; w6 ]- {
-rw-r–r-- 1 vee-core-testnet vee-core-testnet 49M Sep 15 09:00 state.dat) R t& @* a6 i. j
节点部署总结
VSYS的节点部署简单程度绝对会让开发者惊喜。如果是第一次部署节点,不需要改动任何参数和配置,按照文档步骤10分钟以内即可将本地节点搭建完成。此外,很期待VEE社区完善对更多系统以及docker容器的支持。
评测总结
本文通过项目概览、项目特色、github库信息、代码解构、公链对比、节点部署等多维度对VSYS项目进行全方位的技术评测。结论是VSYS项目独具特色,与其定位"区块链数据库及应用平台"相符。VSYS生态工具链完成度非常高,由Sunny King亲自带领的技术团队实力毋庸置疑。具体工程实现上由scala开发的VSYS节点性能高效,考虑到scala完全兼容java技术栈,一旦主网发布必然能借势java生态获得大量开发者的追捧。在公链对比环节,我们可以发现从比特币、以太坊、EOS再到VSYS,代表着公链的版本升级,生态也日趋繁荣,基于SPOS共识机制的VSYS 将成为继以太坊、EOS之后里程碑式的公链项目,值得长期期待。最后,评测小组对VSYS节点进行部署测试,10分钟内即完成本地节点搭建,在评测期间(一周)节点运行状态良好。
成为第一个吐槽的人