身份认证概念原型发布,目前的反馈和我的思考

上文 讨论了基于任意加密货币的用户身份认证设计思路,目前我已经弄出了一个概念原型, 部署在 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)来认证自己;

  • 最大程度避免私钥泄露。

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

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行完全相同。

基于任意加密货币的用户身份认证设计思路

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

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

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

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

  1. 获取随机信息源:客户端发送获取随机信息请求,包含自己的公开身份信息,服务器收到请求后,首先从区块链上检查客户端发来的公开身份信息是否合法,如果合法则生成一个随机信息,回复给客户端,并将生成的随机信息和客户端的公开身份保存一小段时间。

  2. 验证客户端身份:客户端发送服务器给的随机信息,以及签名,服务器首先测试随机信息是否是自己在一小段时间内生成的,如果OK,则通过随机信息获取客户端的公开身份信息,然后验证签名,都通过后身份认证成功,否则身份认证失败。无论成功失败,删除随机信息源。

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

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

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

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

基于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. 同一个用户切换浏览器后,可以复用之前的转账过程,而仅仅做链外的密文交换就可以知道对方的身份了

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

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库来操作。

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

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代币)。而要做开发,可以先利用测试链的源代码调试测试,测试好了之后再切回主链操作,避免不必要的支出和损失。

比特股简介

什么是比特股(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教程部分。

区块链总览

区块链目前(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 则是商业的去中心化存储系统。

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

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问题