小福星 https://blog.xiaofuxing.name/ Pluswave的技术和生活 en-us Wed, 22 Dec 2021 00:00:00 +0800 https://blog.xiaofuxing.name/2021/12/22/some_tips_about_education.html https://blog.xiaofuxing.name/2021/12/22/some_tips_about_education.html <![CDATA[教育杂谈]]> 教育杂谈

1

为人父母,没有经过任何培训,就得扛起教育子女的责任,这件事情挺可怕的。别人怎么样我不能妄加评论,我自己家倒可以说说。 我的父母是普普通通的农民,他们养孩子,就两个字:放羊。可能我的同龄人的父母大多数都是这样的吧。不过放羊和放羊还是有 区别的,最大的区别在于对孩子的期望和交流模式。我的父母对孩子们没什么过高的期望(相反还很低),这样就造就了我们兄弟们 从小就得独立思考和独立规划自己的未来。而交流模式,我发现我的父母和我们的“正式”的交流太少了,少到我们很难和她们成为 生活上和心灵上的朋友。我现在也有了孩子,老大已经上了小学,她就生活在我和我爱人的影响之下,我们也没有经过培训,因此 同样也会出现问题。城市里普篇的问题在于母亲的焦虑和鸡娃,而我作为父亲,又似乎太“放”了。和孩子的交流,我倒是认为自己 改掉了父母和我们交流的那种毛病,尽管如此,我发现孩子并不总是愿意和我分享她的内心世界。小福星现在3年级,作业量比之前 大多了,她有明显的不适应感,最近经常在家里因为作业的事情哭。原因无非是作业太多,她压力大而发泄。为了这事我和她妈没少 操心。好的一点,是她能哭出来,之后我和她还能交流,但同样的事情却总是发生,就让人有点受不了。

2

时代在改变,我们小时候因为物质条件有限,可以在父母期望不高的情况下自己建立目标(哪怕仅仅是摆脱穷困的目标),而现在的孩子们 丰衣足食,她们对于“为什么要学习”这件事情的理解是有相当的疑惑的。我告诉小福星,学习的理由有两种,大的方面,成为国家建设 的有用之才,小的方面,生活得更好一些。小福星懂吗?似懂非懂。我认为这是她们小孩过于缺乏与社会的连接导致的。所以作为父母 的我们,就需要在这个连接方面做一些功课了。那天她哭完,情绪平复下来之后,我问她,“你知道为啥要学习吗?我告诉过你的。” 她说,知道,为了生活得更好。我又问,你觉得这样说对吗?她说,长大以后那么远的事情,我不知道是否对。哈哈,小孩子还缺乏 长远规划的能力,因此那两种说法对她的吸引力就不够了。我就想给他换一个思路,从学习中找点乐子。陪她做数学作业,然后在纠正她的 错误的同时,告诉她各种数字本身有“亲戚”关系,也开始让她自己去找点其他的乐子。她说:“学语文还有点乐趣,因为可以读书,数学 和英语就没什么乐子”。我告诉她说,等你的英语学到现在语文的程度,可以阅读英文小说的时候就肯定有乐趣了;而数学,肯定还有很多 乐子,比如你不是喜欢玩数独游戏吗?“书山有路勤为径,学海无涯乐作舟”,应该是新一代小孩学习的动力所在。

3

帝都的小学,2021年了,还在用收现金这么的原始方法(还要在现金上用铅笔写上名字)交餐费,实在觉得有点说不过去。

4

“标准答案”限制了孩子的想象力。语文试卷上,一个阅读题,说的是一头善良的山羊,被一头困在井下的、花言巧语的狼所骗,把狼 救上来反被吃了的故事。这题有几个填空,其中一个是,山羊被狼吃的时候的心里,小福星填的是“我真不该救狼”,而老师判她错了, 说应该是“很后悔”,我觉得这样教孩子有巨大的问题。“我真不该救狼”是“后悔”的具体内容,表述得比“很后悔”要生动得多,这个空 如果是因为“很后悔”是标准答案就排斥了别的内容,太打击孩子的积极性了。我只好告诉小福星说,我认为你做得很对。

]]>
Wed, 22 Dec 2021 00:00:00 +0800
https://blog.xiaofuxing.name/2019/12/02/blockchain_current_stage_and_direction.html https://blog.xiaofuxing.name/2019/12/02/blockchain_current_stage_and_direction.html <![CDATA[区块链技术的当前发展阶段和挑战]]> 区块链技术的当前发展阶段和挑战

今年10月24日,中共中央政治局就区块链技术发展现状和趋势进行第十八次集体学习,习近平主持学习并发表重要讲话。因为这件事情, 区块链这个概念一下子火起来了,很多人在讨论,政府机构开始给一些优惠政策, 不过,从行业发展的角度,很多人大概不知道什么是区块链,更别说理解区块链到底处于什么阶段,以后大概会怎么发展了。

什么是区块链,Google或在Baidu一下,或在 本博客文章 也有提到,本文不在重复了。本文尝试回答两个问题,1区块链处于什么阶段,2以后发展的关键技术点有哪些。

第1个问题,其实是最近看到了一篇来自哈佛商业评论的文章 区块链的真相 ,发表与2017年1月,把这个问题讲得很透彻,尽管文章发表已经快3年了, 我觉得完全没有过时,因此与读者分享。这里给出这篇文章的快速总结,不过瘾的请看原文。

真相文提到,区块链不是颠覆性技术,是基础设施。区块链的各种技术,比如Hash算法、公私钥密码学算法、P2P网络,都不是什么新东西,但是组合在一起发生了威力, 变成了一个基于数学的、可信的、防篡改的历史账本。对比TCP/IP,基础设施的创新有4个阶段,包含单点、局部、替代和转换。 每个阶段的特点可以从两个维度来区分,一个维度是创新程度的高低,一个维度是协作的复杂度。如下表:

阶段 创新程度 协作复杂度 TCP/IP 区块链
单点 Email :1970s Bitcoin: 2009~
局部 不同类型的局域网:1980s~1990s 联盟链:2013~
替代 WWW:1990s,原来的应用搬到网上 加密货币尝试替代法定货币,障碍很多
转换 Napster, ebay, alibaba, skype, Goolgle: 2000+ 智能合约,真不知道啥时候能大规模应用

这就是对真相文的一个最简单的总结吧。我们现在的阶段,处于局部的发展期和替代的早期,转换还得10年以上甚至更久。 而中国鼓励区块链的发展,不可能违背基础设施发展的基本规律,因此目前来说,就是大力发展联盟链。不过联盟链对于小公司和个人 没啥机会(基本上是大机构的事情),而后来的替代和转换(基于公链或者是公共账本)才是百花齐放的时代,也是现代互联网的商业逻辑的重大升级, 那么这个升级有什么关键性的技术点需要克服和解决吗?笔者看来,至少有3个关键技术点:

第一是自主身份。互联网的身份认证,无非两种模式,1是用户去某一个网站注册,2是通过大的身份提供商(Google/Facebook/微博/微信/QQ)间接认证。 目前的这两种模式有两个问题:一遍一遍的填表和隐私泄漏。区块链时代的自主身份将实现用户自己控制身份,并按需授权使用。在区块链统治的时代, 人们不需要去每个应用那里注册自己的各种信息,而是提前将自己的各种身份信息通过锚定时间戳和加密的方式写入了不可更改的分布式账本,并且在 需要的时候授权应用验证最少可用的身份信息。也就是说,用户夺回了身份所有权,没有第三方数据库记录你的身份信息,自然无法泄漏你的信息,或者 形成各种羊毛出在猪身上的商业模式。

第二是自主的分布式存储。区块链的多方记账模式实际上是通过冗余增加了安全型,因此从成本考虑,要求账本上记录的数据尽可能少,因此需要通过第三方存储来补充区块链的存储。在这种模式中,存储的服务提供商 只是一个黑盒子,没法知道用户存的是什么(因为加密了),用户似乎在使用一个互联网规模的虚拟电脑中的硬盘。与自主身份类似,用户夺回了数据所有权。

第三是区块链的规模和可扩展性。具体涉及两个子问题,交易性能和存储空间的增长。在交易性能方面,目前的公链的TPS(Transactions per seconds 每秒交易数)都没有多高,号称百万TPS的EOS实际性能也就几千, 一个空投就让很多CPU抵押不足用户没法使用这个链了。在存储空间增长方面,目前的各个公链的全节点因为保留了从主网上线以来所有的交易记录, 因此全节点的空间需求都是一直增长的:现在同步一个比特币全节点,需要至少准备300G的硬盘空间(比特币已经运行10年),并且每年大约增长50G。而如果要同步所有的ETH数据, 则至少需要1T(我没有同步过,因此真不知道实际量,但1T肯定是必须的)。公有链的一个特点是人人可参与,可是硬盘随着时间无限增长并不是人人都可以承受的。

从另一个角度看,我列的这3个问题 其实都与经济有关:互联网模式下的隐私泄漏,原因在于平台方让用户“免费”使用服务,而承担了服务的计算、存储和带宽费用,就必然需要通过别的办法来 补偿甚至盈利,那么用户的隐私是大概率会被变现的:也就是说免费的东西是最贵的。在区块链时代,用户应该习惯付费获得这些服务,同时完全保护好自己的隐私。 因此很多面向未来的问题都围绕这个主题:如何构造一个让用户接受的、经济可行的商业模式。自主身份避免了用户一遍一遍的输入密码,也让KYC过程变得异常简单和经济,还保护了用户隐私; 自主的分布式存储厘清了用户和平台的存储权利和义务,避免了免费的陷阱;规模和可扩展性的问题,则是区块链本身的经济模型问题之一。

当然,除了这3个关键技术点,还有跨链、抗量子密码算法等等问题也都很关键,限于作者水平和兴趣,不能一一的列出来。

补充: 这个话题我做了视频,欢迎讨论。

]]>
Mon, 02 Dec 2019 00:00:00 +0800
https://blog.xiaofuxing.name/2018/07/13/graphene_account_features.html https://blog.xiaofuxing.name/2018/07/13/graphene_account_features.html <![CDATA[石墨烯区块链帐号体系的特点]]> 石墨烯区块链帐号体系的特点

这半年比较热门的EOS是石墨烯系列的最新区块链。(之前有BitShares, Steem, PeerPlay, 公信宝,yoyow) 石墨烯系列区块链有一个不同于比特币和以太坊的帐号体系,特点如下:

  • 帐号和公私钥是绑定关系,操作语义通过帐号来指定,交易签名通过私钥来签,公钥验证。
  • 每个帐号有多个角色的公私钥对,例如EOS包括owner和active。
  • 新帐号必须通过区块链注册。
  • 原生支持多重签名。

下面逐个说明。

帐号和公私钥绑定

在比特币区块链上,一个私钥对应拥有“花费该地址BTC余额”的权利。 而地址是公钥的一个变体。 一个钱包软件一般会管理很多组私钥和地址,需要收款时,可生成一个新的地址, 把这个新生成的地址直接发送给付款方即可,无需向区块链注册这个地址。 一个自然的设计就是,比特币的交易必须指定的收款方的地址,虽然那个地址人是没法记住的。

石墨烯系列则不同,多了一层显式的帐号,区块链使用者必须拥有帐号才能操作, 帐号是自己设定的便于记忆的名字,区块链的转帐交易内容是帐号到帐号, 与公钥(地址)或者私钥无关; 只有交易的签名需要使用相关帐号所关联的私钥来签, 区块链的p2p参与者(例如见证人或者超级节点)需要通过帐号关联的公钥来验证签名的有效性。

多个角色的公私钥对

有了帐号体系,是不是每一个帐号对应一个公私钥对呢?不是的, 一个帐号又分成了不同的角色,每个角色的权力不一样。owner角色拥有最大的权力, 可以进行任何操作; 而active角色则与转帐相关。在BitShares区块链上,有memo角色 的私钥并不参与签名,而是与对方的memo角色公钥一起,构成转帐私密备注的对称加密密钥来源 (搜索术语ECIES 或者 ECDH)。在Steem区块链上,还有一个特别的 posting角色,可以用于 发帖。

新帐号必须通过区块链注册

由于多了一层帐号,就涉及到帐号和公私钥对应关系的共识,解决的方法就是需要注册帐号, 通过已有的帐号向区块链广播一个”注册帐号”的交易来注册新帐号,这个交易的内容至少包括

  • 新的帐号名
  • 各个角色对应的公钥以及权重

除了注册帐号,还可以修改帐号,例如更换某个角色的公钥(需要使用owner角色的私钥来签名)。

原生支持多重签名

所谓多重签名,是指需要多个私钥共同签署确认一个交易,否则交易无法达成。帐号的每一个角色 都可以设定由多个公钥来共同管理:设定一个总的最低门限值和每个公钥的权重,签名有效的定义 是多个公钥的签名,并且这些公钥的权重之和大于该角色的最低门限值。举个例子,夫妻二人共同管理 一个帐号的active权限,那么可以一人生成一个公私钥对,注册/修改帐号时把该帐号的 active权限设为

  • 总的门限值2
  • 丈夫的公钥,权重为1
  • 妻子的公钥,权重为1

这样总是需要双方同意才能动用帐号的资金。(当然这是一个简化的例子,由于owner权限更高级,需要把 owner权限也做相应的限制,避免通过owner权限绕过active的多签)

所谓原生支持,是指石墨烯系列区块链提供了多签功能的简单易用的API,直接使用即可。与之对应,如果比特币要支持多签, 则需要编写脚本,涉及复杂的编程逻辑; 如果以太坊要支持多签,需要通过编写智能合约来实现,而编写智能 合约本身是很难的,特别容易出现漏洞。前不久,著名的Parity钱包的一个多签合约漏洞就导致的大量ETH“锁死”。

原生支持多签这个特点,在使用好的情况下可以大大提高安全性:因为黑客需要拿到更多的私钥才能黑掉 一个帐号。

]]>
Fri, 13 Jul 2018 00:00:00 +0800
https://blog.xiaofuxing.name/2017/07/06/security_difference_between_wallet_mode_and_account_mode_of_bitshares_ui.html https://blog.xiaofuxing.name/2017/07/06/security_difference_between_wallet_mode_and_account_mode_of_bitshares_ui.html <![CDATA[比特股钱包模式和账号模式的安全性差异]]> 比特股钱包模式和账号模式的安全性差异

比特股官方钱包,目前支持两种模式,钱包模式和账号模式。老用户一般推荐新用户使用钱包模式,并告诉新用户,钱包模式更安全,那么,到底安全在哪呢?

首先,这两种模式下,密码和私钥仅仅涉及轻钱包、网页或者浏览器本身,不会通过网络泄露给第三方。在网络上传输的数据,都是区块链上公开的数据,谁都可以看。 轻钱包有锁定模式和解锁模式,锁定时,内存里面没有私钥,解锁时,内存里面有私钥。而当用户下单或者转账时,一定需要进入解锁模式,通过用户的私钥来对交易 签名,并广播出去。从这个意义上说,安全性没有什么区别。

在这个基础上,钱包模式一般来说确实更安全,因为钱包的备份文件是用户的主密码加密的,当需要转移机器、浏览器、轻钱包时,需要同时提供钱包备份文件和用户的主密码, 只要保存好自己的备份文件不外泄,一般不会被黑客破解拿到私钥。反之,账号模式下,没有钱包备份文件,所有的私钥都是账号和密码的hash函数,如果密码太弱,那么黑客 可以通过暴力攻击来碰撞得到用户的私钥,对高净值资产的账号,黑客尤其有这个动力。

但是事情也是有两面性的, 如果认为钱包模式安全,而设置一个弱密码,那么一旦备份文件落到他人之手,黑客破解起来反而更加容易;而考虑到账号模式本身的安全机制不足, 一般人会设置一个足够复杂的密码(新版官方钱包甚至要生成一个随机密码),其安全性就会比外泄备份文件的弱密码钱包模式要高得多。

总结一下见下表:

  钱包模式 账号模式
强密码 安全指数 4 安全指数 3
弱密码 安全指数 2 安全指数 1

这里的安全指数没有实际的量化意义,只是用来互相比较,指数越高越安全。最安全是钱包+强密码,这时候即使黑客拿到备份文件也无从下手;而账号模式如果有强密码,也是很安全的,无需担心什么; 反之,弱密码在两种模式下都不安全,只不过钱包模式下多了一个备份级别而已。 从易用性和安全性的平衡来说,账号模式+强密码,既安全,又易用,适合小白使用,老手也可以放心的使用。

注意,钓鱼网站、木马软件窃取用户的私密信息,不在本文讨论范围之内。

]]>
Thu, 06 Jul 2017 00:00:00 +0800
https://blog.xiaofuxing.name/2017/06/08/bitsharesjs_detail_transaction_broadcast.html https://blog.xiaofuxing.name/2017/06/08/bitsharesjs_detail_transaction_broadcast.html <![CDATA[bitsharesjs库详解二:交易广播]]> bitsharesjs库详解二:交易广播

上文 解析了ChainStore,本文继续,说一说如何利用用户的私钥来做交易广播。 如何搭建环境本文不再复述,请参考上文。

例子

运行

交易广播没有测试,只有一个例子文件,做的是转账,利用的是测试链。代码文件在这里 examples/transfer.js。运行方法

npm run example:transfer

不过一行不改,运行结果是这样的

> bitsharesjs@1.2.4 example:transfer /home/zzb/bitsharesjs
> babel-node examples/transfer

(node:21851) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: unexpected server response (200)

这个错误的根源在于,测试链的host改了,需要把第7行改成

7
Apis.instance("wss://node.testnet.bitshares.eu/ws", true)

另外连接错误处理没有加(可帮助定位问题)。请读者自行处理。

仅仅修改第5行,重新运行例子,结果还是有问题的

> bitsharesjs@1.2.4 example:transfer /home/zzb/bitsharesjs
> babel-node examples/transfer

Connected to API node: wss://node.testnet.bitshares.eu/ws
connected to: Test network
synced and subscribed, chainstore ready
(node:22400) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): timeout

一个timeout,让人好疑惑,出什么问题了呢?看了代码知道大约知道转账逻辑是从账号bitsharesjs向faucet账号转账0.1TEST币,用uptick看看bitsharesjs账号看看

uptick --node wss://node.testnet.bitshares.eu/ws info bitsharesjs

发现,没有那个账号!(关于那个timeout错误,实际上应该是没有账号错误,这个错误报得不准,与ChainStore的设计有关,读者可以查看ChainStore解析一文。)因此需要改第4行的私钥和第13行的账号,改成哪个呢?自己通过 测试链UI 注册一个最靠谱。注册完账号并且修改了之后,运行结果应该类似这样

> bitsharesjs@1.2.4 example:transfer /home/zzb/bitsharesjs
> babel-node examples/transfer

Connected to API node: wss://node.testnet.bitshares.eu/ws
connected to: Test network
synced and subscribed, chainstore ready
memo pub key: TEST8Sz5PMkSZftXi7qPL5XNeTMG9SQrxRPcsC4DdFRAvZr5qhgf6M
serialized transaction: { ref_block_num: 0,
ref_block_prefix: 0,
expiration: '1970-01-01T00:00:00',
operations: [ [ 0, [Object] ] ],
extensions: [],
signatures: [] }

然后在测试链UI上就可以发现转走了0TEST,解锁后可看到备注里面就是程序里面的。至于0TEST,实际上是0.1TEST,而代码上的量是10000,为什么呢?所有的资产都会设定一个最小单位,由小数点位数决定。例如测试链上TEST的小数点位数为5,而主链上BTS的小数点位数为5,人民币的小数点位数为4。 设定这个位数之后,所有的链上交易都用整数表示资产数量,其中1表示10的-n次方,n为资产的小数点位数。以测试链核心资产TEST来说,交易广播使用1表示1e-5,10000就是0.1了。整数的好处是没有浮点数加减法带来的误差,但对人来说并不直观,因此给用户显示,需要做一个转换。

代码解析

通过运行完成转账之后,解析一下例子代码:

  1. 第7~8行,websocket API初始化
  2. 第11行,ChainStore 初始化
  3. 13~22行,账号、资产、备注初始化
  4. 24~30行,从链上获取相关的账号和资产
  5. 32~69行,构造转账交易,签名,广播。

特别说明:

  1. FetchChain函数来自ChainStore,提供了一个直接向区块链查询的异步(Promise)接口,resolve时,返回的是Immutable的Map类型。
  2. new TransactionBuilder() 用于构造交易对象
  3. tr.add_type_operation填写交易对象的内容,包括类型和根据类型需要填写的字段
  4. tr.set_required_fees() 异步向区块链获取转账费用,
  5. tr.add_signer(priv_key, pub_key) 签名交易
  6. tr.broadcast() 向区块链广播交易

交易对象的填写

通过上文的代码解析,我们发现即使我们通过拷贝例子代码能够发起转账交易,我们也不知道怎么去下一个限价单,其他的步骤都好理解(或者可以直接抄),唯独 add_type_operation这个函数让人摸不着头脑。这时候需要看源代码,通过阅读 add_type_operation的实现代码,可以知道在 lib/serializer/src/operations.js 里面查找所有的操作和参数类型,例如转账参数从401行开始

401
402
403
404
405
406
407
408
409
 export const transfer = new Serializer(
     "transfer",
     {fee: asset,
     from: protocol_id_type("account"),
     to: protocol_id_type("account"),
     amount: asset,
     memo: optional(memo_data),
     extensions: set(future_extensions)}
 );

把这个参数和上面的 add_type_operation函数对比,是不是很清晰呢?如果要做挂单,显然就得看411行开始的定义了

411
412
413
414
415
416
417
418
419
420
 export const limit_order_create = new Serializer(
     "limit_order_create",
     {fee: asset,
     seller: protocol_id_type("account"),
     amount_to_sell: asset,
     min_to_receive: asset,
     expiration: time_point_sec,
     fill_or_kill: bool,
     extensions: set(future_extensions)}
 );

需要注意的是:

  1. 区块链广播的买单和卖单都是卖单:用A资产买B资产,广播为卖出A资产,获得B资产。
  2. asset类型照例子处理
  3. time_point_sec 类型是时间戳,javascript里面构造一个Date对象即可。下层传输格式为 “%Y-%m-%dT%H:%M:%S”, 表示UTC时间,精确到秒。例如 2022-01-01T03:23:39 。
  4. fill_or_kill一般为false,表示等待限价单被对手吃。如果为true,表示不能成交的话立刻失效。
  5. extensions应该没啥用。(也许匿名交易需要,目前我不知道)

设计点评

关于交易部分,我觉得设计也不够人性化,比起python-bitshares来说易用性很差,需要程序员了解很多链上的细节。作为一个UI直接使用的中间库,明显抽象层次还是太低了。

思考

如何写一个程序,用你的私钥挂一个资产交易的限价单?欢迎留言。

]]>
Thu, 08 Jun 2017 00:00:00 +0800
https://blog.xiaofuxing.name/2017/06/01/uptick.html https://blog.xiaofuxing.name/2017/06/01/uptick.html <![CDATA[比特股命令行神器:uptick]]> 比特股命令行神器:uptick

之前在介绍比特股开源代码时,漏掉了基于Python的uptick和其依赖库 python-bitshares,因为我确实没发现。最近试用了一下uptick, 我认为非常好用,用了之后,我不想用图形界面的钱包了。本文介绍一下uptick和其依赖的库。

作者:Fabian Schuh

这两个项目的作者是 Fabian Schuh,网名 xeroc, github地址: https://github.com/xeroc ,目前比特股理事会成员之一。个人感觉,他写的代码质量比较高。

项目简介

uptick

uptick是基于Python的命令行工具,用于获取比特股区块链上的各种信息,也可以发起交易和转账。

代码地址: https://github.com/xeroc/uptick

python-bitshares

python-bitshares是基于Python的、与比特股API节点交互的库。uptick的底层。

代码地址: https://github.com/xeroc/python-bitshares

文档地址: http://docs.pybitshares.com/en/latest/

安装步骤

  • 确保系统安装了Python3 (3.3 3.4 3.5 3.6应该都没问题)
  • 确保系统安装了基于Python3的最新版本pip(9.0.1)
  • 命令行: pip3 install uptick
  • 命令行: pip3 install pycrypto (这一步理论上不需要,应该是某一个库依赖没写好的bug)

安装完成之后,首先需要设置API节点,否则默认是一个欧洲的节点,国内用的话很慢。国内尝试这两种

uptick set node wss://bit.btsabc.org/ws     #比特帝国节点
uptick set node wss://bts.transwiser.com/ws #transwiser节点

当然,自己编译了并运行了全节点的话,就更快了:

uptick set node ws://127.0.0.1:8090/

这个节点设定同时影响uptick和python-bitshares库,或者说uptick直接修改和使用了库里面的配置。

uptick常用命令用法表

命令 含义 举例
uptick 获取帮助  
uptick −−help 获取帮助  
uptick <COMMAND> −−help 获取特定子命令的帮助 uptick trades −−help
uptick addkey 增加私钥到钱包,一般用active key用于交易  
uptick listkeys 列出钱包中所有私钥  
uptick listaccounts 列出钱包中所有账号  
uptick trades <MARKET> 查看某个市场的交易历史 uptick trades BTS:CNY #最近,基于CNY的BTS成交情况
uptick orderbook <MARKET> 查看某个市场的当前限价单 uptick orderbook BTS:CNY #现在市场上,基于CNY有多少BTS的买单和卖单
uptick buy <AMOUNT> <ASSET> <PRICE> <BASE> 提交买单 uptick buy 2 BTS 0.4 CNY # 想用0.4CNY/BTS的价格购买2BTS
uptick configuration 查看当前配置  
uptick set <NAME> <VALUE> 修改配置 uptick set node ws://127.0.0.1:8090 # 设置API节点为本地重钱包
uptick info 获取各种信息  
uptick info bitcrab 获取账号bitcrab的相关信息  
uptick info BTS 获取核心资产BTS的相关信息  
uptick history <account> 获取某个账号的历史交易记录 uptick hisotry bitcrab
uptick balance <account> 获取某个账号的当前余额 uptick balance os
uptick feeds <ASSET> 获取某个智能资产的喂价信息 uptick feeds CNY
uptick openorders <account> 获取某个账号的未成交限价单 uptick openorders a-bot

python-bitshares代码示例

python-bitshares的文档也写得很好,有python基础的人能很快上手,这里给出两个示例代码。

获取最近一小时的最多50条BTS:CNY交易,计算这些交易的平均价格

#!/usr/bin/env python3

from bitshares.market import Market
market = Market('BTS:CNY')
from datetime import datetime,timedelta
now = datetime.utcnow()
d = timedelta(hours=-1)
onehourago = now + d


trades = market.trades(limit=50,start=onehourago,stop=now)

def getSum(filledOrders):
    cny = 0
    bts = 0
    for order in filledOrders:
        print(order['time'])
        cny += order['base']['amount']
        bts += order['quote']['amount']

    return {'cny': cny, 'bts': bts,
            'price':  cny/bts if bts >0 else 0,
            'size': len(filledOrders) }

print('成交:', getSum(trades))

获取当前市场上BTS:CNY的最多100条限价单,并分别计算买单和卖单的平均价格

#!/usr/bin/env python3

from bitshares.market import Market
market = Market('BTS:CNY')

orderbook = market.orderbook(limit=50)
#区块链最多返回50个买单和卖单,默认limit=25
bids=orderbook['bids']
asks=orderbook['asks']

def getSum(orders):
    cny = 0
    bts = 0
    for order in orders:
        cny += order['base']['amount']
        bts += order['quote']['amount']

    return {'cny': cny, 'bts': bts,
            'price': cny/bts,
            'size': len(orders) }

print('买单:', getSum(bids))
print('卖单:', getSum(asks))

给读者的思考

感谢阅读到这里的朋友,这么枯燥的技术细节您都读完了,能否思考以下问题呢?

  • uptick/python-bitshares可以发起交易,账号存储在本地计算机,安全性如何?
  • 如何利用uptick/python-bitshares发起交易?
  • 如何利用uptick/python-bitshares发起转账?

虽然我的博客、公众号有一些读者,但很少有留言的,我希望读到这里的朋友留言,给出您的任何想法(不限上面的3个问题),谢谢!

]]>
Thu, 01 Jun 2017 00:00:00 +0800
https://blog.xiaofuxing.name/2017/05/25/bitsharesjs_detail_chainstore.html https://blog.xiaofuxing.name/2017/05/25/bitsharesjs_detail_chainstore.html <![CDATA[bitsharesjs库详解一:ChainStore]]> bitsharesjs库详解一:ChainStore

bitshares开发入门:开源代码总览 介绍了比特股开源代码的总体情况,其中,bitsharesjs 位于UI层之下, bitsharesjs-ws 之上。本文尝试开一个系列之头:这个系列全部解析 bitsharesjs 代码。

bitsharesjs 库有三个主要模块,ECC, Chain和Serializer。ECC是关于椭圆曲线密码学的一些贴近钱包操作的库,Chain是关于链上数据获取和交易发起的,Serializer是Chain的工具支持,一般无需直接使用。 本文阐述Chain中的一个类: ChainStore。ChainStore的功能是链上数据的获取和缓存。本文提到的代码,如无特别说明,均以bitsharesjs的根目录为相对目录的起点。

环境准备

  1. 安装Nodejs到本地,建议安装当前的LTS版本,本文写作时,为 6.10.3 (如果已经安装请跳过这一步)
  2. 克隆代码到本地 ( 命令行下执行:git clone https://github.com/bitshares/bitsharesjs.git )
  3. 进入 bitsharesjs目录, npm install

注意:本系列文章依赖bitsharesjs的git版本 bdda47c2250b9b9ecf92d682849c7b5b1efae90f ,请确保一致,否则可能会造成理解偏差,尤其涉及代码行号。

从测试代码说起

测试代码文件: test/chain/ChainStore.js

测试方法,命令行键入

npm run test:chain

注意这个测试会测试 test/chain目录下的所有测试文件, ChainStore只是一个。如果没有本地重钱包,你会发现ChainStore会测试失败。下文教你如何修改代码来做测试。

背景说明:测试使用的是 mocha BDD测试框架 ,并且(整个项目)使用了 babel转码。

第3行

3
  import { FetchChain, ChainStore } from "../../lib";

导入了ChainStore。

第9-15行

 9
10
11
12
13
14
15
   before(function() {
       /* use wss://bitshares.openledger.info/ws if no local node is available */
       return Apis.instance("ws://127.0.0.1:8090", true).init_promise.then(function (result) {
           coreAsset = result[0].network.core_asset;
           ChainStore.init();
       });
   });

所有测试用例运行之前需要做初始化:先连接上全节点,测试代码使用的是本地节点,第10行注释说得明白,如果没有本地节点,那么就使用公网节点,例如openleger的。国内测试,建议改成帝国的: wss://bit.btsabc.org/ws 。 另外第13行有个 bug ,需要在前面加上 return,否则默认 return undefined,整个函数就会resolve掉,可能导致ChainStore没有初始化完成就执行测试用例,会出错的。修改后的代码应该是这个样子:

 9
10
11
12
13
14
15
   before(function() {
       /* use wss://bitshares.openledger.info/ws if no local node is available */
       return Apis.instance("wss://bit.btsabc.org/ws", true).init_promise.then(function (result) {
           coreAsset = result[0].network.core_asset;
           return ChainStore.init();
       });
   });

这样就可以测试了。但是,读者会发现,测试用例不见得全部pass。这里面有另一个BUG,下文详解。

init函数

当底层Api(bitsharesjs-ws提供的Apis)初始化OK时,必须调用ChainStore的init函数初始化,正如第13行所做的那样。

首先, ChainStore这个变量,容易混淆,这个是从 lib/chain/src/ChainStore.js这个文件导入的,而这个文件定义了一个ChainStore类,但本身导出的确实ChainStore类的一个全局Singleton

1352
 let chain_store = new ChainStore();

1352行生成了ChainStore类的一个实例。

1407
 export default chain_store;

1407行导出这个实例。

因此测试代码导入的ChainStore,是ChainStore.js文件中定义的ChainStore类的一个全局实例。这句话很绕口,多读几遍。

回到init函数,该函数返回一个promise,resolve的时候初始化成功。其他函数必须在init函数返回resolve之后调用。正因为这个特点,才有了上文所述第13行的少return的BUG。

4个测试用例的所调用的两个函数

4个测试用例实际上主要调用了ChainStore(Singleton)的两个函数:

  • getAsset
  • subscribe

其中 getAsset是 getObject的封装,表示获取资产。而getObject是一般的“获取对象”函数,而“对象”是bitshares区块链的核心数据。对象的id是3个整数, a.b.c。其中:

  • a表示空间,两个取值:1表示协议对象,这些对象会在websocket和p2p网络上传输;2表示实现对象,用于节点本地存储,可认为是共识数据的衍生数据。
  • b表示类别,协议对象和实现对象都有十多类不同数据。
  • c表示实例,不同类型数据的实例编号。

例如

  • 2.1.0 表示动态全局相关数据,例如一个抓取的实例:

    { participation: 100,
    recently_missed_count: 0,
    accounts_registered_this_interval: 22,
    next_maintenance_time: '2017-05-24T04:00:00',
    dynamic_flags: 0,
    witness_budget: 76200000,
    head_block_id: '0100685ba0b1d1902e8ccea5e0eac2172f679873',
    time: '2017-05-24T03:47:27',
    recent_slots_filled: '340282366920938463463374607431768211455',
    current_witness: '1.6.72',
    current_aslot: 16909777,
    head_block_number: 16803931,
    id: '2.1.0',
    last_irreversible_block_num: 16803912,
    last_budget_time: '2017-05-24T03:00:00' }
    
  • 1.3.x 表示各种类型的资产

  • 1.3.0 核心资产BTS
  • 1.3.113 锚定资产bitCNY
  • 1.2.x 表示各个账号
  • 1.2.0 理事会多重签名账号
  • 1.2.121 理事会成员巨蟹的账号 bitcrab
  • 1.2.12376 理事会成员abit的账号 abit
  • 1.7.x 表示用户提交的限价单
  • 1.8.x 表示call order(我还真没搞清楚是什么意思,请留言)
  • 1.11.x 表示用户相关的活动历史,提交限价单,取消限价单,转账给别人,收到转账等等

常用对象列表 可参看大部分的对象类型。

好,回到getObject函数,这个函数总是立即返回的,返回值有三种情况:

  • 返回 Map 类型 的对象,表示缓存中有了这个对象
  • 返回null,表示没有这个Object(id无效)
  • 返回undefined,表示正在查询API节点,需要以后重新调用

getAsset是getObject的封装,因此返回值同样遵守这个约定。由于getObject立即返回,而调用的时候如果返回undefined,怎么等呢?用 subscribe函数。 subscribe函数是通用的事件监听函数, 当 websocket连接之后,任何从API节点的事件,都会触发所有的监听者(subscriber)。

这个设计本身是否足够好?我认为不够好,因为subscribe会导致大量的无效监听,而getObject和subscribe的联合使用,从理论上说不一定能达到预期的效果: 因为监听者无法区分事件本身,而JS的异步特性会导致不确定性。从测试代码来说,4个测试用例并行执行,和webSocket的事件触发次序的不确定性,会导致subscribe里面的getAsset函数不一定得到想要的结果。如果改写其中的一个测试用例,设成it.only (忽略其他的测试用例),目前我的测试结果是总可以通过的,但从理论上,我仍然不相信这种单个测试用例的测试方法:万一监听到一个不相关的事件呢?从这个意义上来说,测试代码还不好写得正确,现有测试代码怎么改成逻辑自洽的还很难。

另外,就ChainStore来说,测试代码的覆盖也完全不够,下面看看例子代码。

例子代码

例子代码在这里: examples/chainstore.js

运行

npm run example:chainStore

可以发现一直打印ChainStore的全局动态对象 2.1.0的当前值。

例子代码比测试代码简单,用到的函数是getObject,运行例子代码会对上文提到的无效监听设计有直观的认识。

例子代码的修改

例子代码太简单了,只获得一个动态全局对象,不利于理解很多其他的概念。我通过阅读ChainSore.js的源代码,改了下,可以获得巨蟹的账号情况,注意其中的资产和操作历史:

import {Apis} from "bitsharesjs-ws";
import {ChainStore} from "../lib";

Apis.instance("wss://bitshares.openledger.info/ws", true).init_promise.then((res) => {
    console.log("connected to:", res[0].network);
    ChainStore.init().then(() => {
        ChainStore.subscribe(getBitcrabAccount);
    });
});

function getBitcrabAccount() {
    var bitcrab = ChainStore.getAccount('bitcrab');

    if( bitcrab) {
        var bitcrabObj = bitcrab.toJS();
        console.log('my account', bitcrabObj);

        var balances = bitcrabObj.balances;

        if( balances ){
            for (var assetId in balances ){
                var asset = ChainStore.getAsset(assetId);

                if( asset ){
                    var assetObj = asset.toJS();
                    console.log('asset:', assetId, assetObj);

                    if( assetObj.dynamic_asset_data_id) {
                        var dynamicAsset = ChainStore.getObject(assetObj.dynamic_asset_data_id);

                        if( dynamicAsset ){
                            var dynamicAssetObj = dynamicAsset.toJS();
                            console.log('asset dynamic:', assetId, dynamicAssetObj);
                        }
                    }
                }

                // var balance =;

                var balance = ChainStore.getObject( balances[assetId]);
                console.log('asset balance:', assetId, balance.toJS());

            }
        }

        var history = bitcrabObj.history;

        if( history ){
            history.forEach( function(h){
                console.log('history', h);
                console.log('opration', h.op);
            });

        }
    }

}

运行修改的例子代码会不停的输出巨蟹的账号相关信息。关于操作历史,最重要的是什么操作?op的数据结构是二元组,第一个数表示操作类型,第二个对象表示具体的数据。而操作类型可以在 lib/chain/src/ChainTypes.js 里面找到,代码我就不贴了。

总结

关于ChainStore的代码解读就这些了,这个过程我总结下来:

  • ChainStore的接口设计不算特别合理。怎么样才更好呢?是一个值得思考的问题。
  • 业务逻辑和代码需要结合起来,比如a.b.c对象的意义,操作类型的意义。
  • ChainStore测试和例子的质量不高,大体可判断,bitsharesjs总体的代码质量有待改进,如果对质量要求高,可以考虑直接使用钱包和节点的 JSON RPC API。
]]>
Thu, 25 May 2017 00:00:00 +0800
https://blog.xiaofuxing.name/2017/05/17/bitshares_ui_wallet_and_account_management.html https://blog.xiaofuxing.name/2017/05/17/bitshares_ui_wallet_and_account_management.html <![CDATA[源码解析:bitshares-ui的钱包和帐号管理]]> 源码解析:bitshares-ui的钱包和帐号管理

本文试图从一个宏观架构的层面解析bitshares-ui这个应用中的钱包和账号管理,为作者接下来实现一个通用的私钥管理器做准备。

用到的库和标准

alt

altFlux架构 的轻量级和紧凑的实现,支持ES6语法。

Flux架构是Facebook开源的一种用户界面程序架构,特点是单向数据流,核心组件包括 Actions 、 Stores 、Views 和 Dispatcher 。其中

  • Actions表示动作,可能带参数,一般由Views根据用户操作,通过dispatcher广播出来
  • Stores存储应用的数据,监听并响应dispatcher广播的、与自身相关的Actions,修改自身的数据,当修改时,广播一个 change事件
  • Views代表用户界面,从Stores拿数据,展示给用户,并在stores的change事件发生时,重新获取数据刷新界面

Flux架构的好处:

  • 相对于MVC来说,去掉了Controller,强化了数据层;
  • Stores作为各个View的统一数据来源,为各个View提供了同步的数据;
  • 对数据的修改不是直接的,分离了用户操作意图和实际的数据修改,更容易调试。

下图是一个Facebook提供的直观的Flux架构图。

../../../_images/flux-facebook.png

alt库提供了Flux架构所需要的Actions、Stores和dispatcher API。

indexedDB

indexedDB 是一个W3C建议标准,用于在浏览器中存储结构化的对象数据库,是过时标准 WebSql 的替代。

indexedDB的存储分为以下几个层次:

  • 域,浏览器为不同的域(不同的应用)存储不同的数据库集合,避免跨域数据盗用
  • 数据库,同一个域下面可以有不同名称的数据库,每个数据库有相对独立的应用目的
  • 对象商店(Object Stores),每一个数据库可以包含多个对象商店,对象商店可类比Sql数据库中的表
  • 对象,对象商店中的一个实体,可类比Sql数据库中的行

对象商店可以有不同的形式,键值对形式和对象集合形式。

indexedDBShim

由于 IndxedDB标准比较新,各个浏览器实现有差异,有些还有BUG,因此为了更好的兼容性,indexedDBShim 项目被开发出来,为各种javascript环境(不同的浏览器甚至Nodejs)提供一致的indexedDB API。 下文引用github官网的项目说明

Use a single, indexable, offline storage API across all desktop and mobile browsers and Node.js.

Even if a browser natively supports IndexedDB, you may still want to use this shim. Some native
IndexedDB implementations are very buggy. Others are missing certain features. There are also
many minor inconsistencies between different browser implementations of IndexedDB, such as how
errors are handled, how transaction timing works, how records are sorted, how cursors behave,
etc. Using this shim will ensure consistent behavior across all browsers.

indexedDBShim在使用的时候,可以强制在支持indexedDB的浏览器也用shim(websql模拟),好处是啥?也许这样更稳定更不容易出错,因为websql的实现各个浏览器都是成熟的和一致的(sqlite)。

tcomb

javascript是动态强类型语言,由于缺乏静态类型检查,写代码容易出BUG。 tcomb 是javascript的运行时类型定义和检查库,作用类似Typescript,主要区别在于,tcomb是运行时检查,Typescript是编译时检查。与tcomb类似的库有 joi ,不过joi不直接支持浏览器环境,而tcomb同时支持浏览器和nodejs。tcomb定义的数据结构有助于我们理解程序逻辑。

相关文件

本文提到的文件,都以 bitshares-ui 项目的根目录为相对目录的起点。 下文给出简表。

文件名 说明
web/app/alt-instance.js alt全局Singleton
web/app/idb-instance.js indexeddb实例封装
web/app/stores/BaseStore.js 基于alt库store的其他store的基类,一种混合编程范式
web/app/stores/WalletDb.js 钱包Store
web/app/stores/PrivateKeyStore.js 私钥Store
web/app/stores/AccountStore.js 账号Store
web/app/stores/tcomb_structs.js 各种数据结构定义

存储的层次

存储的层次从最底层(离用于使用最远),到最上层,可分为钱包备份层,Web浏览器中的数据库层和内存层。

钱包备份层

钱包备份层是备份在硬盘上的钱包文件,可以跨浏览器,跨终端导入导出使用。钱包备份文件需要使用用户的主密钥解密才能导入,解密方法,请看 使用NODEJS解密bitshares网页钱包备份文件

Web浏览器中的数据库存储

如果在某个网页钱包(例如 比特帝国Openledger )上注册或者恢复了钱包,那么钱包中的数据会存储在浏览器的数据库中,操作的接口是 indexedDB,而由于bitshares-ui的实现强制使用了 indexedDBshim的shim模拟,实际上这些数据存储在Websql里面。 当钱包应用打开时,会读取一些钱包中的账号数据,与区块链API比对,可拿到用户的名称和余额等等信息。而当增加账号或者修改账号的公钥时,会通过上文所述Flux架构及其alt实现,最终修改数据库中的信息。私钥这种敏感信息在数据库中永远是加密状态。

Web浏览器的运行内存

网页钱包的运行数据会在内存中体现,表现形式是Flux架构的各种Store。

钱包相关的数据和加密方法

与钱包相关的数据,从web浏览器的数据库层来解析比较好理解。在这一层,包括三个对象商店,分别是 wallet, privatekey和 link_accounts。

wallet

wallet商店一般包含一个对象,表示用户的钱包的基本信息

wallet的结构,可参考文件 web/app/stores/tcom_structs.js 第27到41行

27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
 let WalletTcomb = t.struct({
   public_name: t.Str,
   created: t.Dat,
   last_modified: t.Dat,
   backup_date: t.maybe(t.Dat),
   password_pubkey: t.Str,
   encryption_key: t.Str,
   encrypted_brainkey: t.maybe(t.Str),
   brainkey_pubkey: t.Str,
   brainkey_sequence: t.Num,
   brainkey_backup_date: t.maybe(t.Dat),
   deposit_keys: t.maybe(t.Obj),
   // password_checksum: t.Str,
   chain_id: t.Str
}, "WalletTcomb");

各字段意义如下表:

字段名称 意义
public_name 钱包名字,一般为default,用户可管理多个钱包
created 钱包创建时间
last_modified 最后修改时间
backup_date 备份时间
password_pubkey 主密钥生成的ECC公私钥对中的公钥
encryption_key 由主密钥加密的,用于加密私钥的密钥(AES密钥)
encrypted_brainkey 加密的脑钱包种子,用于生成ECC公私钥对(HD)
brainkey_pubkey 脑钱包种子生成的ECC公钥(与HD无关)
brainkey_sequence brainkey序号,下一个密钥对从这里算
brainkey_backup_date brainkey备份时间
deposit_keys 不清楚
chain_id 石墨烯区块链ID,可区分主链测试链

注:HD表示Hierarchical Deterministic, 从一个种子开始,可序列化的、确定性的生成多个私钥,可参考 这篇文章 。 一般脑钱包(brain wallet)可以用例如12个随机英文单词作为种子,因此brainkey就指种子本身。

privatekey

privatekey表示用户的各种私钥:

  • owner key, 账号拥有者私钥,可以通过这个私钥修改其他密钥设置
  • active key, 活动私钥,可以通过这个私钥签署交易广播
  • memo key, 备注私钥,可以解密交易对手发过来的备注

privatekey结构,参考同一个源文件的第43-50行:

43
44
45
46
47
48
49
50
 let PrivateKeyTcomb = t.struct({
   id: t.maybe(t.Num),
   pubkey: t.Str,
   label: t.maybe(t.Str),
   import_account_names: t.maybe(t.Arr),
   brainkey_sequence: t.maybe(t.Num),
   encrypted_key: t.Str
 }, "PrivateKeyTcomb");

最有用就是2个字段:

  • pubkey,公钥,
  • encrypted_key, 加密的私钥

encrypted_key的加密密码在哪里?答案是加密存储在wallet对象的 encryption_key字段里(见上表),而后者的加密密码是用户的主密钥。

另外:

  • brainkey_sequence 表示这个私钥的生成序列号,wallet中 brainkey_sequence为所有privatekey中最大brainkey_sequence + 1。

linked_accounts

linked_accounts表示用户的账号信息,主要包含两个字段,账号名称和区块链id。

钱包锁定和解锁

在privatekey对象中,存在一个encrypted_key,需要解密才能使用。当用户解锁钱包时,wallet对象中的encryption_key被解密,并保留在内存一段时间。 通过解密后的密码,可以解密出私钥,进而进行签名操作。当钱包锁定时,wallet对象中没有明文的密码,无法解密出私钥进行计算。

小结

本文讨论了bitshares-ui源代码中关于钱包和账号的管理方法,希望对本文读者有所帮助。

]]>
Wed, 17 May 2017 00:00:00 +0800
https://blog.xiaofuxing.name/2017/05/10/more_on_ecc.html https://blog.xiaofuxing.name/2017/05/10/more_on_ecc.html <![CDATA[椭圆曲线密码学相关概念与开源实现]]> 椭圆曲线密码学相关概念与开源实现

引子

在阅读并尝试使用椭圆曲线加密相关代码时,很多时候需要对底层的概念有所了解,而不仅仅限于使用。例如前文 NODEJS中椭圆曲线签名和验证 就仅仅限于加密货币领域和NODEJS。本文尝试对椭圆曲线密码学的相关概念做一个梳理,这样能够帮助读者以及我自己更好的去挑选和使用相关的开源代码。

概念与缩写

简表如下:

缩写 英文全名 中文翻译
EC Elliptic Curve 椭圆曲线
ECC Elliptic Curve Cryptogphay 椭圆曲线密码学
ECDSA Elliptic Curve Digital Signature Algorithm 椭圆曲线数字签名算法
DH Diffie-Hellman Key Exchange Diffie-Hellman密钥交换
ECDH Elliptic Curve Diffie-Hellman Key Exchange 椭圆曲线Diffie-Hellman密钥交换
IES Integrated Encryption Schema 集成加密框架
ECIES Elliptic Curve Integrated Encryption Schema 椭圆曲线集成加密框架
KDF Key Derivation Function 密钥(私钥)生成函数

说明与参考:

EC就是椭圆曲线,是一个数学上的概念,注意并不是椭圆。ECC是基于椭圆曲线的公私钥密码体系,ECDSA就是这个密码体系下的签名(与验证)算法。DH是两个人名字首字母缩写,因为他们首次发明了在敌意网络环境下安全的利用公私钥加密算法协商出对称加密密钥的方法, 可参考 这个维基页面 。ECDH就是利用椭圆曲线公私钥密码体系来交换对称加密密钥的方法。IES用于文件或者磁盘加密,是一种混合公私钥密码和对称加密方法的块加密系统, 可参考 另一个维基页面 ,其原理与DH密码交换相同。KDF是指一个生成密钥(私钥)的函数。

关于ECC和ECDH,可以看这两个Youtube视频:

Elliptic Curve Cryptography Overview 以及 Elliptic Curve Cryptography & Diffie-Hellman

不愿意或者没有能力看视频的朋友,我给出两张截图。

../../../_images/ecc1.png

这张图很形象的描述了ECC的数学模型和加密安全性,以及什么是私钥,以及密钥长度。

../../../_images/ecc2.png

这张图说明在ECDH密码交换时,我们需要给定哪些域参数。

参数与标准

椭圆曲线本身是数学模型,而曲线的参数在使用的时候需要公开并且一致,否则多方无法参与(张三用A曲线而李四用B曲线是没法对话的)。选择什么样的参数才能安全又高效呢?

SECG 组织(Standards for Efficient Cryptography Group)给出了相关标准,包括

  • SEC-1 椭圆曲线密码学
  • SEC-2 椭圆曲线密码学推荐的域参数
  • SEC-4 暂时忽略,我也不知道是啥

SEC-2里面给出了各种参数,并且每一组参数给出了一个命名,这样在使用的时候,通过名称就确定了参数。例如secp256k1就是比特币率先采用的一组参数,在加密货币领域十分流行。

开源实现

openssl

openssl 是著名的C库和命令行工具箱,实现了各种密码学的函数,包括椭圆曲线。 参考 openssl中ECC命令行操作 可以了解到Openssl命令行 的ECC密钥管理和ECDH以及ECDSA算法的使用。本文列出一些,读者可以照做,加深认识。需要注意的是,Mac OS上自带openssl太旧,需要使用 Homebrew 安装的版本。

openssl genpkey -genparam -algorithm ec -pkeyopt ec_paramgen_curve:secp256k1

生成了PEM格式的椭圆曲线域参数文件到标准输出。

openssl genpkey -genparam -algorithm ec -pkeyopt ec_paramgen_curve:secp256k1 -out secp256k1.param

生成了PEM格式的椭圆曲线域参数文件到 secp256k1.param 文件里面。

openssl genpkey -paramfile secp256k1.param -out my.key

利用参数文件生成PEM格式(明文)的私钥文件。

openssl genpkey -aes256 -paramfile secp256k1.param -out my.key

利用参数文件生成AES加密的私钥文件,即使文件泄露,黑客没有密码也无法使用私钥。

openssl pkey -in my.key -text -noout

查看密钥。

openssl dgst -sign my.key -sha512 file-to-be-signed -out signature-file

生成签名文件。

openssl ec -in my.key -pubout -out pub.pem

私钥转公钥文件。

openssl dgst -verify pub.pem  -sha512 -signature signature-file  file-to-be-signed

公钥验证签名。

eccrypto

eccrypto 是一个Javascript上的ECC库,特点如下:

  • 同时支持浏览器和Nodejs, API相同。
  • 支持哪种椭圆曲线? secp256k1。
  • 如果浏览器支持,使用W3C正在制定WebCrypto标准。
  • 如果可能,使用Nodejs内置的Crypto模块。

CryptoCoinJS

CryptoCoinJS 是一个项目组,创建了多个用于加密货币的Javascript项目,其中的 Ecurve 项目是一个通用的ECC库,支持各种参数的椭圆曲线。

]]>
Wed, 10 May 2017 00:00:00 +0800
https://blog.xiaofuxing.name/2017/05/07/ethereum_introduction.html https://blog.xiaofuxing.name/2017/05/07/ethereum_introduction.html <![CDATA[以太坊简介]]> 以太坊简介

在加密货币和区块链领域,以太坊(Ethereum)是目前除比特币之外最有影响力的公有链(基本上可以说没有之一)。以太坊之所以著名,笔者认为与两个主要因素有关:

  • 首次实现图灵完备的智能合约平台
  • 以太坊创始人Vitalik Buterin的技术能力和社区运营能力都是一流的

什么是智能合约?简而言之,就是机器自动执行的法律合同。一般意义法律合同的执行,是由合同参与方手动来操作的,当遇到对合同文本理解不一致或者恶意抵赖的情况,当事人可 通过法院来仲裁甚至强制执行。而智能合约,就是用计算机代码编写的合同,如果代码没有缺陷,当外部条件成立(例如时间到了)时,其执行是自动的,不存在恶意抵赖的情况。因此, 智能合约和传统合同的主要区别有两点:

  • 传统合同采用中文或英语等人类语言,存在二义性;智能合约采用计算机编程语言,没有二义性。
  • 传统合同的执行靠参与方自觉以及法院仲裁,是人来做;智能合约由计算机自动执行,人只需要在适当触发执行条件即可。

关于以太坊的编程学习,中文方面有一个 以太坊爱好者网站 。如果英文不错,可以到 Udemy 去刷视频教程, 其中 EthereumDeveloper 课程还挺好的,内容丰富,如果碰到打折,10美元就可以购买。

最后简单介绍一下以太坊钱包、帐号和比特股钱包、帐号的不同。比特股中,钱包和帐号与现实世界的钱包和帐号很类似,钱包相当于现实世界的钱包,帐号相当于现实世界的银行卡帐号。一个钱包里面可以包含 多个银行卡:对应比特股来说,一个钱包里面可以有多个帐号。以太坊的钱包有两种意思,一种与比特股和现实世界类似,指管理帐号的存储介质和程序;另一种,却表示一种帐号类型,以太坊有两种帐号 类型,一种叫外部帐号,一种叫合约帐号。外部帐号是人控制的帐号,可以转帐和触发合约帐号执行合约;合约帐号就智能合约的载体帐号,包含程序字节码和存储状态,可在特定条件下执行合约。合约帐号 也叫钱包帐号。因此,以太坊中的钱包就有两种不同的意思,需要根据上下文来区分。

关于以太坊的介绍到此结束,感谢看到这里的朋友!

]]>
Sun, 07 May 2017 00:00:00 +0800
https://blog.xiaofuxing.name/2017/05/03/decrypt_bitshares_web_wallet_with_nodejs.html https://blog.xiaofuxing.name/2017/05/03/decrypt_bitshares_web_wallet_with_nodejs.html <![CDATA[使用NODEJS解密bitshares网页钱包备份文件]]> 使用NODEJS解密bitshares网页钱包备份文件

有一位我的博客读者,问了我一个问题,比特股钱包的密码忘了,想写一段程序暴力破解,可是没有找到相关的代码和调用方法。我带着这个问题,去代码里面找了找,写了一段 简单的程序 ,本文阐述一下这段程序的逻辑。

其实主要的逻辑在19~61行,也就是函数decryptWalletBackup,是从bitshares-ui拷贝出来的(请看18行注释)。这个函数接受两个参数,backup_wif和backup_buffer。前者是wif格式的私钥(参考 这篇博文 ), 后者是钱包备份文件(.bin)的内容。backup_wif怎么来的呢?参考第71行,是密码的确定性函数。

decryptWalletBackup函数的调用在74行,如果密码正确,75行console.log会被调用,否则,76行console.error会被调用。如果用nodejs来做暴力破解,不断的修改密码看看decryptWalletBackup这个函数是否能resolve promise即可。

其实,代码解析到这里,也许读者朋友会觉得很无趣,因为这个解析显得有些简单了。那么,难在什么地方呢?一般来说,知识不难,获取知识并应用的过程比较难。授人以鱼,不如授人以渔 : 具体来说本文尝试回答两个主要问题,并在阐述第二个主要问题时提出两个小问题和我的思考。

第一个问题,如何从开源代码中快速准确找到需要的功能?这其实是一个代码阅读的方法问题。我的回答: 要从代码中重建作者的逻辑框架,并进行层次合理的抽象 。就一个特定问题来说,当一个人不知道从哪里去找相关代码的时候,需要整体理解代码的框架,然后针对性的阅读感兴趣的部分,并且按照自己的理解去做局部测试。就好比玩乐高积木, 别人搭好的建筑,我们需要局部借鉴并且建一个新的,做法就是恰好把需要的局部拆下来,再重新搭。比如本文讨论的问题,要本地暴力破解,想知道代码在哪,那么首先得大体理解 bitshares-ui 代码整体的结构:

  • 知道这是一个基于React(package.json里面的依赖,以及源代码里面有很多jsx文件)和Webpack(package.json里面的各种快捷命令)的项目
  • 下层依赖什么库?从 package.json 里面看
  • bitshares-ui代码结构?看子目录,需要大体理解 React的组件编程方法论
  • 从顶层组件开始,大体理解代码和使用网页钱包的关系

从这个角度出发,根据目标分解,逐步的去找,就能找到想要的任何组件以及其调用的任何库的方法。

第二个问题,如何控制抽象级别并且避免陷入细节?这个问题比第一个问题更具体一些,需要时常锻炼。回答第一个问题时说到的“合理的抽象”,也是一个意思。与盲人摸象一样,其实如果每个盲人关注大象的每一个局部,只要目标允许,就是合理的,关键需要理解自己所关注的局部和整个大象的关系,不把局部代替为整体就好。 研究代码,并不需要一下子把所有的部分都搞明白才能修改或者使用局部代码,从代码整体的结构往下看,一级一级找下去,找到能满足目标的可能解决方案,就放手去验证,并通过实际操作反馈,调整自己的逻辑假设,重新验证,这样反复迭代几次,可迅速完成目标。经常训练的话,迭代次数可为个位数。其实,我本人并没怎么学过和使用过 React,但并不妨碍 我从总体上理解React的组件编程模型,也不妨碍我从大型React项目(bitshares-ui)中找到感兴趣的部分(网页钱包备份解密方法)并加以利用。关注目标,通过抽象来隐藏细节,理清组件之间的接口关系,是关键。我这里列举2个小问题并给出我的思考,也许读者就更好理解了。

小问题1:如何使用其他编程语言(C++/Python/Go)来解密网页钱包?由于这个问题的上下文与本文讨论的主要问题不一样,因此需要的知识也不一样了。要自己实现的话,需要从bitsharesjs库内部,理解网页钱包的格式、压缩和加密方法,并从其他编程语言将这些逻辑重组。问题是,如果没有这样一个目标,就无需理解这么多, 直接理解前文的接口调用即可。

小问题2:网页钱包备份的格式怎么样的?这个问题可从问题1派生。我的答案是,我不甚了解,但如果有需要的话,可以分N层去解释。可以肯定的是,网页钱包的前33个字节为一个临时公钥的二进制形式,后面是AES加密存储的内容。在这个AES加密内容中,必定有一部分与前面的公钥相关,使得解密过程可以直接判断密码是否正确。这个结论是我从 bitshares-ui中的那个文件中的代码推导出来的,但是更细节的结构,例如解密过程怎么验证密码的正确性,就得研究bitsharesjs库中 AES.decrypt_with_checksum的实现了。

好,本文到此结束,感谢看到这里的朋友。如果你有任何问题,欢迎联系我,也许我能帮你解决一个问题,并同时分享些心得给更多的朋友。

]]>
Wed, 03 May 2017 00:00:00 +0800
https://blog.xiaofuxing.name/2017/04/26/login_with_any_cryptocurrency_prototype_released.html https://blog.xiaofuxing.name/2017/04/26/login_with_any_cryptocurrency_prototype_released.html <![CDATA[身份认证概念原型发布,目前的反馈和我的思考]]> 身份认证概念原型发布,目前的反馈和我的思考

上文 讨论了基于任意加密货币的用户身份认证设计思路,目前我已经弄出了一个概念原型, 部署在 heroku 上,有Steem账号或者比特股账号的可以去尝试下。代码是开源的,有兴趣的读者可以查看 multi-currency-loginWifSign 。 我在各个渠道发布了这一消息,已经收到一些朋友们的关注和反馈,本文做个总结并继续讨论身份验证这个话题。感谢 abit(@steem), Alex(@yoyow)以及Patrick(@qtum)给我提供的非常有价值的反馈,感谢所有支持我这个想法的朋友!

反馈1: 在第二步签名的时候,用户不相信即将去往的 签名的网页 ,在这个上面填写私钥或者密码,万一被恶意拿走怎么办?

我的思考,这当然是一个最大的问题,同时也蕴藏着很大的机会。从技术角度说,我目前能提供的信任是:

  • 代码是 开源 的,用户可以自己部署一个类似的网页

我不能提供的信任是:

  • 开源的代码并没有经过严肃的安全审计,也许存在恶意代码或者漏洞
  • 即使提供了严肃的安全审计,也没法保证部署在githubpages上的网页是安全可靠的。

另外还有非技术层面的信任问题,本文不讨论。

反馈2: 这个方法不能有效的避免中间人攻击问题。

我的思考,不妨假设用户Alice访问一个论坛采用了现在这个原型的认证方法,中间人攻击分为如下两个方面:

客户身份冒用, 即有一个Bob夹在Alice和论坛服务器中间,当Alice的身份认证通过后,冒用Alice的身份。我认为这是最重要的安全隐患,也是一个可以考虑的技术改进之处。从网络层次上来说,我这个身份认证系统处于 应用层面,而成熟的https(SSL)处于传输层,SSL技术本身是可以解决中间人攻击的。但是,大多数用户使用的https(SSL)是用来防止服务器身份冒用的,只有部署了客户端证书的https才能避免客户端身份冒用,如果这个方法 能够与https客户端证书相结合,下沉到传输层,那么能够有效的解决中间人攻击问题,也更有意义;但是如果不能做到这一点,应用层的身份认证仍然有其自身的价值。

服务器身份冒用,即有一个Bob夹在Alice和论坛服务器中间,对Alice冒充服务器。这个问题也分为传输层和应用层,传输层https已经解决了这个问题(除了CA本身是一个问题)。应用层的话可以用类似的方法来验证服务器, 上文 已经描述过,只不过目前的原型里没有,我倾向于认为这个功能不重要。

总之,中间人攻击问题重要,但我认为,解决这个问题犹如锦上添花,不是雪中送炭。

反馈3:易用性问题。可以弄浏览器插件啥的。

我的思考,完全同意。易用性是普通用户愿不愿意去使用这个方法的关键,也是论坛愿不愿意部署这种方法的关键。这个问题和第1个信任问题在一起,构成了我这个思路能否继续顺利实施的关键。我基于现有的技术,对这两个问题有了一个 新的思考:开发独立的,经过安全审计的第三方授权应用(类似目前概念原型中的WifSign)。这个应用完成如下功能:

  • 内部加密存储各种私钥。
  • 用户通过好记的名字管理这些私钥。
  • 响应用户的签名请求,对挑战数据进行签名并返回签名结果。流程类似OAuth,但没有第三方服务器。

因为这个经过安全审计的第三方授权应用只有一份,并且运行在用户自己的计算机上,因此一旦做出来并且成功推广,可以让不止一个网站或者论坛使用,具有以下价值:

  • 用户避免到处注册;
  • 用户无需借助大公司的平台(QQ、微信、微博、Google、Facebook)来认证自己;
  • 最大程度避免私钥泄露。

好,本文到此结束。谢谢看到这里的朋友,如果你有任何意见和建议,欢迎留言。如果无法留言,欢迎发邮件给我。当然,欢迎转发本文链接!

]]>
Wed, 26 Apr 2017 00:00:00 +0800
https://blog.xiaofuxing.name/2017/04/18/ecc_in_nodejs_introduction.html https://blog.xiaofuxing.name/2017/04/18/ecc_in_nodejs_introduction.html <![CDATA[NODEJS中椭圆曲线签名和验证]]> NODEJS中椭圆曲线签名和验证

上文 介绍了基于任意加密货币的用户身份认证设计思路,本文承接这个思路,介绍在Nodejs中如何做基于椭圆曲线的签名和验证方法,为后续生成完整的身份认证库和Demo做准备。

我调查了两个可用的库,一个是 steem-js , 另一个是 bitsharesjs ,目前倾向于使用后者,原因本文逐步展开。为叙述方便,首先把签名和验证的概念源代码贴上

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
const steem = require('steem');
const bitshares = require('bitsharesjs');
const bitsharesjsws = require('bitsharesjs-ws');
const env = process.env;
const role = 'active';
// Steem 有4种角色: owner, posting, active, memo
// Bitshares 有3种角色: owner, active, memo


bitsharesjsws.ChainConfig.setChainId(
  '4018d7844c78f6a6c41c6a552b898022310fc5dec06da467ee7905a8dad512c8');
// bitshares主链, 自动设定前缀为BTS(默认为GPH)

// bitsharesjsws.ChainConfig.setPrefix('STM');
// 假如取消上面这行注释,还可以手工设定前缀,这样的话可以兼容Steem区块链。

const userName = env['username'] || 'test';
const passphrase = env['password' ] || 'the very long and stupid password';


var wifSteem = steem.auth.toWif(userName, passphrase, role);
var btsKeys = bitshares.Login.generateKeys(userName, passphrase, [role]);
// Steem的API提供wif格式的私钥,而Bitshares的库提供可以计算(签名和验证)的私钥和公钥
// Wif格式来自BitCoinWiki

console.log('private key( Wallet import format ):');
console.log('steem:', wifSteem);
console.log('bitshares:', btsKeys.privKeys[role].toWif());
// 从上面可以看出 bitshares和Steem采用相同的算法生成私钥(私钥相同)

console.log('public key:');
console.log('steem', steem.auth.wifToPublic(wifSteem));
console.log('bitshares:', btsKeys.pubKeys[role]);
// 公钥也相同,但是前缀与哪个链有关

// Signature function is not exported from steem.
const bsSignature = bitshares.Signature;

const testBuffer = new Buffer('a random buffer for sign/verify test', 'utf-8');
const testBuffer2 = new Buffer('another random buffer', 'utf-8');
var sign = bsSignature.signBuffer(testBuffer, btsKeys.privKeys[role]);


// console.log(bsSignature.verifyBuffer(signBuffer, btsKeys.pubKeys[role]));
console.log(sign.verifyBuffer(testBuffer, btsKeys.privKeys[role].public_key));
console.log(sign.verifyBuffer(testBuffer2, btsKeys.privKeys[role].public_key));
console.log(sign.verifyBuffer(testBuffer, bitshares.PublicKey
    .fromStringOrThrow(btsKeys.pubKeys[role])));
var sign2 = bsSignature.fromBuffer(sign.toBuffer());
console.log(sign2.verifyBuffer(testBuffer, btsKeys.privKeys[role].public_key));
console.log(sign2.verifyBuffer(testBuffer2, btsKeys.privKeys[role].public_key));
console.log(sign.verifyBuffer(testBuffer, bitshares.PublicKey
    .fromStringOrThrow(btsKeys.pubKeys[role])));

然后逐渐讲解。 整个代码分为两个部分,1-34行是公私钥生成;36-53行是签名和验证。

首先看公私钥生成。 前两行引用了2个库,第1行是steem, 第2行是bitsharesjs,无需多说。第3行引用了一个bitsharesjs依赖的底层库,与第10-15行有关,下文详解。第4行引用env,与第17、18行有关:可以使用环境变量设置自己的用户名和密码。第5行设定一个常数角色,第6、7行有注释。 关于角色,这里还要解释下,在bitshares和steem体系下,区块链上的广播都不涉及密码,不同的行为需要不同角色的公私钥对:私钥签名,公钥验证;或者公钥加密,私钥解密。例如active角色与转账行为有关,涉及转账的需要这对公私钥;而owner角色与账户设定有关,修改账户设定需要这对公私钥;而memo角色与备注相关, 可以在转账给他人的时候利用对方的memo公钥加密,而对方需要利用memo私钥解密。由于Steem面向内容市场,因此多了一个posting角色,用于发表文章、支持反对等等。Steem的上层(steemit网站)设定,通过用户名、密码、角色的组合,使用确定性算法生成公私钥对;而bitshares刚刚支持这种方法。

先跳过第10-15行。

第17-18行声明了两个变量,用户名和密码。第21-22行分别使用Steemjs和Bitsharesjs库生成了两对私钥,注意23-24行的注释很重要,“Steem的API提供wif格式的私钥,而Bitshares的库提供可以计算(签名和验证)的密钥,包含私钥和公钥”:这也就是本文一开始提到的我倾向于bitshares库的一个原因。“Wif格式来自BitCoinWiki”: Wif,全称Wallet Import Format,钱包导入格式,来自 这里 。Wif的发明,完全是解决人机接口的问题:对于底层算法,私钥是一个大整数,而这个大整数不方便人们导入导出,因此用这种文本格式来记录、拷贝、导入导出;在bitshares客户端看到的私钥,以及 steemit账号设定里面看到的私钥,都是这种格式。

第26-29行分别以Wif格式输出了两个库生成的私钥,如果读者运行程序,就会发现二者相同。

第31-34行输出公钥,这里面公钥的格式是石墨烯区块链自定义的,与Wif类似,但是前缀与哪个链相关。读者运行程序可以看到这两行的输出大部分是一样的,但前缀不同。Steem公钥前缀是STM,Bitshares默认公钥前缀是GPH。程序一行不改,33行运行结果,前缀是BTS;如果将10行注释掉,可以看到33行运行结果,前缀是GPH;如果将14行注释去掉,33行运行结果,前缀是STM。 这里回过头来说明一下第10行和14行所涉及的两个API。第10行涉及一个API: bitsharesjsws.ChainConfig.setChainId,设定哪个公有链,一共包含4个,具体可查看 这里的代码 ,(不过这个列表并不包含Steem链)。设定公有链直接就会有一个副作用,同时设定了 公钥的前缀,当然也可直接设定前缀,利用的就是bitsharesjsws.ChainConfig.setPrefix 这个API,正如第14行那样。

公私钥生成的代码解析到此结束,下面阐述一下签名和验证。第36-37行,定义了一个新的常数,bsSignature,这是bitsharesjs库提供的签名接口,而Steem库并没有对外暴露类似的接口。39-40行定义了两个不相同的缓冲区,而41行利用上面生成的私钥,对第一个缓冲区做了一个签名。45-47行分别是这个签名对两个缓冲区的验证方法,运行时可以看出分别输出 true 、 false、true。其中, 45行和47行都是对签名的缓冲区进行验证,只是公钥的来源稍稍有些区别;而46行对不同的缓冲区进行验证,因此输出false。

第49行重新生成了一个签名,目的是模拟签名的序列化和反序列化过程,可以使用这种方法,序列化后将签名在网络上传输,对端反序列化得到一个签名对象,并且50-52行重新利用新得到的签名对象验证两个缓冲区,结果与45-47行完全相同。

]]>
Tue, 18 Apr 2017 00:00:00 +0800
https://blog.xiaofuxing.name/2017/04/13/user_authentication_with_any_cryto_currency.html https://blog.xiaofuxing.name/2017/04/13/user_authentication_with_any_cryto_currency.html <![CDATA[基于任意加密货币的用户身份认证设计思路]]> 基于任意加密货币的用户身份认证设计思路

上文 讨论了一种链内+链外结合的,基于比特股的身份认证思路,在文末,我提到了一个思路,无须链内转帐, 本文讨论这个方案。

加密货币的身份标示都有一个特点:公私钥体系。用户自己保存私钥,但在链上广播了公钥或者由公钥生成的地址。通过链上转帐当然能确认帐号归属,但实际上,只要能向对方证明自己拥有某个对应公钥的私钥即可。 而这个证明其实非常简单:给定随机信息源,私钥拥有者能够正确签名,发给对端,对端通过公钥来验正签名是否合法。

这里面有个关键在于随机信息源,如果这个信息源不够随机,那么攻击者很可能通过重放攻击来声称自己是另一个人。为了确保随机,协议上需要加上一个限制:由对端(最后要验证身份的人)首先给定这个信息源,然后由私钥 拥有者签名发送给对端。

http(s)的服务器和客户端都可以通过这种方法验证对方。服务器验证客户端分为两个http请求:

  1. 获取随机信息源:客户端发送获取随机信息请求,包含自己的公开身份信息,服务器收到请求后,首先从区块链上检查客户端发来的公开身份信息是否合法,如果合法则生成一个随机信息,回复给客户端,并将生成的随机信息和客户端的公开身份保存一小段时间。
  2. 验证客户端身份:客户端发送服务器给的随机信息,以及签名,服务器首先测试随机信息是否是自己在一小段时间内生成的,如果OK,则通过随机信息获取客户端的公开身份信息,然后验证签名,都通过后身份认证成功,否则身份认证失败。无论成功失败,删除随机信息源。

客户端验证服务器只有一个http请求:

  1. 发送验证随机信息源:客户端发送服务器验证请求,包含客户端生成的随机信息,服务器根据客户端发来的随机信息生成签名,回复给客户端自己的公开身份信息和签名,客户端从区块链上对比公开信息,一致后验证签名,都通过后身份认证成功,否则身份认证失败。无论成功失败,删除随机信息源。

虽然这个验证方案对所有的加密货币都适用,但实际应用时,基于石墨稀的区块链(比特股、steem)更有推广优势,因为其中的用户标示不是公钥本身,更有意义。就比特股和Steem来说,Steem的单一用户名密码的上层策略对用户更友好,需要验证时, 界面直接提示用户输入steem上的用户名密码,并告诉用户这个密码不会外泄,只有生成的加密私钥保存在浏览器中。比特股客户端历史上都是用钱包模式,刚刚增加的用户名密码模式和Steem是类似的,但大多数人都还没用上,主流交易所的客户端还没有更新。

我准备按照这个思路做一个身份认证库,并弄出一个或若干个Demo来,最后整合到现有的开源论坛系统(nodebb、discourse)中。最后一步整合我不是很熟悉,读本文的小伙伴愿意参与的话请注意我的博客和公众号更新,随时和我联系。我的邮箱: pluswave AT xiaofuxing DOT name (为防止爬虫发垃圾广告,请替换 AT 和 DOT,删掉空格)。我的初步想法, 这是一个志愿合作行为,我有一个盈利计划,暂不公开。

]]>
Thu, 13 Apr 2017 00:00:00 +0800
https://blog.xiaofuxing.name/2017/04/11/user_authentication_with_bitshares_2_0.html https://blog.xiaofuxing.name/2017/04/11/user_authentication_with_bitshares_2_0.html <![CDATA[基于bitshares的身份认证系统设计思路]]> 基于bitshares的身份认证系统设计思路

身份认证一直就是一个麻烦的问题。互联网时代,每个论坛注册一个用户名密码让人很烦恼,于是出现了OAuth,允许用户通过第三方网站(例如Google、Facebook、微博、QQ、微信)来认证自己,但仍然需要第三方公司的信任背书。 即将到来的区块链时代,能不能通过区块链相关的技术解决这个问题呢?当然方法有很多种,例如 blockstack 和其上的 onename ,就是尝试通过虚拟链来解决这个 问题的,而 崛起币 则通过区块链自身的键值对功能衍生出ssh登录和ssl身份验证。笔者认为,基于区块链的身份认证系统,在很长一段时间内,仍然是多种系统并存的,取决于市场竞争中哪个区块链会胜出。

我想设计一种基于现有区块链的互联网身份识别系统,初期包括比特股和Steem。网站和用户直接通过区块链自身作为公信第三方,互相知道对方的账号。在这个基础上可以做很多增值的应用,例如打赏和优惠券发放啥的。

静态部署图如下:

graph 身份认证静态部署 { browser[shape="box"] site[shape="box"] blockchain[shape="insulator", label=""] user -- browser browser -- site browser -- blockchain site -- blockchain }

以bitshares为例,核心步骤是:

  1. 用户通过链外(https)报告自己的账号到服务器。
  2. 服务器通过自己的比特股账号向用户的账号发起一个特殊转账,包含加密的备注。
  3. 用户接收转账,确认了服务器的账号。
  4. 用户通过私钥解开转账的备注消息,并从链外通过服务器的公钥加密发给服务器。
  5. 服务器从链外收到用户发来的备注,用私钥解密,与自己之前发送的备注比较,确认了用户身份是否伪造。
  6. 互相就就确认对方了,再加cookie啥的避免重复认证。
  7. 其他增值应用功能都,通过私钥区块链广播,发起交易。

不妨把这个过程故事化了。假设张三想要访问一个李四做的论坛,但首先李四想要知道张三就是张三,张三也要知道李四就是李四,还好双方都在bitshares上注册了账号,分别就是张三和李四,于是借助于bitshares这个区块链和本文讨论的身份验证系统,一步一步达到了互相认证的目的。 张三首先访问了一个论坛网站,由于浏览器没有任何记录,上面出现一个按钮,写着“通过bitshares登录”,张三觉得这个按钮不错,于是点了一下。点过之后出现一个界面,要求张三用恢复bitshares钱包的方法来在这个域名下恢复钱包,于是张三恢复了自己bitshares钱包,并且把账号告诉 了网站服务器,网站服务器不知道张三真的是否注册或者就是张三,于是告诉访问者,我是李四,通过安全手段(bitshares)你转一笔小钱,里面还有一个暗号,你注意收下,并且把暗号再告诉我;服务器同时按照这个说法给张三转账。张三在区块链上监听来着李四的转账,果然收到了,并且看到了暗号,于是他 又把暗号通过链外告诉服务器。通过这个交互,张三知道服务器控制了李四这个身份,服务器知道使用服务的人就是张三,张三在浏览器上重定向到论坛的页面,并且有了浏览器本地的钱包。在最后一个交互(张三发确认暗号)中,服务器记录张三,并且发送cookie给张三使用的浏览器,用来确认后续链外行为是张三本人做的。

以上讨论正常流程,下面说下哪些分支流程会导致认证失败。

  1. 张三不相信在另一域名下解开bitshares钱包。这个无法在系统内解决,也许硬件钱包是一个好办法。
  2. 张三欺骗服务器,说自己叫张四,可是由于张三没有张四的私钥,就无法解开服务器发送转账的备注,因此服务器就知道认证失败了。
  3. 服务器欺骗张三,说自己叫李三,可是服务器无法以李三的名义发送转账,因此张三的浏览器就知道服务器撒谎了。
  4. 任何步骤超时,认证也会失败。

最后,考虑这个问题:如何防止DDoS攻击?由于服务器发送转账有成本,而一开始声称自己是谁的客户端没有成本,因此可以用这个方法欺骗服务器做无效的转账。这个应该通过多方面来防范:

  1. 链外做流量限制
  2. 链上,发行仅用于认证目的的没有什么价值的用户自定义资产
  3. 同一个用户切换浏览器后,可以复用之前的转账过程,而仅仅做链外的密文交换就可以知道对方的身份了

写到这里,我的脑子里突然又浮现了另一种想法,那就是无须在链上转账就能通过链下的声明和公私钥信息交换就可以认证对方了,岂不是更妙?不过还没有完全成形,等想好了下回再发一篇文章。

]]>
Tue, 11 Apr 2017 00:00:00 +0800
https://blog.xiaofuxing.name/2017/04/07/bitshares_cli_wallet_introduction.html https://blog.xiaofuxing.name/2017/04/07/bitshares_cli_wallet_introduction.html <![CDATA[bitshares命令行钱包简介]]> bitshares命令行钱包简介

上文 简要介绍了bitshares的核心代码,本文聊一聊命令行钱包,我在这里简要列出编译和使用步骤。不过,首先要注意的是还是上文强调的,是准备在测试链上测试还是在实际链上跑,二者的代码不同。 本文以测试链为例,这样开发的时候无需顾虑费用问题。想要使用实际链的同学请自行替换相关内容。下文所有内容,都是基于linux 命令行的。我的整个测试是在centos7上完成的,但我感觉ubuntu16.04可能会更好,没有实际验证。首先是编译。

第一步是拷贝代码到本地:

git clone https://github.com/BitSharesEurope/graphene-testnet.git

第二步是准备boost库,需要注意的是仅仅支持 boost1.57到1.60,小于1.57或者大于1.60都不行,如果系统的库不是这个版本范围,需要下载范围内的boost源代码进行编译安装。具体步骤省略。

第三步是编译testnet源代码,分为两个命令:

cmake -DCMAKE_BUILD_TYPE=Debug -DBOOST_ROOT=$PWD/../boost_install_1_59_0
make

请注意替换 “-DBOOST_ROOT=” 后面的实际路径,或者使用系统的,就省略整个 “-DBOOST_ROOT=” 选项。

编译完成,可以使用命令行钱包了。在命令行上键入:

./programs/cli_wallet/cli_wallet --server-rpc-endpoint=wss://node.testnet.bitshares.eu/ws

就可以开始使用了。这是一个新的交互式命令环境。使用的时候可参考这个 文档 来理解。本文一步一步来拆解。

第一次使用命令行钱包的时候,并没有钱包文件,提示符是:

new >>>

使用:

set_password mypassword

命令来设置一个钱包保护密码(请自行替换mypassword,下文不再重复),提示符变成:

locked >>>

表示钱包已经创建好并锁定了。这时需要先解锁:

unlock mypassword

提示符又变成:

unlocked >>>

表示钱包已经解锁。下面一步要导入一个账号来操作。导入账号的办法是先通过 网页钱包 注册一个账号,然后去查看私钥。注意是账户权限里面的私钥。这样得到一个账户名和私钥, 使用下面的命令来导入账号:

import_key "ptest2" 5KahXPRjB221ntufwFtqUCb1n7nrouofxmpyMetSZ6kB8T9oQw2

请注意上面ptest2账号是我注册的,私钥在写本文的时候也正确,如果你运气好,也许能直接导入,否则还是自己老老实实去网页钱包注册一个,再替换上面的账号和私钥吧。成功导入账号后,命令行会把钱包文件存起来,存为当前目录下的 wallet.json文件。

导入账号后可以干什么呢?可以看看账号:

list_my_accounts

例如会显示ptest2账号的id 是 “1.2.987”, 而这个id的意义可以参考 这里 简单的来说,1.2表示账号类型,987表示该类型下面的id,与网页钱包头像下面看到的数字是一样的。可以对照:

libraries/chain/include/graphene/chain/protocol/types.hpp

源代码来加深理解,例如所有的协议类型定义:

enum object_type
{
   null_object_type,
   base_object_type,
   account_object_type,
   asset_object_type,
   force_settlement_object_type,
   committee_member_object_type,
   witness_object_type,
   limit_order_object_type,
   call_order_object_type,
   custom_object_type,
   proposal_object_type,
   operation_history_object_type,
   withdraw_permission_object_type,
   vesting_balance_object_type,
   worker_object_type,
   balance_object_type,
   OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types
};

还可以查看余额:

list_account_balances

更多命令,可以获得帮助:

help

键入Ctrl-D可退出命令行钱包,下次再进入的时候,由于wallet.json文件已存在,自动变成锁定状态。账号也不用再导入了。

命令行除了交换式使用,更常用的方法是 JSON-RPC模式,需要键入如下命令:

./programs/cli_wallet/cli_wallet --server-rpc-endpoint=wss://node.testnet.bitshares.eu/ws  -H -d

然后在另一个终端采用HTTP JSON-RPC来调用就可以了。例如下面的命令可以测试:

curl --data '{"jsonrpc": "2.0", "method": "is_locked",  "id": 1}' http://127.0.0.1:8093/rpc
curl --data '{"jsonrpc": "2.0", "method": "unlock", "params": [ "mypassword" ], "id": 2}' http://127.0.0.1:8093/rpc
curl --data '{"jsonrpc": "2.0", "method": "is_locked",  "id": 3}' http://127.0.0.1:8093/rpc

还可以使用你喜欢的编程语言的 JSON-RPC 2.0库来操作。

好,本简介到此结束,感谢看到这里的朋友!如果对你有帮助,请传播;如果有问题,请留言!

]]>
Fri, 07 Apr 2017 00:00:00 +0800
https://blog.xiaofuxing.name/2017/04/07/bitshares_dev_introduction.html https://blog.xiaofuxing.name/2017/04/07/bitshares_dev_introduction.html <![CDATA[bitshares开发入门:开源代码总览]]> bitshares开发入门:开源代码总览

首先看bitshares简单部署示意图:

graph bitshares部署示意图 { node1[shape="box"] node2[shape="box"] node3[shape="box"] node4[shape="box"] node1 -- node2[constraint=false] node1 -- node3[constraint=false] node1 -- node4[constraint=false] node2 -- node3[constraint=false] node2 -- node4[constraint=false] node3 -- node4[constraint=false] "cli_wallet" -- node1 "web_wallet" -- node2 }

node1到node4是区块链的见证节点,互相连接成网状结构,cli_wallet是命令行客户端,通过websocket + JSON API与任意见证节点相连,web_wallet是网页客户端,通过同一套API与任意见证节点相连。

bitshares核心代码分为以下几个部分:

  • bitshares-core 见证节点和命令行钱包,C++代码,基于石墨烯区块链。
  • bitshares-fc bitshares-core依赖的一个快速编译C++工具库;
  • bitshares-ui 网页钱包,Javascript代码,基于React和Webpack。
  • bitshares-js bitshares-ui依赖的js库,包括两大底层功能,读取区块链数据,使用账号签署交易
  • bitsharesjs-ws bitshares-js依赖的js库,封装了与见证节点进行通信的细节,为bitshares-js提供API。

核心代码依赖关系图如下:

digraph 核心代码依赖关系 { "bitshares-core" -> "bitshares-fc" "bitshares-ui" -> "bitshares-js" "bitshares-js" -> "bitsharesjs-ws" }

开发代码,时常需要测试,而不希望直接在bitshares主链上操作。如果不是开发见证人节点代码,那么可以利用已有的公开测试网络(测试链)。bitshares的测试链原始记录在 这里 。 其中包含两个链接,一个是测试链的 网页钱包地址,另一是测试链的 相关文档 。从网页端入口可以创建测试账号,升级终身会员,发行资产等等,熟悉bitshares 的各种用户行为而无需花费BTS代币(每个新创建的账号有20000TEST代币)。而要做开发,可以先利用测试链的源代码调试测试,测试好了之后再切回主链操作,避免不必要的支出和损失。

]]>
Fri, 07 Apr 2017 00:00:00 +0800
https://blog.xiaofuxing.name/2017/04/05/bitshares_introduction.html https://blog.xiaofuxing.name/2017/04/05/bitshares_introduction.html <![CDATA[比特股简介]]> 比特股简介

什么是比特股(bitshares)?从用户层面说,比特股是去中心化的资产交易所。这里的资产包括各种加密货币,例如比特币(BTC)、以太坊(ETH)、达世币(DASH);也包括价值锚定货币,例如bitUSD与美元锚定,bitCNY与人民币锚定;还包括用户发行资产(UIA, user issued asset)。 说比特股去中心化的交易所,就相对于中心化交易所来说了,中心化的交易所就相当于银行,人们在银行有个账户,账户里面有一个余额,记录在银行的数据库里面。转账交易等行为实际上就是在银行的数据库里面做价值转移记录,修改对应账户的余额。在加密货币领域,普通 用户可以到这类交易所通过法币购买加密货币(例如比特币),实际上就是在交易所的户头里面分配一定的加密货币到用户的账户里面。中心化交易所和银行一样,有如下风险:

  1. 准备金不足。一般银行只保留一定比例(远远小于100%)的存款用于应对用户提款,当用户对银行失去信任时,突发大规模提款,银行可能因为准备金不足而倒闭。中心化交易所同样面临这个问题。
  2. 黑客篡改数据库。当黑客有能力直接修改银行的户头时,就有效的攻击了银行系统。同样的在加密货币领域,著名的中心化交易所门头沟交易所因为黑客或者监守自盗的原因丢失了大量的比特币而破产。

为了应对中心化交易所的风险,普通用户要使用加密货币,一般建议要从交易所购得加密货币后要立即提现到自己的客户端钱包,甚至是冷钱包(正如人们把现金从银行取出来放到家里一样)。但这样对想要时常交易的用户又不方便。bitshares尝试解决这个问题。bitshares客户 的所有资产由用户的浏览器或者其他客户端加密保存,而不依赖于中心化的交易所。为了验证这个说法,对比特股不熟悉的用户不妨去开一个账户,然后在另一个入口恢复账户。

OpenLedger的开户入口在 这里 ,值得注意的是,用户在这里填写的密码并不会发送(或者加密发送)到中心化的服务器上去,而是有以下目的:

  1. 随机生成各种角色所用的私钥并加密保存到浏览器;
  2. 在创建账户时,向区块链广播账户各种角色的公钥;
  3. 作为锁定本地钱包的密码,当钱包锁定时,无法解密出私钥,只有解锁后才能使用私钥,才能交易。
  4. 在更换交易所入口恢复账户时,导入加密钱包备份时校验密码是否正确,通过后恢复账户,不通过则提示用户密码输入错误;

创建账户后,可以到另一个交易所 比特帝国 恢复账户,可以发现账户上的历史交易记录一模一样。甚至,还可以下载 比特股的客户端 ,同样可以恢复账户。作为开发人员,可以下载 客户端的源代码 ,生成一个本地版的交易所UI,还可以部署到自己的VPS上。比如我就部署了一个 客户端界面

注意:在最新版本的客户端上,有两种模式可创建用户账户,钱包模式和账户密码模式,而本文撰写的时候,OpenLedger和比特帝国上部署的客户端还是老版本,仅仅支持钱包模式。上文所述是针对钱包模式来说的。钱包模式与账户密码模式的不同在于,钱包模式的私钥是随机生成的,因此需要备份钱包和导入钱包来恢复账户;而账户密码模式的私钥,是账户密码 的确定性函数,仅仅需要账户密码就可以在另一个浏览器恢复账户,同时为了安全,需要更高强度的密码。

从一个交易所入口创建的账户可以从另一个入口或者本地客户端恢复,甚至可以自己编译客户端,足以说明比特股的的确确是去中心化的。账户的创建和各种交易都会被记录到比特股的区块链上,而不是中心化交易所的数据库里。

从用户角度理解比特股还只是开始,比特股从组织架构角度、金融角度和技术角度来看,都是了不起的创新。详细了解比特股可以参考如下资料:

  1. youtube上的bitshares TV频道 ,尤其是 Bitshares 101系列。
  2. 比特帝国教程
  3. 微信公众号 HelloBTS ,里面有比特股的专门文章,包括翻译上面的 Bitshares 101教程部分。
]]>
Wed, 05 Apr 2017 00:00:00 +0800
https://blog.xiaofuxing.name/2017/03/28/blockchain_introduction.html https://blog.xiaofuxing.name/2017/03/28/blockchain_introduction.html <![CDATA[区块链总览]]> 区块链总览

区块链目前(2017年3月)是一个在投资界和金融界比较火的概念,作为一个没有相关经验的人,如何入门呢?这里有笔者的一些经验,分享给本文读者。

区块链的概念源于比特币,是比特币的底层技术,简单来说是一种去中心化的、只能追加的、不可篡改的公共账本,通过技术、社会契约、经济刺激、经济惩罚等手段,让作弊成本最大化。

区块链的核心特性:

  1. 去中心化。目前互联网主要的技术和服务都是中心化的,例如淘宝、支付宝、百度、QQ、微信、Facebook、Google这些,背后都是商业公司,商业公司为用户提供服务,同时存储和使用用户的数据。这里面存在几个问题:1用户必须相信中心化服务提供者;2中心化服务提供者可以通过大数据来赚钱,用户却要承担隐私泄露的风险;3一旦这些服务提供者的中央数据库被黑客入侵,数据就可以被篡改。区块链则是一种分布式的数据库,这个数据库的内容向参与的所有节点公开,并且受到多数节点的验证,这种机制本身可以有效的解决上面的问1,用户不必相信中心化服务提供者,而只要相信区块链技术本身;也可以解决问题3,由于没有中央数据库,黑客必须有足够的力量去攻击足够多的节点才能篡改数据。
  2. 只能追加。区块链数据库的另一个特征是没有改写行为,所有的数据都是随着时间追加上去的,这是要实现不可篡改的一个必要条件。换句话说,区块链本身对所有参与者来说,记录了一个不可篡改的、统一的历史。随着时间的推移,区块链上的数据越来越大。例如,当前部署一个比特币核心节点,需要同步100G的历史数据,这个特性要求区块链的上的数据具有某种价值,又不能太大。转账交易显然具有这种特点,因此区块链首先被应用于加密货币领域,实在有历史的必然性。
  3. 经济内生。区块链不仅仅是技术,同时包含经济学和博弈学的理论,但这些理论本身还不成熟,区块链的发展可以说是缺乏这些成熟理论的大规模的社会实验。经济内生性也是众多炒家(尤其是中国炒家)在炒币的一个基本原因,正如2000年之后的炒房。

详细了解区块链可以看如下几个参考文献:

  1. 2016年10月,由ChinaLedger发布的《中国资本市场分布式总账技术和应用白皮书》。这个白皮书作为ChinaLedger的一个行动纲领,言简意赅的描述了联盟企业打算如何在中国的资本市场部署区块链技术,这本书很有意思,包含中国特色的创新,金融和资本领域区块链从业者必读。
  2. 同样在2016年10月,由工信部指导,中国区块链技术和产业发展论坛发布的《中国区块链技术和应用发展白皮书》。这个白皮书偏向技术层面,可以总览区块链技术的发展状况。
  3. 2015年底,由普林斯顿教授 Arvind Naryanam 主编的《区块链–技术驱动金融》,2016年由中信出版社引进,翻译成中文发行。这本书稍稍有点过时,但其对区块链产业和技术结合的理解是相当独到的,既有大局,技术和商业细节也讲得非常透彻,无论是技术人员还是市场人员或者是创业者,本书绝对值得一读。
  4. 2016年,龚鸣(网名“暴走恭亲王”)编写的《区块链社会–解码区块链全球应用与投资案例》,从应用和投资的层面给出了区块链个各种应用案例,非常适合投资人和创业者阅读。

以下笔者列出区块链相关的国内网站列表:

  1. 巴比特论坛
  2. 比特股门户网站
  3. 区块链铅笔 ,是上述《区块链社会》作者龚鸣做的一个教学和咨询站点。其中有一个 动画课程 系列,是极佳的区块链入门材料。

读这些书,参考以上网站上的文献,我的脑子里慢慢形成了一个概念:区块链不仅仅是技术,更重要的是商业内生、经济内生的。

比特币采用了工作量证明(PoW, Proof Of Work)的共识机制,这个机制具有商业属性:当足够多的挖矿计算机进行挖矿比赛时,除非单一实体能够控制超过51%的算力,否则比特币的交易就不可伪造,不可欺诈。挖矿需要专门的硬件,还需要耗电。如果一个人或者组织足够有钱,那么可以尝试控制51%的算力, 去攻击比特币网络,但结果就是:虽然比特币网络被攻陷,但没有人从中受益。这种经济导向的设计,无法仅仅通过技术本身来解释。

工作量证明机制能够有效的避免伪造和欺诈,但没有准入机制的挖矿和竞争,导致成本居高不下。为了让加密货币技术更加实用,替代的共识机制以及竞争币被发明出来,同样也是基于商业考虑。最有名的替代共识机制是股权证明(POS, Proof of Stake)和代理股权证明(DPOS, Delegated Proof of Stake),这两种方法要比工作量证明经济得多。

公有链(区块链根据参与者的准入机制,可分为公有链、私有链和联盟链。具体定义本文不再重复。)以及其社区的发展是螺旋式上升的。例如比特币:从2009年创世区块开始,到2010年有一个程序员花掉10000比特币买2个披萨,这段时间,比特币仅限于极客圈和研究领域;那时如果要制造51%算力攻击要容易得多。由于比特币开始没什么价值,也就没什么人有动力参与进来,也没有人去攻击它;慢慢的,随着比特币被广泛认知,比特币能够被现实世界定价, 越来越多的人参与到挖矿的过程中去,导致了比特币网络的更加稳定,攻击的成本越高;由于比特币网络越稳定,人们就越相信它,比特币就越值钱。这就形成了一个正反馈的三角循环。

这种螺旋上升的发展特点,会导致新建一个公有链需要一个启动(bootstrap)过程,并且这个启动过程伴随被攻击的风险。这同样也是商业导向的。例如以比特币的哈希算力和现有的矿池分布,某个矿池可以比较容易的去分配算力,攻击与比特币采用相同挖矿算法的竞争币,其目的可以仅仅是保持比特币的核心优势地位。这样的攻击情确实发生过。这就是为什么竞争币在国内还有一个名字:山寨币。

与此同时,不同的竞争币和公有区块链还是大量涌现,这其实是因为比特币本身和社区机制还不足以解决一些问题。上文提到的不同的共识算法就是一个例子,比特币太耗电了。比特币的另一个弱点是支持的每秒钟交易量(Transaction Per Second, TPS)很低,为7。这个问题导致比特币的使用成本在逐渐增高,社区为如何扩容而争论不休,多次面临硬分叉的危险:本文撰写的时候,恰逢比特币无限和比特币核心两个团队互相指责, 比特币有可能在数月后分裂成两种币。比特币本身的匿名性还不够。 比特币仅仅对转账交易友好,很难发展出智能合约。下面列举一下几个有代表意义的竞争币和其他的公有链,以及它们尝试解决的问题。

域名币(Namecoin)是分布式的域名系统(DNS),是竞争币早期最成功的一个,目前还在运营,实现了去中心化的域名解析。

崛起币(Emercoin)是去中心化的键值对存储,可以看成是域名币的泛化。在崛起币的基础上可以做很多有意思的应用,包括ssh身份验证,ssl客户端验证,数字身份证明,产品防伪等等。

达世币(Dashcoin)尝试解决的是比特币的吞吐量和匿名性问题,他们的市场推广做得相当成功。

以太坊(Etherum)是一个雄心勃勃的项目,是分布式应用和智能合约的平台,提供了图灵完备的合约编程语言,克服了比特币单一为加密货币设定的限制。

比特股(bitshares)是采用DPOS共识机制的分布式自治组织(Decentralized Autonomous Orgnazation, 简称DAO),其特点是设计吞吐量能达到 200000TPS,实际由于目前的网络带宽限制,可以达到1000TPS,但可以看出,它的吞吐量要大得多,与比特币不在一个数量级上。比特股之上建立了去中心化的加密货币交易所,是未来的交易所形式。

与竞争币不同,有些问题不能在区块链内部解决,需要配套方案。我意识到有两个主要问题:1是身份识别,2是大规模存储。

身份识别要解决的是”谁在用我的服务“的问题。中心化互联网时代,身份识别主要有两种手段:服务提供者要求客户注册,或者依附于大的服务商(Google、Facebook、Github、新浪微博、QQ、微信)。这两种方法无法解决以下两个问题:身份被盗用,用户反感到处注册。身份识别的目前尝试包括blockstack和崛起币,后者上文介绍过, 而blockstack本身是以区块链(目前是比特币区块链)之上的虚拟链为基础构造的、分层的、去中心化的身份认证系统。

由于区块链只能追加不可更新,大规模存储必须是链外的,而去中心化的区块链如果配套中心化的存储,显然不是完整的去中心化方案。因此去中心化的存储技术出现了。 星际文件系统 (Inter Planet File System, 简称为IPFS),是著名的开源项目,尝试解决分布式存储问题。Storj 则是商业的去中心化存储系统。

]]>
Tue, 28 Mar 2017 00:00:00 +0800
https://blog.xiaofuxing.name/2017/03/06/create_dynamic_component_using_js_in_angular2.html https://blog.xiaofuxing.name/2017/03/06/create_dynamic_component_using_js_in_angular2.html <![CDATA[angular2中动态创建Component]]> angular2中动态创建Component

当使用Angular2,并且与第三方库打交道时,有时需要使用JS代码(TS代码)动态创建一个Component,然后把这个Component的原生dom对象传入到第三方库的API中去。这如何做到呢?简单来说分为4步:

  1. 使用 ComponentFactoryResolver 和目标Component类对象,创建一个ComponentFactory;
  2. 使用 ViewContainerRef 的 createComponent方法,传入刚刚得到的ComponentFactory对象,实例化一个Component;
  3. 给Component的instance中的 @Input 属性赋值。
  4. 引用 Component.location.nativeElement,丢给第三方库的API。

参考 Angular2中动态注入Component

]]>
Mon, 06 Mar 2017 00:00:00 +0800
https://blog.xiaofuxing.name/2017/03/06/refer_dom_element_without_global_id_in_angular2.html https://blog.xiaofuxing.name/2017/03/06/refer_dom_element_without_global_id_in_angular2.html <![CDATA[angular2中避免使用id引用dom元素]]> angular2中避免使用id引用dom元素

当使用Angular2,并且与第三方库打交道时,有时需要在Component中直接引用一个dom元素(例如高德地图API)。常见的方法是使用id属性。但模块化编程不推荐这种方法:因为id要保证整个HTML文档唯一,而这个保证会随着项目的变大而不可控。正如C语言中的全局变量是有害的东西一样,任何情况下,在javascript/html语境中,要尽量避免使用dom元素的id属性。 问题是,如何做到呢?

答案是使用 ElementRef。

首先,在模板中,使用 # 为某个dom元素标定出一个模板局部变量。

例如:

<div #myVar></div>

然后,在Component中,导入 ElemntRef、ViewChild和 AfterViewInit:

import { ElmentRef, ViewChild, AfterViewInit } from "@angular/core";

再次,在Component中定义属性:

export class TheComponent {
    @ViewChild('myVar') ele: ElementRef;
}

最后,使用 ElmentRef.nativeElement:

export class TheComponent implements AfterViewInit {
    @ViewChild('myVar') ele: ElementRef;

     ngAfterViewInit(){
         console.log(this.ele.nativeElement)
     }
}

参考 这个stackoverflow问题

]]>
Mon, 06 Mar 2017 00:00:00 +0800
https://blog.xiaofuxing.name/2017/02/24/install_docker_on_debian_jessie.html https://blog.xiaofuxing.name/2017/02/24/install_docker_on_debian_jessie.html <![CDATA[在Debian8上安装Docker并加速]]> 在Debian8上安装Docker并加速

Docker有啥好处?先安装了再说。

安装方法: 按照这个 安装步骤文档 做就好了。

关键是,中国需要加速, 方法有2:

  1. 注册 daocloud 帐号, 然后在 daocloud加速文档 获取加速方法。
  2. 注册 阿里云 帐号,然后在 阿里云文档 获取加速域名。

本质上,两种加速方法是类似的,daocloud和阿里云分别给注册的帐号绑定一个加速域名,放在 /etc/docker/daemon.json 里面,参考 dockerd文档 中关于 –registery-mirror的说明。 /etc/docker/daemon.json 内容为如下形式(需要替换两处<>中的内容)

{
 "registry-mirrors": ["http://<yourdaocloudacc>.m.daocloud.io", "https://<youraliyun>.mirror.aliyuncs.com"]
}

daocloud和阿里云加速可单独使用,也可混合使用。

]]>
Fri, 24 Feb 2017 00:00:00 +0800
https://blog.xiaofuxing.name/2016/11/29/exclusion_for_bug.html https://blog.xiaofuxing.name/2016/11/29/exclusion_for_bug.html <![CDATA[解决问题的思路:排除法]]> 解决问题的思路:排除法

我最近又一次刚刚解决了工作中的一个不大不小的技术问题,想分享下自己的这个简单的方法论:排除法。

先说说这个问题是什么,我又是怎么解决的。因为产品需要,购买了淘宝上的一种mini主机,决定采用Linux系统做产品,首先就要解决发行版的问题。经过比较选择,初步选用Manjaro Linux。但是上周突然发现一个很诡异的现象:启动时间太长,每次启动的时间从几秒钟到2分半种不等。我这么描述,实际上不够精确,那么启动时间是什么呢?从电源开启,到自动登录完成并且桌面显示出来了,总共的时间。而这里面最有意思的是会在桌面显示出来之前会有一个黑屏时间,这个时间是不太固定的。作为企业产品,启动时间慢和不固定都是不能容忍的。那么,怎么来解决这个问题呢?其实,我一直在使用排除法,缩小问题的范围,直到最终精准定位。

第一个范围排除:显示相关还是其他?为了搞清楚系统是不是其他部分都启动了,而显示部分可能会滞后,将系统的ssh服务开启,当发现“卡死”的时候,立刻从另一个机器去远程登录,发现每次都能登录,因此排除了其他问题,确认与显示相关。

第二个范围排除:是否与自动登录相关?把系统的自动登录关掉,让启动的时候必须输入用户名密码,结果发现:问题消失!这就证明了,问题与自动登录相关。

第三个范围排除:卡在自动登录之前还是自动登录之后?由于是黑屏,很不给力,找一下自动登录的机制,发现可以设置自动登录的延时,默认是0,改成3秒,发现登录界面闪过,出现了鼠标和背景,不动。原来黑屏的现象变成了固定背景的问题。初步判断是在自动登录之后。因此需要研究从登录到桌面出现,需要经过哪些步骤。

第四个范围排除:进程筛选。研究了X11的Display Manager(本例中lightdm)和桌面系统(本例中Xfce)的进程父子关系,通过ssh,找出卡死的进程,最后发现卡在 gnome_keyring_daemon程序上。

最后,综合判断,给出结论。gnome_keyring_daemon程序在初始化的时候会读 /dev/random获得随机数,而 /dev/radom 这个内核接口需要足够的熵来产生随机数,当采用自动登录的方案时,如果对系统没有任何刺激,内核获得熵的速度会比较慢,导致 gnome_keyring_daemon阻塞;此时如果动一动鼠标,则很快进入桌面系统;在手动登录的方案中,用户输入密码和回车就产生了足够的熵,进入系统也就顺利了。

当然,上面说到的是一个简化的排查模型,排除法只是一种方法,需要和其他的一些条件一起使用。

首先,解决问题的信念。没办法,作为产品,不解决这个问题没法用。当遇到岔路时,尽管问题没有解决,但也学到了更多的知识,不能气馁。我解决这个问题的过程中一直有一个支线问题在干扰我:系统会识别出一个没有硬件连接的笔记本显示屏。为了排除这种可能,我通过配置让系统忽略这个不存在的显示屏,发现问题依旧。这个做法有两个好处:排除了多屏问题,我顺便理解了X11的忽略显示屏配置方法。另外,当自动登录相关的结论出来之后,作为绕过问题的策略,可以设为手动登录而不去研究,但这样导致浅尝辄止,除非有更重要的事情,这种刨根问底的精神不能丢。

其次,对比尝试。第二个范围排除,我的灵感源于这样一个偶然事实:当黑屏的时候,我动动鼠标或者敲击键盘,总是能进入系统。没有这个尝试,我很难想到登录的问题。

第三,知识储备。我对X11其实理解得不算太全面,这次为了解决这个问题,不得不去更深的理解了不同部分的关系,尤其是启动顺序。

]]>
Tue, 29 Nov 2016 00:00:00 +0800
https://blog.xiaofuxing.name/2016/11/07/mounting_nfs_server_on_mac_os_x.html https://blog.xiaofuxing.name/2016/11/07/mounting_nfs_server_on_mac_os_x.html <![CDATA[在OS X系统上挂载局域网上的NFS]]> 在OS X系统上挂载局域网上的NFS

我这个MacBookPro硬盘空间太小了,总是不够用,于是总想着各种折腾,如何更好的利用外部空间。方法有两种,外接USB硬盘和利用局域网中的其他机器硬盘。公司这边有一个centos 7的服务器常开着,硬盘又大,不用浪费了。 之前使用sshfs,但用起来感觉开销有些大。于是又折腾NFS。其实做完了很简单,不过要注意细节。本文记录下。

Server设定

centos7上需要安装nfs:

$ sudo yum install nfs-utils nfs4-acl-tools

然后配置 /etc/exports 文件:

/home/yourid       192.168.0.0/16(insecure,rw,all_squash,anonuid=1000,anongid=1000)

这里的几个选项比较关键,insecure允许客户端源端口>1024,这样在OS X上,普通用户就能挂载。另外把所有用户都映射到用户 yourid 的 User ID和 Group ID,这样就不会有权限问题。

打开防火墙的2049(NFS V4)端口:

$ sudo iptables -A IN_public_allow -s 192.168.1.0/24 -m state --state NEW -p tcp --dport 2049 -j ACCEPT

开启服务

$ sudo service nfs start

OS X客户端

命令行下直接挂载:

$ mount -t nfs -o vers=4 192.168.1.50:/home/yourid remote
]]>
Mon, 07 Nov 2016 00:00:00 +0800
https://blog.xiaofuxing.name/2016/10/28/angular_formly_intro_example_s_code.html https://blog.xiaofuxing.name/2016/10/28/angular_formly_intro_example_s_code.html <![CDATA[angular-formly入门例子代码解析(一)]]> angular-formly入门例子代码解析(一)

前文 简要介绍了angular-formly,本文对angular-formly的 入门例子 的代码做一个初步注解。在这个例子中,一共有六个表单项,展示了angular-formly的一些特点。

首先看如何引用这个库。HTML代码中的head部分,引用了一些必要的库,包括bootstrap(仅CSS部分), api-check, angular, angular-formly, angular-formly-templates-bootstrap, 如下:

<head>
<!-- Twitter bootstrap -->
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.css" rel="stylesheet">
 <!-- apiCheck is used by formly to validate its api -->
 <script src="//npmcdn.com/api-check@latest/dist/api-check.js"></script>
 <!-- This is the latest version of angular (at the time this template was created) -->
 <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.js"></script>
 <!-- This is the current state of master for formly core. -->
 <script src="//npmcdn.com/angular-formly@latest/dist/formly.js"></script>
 <!-- This is the current state of master for the bootstrap templates -->
 <script src="//npmcdn.com/angular-formly-templates-bootstrap@latest/dist/angular-formly-templates-bootstrap.js"></script>
 <title>Angular Formly Example</title>
</head>

JS代码中,顶层的Angular模块依赖:

var app = angular.module('formlyExample', ['formly', 'formlyBootstrap'], function config(formlyConfigProvider) {
 // set templates here
 formlyConfigProvider.setType({
   name: 'custom',
   templateUrl: 'custom.html'
 });
});

这个例子中顶层模块依赖了 formly和formlyBootstrap这两个module,formly是必须的,formlyBootstrap是Bootstrap特定的模板。config函数这里暂时忽略。

其次看看如何构造一个最小的formly应用。核心的HTML模版代码如下:

<form ng-submit="vm.onSubmit()" name="vm.form" novalidate>
   <formly-form model="vm.model" fields="vm.fields" options="vm.options" form="vm.form">
     <button type="submit" class="btn btn-primary submit-button" ng-disabled="vm.form.$invalid">Submit</button>
     <button type="button" class="btn btn-default" ng-click="vm.options.resetModel()">Reset</button>
   </formly-form>
 </form>

angular-formly最主要就是提供了 formlyForm这个directive,包含4个属性,其中,model和fields是必须的,model属性是指向controller里面所有表单项对应的变量(双向绑定变量)的父对象,fields属性对应controller里面的一个数组,该数组中每一个元素对应一个逻辑表单项。这里对照下controller中的关键代码:

vm.model = {
  awesome: true
};

vm.fields = [
  {
    key: 'text',
    type: 'input',
    templateOptions: {
      label: 'Text',
      placeholder: 'Formly is terrific!'
    }
  },
  /** 其他fields目前省略 */
];

换言之,根据目前摘出来的代码,我们可以期待目标程序会包含一个表单项,类型为 <input type="text"> ,与控制器中的 vm.model.text 变量绑定。这里可以把这个例子更加简化一些, html中的模板代码只需要写成:

<formly-form model="vm.model" fields="vm.fields" >
</formly-form>

在这个基础上,只要在controller代码中扩充vm.fields数组,就定义了更多的表单项,而这个特性,恰恰是angular-formly最让人喜欢使用的原因。本文到此结束,更多代码解析,等我再写。

]]>
Fri, 28 Oct 2016 00:00:00 +0800
https://blog.xiaofuxing.name/2016/10/14/handbrake_introduction.html https://blog.xiaofuxing.name/2016/10/14/handbrake_introduction.html <![CDATA[视频转码利器handbrake]]> 视频转码利器handbrake

最近给小福星购买了巧虎会员,那些DVD不好总是拿电脑放,前一阵子我又自作主张的买了个外置光驱,被媳妇数落说:“你看看你总是不考虑我们(作者注:非IT技术人员)的感受,偏要买个光驱而不是DVD播放机,每次给孩子放巧虎DVD还得用电脑,麻烦,好好的电视不用!”弄得我怪不好意思的。因此我就必须想办法给DVD备份,让播放更简单。因为家里的电视支持外置U盘上MP4视频格式播放,因此把DVD转换成MP4就有作用了。

handbrake 是图形界面的视频转码工具,之前用过,这次再用来备份DVD,感觉很好用。所以写此文给看官推荐下。handbrake支持三大主流操作系统,在Mac OSX上安装和使用比较傻瓜,Windows上我估计也一样(毕竟很长时间不用Windows了),而在Linux上用来备份DVD的话,有一个小小的坑。这个坑与handbrake没有关系,与锁区加密有关。我的原则是这样的:handbrake使用之前,系统必须能够正常播放该DVD。而播放DVD的软件,我推荐 VLC

在Linux系统(我用的是Debian 8)上,巧虎DVD开始无法播放,Google了下发现DVD是锁区加密的,需要安装 libdvdcss 这个库才行。因此按说明安装好它,DVD就能正常播放了。DVD正常播放之后,handbrake也就能够使用了。目前的几期巧虎DVD,采用H.264编码,压缩完成的比特率约为1.2Mbps,图像大小720x576,30分钟的视频,300MB就压缩完成。还是非常节省空间的。关键是,在家里面把U盘插在电视机上就可以直接播放,媳妇乐了,小福星想看也就方便了。

]]>
Fri, 14 Oct 2016 00:00:00 +0800
https://blog.xiaofuxing.name/2016/10/10/angular_formly_introduction.html https://blog.xiaofuxing.name/2016/10/10/angular_formly_introduction.html <![CDATA[angular-formly简介]]> angular-formly简介

angular-formly [1] 是 Angular (1.x)的表单构建库,因为工作我用到了这个库,觉得很有意思,写出来分享。本文首先尝试对这个库做简单的介绍。全文按三个问题组织。

问题一:为什么作者会写这个库?

其实,这个问题得问作者(请自行到github和项目主页去查)。但另一个类似的问题我就好回答了,为什么我找到并使用了这个库?最开始用angular构建表单的时候,我尝试了三种方法:

  • 手写每个表单域
  • ng-repeat
  • ngbs-forms

继续阅读...

]]>
Mon, 10 Oct 2016 00:00:00 +0800
https://blog.xiaofuxing.name/2016/09/20/reliable_ssh_port_forwarding.html https://blog.xiaofuxing.name/2016/09/20/reliable_ssh_port_forwarding.html <![CDATA[可靠的ssh反向端口转发方法]]> 可靠的ssh反向端口转发方法

基本问题:如何远程管理在NAT后面的Linux计算机?ssh反向端口转发比较不错。

digraph rportforward { "远程主机" -> "中转服务器" [ label="ssh反向端口转发" ] "客户端"  -> "中转服务器" [ label="ssh" ] "中转服务器" -> "远程主机" [ label="ssh" ] }

反向端口转发示意图

其中,中转服务器位于互联网,有独立的IP地址。简单的端口转发命令是

ssh -R <rPort>:127.0.0.1:22 <mServerHost>

但是简单使用这个命令是不可靠的,涉及到如下问题:

  • 需要远程主机的交互终端一直开着
  • 断线检测不可靠,会出现假连接
  • 无法自动重连

针对第一个问题,解决方案是采用 screen 程序,让screen 接管交互终端,这样比较不容易被误杀。

针对第三个问题,解决方案是采用 autossh程序,这是个能够自动重联ssh的程序,最简单的使用方法是直接替换 ssh

autossh -R <rPort>:127.0.0.1:22 <mServerHost>

但是,仅仅这样还是不够的,因为autossh自己并没有很好的解决断线检测的问题,但是提供一种机制:

  • monitor端口

简单的说,就是通过ssh做两个隧道,把数据发到远端再转发回来,如果没收到,就表明链路断了。这种方法使用 -M 参数:

autossh -M <mport> -R <rPort>:127.0.0.1:22 <mServerHost>

实验发现,这种方法也不是很靠谱。不用monitor端口,可以利用 SSHv2协议的KeepAlive机制,需要客户端和服务端配合,我发现这是最可靠的:

客户端,需要增加两个选项 ServerAliveInterval=15, ExitOnForwardFailure=yes, 服务端,需要增加两个选项: ClientAliveInterval=15 和ClientAliveCountMax=3 。

这样客户端和服务器端都会采用心跳来检查连接的健康状态,没有心跳就会断开连接。而客户端的 ExitOnForwardFailure=yes则确保新的连接必须成功,不成功则退出,然后 autossh接管异常退出,重连,客户端的命令行参数如下

autossh -R <rPort>:127.0.0.1:22 -o ServerAliveInterval=15 -o ExitOnForwardFailure=yes <mServerHost>

[2016.10.28更新] 在Centos 7系统上 autossh 的 -M 选项是必须的,因此改为

autossh -M 0 -R <rPort>:127.0.0.1:22 -o ServerAliveInterval=15 -o ExitOnForwardFailure=yes <mServerHost>

服务器端需要修改 /etc/ssh/sshd_config 文件,然后reload sshd服务。

[2017.05.22更新]

更多参数:

  • -f 表示autossh自己进入后台执行,
  • -qTN表示不分配终端,仅用于转发。因此改为
autossh -M 0  -f -qTN -R <rPort>:127.0.0.1:22-o ServerAliveInterval=15 -o ExitOnForwardFailure=yes <mServerHost>

另外,此方法对正向端口转发同样有效:

autossh -M 0  -f -qTN -L <localPort>:<remoteHost>:<remotePort>-o ServerAliveInterval=15 -o ExitOnForwardFailure=yes <mServerHost>
]]>
Tue, 20 Sep 2016 00:00:00 +0800
https://blog.xiaofuxing.name/2016/09/08/best_css_minifier_tools_with_gulp.html https://blog.xiaofuxing.name/2016/09/08/best_css_minifier_tools_with_gulp.html <![CDATA[最佳的css最小化工具]]> 最佳的css最小化工具

看客:这是一个坑,我先站上,有时间再多补补内容!

今天解决一个很诡异的BUG。最后证明是CSS工具的BUG,因为 gulp-cssnano 压缩CSS后导致了浏览器兼容性问题(IOS8.1不兼容),将流程中cssnano这一步去掉了,问题解决。

想到如下问题:

  • 什么是最好的CSS最小化工具?

把这个问题的范围缩小下:有gulp插件的开源工具中,什么是最好的CSS最小化工具?看起来有两个选项

  • css-nano
  • clean-css

[9.20更新] 经过替换,看起来 clean-css 更优。

换个角度,想到另一个问题:

  • 如果主流浏览器都支持gzip作为content-encoding的话,部署服务器的时候也支持这个选项,css最小化是否就没啥意义了呢?

[9.20更新] 这两个不是一个层面的问题,CSS最小化会在CSS语义层面进行合并(例如去除相同的CSS规则),而gzip是一种无损压缩。

]]>
Thu, 08 Sep 2016 00:00:00 +0800
https://blog.xiaofuxing.name/2016/08/30/material_design_font_compatibility_with_uc_browser.html https://blog.xiaofuxing.name/2016/08/30/material_design_font_compatibility_with_uc_browser.html <![CDATA[material design图标字体在UC浏览器(Android)的兼容性问题]]> material design图标字体在UC浏览器(Android)的兼容性问题

调试这个兼容性(UC浏览器,Android版本11.0.4.846)问题发现两个坑:

第一,按官方文档 [1] 的第一种方法(Setup Method 1. Using via Google Web Fonts),UC浏览器并不工作,需要手工加上以下CSS代码:

 .material-icons {
/* Support for  UC ! */
text-rendering: optimizeLegibility;


}

第二,如果使用官方文档 [1] 的第二种方法或者使用 bootcdn [2] 的加速服务,也不工作,需要去掉两行(下面代码的6,7行)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@font-face {
font-family: 'Material Icons';
font-style: normal;
font-weight: 400;
src: url(https://example.com/MaterialIcons-Regular.eot); /* For IE6-8 */
src: local('Material Icons'),
     local('MaterialIcons-Regular'),
     url(https://example.com/MaterialIcons-Regular.woff2) format('woff2'),
     url(https://example.com/MaterialIcons-Regular.woff) format('woff'),
     url(https://example.com/MaterialIcons-Regular.ttf) format('truetype');
 }

悬而未决的疑惑:

Google的字体文件 [3] 到底对应的是github上发布的哪个版本?

参考

]]>
Tue, 30 Aug 2016 00:00:00 +0800
https://blog.xiaofuxing.name/2016/06/11/aria2c.html https://blog.xiaofuxing.name/2016/06/11/aria2c.html <![CDATA[aria2]]> aria2

很久没有更新博客了,还是应该坚持写。推荐一个命令行版本的多线程下载工具 aria2 [1] ,可16倍加速下载,原理和网络蚂蚁一样。aria2 还支持断点续传。

我一般会使用 alias,把如下代码放到 ~/.bashrc 里面:

alias aria2c='aria2c --max-connection-per-server=16 -s 16 --min-split-size=1M'

这样,命令行使用时,不需要写更多的参数,默认就16倍加速了:

$ aria2c http://www.example.com/large/file
[1]https://aria2.github.io
]]>
Sat, 11 Jun 2016 00:00:00 +0800
https://blog.xiaofuxing.name/2016/06/11/file_model_and_file_reader_for_angular.html https://blog.xiaofuxing.name/2016/06/11/file_model_and_file_reader_for_angular.html <![CDATA[angular中文件表单的模型和数据绑定、上传方法]]> angular中文件表单的模型和数据绑定、上传方法

angular (1.x)中,模板如下

<input type="file" ng-model="data.file" >

是无法绑定到Controller中的变量 ($scope.data.file) 的。如何解决?使用 angular-file-model [1] 之后,就可以在模板中用下面的形式

<input type="file" file-model="data.file" >

当选定文件后, $scope.data.file 就会成为一个 File object。

但是如何及时预览图像呢?

继续阅读...

]]>
Sat, 11 Jun 2016 00:00:00 +0800
https://blog.xiaofuxing.name/2016/03/29/simple_crawler.html https://blog.xiaofuxing.name/2016/03/29/simple_crawler.html <![CDATA[简单爬虫]]> 简单爬虫

想给闺女下载些MP3,百度一下倒是有些网站有,可惜下载麻烦,于是想到写个爬虫来自动下载。原来没写过,脑子里闪过几个思路后,决定先用phantomjs [1] 试试。

很快写了一个phantomjs的版本,测试了下,用是能用,就是有些慢。嗯,phantomjs能够渲染页面,但渲染页面恰恰是我这个爬虫不需要的,要是专注dom解析,应该能节省不少时间。于是google到 jsdom [2] ,改了改,发现好用多了。

源代码在 这里

我的思路:

  • Nodejs, 开发快
  • 不自己下载,丢给bash/wget,可以把生成好的脚本丢到Openwrt路由器上

基础工作:

  • 使用Chrome inspector,分析网站结构,选用任何技术都无法避免的前提。
[1]http://phantomjs.org
[2]https://github.com/tmpvar/jsdom
]]>
Tue, 29 Mar 2016 00:00:00 +0800
https://blog.xiaofuxing.name/2015/10/25/round_score_with_angular_and_nvd3.html https://blog.xiaofuxing.name/2015/10/25/round_score_with_angular_and_nvd3.html <![CDATA[使用angular-nvd3构造圆形分数]]> 使用angular-nvd3构造圆形分数

目标简介

一个漂亮的分数显示,从0-100,就像下面几个图那样:

../../../_images/40.png ../../../_images/80.png ../../../_images/95.png

所用到的技术

angularjs [1] 是非常著名的前端MVC框架,由google支持的开源项目。

d3 [2] 是基于SVG的数据可视化技术,是教底层的可扩展的库。

nvd3 [3] 是对d3的封装,实现了柱状图、折线图、饼图等常用的统计图形的定制库,增加了d3的易用性。

angular-nvd3 [4] 是nvd3在angular框架中的进一步封装,使得其在angular用户中更加易用。

要使用angular-nvd3,html页面中需要包含如下片段(假设各个模块已经下载到本地,使用cdn的读者自行替换):

<link href="bower_components/nvd3/build/nv.d3.min.css" rel="stylesheet">
<script src="bower_components/d3/d3.min.js"></script>
<script src="bower_components/nvd3/build/nv.d3.min.js"></script>
<script src="bower_components/angular/angular.min.js"></script>
<script src="bower_components/angular-nvd3/dist/angular-nvd3.min.js"></script>

实现思路

在nvd3中,由于饼图(pieChart)可以定制成中间掏空环形图(donut),我们可以以此为基础来构造整个图形:

  1. 外圈绿色和灰色构成的环形图;
  2. 中圈白色和黄色构成的环形图;
  3. 内圈直接使用div

外圈环形图需要两个值, score和100-score; 中圈一样; 内圈通过css设置border-radius为50%来构成一个整圆形,使用line-height来居中。

不过,怎么使这两个环与中间的圆都是同心圆呢?这需要如下两个条件:

  • 容器为正方形,绝对定位
  • 两个nvd3图的选项,四边的border都是0

参考源代码

index.html
<html>
  <head>
    <title>漂亮的分数</title>
    <link href="bower_components/nvd3/build/nv.d3.min.css" rel="stylesheet">
    <script src="bower_components/d3/d3.min.js"></script>
    <script src="bower_components/nvd3/build/nv.d3.min.js"></script>
    <script src="bower_components/angular/angular.min.js"></script>
    <script src="bower_components/angular-nvd3/dist/angular-nvd3.min.js"></script>

    <link href="index.css" rel="stylesheet" >
  </head>
<body ng-app="demoApp">
<h1>漂亮的分数</h1>

<div style="width: 400px; height: 400px; position:absolute;" ng-controller="DemoCtrl" >
  <nvd3 options="chart.options" data="scoreData" class="full" ></nvd3>
  <div style="width: 75%; height: 75%; margin-left: 12.5%; margin-top: 12.5%; position:absolute;">
    <nvd3 options="chart.options_inner" data="scoreData" class="full" ></nvd3>
  </div>
  <div style="width: 80%; height: 80%; margin-left: 10%; margin-top: 10%; position:absolute;">
    <div style="position:absolute; width: 50%; height: 50%; left: 25%; top: 25%; background: black; border-radius: 50%; text-align: center; color: white; line-height: 45px;">

      <span style="font-size:2.5em; line-height: 160px;" >{{score}}分</span>
    </div>
  </div>
</div>

<script type="text/javascript">

  angular.module("demoApp", ['nvd3'])
  .controller("DemoCtrl", function($scope){

        $scope.xFunction = function(){
            return function(d){
                return d.key;
            };

        };
        $scope.yFunction = function(){
            return function(d){
                return d.value;
            };

        };

        function init(){
            $scope.score = 95;
            $scope.chart = {};

            $scope.chart.options = {
                chart:{
                    type: 'pieChart',
                    margin: { left: 0, top: 0, right: 0, bottom: 0},
                    donut: true,
                    donutRatio: 0.60,
                    x: $scope.xFunction(),
                    y: $scope.yFunction(),
                    showLabels: false,
                    showLegend: false,
                    color: ['rgb(130,201,63)', 'lightgray']

                }
            };
            $scope.chart.options_inner = {
                chart:{
                    type: 'pieChart',
                    margin: { left: 0, top: 0, right: 0, bottom: 0},
                    donut: true,
                    donutRatio: 0.5,
                    x: $scope.xFunction(),
                    y: $scope.yFunction(),
                    showLabels: false,
                    showLegend: false,
                    color: ['transparent', 'rgb(252,225,11)']

                }

            };

        }

        init();

        $scope.$watch('score', function(nv, ov){
            if( nv ){

                $scope.scoreData = [
                    {key: 'score', value: nv },
                    {key: 'other', value: 100-nv }
                ];
                $scope.chart.data = {
                    key: 'thekey',
                    data: $scope.scoreData

                };

            }

        });

  });


</script>


</body>
</html>
index.css
.full{
    position:absolute;
    width: 100%;
    height: 100%;
}
]]>
Sun, 25 Oct 2015 00:00:00 +0800
https://blog.xiaofuxing.name/2014/08/27/media_source_extensions_inside_webkit.html https://blog.xiaofuxing.name/2014/08/27/media_source_extensions_inside_webkit.html <![CDATA[Media Source Extensions inside Webkit]]> Media Source Extensions inside Webkit

Introduction

Webkit [1] is a famous web layout/rendering engine, which is opensource, active development by Apple (Safari browser is based on it). In history, Google Chrome is also based on Webkit, but after Google created their own (webkit) branch Blink, Chrome is based on Blink now.

Html5 video [2] (and audio) element is a technology that provide a standard HTML API for video(audio) playing without a plugin(like flash). In Web video content providers, Youtube and Netflix support html5 video. In Web browsers, Chrome / Safari / Firefox support html5 video.

Media Source Extensions [3] (MSE) is a W3C specification (Work in progress) that allows Javascript to dynamically construct media streams for html5 video and audio element.

In this blog post I want to analysis and show the architecture of MSE inside Webkit Source code. First an introduction to html5 video implementation itself( without MSE), then the big picture of MSE, finally key interface classes. Please note it is better to have Webkit source code to follow this blog, so if you don’t have it now , please check Webkit [1] site to get one. For impatient audiences I myself suggest to use

git clone git://git.webkit.org/WebKit.git

to take advantages of git (comparing to svn).

As Webkit and MSE is changing, for this blog I am writing, I refer to webkit version r159335.

继续阅读...

]]>
Wed, 27 Aug 2014 00:00:00 +0800
https://blog.xiaofuxing.name/2013/12/01/sdcard_recovery.html https://blog.xiaofuxing.name/2013/12/01/sdcard_recovery.html <![CDATA[sd卡数据恢复记]]> sd卡数据恢复记

每天手机关机,突然有一天(上周)早上起来开机,提示“SD卡错误,需要重新格式化!” 惊出一头汗。拒绝格式化,关机重启,还是那样。肯定是坏了。拿到Linux上查一下分区表,没了。冷静,备份整个卡先,dd了搞一个8G的映像。然后再看看怎么折腾恢复数据。

继续阅读...

]]>
Sun, 01 Dec 2013 00:00:00 +0800
https://blog.xiaofuxing.name/2013/11/17/haskell_draw_class_diagram_from_python_source_code.html https://blog.xiaofuxing.name/2013/11/17/haskell_draw_class_diagram_from_python_source_code.html <![CDATA[haskell学习笔记:从Python源代码生成类图(继承关系图)]]> haskell学习笔记:从Python源代码生成类图(继承关系图)

我想搞一个通过reST直接写论文的东东,想法如下:

digraph name{     node[shape=box]     reST -> latex -> pdf }

而现状是: 从latex到pdf有了 模板 ,而Sphinx或者直接docutils都可以生成pdf, 但还不能直接生成模板所需要的那个样子(主要是表格和插图引用有问题),所以想系统的学习下docutils的源代码好hack,找了一个旧版本的 Presentation ,把代码也从 sf.net 下面搞到本地了,然后搞明白其关键的数据结构是 文档树 也就是 nodes.py所定义的数据结构。

说了半天还没没有到正题,nodes.py有2205行,132个类定义,我想搞一个UML类图反映继承关系,正好用用这几天刚学的haskell来测试下。哦, 忘了提一个附加背景知识了, plantuml 利用 graphviz 可以通过文本 转换UML图,那么我的问题就分解为如何利用haskell来生成plantuml所需要的输入文本。

继续阅读...

]]>
Sun, 17 Nov 2013 00:00:00 +0800
https://blog.xiaofuxing.name/2013/11/15/rest.html https://blog.xiaofuxing.name/2013/11/15/rest.html <![CDATA[使用 reStructuredText 生成中文pdf]]> 使用 reStructuredText 生成中文pdf

reStructuredText [1] 这个好东西,我要多多的用,还要多多的推广。这个博客的底层技术就是它!

思路

  1. 通过rst2xetex 生成tex文件
  2. 通过xelatex 生成pdf文件 (Debian下懒人办法: 安装texlive-full)
  3. rst2xetex的时候指定 –documentclass=ctexart
  4. Linux下需要若干字体文件 [2]

Makefile

%.pdf: %.tex
       xelatex $<
       xelatex $<
       xelatex $<

%.tex: %.rst
       rst2xetex --documentclass=ctexart  $< $@

字体文件列表

有多余的,我没去管。

$ /usr/share/fonts/windows$ ls
04B_21__.TTF  ariblk.ttf   framdit.ttf  FZZYJW.ttf    l_10646.ttf  mingliu.ttc   raavi.ttf     SIMHEI.TTF    timesbi.ttf   verdanai.ttf  wst_germ.fon
app932.fon    batang.ttc   framd.ttf    gautami.ttf   latha.ttf    modern.fon    roman.fon     SIMKAI.TTF    timesi.ttf    verdana.ttf   wst_ital.fon
app936.fon    comicbd.ttf  FZFSJW.TTF   georgiab.ttf  lsansdi.ttf  msgothic.ttc  script.fon    SIMSUM.TTC    times.ttf     verdanaz.ttf  wst_span.fon
app949.fon    comic.ttf    FZHTJW.TTF   georgiai.ttf  lsansd.ttf   msmincho.ttc  segoeuib.ttf  STXINGKA.TTF  trebucbd.ttf  vrinda.ttf    wst_swed.fon
app950.fon    courbd.ttf   FZKTJW.TTF   georgia.ttf   lsansi.ttf   mvboli.ttf    segoeuii.ttf  sylfaen.ttf   trebucbi.ttf  webdings.ttf  ZWAdobeF.TTF
arialbd.ttf   courbi.ttf   FZLSJW.ttf   georgiaz.ttf  lsans.ttf    palabi.ttf    segoeui.ttf   symbol.ttf    trebucit.ttf  wingding.ttf
arialbi.ttf   couri.ttf    FZSSJW.TTF   gulim.ttc     lucon.ttf    palab.ttf     segoeuiz.ttf  tahomabd.ttf  trebuc.ttf    wst_czec.fon
ariali.ttf    cour.ttf     FZXBSJW.ttf  impact.ttf    mangal.ttf   palai.ttf     shruti.ttf    tahoma.ttf    tunga.ttf     wst_engl.fon
arial.ttf     estre.ttf    FZXKJW.ttf   kartika.ttf   micross.ttf  pala.ttf      SIMFANG.TTF   timesbd.ttf   verdanab.ttf  wst_fren.fon
]]>
Fri, 15 Nov 2013 00:00:00 +0800
https://blog.xiaofuxing.name/2013/11/03/unboxedtypeofhaskell.html https://blog.xiaofuxing.name/2013/11/03/unboxedtypeofhaskell.html <![CDATA[Haskell学习笔记: Unboxed Type]]> Haskell学习笔记: Unboxed Type

上文 提出了一个问题,怎么能写一个length函数能和内置的length一样快?最简单的办法莫过于 查看GHC源代码 了。

库里面的源代码确是这个样子:

-- | /O(n)/. 'length' returns the length of a finite list as an 'Int'.
-- It is an instance of the more general 'Data.List.genericLength',
-- the result type of which may be any kind of number.
length                  :: [a] -> Int
length l                =  len l 0#
  where
    len :: [a] -> Int# -> Int
    len []     a# = I# a#
    len (_:xs) a# = len xs (a# +# 1#)

搞不明白 Int# 是什么玩意,到处查,最后发现上面那个Slide里面有提示(自己太粗心了):

All those # marks are for “unboxed types”, which are faster but not asymptotically

然后再Google, 就可以找到 unboxed type的定义 。于是自己可以写一个和库里面源代码一模一样的 length:

{-# MagicHash #-}

import Data.List
import GHC.Prim
import GHC.Exts

length1                  :: [a] -> Int
length1 l                =  len l 0#
  where
    len :: [a] -> Int# -> Int
    len []     a# = I# a#
    len (_:xs) a# = len xs (a# +# 1#)

用GHCi测试了下,速度比上回的好,但还是不如内置的length,这是为什么?我猜大约是编译器优化吧。

]]>
Sun, 03 Nov 2013 00:00:00 +0800
https://blog.xiaofuxing.name/2013/10/27/zhinengkefu_more.html https://blog.xiaofuxing.name/2013/10/27/zhinengkefu_more.html <![CDATA[微信公众平台应用设想(高级智能客服)]]> 微信公众平台应用设想(高级智能客服)

前面 提到的是“简单的”“智能”客服, 说它简单,是因为两点:它只能够做对话,用户需要通过手动发送消息来操作;说它智能,如果后台足够强大,这种对话也可以干出很多花样的:例如实时天气查询,交通查询,多人文字游戏等等。本文实际上叫“设想”不太合适,因为这些东西是成功案例中已经存在的,我只不过重新组织了一下;但为了系列文章标题的一致性,就不改了,看官们将就一下吧。

继续阅读...

]]>
Sun, 27 Oct 2013 00:00:00 +0800
https://blog.xiaofuxing.name/2013/10/27/weixin_mediasource.html https://blog.xiaofuxing.name/2013/10/27/weixin_mediasource.html <![CDATA[微信公众平台应用设想(媒体信息源)]]> 微信公众平台应用设想(媒体信息源)

上文 给出了一个简单的微信公共平台应用,本文介绍另一种可能更简单的应用:媒体信息源。

继续阅读...

]]>
Sun, 27 Oct 2013 00:00:00 +0800
https://blog.xiaofuxing.name/2013/10/26/mpofweixin.html https://blog.xiaofuxing.name/2013/10/26/mpofweixin.html <![CDATA[微信公众平台的应用设想(智能客服)]]> 微信公众平台的应用设想(智能客服)

微信 是最近很流行的短信替代服务, 微信公众平台 是微信的第三方开发接口,我之前并不关注,一个朋友想用这个公共平台来替代之前的手机App(物联网数据采集),我应邀对其进行技术评估。本博客准备写一个系列,每篇文章一个应用,并给出3个问题的答案: 做什么?怎么做?技术难点在什么地方?本文给出一个最简单直接的应用:智能客服。

继续阅读...

]]>
Sat, 26 Oct 2013 00:00:00 +0800
https://blog.xiaofuxing.name/2013/10/26/rand.html https://blog.xiaofuxing.name/2013/10/26/rand.html <![CDATA[随机数生成器]]> 随机数生成器

我自己的课程作业,感觉写得还行,放到这里来,免得丢掉了。

摘要:本文对随机数生成器的不同使用环境及要求、类别及检验方法进行综述。
关键词:随机数生成,RNG,PRNG,TRNG,随机性检验

继续阅读...

]]>
Sat, 26 Oct 2013 00:00:00 +0800
https://blog.xiaofuxing.name/2013/10/23/lengthinhaskell.html https://blog.xiaofuxing.name/2013/10/23/lengthinhaskell.html <![CDATA[初学haskell]]> 初学haskell

最近学习 haskell ,在 Real world Haskell第三章 中有个作业,自己实现一个对list求长度的函数。这个问题还真不简单。

继续阅读...

]]>
Wed, 23 Oct 2013 00:00:00 +0800
https://blog.xiaofuxing.name/2013/10/09/strange_behavior.html https://blog.xiaofuxing.name/2013/10/09/strange_behavior.html <![CDATA[奇怪的现象]]> 奇怪的现象

环境 Embedded Linux 2.6.34, busybox 1.17.2 。

没有废话,直接拷贝一个Commit log。

this condition is something strange, let me demostrate:

#ifconfig eth0 192.168.3.10
#ifconfig eth1 192.168.3.11
#ifconfig eth0 down
#ping 192.168.3.10

you will find the last ping still success even if you run it from another machine. To overcome this, you need to do:

#ifconfig eth0 0.0.0.0

before putting it down.

]]>
Wed, 09 Oct 2013 00:00:00 +0800
https://blog.xiaofuxing.name/2013/10/09/baddesign.html https://blog.xiaofuxing.name/2013/10/09/baddesign.html <![CDATA[坏设计的例子:挑战认证]]> 坏设计的例子:挑战认证

一个合作公司,给出了一个挑战认证的设计,需要我在客户端实现。这个设计真是太烂了。给他们提建议了他们也不理,很不爽。这里写下这种设计,算是一个反面教材吧。

Client -> Server : Register(SerialNum) Server --> Client : RegisterOK(Challenge) Client -> Server : Authenticate( Hash(SerialNum || Challenge)) Server --> Client: AuthenticateOK

图1

继续阅读...

]]>
Wed, 09 Oct 2013 00:00:00 +0800
https://blog.xiaofuxing.name/2013/10/07/first.html https://blog.xiaofuxing.name/2013/10/07/first.html <![CDATA[开篇庆祝]]> 开篇庆祝

xiaofuxing.name 博客上线了。我,Pluswave, 小福星的父亲,用这个域名和博客名称,纪念8个月大的女儿刚刚起了这个小名。

]]>
Mon, 07 Oct 2013 00:00:00 +0800