https://baijiahao.baidu.com/s?id=1581755535769652543&wfr=spider&for=pc

这篇文章主要讲解比特币是什么?它的运行原理是什么?

比特币与其说是计算机学的成功,不如说是密码学的成功。在它的基本运行原理中,运用了太多密码学的经典知识。本文也是从私钥、公钥、Hash算法等密码学知识出发,深入浅出的全面剖析了比特币的运行方式。而像Json,Merkle Tree,Bloom过滤器等计算机学的知识就显得不那么关键了,在文中也就不过多介绍了,如果想了解也可以给我留言,一起交流。

写这篇文章时,花心思最多的地方就是文章的讲述逻辑。我一直认为技术文章从来就是不好读的,但如果有一个好的逻辑,有了“前因后果”、“深入浅出”,也许你读起来也就没那么抵触了。当然说起来简单,做起来难。由于比特币的运行原理非常的复杂,难以找到一条完美的讲述线索,现在你看到的文章,也是几经修改后的成果。希望它能帮你掌握比特币复杂的技术原理。

一、什么是比特币

比特币是一种在网络世界里的电子货币、虚拟货币。

而“电子”货币跟你手里的纸币的区别就是:它没有实体,它只是计算机世界里的一串数字。

可能你会说:没拿到手里的钱还是钱吗?我觉得“电子”货币根本不能用来买东西的~

但为什么不能呢?就因为它是计算机世界里的一串数字,就不能用来买东西了?

这很可能是每个人的直观感受:一串计算机里存储的数字,它没有任何价值啊,我们为什么要接受它,并把我的有价值的商品给对方?

可是想想你手里的钞票吧,它也只是一张普普通通的纸而已,一张纸的实际价值不比计算机里的数字高多少吧~那你为什么就相信这张纸了呢?

所以关键点在这里:一种货币能不能用来买东西,不是它自身的价值决定的,而是你身边的每一个人是否相信这种货币?如果大家都相信这种货币,不论这个货币是什么形态的,都可以用来买东西。

与“是否能信任电子货币”相对应的是,手里的纸币就值得人们信赖吗?一个国家的纸币是由这个国家央行发行并提供信用担保的,也就是由国家确保你手里的纸币一定能够花出去。但是如果国家耍无赖,你手里的纸币也会贬值,甚至一文不值。那些提着一塑料袋钞票才能买到一个鸡蛋的国家,是真实存在的。

那么为什么大家会相信比特币?我们在下一章节展开介绍。

延伸阅读1——石币之岛的故事之一:雅蒲岛是在太平洋西部加罗林群岛中的一个岛,也是密克罗尼西亚联邦(西太平洋的一个岛国)最西的一个州。上面的原著土著居民使用石头作为货币。1903年美国人类学家William Henry Furness在雅浦岛上住了几个月,并把土著人使用石头作为货币的情况记载在他的著作《石币之岛》中。据书中记载,德国政府1898年从西班牙殖民者手中买下这个岛屿,并要求几个部落的酋长组织修路。但修路对于土著居民完全没有意义,而德国殖民者许诺给予土著的德国马克(德国的货币)在土著人的眼里可能只能用来生火(因为那时土著们连擦屁股都用树叶),所以德国人的命令下达了多遍都无人理睬。后来德国政府研究了雅浦岛的文化习俗,突然找到了突破点:他们下令对违抗命令的部落征税,征税的方式是,派酷吏到每家每户,把他们最珍贵的石币上涂上黑十字标记,并声明这些石币已经归的过政府所有了。这个办法奏效了,所有的土著人都觉得政府抢劫了自己,为了使那些石头不被抢走,只得乖乖去替政府修路。故事的结尾,路修好了,德国政府就把那些标记抹去了,于是土著们的生活又恢复了自己“富有”的生活。

延伸阅读2——一个国家货币的沦陷:2015年6月,津巴布韦央行宣布采取“换币”行动,从6月15日起至9月30日内,175千万亿津巴布韦元可换5美元(对于2009年以前发行的津元,250万亿津元可兑换1美元)。从最早的比美元更值钱,到现在连计算器都按不过来,津元如何一步步成为人类货币史上的耻辱?津巴布韦是一个矿产资源丰富,土地肥沃的非洲南部国家,于1980年独立,曾经经济实力仅次于南非,曾被誉为“非洲面包篮”。津元最早比美元值钱,1980年独立的时候,津元与美元汇率为1:1.47。但在2000年时,津巴布韦政府推行了激进的土地改革政策,简单点说就是把白人农场主的土地没收,然后分配给自己的“黑人兄弟”,致使津巴布韦的农业、旅游业和采矿业一落千丈,经济逐渐濒于崩溃。2001年开始,津巴布韦政府财政上出现严重赤字,为了贴补这种入不敷出的局面,津政府开始大规模印发钞票,引发的通货膨胀令人触目惊心。2006年8月,津央行以1比1,000的兑换率用新元取代旧币。2008年5月,津央行发行1亿面值和2.5亿面值的新津元,时隔两周,5亿面值的新津元出现(大约值2.5美元),再一周不到,5亿、25亿和50亿新津元纸币发行。同年7月,津央行发行100亿面值的纸币。同年8月,政府从货币上勾掉了10个零,100亿新津元相当于1新新津元(新津元二代)。2009年1月,津央行发行100万亿面值新新津元。于是,一个在2001年,还可以以100比1兑换美元的货币,在十年不到的时间里,就变成要以10的20几次方比1兑换美元的垃圾。2009年4月,津政府宣布,新津元退出法定货币体系,以美元、南非兰特、博茨瓦纳普拉作为法定货币,以后的几年中,澳元、人民币、日元、印度卢比又加入到津国法定货币体系。(下图为面值100万亿的津巴布韦元)

二、为什么信赖比特币

1、你会相信什么样的货币?

一种货币,如何才能让大家都信赖它呢?

最重要的一点,当然就是这种货币是否可靠!人们对于一种货币是否“可靠”的最初认知就是:看得见摸得着。

回想一下你们的长辈,是不是有过“把钱存在银行不安全,压在箱底才最安全”的想法?这就是老一辈人对于“可靠”的理解。甚至后来,即便大家开始接受把钱存在银行中,但老一辈人仍然更倾向选择更像货币实物的存折,而非银行卡。他们感觉每月明细清清楚楚打在折子上会让人更加放心,而如果是银行卡,则只能到柜面让柜员帮你查到里面的内容(那时ATM还少的可怜,更没有什么网银、手机银行云云)。因此,即便手里已经没有钞票了,即便钞票已经变为了存折本上一行行密密麻麻的小字,也要拿在手里看在眼里最有安全感。这就是对于货币实物的依赖。

但是随着时代的发展,越来越多的人开始接受看不见的货币了。拿到钱先存到银行是最普遍的做法,而且存折在很多银行已经开始退出历史舞台。通过转款等非现金交易的方式在大额交易中已经非常普遍,现如今,用手机微信零钱包里的一串数字作为零钱进行支付的方式,大家也已经习以为常了。

为什么老一辈人觉得“看得见摸得着”更有安全感?因为在哪个年代,只有货币在我的手中,才能控制好两件事情:

1、我手中(收到)的货币数量是不可被更改;

2、只有我的授权才能支付;

看得见摸得着=我手中(收到)的货币数量不可被更改+只有我的授权才能支付

后来,人们开始逐渐信任银行,并把“看得见摸得着”的钱存入银行,这是因为银行具备保证“我手中(收到)的货币数量不可被更改+只有我的授权才能支付”的能力。银行通过建立庞大的IT系统,存储每个客户资金的数据,确保这些数据不被篡改,并通过技术手段确保资金在得到所有者的授权后才能支付。所以在银行的系统里,通常会有一个存储了所有储户账户余额的表单,我们把它叫做Ledger,即账本。就像下面这样:

Ledger告诉了我们,Alice有5.3元,Bob有100元……银行会投入大量的精力和物力去保证系统中这张表的准确性,确保每个人对应的金额不能随意的增减。他们建立自己的机房、搭建独立的网络环境、购买最先进的服务器、聘请最资深的专家。也正是因此,我们才相信银行能够保证Ledger的准确性,于是我们就把钱存到了银行。

后来,就像相信银行存储的Ledger一样,我们还同样相信政府存储的每个人的社保信息,相信支付宝里的余额信息,相信微信里的零钱信息……

渐渐的,我们的“身家性命”真的全部变成了这些机构服务器上存储的一个文件、一串数据。这些机构也在想尽办法让自己的数据更加的安全、系统更加健壮,比如:采用各种数据备份机制、采用分布式存储技术……但即便如此,也偶有灾难事件发生。

延伸阅读3——旧闻两则:(1)这是《经济学人》杂志比较著名的一个故事:2009年洪都拉斯警方冲进了Mariana Catalina Izaguirre家里并驱逐她离开她住了30多年的家。原因是来自当地政府房屋委员会的资料显示,该房屋属于另外一个人,而这个“房主”向法院申请驱逐令,最终 M.C.Lzaguirre女士被迫离开。而等到政府房屋委员会纠正了自己资料上的错误时,M.C.Lzaguirre女士的家已经被拆掉了。(2)2015年5月28日11时起,携程官网及APP无法使用,直至当天23时29分,才全面恢复正常。官方消息是由于删除了生产服务器上的执行代码导致。也有消息表示是由于数据库数据遭到恶意删除导致此次事件。

于是人们开始思考一个问题:是否可以有一种不依靠某一家机构的人力、物力、技术能力,而是借助整个互联网的力量,为每个人的财富数据提供保障的方案呢?

就这样,比特币出现了!

2、比特币去中心化的存储机制

与传统的存储方案不同,比特币采用了一种特殊的去中心化的账本存储方案。简单说就是:所有加入比特币网络的电脑上,都会存储一份这样的账本。

由于账本存储在网络的各个节点上,其中一个节点出现问题,还是可以从网络上的其他节点获取到正确数据的。可以对比一下传统中心式的存储体系与比特币去中心化的存储体系的区别。

由于这种去中心化存储账本的特点,在某一个节点更新账本数据的时候,就要通知其他节点一起修改账本记录。举一个例子,如果Alice给Bob转了5个比特币(BitCoin,也称做BTC),那么最初进行这笔转账处理的节点,就要把这个处理的情况传播给临近的其他节点,然后其他节点再传播给临近的节点……直到网络上所有的节点账本都被更新了。

3、比特币交易——如何实现“只有我的授权才能支付”

在这种去中心化的环境下,没有银行这种机构的统一管理,不需要建立服务器集群或聘请庞大的维护团队,所有比特币的交易业务都是基于预先设定好的程序或算法,在互联网上自发生长。

那么,在这样一种去中心化的环境中,如何能保证收款和支付的安全性,最终达到“我手中(收到)的货币数量不可被更改+只有我的授权才能支付”的目标呢?

首先,来看“只有我的授权才能支付”这一点,比特币是用“私钥”和“公钥”来实现的。

“公钥”“私钥”是现代密码学非对称性加密里面的概念,而非对称性加密又与对称性加密相对应。对称性加密中只有一个密钥,即用来加密又用来解密。比如以前战争年代常用一本密码字典(比如什么字对应哪本书中的第几页第几个字)把一句话加密为密文。这本密码字典就是密钥,可以看出密钥十分关键,如果密钥泄露,那拿到密钥的人就可以进行解密。这种加密方式的缺点显而易见,你要让对方解密你的密文,就要把这个密钥也给对方,这样就极大的增加了密钥泄露的概率。

这一情况,直到非对称加密出现后才得到改观!

非对称加密中,每个人都有两个密钥,一个“公钥”,一个“私钥”。“公钥”是公开的。而私钥只有自己手里才有。“公钥”、“私钥”的特点可以简单理解为:用一个人的“公钥”加密,只能用这个人的“私钥”解密;而用一个人的“私钥”加密,只能用这个人的“公钥”解密。

这样,就解决了传递“密钥”过程中,“密钥”泄露的问题。因为如果我想给你一个只有你能看的信息,因为我手里有你的“公钥”,而你的“私钥”只在你手里才有,因此我只要用你的“公钥”加密就行了。

比特币系统就是采用了这一加密方式。但你可能会好奇,比特币系统里的“私钥”、“公钥”机制是怎么实现的?

简单来说,比特币系统让每个参与交易的人,先随机产生一个字符串,作为自己的唯一的“私钥”。然后会通过“私钥”生成对应的唯一“公钥”。生成后,“公钥”在比特币网络上公开给每个人,而“私钥”你要自己藏好,不能让别人知道。由于“私钥”生成“公钥”的过程是不可逆的,因此别人即便拿到了你的“公钥”,也不可能知道你的“私钥”是什么。这样,我们很容易就能实现下面的功能:当别人用你的“公钥”锁定一个数据时,只有拥有“私钥”的人(也就是你)才可能解锁这个数据。

注:举个简单(但不实际)的例子,比如你随机生成两个质数(173、881),拼成一个6位“私钥”173881,我们生成“公钥”的规则就是将两个质数相乘173X881=152413。这时网络上的人都会拿到“公钥”152413。当某个人想让某个数据只能被你修改时,他可以在这段数据后面加上“公钥”152413,并声明,只有私钥左3位乘右3位等于公钥的人,才可以修改这个数据。这样也就只有你能修改这个数据了。如果,网络上有个黑客想不经你的同意篡改这个数据,即便黑客知道“私钥”生成“公钥”规则是两个质数相乘,他也不知道是哪两个质数,因此,他只能进行X乘Y的暴力破解找到“私钥”:001X001,001X002,001X003,002X001,002X002……。当质数很大时这个过程是比较艰难的。当然上面例子中所使用的规则太过简单,用计算机暴力破解是可以很快通过“公钥”找到“私钥”的。但是比特币系统使用的椭圆曲线算法从“私钥”生成“公钥”,现有技术手段很难破解。同时,实际的比特币系统中私钥解密过程也是很复杂的,是通过一种基于逆波兰表示法的堆栈执行语言实现的。如果有兴趣深入了解,你可以google一下或者翻阅《精通比特币》一书。

好了,明白了比特币系统中的“公钥”和“私钥”原理后,后面理解起来就会容易很多:

我们把参与转账的人变得多一些,假设有3个人,Charles先给Alice转了5个BTC,然后,Alice又把这5个BTC转给了Bob。

我们站在Alice的位置进行分析,Alice为何能给Bob转这5个BTC呢?因为Alice能够用自己的“私钥”解锁这5个BTC。而这5个BTC是如何锁定的呢?聪明如你一定想到了,Charles给Alice转帐时用Alice的“公钥”锁住了这5个BTC。

而Alice再给Bob转账时,又用Bob的“公钥”把这5个BTC给锁定了。只有当Bob要用这5个BTC时,才能用自己的“私钥”解锁并使用这5个BTC,其他人因为没有Bob的“私钥”,是不能动用这5个BTC的。

所以,比特币系统里很智慧的一点:就是把一个人的“公钥”当作这个人的收款账号(或收款地址)。这样当你在给别人转账时,你输入了别人的收款账号——也即别人的“公钥”,比特币系统会自动帮你把这笔款用那个人的“公钥”锁定。这样这笔钱就属于他了,将来他必须用他的私钥解锁,才能使用这笔钱。这样也就达到了“只有我的授权才能支付”这一点。

注:其实公钥到收款地址还需要做一些转换,本文为了写得简单易懂就忽略这些细节了,但是收款账号与公钥是有极强的相关性的。

神奇吧,钱并没有实际握在你的手里,只是存在于比特币的网络里,但它就是属于你!

延伸阅读4——触不到的货币:《货币的祸害》一书里提到这么一个故事:1932年,法兰西银行担心美国不再盯住金本位(按20.67美元换1盎司黄金的传统价格用美元兑换黄金),于是要求纽约联邦储备银行将它存在美国的大部分美元资产的转换成黄金。美国的黄金储备开始减少,法国的黄金储备开始增加,美元走软,法郎走强,并导致了1933年的银行业恐慌。但事实上黄金并没有留到法国,仍然在美联储的地下金库里,法兰西银行的美元兑换为黄金的形式是将美联储银行底下金库中美国抽屉中的金块搬到法国抽屉中。我们再来看看美国这个神秘的地下金库,从网上查到的资料看,纽约联储金库位于地下25米,比海平面低15米,比纽约地铁系统还低10米左右。金库建成近80年来,从未发生过劫持偷盗等案件。进入金库的正常通道直邮一个钢门,重达90吨,高度近3米,TNT也不能伤其分毫。在金库里,共有122个储藏间,最大的可以存放近11万块金砖,堆起来有3米高、3米宽、5米多长。整个金库存放的黄金约7000多吨,约占全球官方黄金储备2.9万吨的四分之一。在纽约联储金库里,交易只是换房间,因为很多国家的黄金储备存放在纽约联储地下金库中,这主要出于两点考量:一是交易的便捷性;二是运输造成的安全成本和经济成本。交易双方达成交易后,黄金只是从一个带有编号的房间搬到另一个编号的房间。在金库中有许多来自世界各地的金砖搬运工,每天的工作就是将金砖搬到车上运到指定的储藏室,再卸下来码放整齐。但是,没有人知道是金砖是从哪国搬到哪国,因为房间上只有编号。看到这里,你也许已经有个概念了,货币的概念一直在变迁,每个场景下都有不同的含义。在一些地方,例如使用石币的雅浦岛,再例如有着地下金库的美联储银行,货币只是一种记账方式,比特币等区块链货币也是如此。当Alice宣称她拥有1000枚比特币,不是说她的抽屉或某个保险箱里安安静静的躺着1000枚比特币,而是说比特币网络上有1000枚比特币是属于Alice的比特币地址(即用Alice的公钥加密过的),只有Alice有权动用。(下图为美国电影《纽约大劫案》[也译作:虎胆威龙3]中模拟出的地下金库场景)

看到这里,我想你应该不介意再趁热打铁多了解一些比特币交易数据结构方面的知识,因为这对你后面知识的理解会很有帮助:)

上面提到,Alice给Bob转账,既要用Alice的“私钥”解锁,又要通过Bob的“公钥”加密,因此,比特币交易的数据结构分为inputs和outputs两部分:

inputs主要是Alice(这笔转账的输入方,也即”发起方")的信息,主要包括:

ScriptSig——Alice的数字签名信息,即:Alice的“私钥”信息的Hash值,这个Hash值里包含了“私钥”的特征,是用来“解锁”用的。(如果你不明白Hash值是什么,不要紧,在这里并不关键,而且后文会详细介绍Hash值)

outputs主要是Bob(这笔转账的输出方,也即“接受方”)的信息,主要包括:

Amount——金额

scriptPubKey——用Bob“公钥”生成的一段锁定脚本,里面包含着Bob“公钥”的特征,将这笔金额锁定后,只有含有Bob“私钥”特征的签名信息才能够解锁。

你有没有觉得少点什么?对了,上面的数据结构里只有Alice的“私钥”信息,却没有Alice的“公钥”信息,如何能实现对Alice“私钥”与“公钥”的匹配,从而实现解锁呢?

原来,在inputs里,还要有一个交易序号(txn),这个txn代表Charles给Alice转5个BTC的那笔交易,因为在那笔交易里,Alice是接收方,交易的outputs里面有Alice的“公钥”地址。

当Alice使用比特币时,比特币系统会从Alice给Bob转账的交易中,找到Alice的ScriptSig(相当于是“私钥”),再通过这个交易里记载的上一个交易的txn,顺藤摸瓜找到上一个交易中Alice的scriptPubKey(相当于是“公钥”),然后进行“私钥”和“公钥”的验证,验证通过后,Alice才有权使用这5个比特币,Alice给Bob转账的交易才能成功。

讲到这里,你应该已经了解了比特币系统中,交易数据是什么样子。它们绝大多数都是下面这个样子的:

inputs

txn: 我要转出的比特币是从哪笔交易来的?

scriptSig: 用我的“私钥”生成的解锁脚本,以便解锁我要转出的比特币

outputs

Amount:我要转出的比特币金额多大?

scriptPubKey: 将这笔金额用收我钱的那个家伙的“公钥”锁定

而且你可以通过txn一直追溯下去,一直追溯到这笔比特币的源头:

到这里,又出了个新问题,如果Alice转给Bob的交易是5个BTC,那这笔交易的上一笔交易一定也必须是5个BTC吗?

比特币系统给了你一个很灵活的处理体系,在这个体系下,inputs可以是多笔:

例如下面这个例子,Bob收到了Alice的5个BTC,这5个BTC是由Charles给Alice的3个BTC和Fred给Alice的2个BTC组成的。当然Alice收到的这两笔转账交易一样有它们之前的交易,Fred给Alice的2个BTC是通过一笔之前的交易得到的(inputs里有一笔交易txn#772...),而Charles给Alice的3个BTC是通过两笔之前的交易得到的(inputs里有两笔交易txn#343...,txn#05a...)。

在真实的比特币网络中,你会看到更为复杂的交易结构,比如下面这笔交易,它是由6笔交易作为inputs。6笔交易总共的比特币金额是139.616 BTC。接下来,你会发现一个有趣事情,就是这笔交易的outputs对应多个“公钥”脚本(也就是多个收款地址):一个是收款方“公钥”信息,一个是你自己的“公钥”信息。收款方“公钥”信息的Amount是139.606 BTC,你自己“公钥”信息的Amount是0.01 BTC。它的意思是:前面汇款给我的6笔交易总共包含139.616 BTC,我只要支付其中的139.606 BTC给收款人,剩下的0.01 BTC用我自己的“公钥”信息锁定,也就相当于将剩下的“零钱”转回给我自己。

看到这里,你已经了解了比特币交易的主要特点了。比特币网络里充斥着这样数以千万记的比特币交易,交易间通过交易序号txn连接起来,通过这些由交易序号串联起来的路径,你可以了解你拿到的比特币从哪来的?要到哪里去?编织成一个错综复杂的支付路径。

3、比特币账本——如何实现“我手中(收到)的货币数量不可被更改”

还记得上一节中我们提到的账本Ledger吗?

保证“我手中(收到)的货币数量不可被更改”也意味着保证账本中我的账户余额不能被随意的篡改。这是怎么实现的呢?

其实,在比特币系统中,这张Ledger中没有每个人的余额,有的只是那几千万条交易:

那每个人的余额怎么来的呢?是的,就是你想到的最麻烦的办法,比特币系统按照每个人的收款地址(相当于每个人的“公钥”),将这个收款地址下所有的转入、转出额度加总,就得到了这个收款地址的余额了。比如:下面例子中,一个以13kjhfg开头的收款地址,他的余额就是这么一点一点累加出来的~

这样的账本下,你的余额是无法篡改的,或者说,如果你要篡改余额,那就必须伪造交易。但是从前面的文字,你应该也明白交易与交易之间的关系是多么的紧密,随便伪造一笔交易是十分困难的。下面的章节会进一步说明伪造、篡改交易的其它困难~

4、区块链上的交易为何无法伪造或篡改?

1)伪造最近的一笔交易

如果你想伪造最近的一笔交易,那么很不幸,几乎是不可能的。

还记得上文我们提到的比特币交易结构吗?每一笔比特币交易都能够追溯到它的上一笔交易。因此,下面的例子中,当Alice从其他3笔交易中分别收到1BTC、2BTC、2BTC后,将这3笔交易作为inputs,向George发起一笔5个BTC的转账时,比特币系统会先去往Alice收到BTC的3笔交易中,检查一下Alice是否已经用过这3笔交易中的BTC。只有这3笔交易中的BTC都未曾被支付过时,比特币系统才认为Alice向George的转账是合法的。

“检查Alice收到的BTC是否被支付过”在比特币系统中是很好实现的,只要先找一下哪些交易的outputs中含有Alice的地址(这些就是支付给Alice的交易),然后检查一下它们的交易序号txn是否出现在其他交易的inputs里(确认这些交易是否被支付过)。

但是在全网千万笔交易的环境下进行这个检查会很耗时,因此比特币系统会生成一个未被支付交易索引,随着每次新交易的产生,这个索引会不断更新:去掉被支付的交易,加入新的未被支付的交易,并随着新交易信息传递给网络上的每一个节点。所以,实际上检查“Alice收到的BTC是否被支付过”时,看一下inputs里的交易的txn是否在未被支付交易索引中即可~

另外,前文提到的比特币系统对于交易inputs和outputs的处理方式,也会保证inputs的金额之和与outputs的金额之和相等,从额保证每笔交易的金额不能随意篡改。

2)篡改历史交易

既然上面提到,每一笔交易与它前一笔交易是紧密相关的,因此新发生的交易等于被旧交易牵制着,也就不能随意篡改、伪造。那你可能有疑问了,那么我是否可以通过伪造或篡改历史交易,将整个交易链条上的交易全部篡改,从而改变你的交易行为以及你的账户余额?

答案是:不可能的。

要了解为什么“不可能”,我们需要先学习一个小知识——“Hash函数与Hash值”。

“Hash函数”的作用是将一大段文字内容,按照一定的规则输出为一个定长的摘要信息,这个摘要信息即“Hash值”。这个“Hash值”只与原来的文字有关,即一模一样的文字的“Hash值”是一样的,但只要文字稍做修改,“Hash值”就会变化。由文字生成Hash值的过程是不可逆的,也就是说我只能从文字得到Hash值,但是从Hash值是反推不出代表什么文字的。

如果你觉得不好理解可以想想一串数字求余数的过程,比如我们有个很大的数字:231451723794,拿它对3111117相除求余数,得到余数174579。我们就可以把174579看成231451723794的摘要。此外,如果这时改动231451723794中任意一个数字,这个得到的余数极大概率是不一样的,基本实现了数字与摘要的一对一关系。同时,如果先给你余数174579,你还原回231451723794是很难的。

当然,上面的求余数太简单了,每3111117个数字,余数就会重复一次。Hash算法采用了复杂得多的取余数算法,以尽量保证两个不同的文本输出的Hash值不一样,并尽量保证Hash值不能反推回原始信息。如果很不幸,两个不同的文本输出了同样的Hash值,这种情况就叫做“碰撞”。“碰撞”是我们不希望看到的情况,因此,Hash函数的算法也在不断的更新换代,MD4、MD5、SHA等等都是不同的Hash函数算法,随着算法越来越复杂,“碰撞”的概率越来越低,一次Hash运算处理的时间也越来越长。比特币采用的是SHA256的Hash算法。对于SHA256的Hash算法,我们来看一个Hash函数输出结果的实例:

可以看到,一句话仅仅多了一个句号,它的Hash值就大相径庭了。

比特币系统是如何运用Hash函数的呢?

比特币系统在进行交易数据存储的时候,会将交易分组打包存储(目前一个包的大小为1M,而一笔交易数据至少250字节,基本上每个包可以容纳近千条交易),这个包就是我们常说的区块(Block)。区块与区块之间,会通过每个区块的特征参数连接起来,即,每个区块都记录了前一个区块的特征参数,形成了一种链式的存储结构,“区块链”这个闻名遐迩的词语就是打这儿来的~

那这个将每个区块都串联起来的特征参数到底是什么呢?

聪明如你一定猜到了,这就是这个区块所包含“内容”的Hash值。

一般一个区块中包含哪些“内容”呢?一是上一个区块的Hash值,二是一堆交易,三是一个叫做Nonce的变量(Nonce做什么用的后面的章节会详细介绍,这里暂时留下一个悬念)。所以,一个区块的Hash值,可以简单理解为对上面三项“内容”求出的一个Hash值。

综上,生成一个区块的大致过程基本是这样的:

a、交易发生后进入计算机的内存,先进行一些基本验证(比如:这笔交易的input中引用的交易,是否是未被支付过的交易)。若验证不成功,则交易会被认为是invalid Transaction——无效交易。若验证成功后这些交易会被认为是Unconfirm Transaction——未确认交易,“未确认交易”会静静的躺在内存的有效交易池中等待被装入区块中。由于前面提到,比特币是一种全网记账的系统,因此这笔交易发生后,也会在全网广播,周边的计算机节点接到这笔交易后,也一样先放入内存,再进行验证,验证通过即等待被打包进区块。

b、比特币网络中负责发起记账动作的节点会从内存的有效交易池中,抽取近千笔Unconfirmed Transaction,然后进行打包。打包时,会将上一个区块的Hash值也加入包中。

c、然后对整个包求Hash值,这个Hash值就是这个区块的特征参数。这个特征参数很重要的,因为后面再生成新的区块时,还要用到它。

d、由于比特币是一种全网记账的系统,因此,当该节点生成新区块后,整个过程并没有结束,该节点接下来会发起一次全网记账。它会将新区块的数据广播给周边的节点,周边的节点再传递给周边的节点,直到全网都收到这个信息。当周边的节点收到这个信息后,也开始在本地进行一样的处理,即:将新区块数据记录到本地的电脑中,以确保本地的区块链数据更新为最新的数据。

看到这里,你会发现一个有趣的现象,比特币这种链式存储的结构,每一个区块的Hash值都是由上一个区块的Hash值决定的,因此,如果你要修改了历史上的一笔交易,那么这笔交易所在的区块数据的Hash值就会变化,那么引用这个Hash值的下一个区块的Hash值也要变化,影响一直随着区块链延伸下去。

现在,你应该明白修改历史交易的难度了。修改历史交易并不只是修改几百笔交易那么简单,修改历史交易,意味着该笔交易后面所有的数据记录全部出现不匹配的情况。如果你要调整后面所有的数据让他们匹配起来,这几乎是一项不可能的任务。

5、比特币系统运行机制的高阶知识——双重支付风险与防控机制

看完上面的内容,你已经基本对比特币和区块链的知识有了一定了解。下面的内容就是一些进阶知识了,有兴趣的读者可以继续往下看,有些烧脑但很有意思~

你可能会说,哇,比特币看似无懈可击嘛,无法伪造和篡改交易,余额是每笔交易累起来的,非常的安全。

但事实真的是这样吗?其实还有一种特殊的风险,我们慢慢来分析。

我们先来回顾一下一笔交易发生的过程:

比特币交易发生后,会在自己这个节点进行验证,并同时在全网广播,周边的节点接到广播后,也开始验证。一旦交易验证通过,则更新到各自内存的有效交易池中。

由于在全网广播的过程中,无法控制先到达哪个节点后到达哪个节点,因此,节点先接到哪笔交易,后接到哪笔交易完全是随机的。

比如,上面这个例子中,最右下角那个节点就先接到了后发起的交易。

这样,会存在这样一种情况:Alice给Bob地址转账的同时,又发起了一笔给自己地址的转账。

在交易进行广播的时候,一些节点收到了Alice给Bob地址转账的交易,另一些节点收到了Alice给自己地址转账的交易。于是,先收到Alice给Bob转账交易的节点,会验证通过Alice给Bob转账的交易(因为后面来的Alice给自己地址转账的交易,会因为inputs中的交易已被支付给Bob而被拒绝),并放入自己的有效交易池中。类似的,先接到Alice给自己转账交易的节点,会验证通过Alice给自己转账的交易,并放入自己的有效交易池中,如下图。

这时,网络上的节点分为两个阵营,即Bob阵营和Alice阵营。

现在我们进入下一步,组装区块。

Bob阵营的节点会组装出一个区块,接到整个区块链的最后;而Alice阵营的节点会组装出另外一个区块,接到整个区块链的最后。这样,比特币系统的区块链就分叉了,出现了两条链,以哪条链为准呢?

按照比特币系统的运行规则,要以最长的链为准。

这里,我们再举个直观一些的例子来说明:

最开始,比特币网络上相安无事,记录的都是一条统一的链条。

在某一时刻,加拿大的节点发现了一个红色区块,与此同时,澳大利亚的节点发现了一个绿色的区块,它们将新区块接在了链条的最末端,各自开始向周围节点进行广播,它们周围的节点逐步都收到了它们的信息。为了形象,我们把加拿大节点的广播路径显示为红色,把澳大利亚节点的广播路径显示为绿色,可以看到,它们在逐渐把蓝色变为自己的颜色。

最终,网络上出现了两个链条。一部分节点记录着包含红区块的链,一部分节点记录着包含绿区块的链。

这时,红色阵营的节点在不断的尝试在自己链条(即:末尾为红色区块的链条)结尾新增区块,而绿色阵营的节点也在不断的尝试在自己链条(即:末尾为绿色区块的链条)结尾新增区块。

突然,绿色阵营位于俄罗斯的节点发现了新的区块,即粉色区块,于是这个新节点开始了新一轮广播。

这次广播比较有趣,当广播到绿色阵营的节点,绿节点们很开心的在自己链条结尾加上了粉色区块,而当广播到红色阵营的节点,红节点们则立刻倒戈,遗弃掉自己的红区块,而是保留下更长的“绿色区块+粉色区块”的链条。

如果你的交易刚好在被遗弃的区块里,那你这笔交易将被重新扔回内存里!

看到这里你是不是开始有一些担心了?回到Alice给Bob转账买东西的场景。Alice给Bob转账的交易被记录在区块链中后,Bob给Alice寄出了商品。但就在此时,Alice生成了一条更长的包含Alice给自己转账交易的区块链,这时,那条包含Alice给Bob转账交易的区块链就会被更长的链所取代。

而Alice给Bob转账的那笔交易,回到内存中后也进不了有效交易池。它会被认为是一笔invalid Transaction,而被遗弃掉。因为,在验证时会发现,这笔交易inputs里的那笔交易并不是一笔未支付的交易,因为它已被支付给Alice自己。结局是,Bob寄出了物品,却没有收到钱~

这就是比特币里常常提到的双重支付风险(也叫“双花”风险)。

为了解决这个问题,比特币系统就要想出一种办法,让Alice不能轻易的制造出更长的链条。

这种办法就是:让所有的节点计算一道很难的谜题,只有找到答案的节点才有权组装新的区块,并发起一次记账动作。

那么这道谜题是什么呢?就是让新区块的Hash值小于一个目标值。很难理解吧?来听我慢慢解释。

还记得前面章节提到,每一个区块都会有一个Hash值吗?而这个Hash值,是通过区块中的三项内容计算出的:一是上一个的Hash值,二是一堆交易,三是一个叫做Nonce的变量。(讲到这里,Nonce这个变量终于要闪亮登场了!)

每个节点在组包时,节点会不断的调整Nonce的大小,使得由“1.上一个区块的Hash值,2.一堆交易,3.Nonce变量”这三项内容组成的文本串发生变化,从而使其对应的Hash值也发生变化~

重点来了,Hash值在不断的变化,而只有当某次计算出的Hash值小于目标值的时候,这个节点才能宣布自己解出了谜题,并可以发起一次全网记账动作!而记账的内容呢?就是以“上一区块的Hash值、一堆交易、目前的Nonce变量值”等信息组成的一个新区块。

举个例子~

一串文本“I am Satoshi Nakamoto”(我是中本聪)如果后面加上一个不断变化的nonce,那它的Hash值也会不断的变化:

假设我们要找到的目标值是以0开头的(即小于10000000....)Hash值,我们发现,当nonce为13时即满足了条件。而比特币系统中也是这么做的:

在上面的例子中,我们会发现很快就找到了答案,这是因为题目太简单了。如何加大难度呢?找一个00开头,或者找一个000开头的Hash值肯定更具挑战性。而且前面的0越多,谜题也就越难。

在真正的比特币系统中,目标值通常是一个由多个0开头的16位数字,这就导致每个节点要进行数以亿计次计算,才可以找到满足条件的Hash。同时,比特币系统还会调整目标值,以达到控制谜题难度的目的。

其实特别好理解的比喻就是扔骰子,你可以看成是节点们在不断的扔2个骰子,当扔到小于目标值的数字时就能成功拿到组包记账的权利。而目标值一开始是12,于是很简单,只要节点们不扔到两个6就满足条件。这时比特币系统觉得大家玩的太嗨了,要提高一下难度,于是把目标值调整成3了,于是节点们都哭了,因为只有扔到两个1才能满足条件~

为什么要控制谜题的难度呢?因为不断有新的节点加入到比特币网络中,解题的节点越多,就越可能在短时间内算出满足条件的Hash值。于是,比特币系统就要调整谜题难度,以确保基本每10分钟才能解出一次谜题。

这种通过解谜题来确立节点组包记账权的方式,有一个在区块链界响当当的名字——工作量证明Proof of Work(PoW)。

所以,在上面的情况下,Alice是不可能迅速形成一条更长的链条的。她如果想实现双重支付,就必须与其它节点赛跑。但是她是不可能赢过其它节点的,因为她只有一个节点~

你可能会说,如果Alice有一台运算速度很快的电脑呢?或者,有1百台、成千上万台强大的电脑呢?那她也没有取胜的可能,因为她对抗的是全世界所有的节点~

很明显,只有当Alice控制了全世界多一半的节点时(如控制了全世界51%的节点),她制造出更长的链条的可能性才会更大~这就是一些比特币相关文章中,经常提到的“51%攻击”。

6、比特币系统运行机制的其它知识

其实讲到这里,比特币系统运行的绝大部分知识就已经介绍完了。下面你看到的一些名词或短语,是你在看比特币文章时可能遇到的,所以也在这里逐一解释一下。

1)节点

看完上面的文字,相信你对于比特币系统全网记账的机制已经有了比较深刻的印象了。在全网记账的过程中,每个节点在里面起到的作用就至关重要。上面提到了节点与节点间会进行交易广播、会进行区块链信息的广播,节点还可以进行记账。其实真实比特币网络中,不同的节点会有不同的功能,下面把比较重要的几种节点简单介绍一下:

a.全节点:这种节点会将历史上所有的区块数据(包含所有交易)都下载下来,因此,这种节点可以独立的进行比特币地址的余额验证、交易有效性验证、历史交易验证等工作。由于全节点需要保留比特币网络上所有的交易数据,因此它会根据网络上广播的新区块信息,不断的新增最新的数据,保证区块链数据处于最新的状态。由于上述原因,全节点对比特币交易的验证是最安全的。我自己安装的就是这种节点类型的客户端。但是这种节点的缺点也很明显,由于需要下载历史上所有交易数据,这种节点显得特别笨重。根据我自己的经验,2013年时全节点大小在几十个G,最近(2017.8)全节点大小已经达到130G左右。

b.SPV节点:由于移动设备的飞速发展,在手机、Pad等便携设备上进行比特币交易的需求越来越旺盛。显然在存储空间有限的便携设备上,无法安装“全节点”。因此比特币系统支持一种轻量级的节点客户端。这种客户端只会下载区块的关键数据,比如区块的Hash值,Nonce数值等数据~通过这些数据就可以知道区块链概况。这些关键数据只有区块全量数据的1/1000,因此客户端会显得很轻便。但SPV节点的问题是,在进行交易验证时,必须通过网络从全节点处获取验证所需的信息,才可以进行验证。如果你身边有黑客建立的伪节点(如受到Sybil攻击),可能会干扰你的验证过程。因此,要保证万无一失的安全性,最可靠的方法还是建立一个“全节点”。

c.矿工节点:前面讲到,部分节点要通过大量、不停歇的计算,去争取组装区块及发起全网记账的权利(上面提到过,这个过程叫工作量证明PoW)。这样的工作不是每个节点都要做的,而只有矿工节点才会去做。为什么叫做“矿工”节点呢?因为每次这类节点算出谜题并争取到组包记账的权利时,比特币系统会给这个节点奖励一定数量的比特币。这个过程非常像是一个矿工在很费力的挖矿,奖励的比特币就是这个矿工挖到的矿。这也是为什么,有的文章说“挖到1个block,就可以得到XXX个比特币”,其实它的实际意思是:矿工节点通过不停运算,争取到组装1个新block并发起全网记账的权利后,可以得到XXX个比特币的奖励。讲到这里,你也该明白:其实只要控制了51%的矿工节点就可能发起51%攻击。

注:可能你会好奇,奖励的比特币怎么打入矿工的账户的呢?其实简单来讲,奖励的比特币也是一笔交易,但这笔特殊的交易没有inputs,只有outputs。outputs里记录的就是矿工节点上登记的公钥地址~所以挖出的比特币只有矿工节点所有者用自己的私钥才可以解锁,这样就实现了对矿工的奖励。

上面介绍的是最典型的几类节点,在比特币网络上还有矿池节点等其它一些节点,就不做过多介绍了。当然上面几类节点的功能完全可以搭建在同一个节点上,这取决于节点搭建者具体想用节点来做什么。

2)比特币的总量

按照比特币系统的设计原理,比特币的发行总数是个定值——2100万。而比特币的发行,本质就是对“挖矿”的矿工给予的奖励资金。因此,“挖矿”的奖励金额是递减的,每四年会减半一次,会逐渐趋近于0。

也是因此,比特币是一种不能随意增发的货币。

3)比特币的货币紧缩

货币紧缩不仅体现在上面提到的不能随意增发。想象一下这样的情景,Alice的拥有5个比特币,但有一天,她的电脑完蛋了,硬盘上的数据全部丢失,包括她的私钥,而且她没有备份她的私钥。这意味着什么呢?她的5个比特币将永远无法使用,因为用公钥倒推不出私钥,而且其它人也没有她的私钥。这5个比特币将永远“死”去。

随着时间的推移,像Alice一样的人会越来越多,“死”去的比特币也会越来越多,所以这也将加剧比特币的紧缩。

4)随机生成的私钥

前面讲到,每个用户的私钥是由比特币钱包随机生成的。可能有人会有疑问,这样随机生成的私钥安不安全,会不会我随机生成私钥跟别人的私钥恰好重复了?这个你不用过于担心,因为,比特币私钥是一串很长的文本,理论上比特币私钥的总数是:

这是个什么概念?有人估计过地球上的沙子是7.5乘以10的18次方,然后你想象一下,每粒沙子又是一个地球,这时的沙子总数是56.25乘以10的36次方,仍然远小于比特币私钥的总数。所以在这样一个庞大的地址空间下,私钥重复的概率微乎其微。

5)六个区块的证明

还记得上面提到的,某些特殊的情况下区块链可能分叉,这些情况可能是恶意的双重支付攻击;也有可能是碰巧在同一时点,地球上两个矿工节点同时计算出了小于目标值的Hash值,并同时发起了组包记账(这种情况发生的概率极低,但确实曾经发生过)。分叉时,比特币系统会自动选择最长的链条,抛弃短的链条。在这种机制下,越新生成的区块约有可能被抛弃,越早生成的区块越稳定、越安全。

所以一般在比特币网络上,会有一条不成文的约定,就是只有你的交易达成并被装入区块后,后面又生成了5个新区块后(加上包含你交易的区块总共6个区块),你的交易才是基本安全的。以每10分钟生成一个区块的时间来计算,也就是你在交易被确认后1个小时左右才能真正确认你的交易是可靠的。

三、总结比特币系统是如何运行的

最后,我们来把比特币系统的运行方式串一遍,也算是一个总结。

1、首先,每个比特币的用户会通过比特币客户端生成一个私钥,并通过私钥生成公钥(公钥与收款地址相关性极强,可以简单理解为收款地址就是公钥),这等于就是这个用户的户头~别人向这个人转账必须输入这个收款地址。

2、用户Alice向Bob转账时,输入了Bob的收款地址,也即是用Bob的公钥将这笔比特币锁定了,未来只有Bob才能使用这笔比特币。

而用户Alice为什么能使用这笔比特币呢?因为,比特币系统可以追溯到上一笔Charles给Alice转账的交易,在那笔交易里,用户Charles用Alice的转账地址,将比特币锁定,因此只有Alice使用其私钥解锁,才能够使用这笔钱。

所以比特币的绝大部分交易都是由inputs(来自),outputs(目标)组成。inputs用来追溯上一笔交易,以便明确转出者是否有权动用这笔钱,outputs用来进行一次新的加密,加密后只有收款者才能解密并动用这笔钱。

3、交易发生后,将广播全网。很短的时间内,全网所有的节点会接到这笔交易。接到这笔交易后,每个节点会先把交易放入内存,然后对交易进行合法性检验,检验通过后,这笔交易进入有效交易池,等待被装入区块。

4、与此同时,网络上面所有的矿工节点,正在疯狂的计算着谜题。谜题的解题方式就是:将有效交易池里的近千笔交易(TX0,TX1,TX2...),上一个区块的Hash值,Nonce参数组合成一个文本,然后计算这个文本的Hash值。通过Nonce的不断变化,计算出的Hash值也会不断变化。

直到当某一个节点成功计算出小于目标值的Hash值,这个节点就解答出了谜题,并有权将计算Hash值所使用的信息组装成一个新区块,记录在自己的硬盘上,并发起一次全网记账。

周围的节点在收到广播的消息后,也都记录下这个新区块。

5、由于矿工节点每次都会使用含有上一个区块Hash值的文本来计算当前区块的Hash值,因此,每一个区块都有上一区块的基因,这使得区块们串成了一个牢不可破的链条。

如果篡改某一区块中的某一交易,那么其后所有的区块数据都无法匹配了。也就形成了区块链不可篡改的特点。

6、比特币系统就这样周而复始的更新着自己的区块链条,不断的进行全网记账,不断的运行下去。

结语:

我从2011年开始接触比特币,还记得2013年自己买了一块儿板卡,注册到矿池挖矿的情景(当然好景不长,几个月板卡就烧掉了~)。后来逐渐专注于比特币技术原理。很早就关注这个话题,很早就希望在这里把自己的所学所想与大家一起分享,但工作繁忙一直没有时间起笔,直到6月份抽出时间开始写起这篇文章。本来以为很快就能写完,结果断断续续写了两个月。一些概念之前觉得挺明白的,但真要写出来又模糊了,于是又翻了几遍《精通比特币》,巩固了一下自己的知识。所以写这篇文章对我来说也是收获满满。

这篇文章个人认为还是比较容易读懂的,但是篇幅有些长,所以如果你没有耐心一次看完,可以收藏起来慢慢研究。个人认为文章中已经把比特币的一些重要的概念都讲到了,而且也算讲的比较深入。文章中的很多配图来自于一个叫做《How Bitcoin Works Under the Hood》的视频(有一些配图是我结合这个视频的截图及网络图片自己制作出的动画,希望您能喜欢)。这个视频我认为也是快速、全面了解比特币的一个很好的读物,如果您感兴趣也可以看看。在文章中,我根据自己的理解将视频的一些讲解顺序做了调整,并以自己的叙述方式讲解出来,以方便大家更好的理解“比特币系统是如何运行的”。最后,感谢大家阅读了这么长的一篇文章~

比特币运行原理[z]的更多相关文章

  1. Linux X Window System运行原理和启动过程

    本文主要说明X Window System的基本运行原理,其启动过程,及常见的跨网络运行X Window System. 一) 基本运行原理 X Window System采用C/S结构,但和我们常见 ...

  2. Jmeter结构体系及运行原理

    Jmeter结构体系 把Jmeter的结构体系拆分为三维空间,如图: X1~X5:是负载模拟的一个过程,使用这些组件来完成负载的模拟: X1:选择协议,模拟用户请求,检查服务器响应是否正确,然后收集结 ...

  3. Flink 集群运行原理兼部署及Yarn运行模式深入剖析

    1 Flink的前世今生(生态很重要) 原文:https://blog.csdn.net/shenshouniu/article/details/84439459 很多人可能都是在 2015 年才听到 ...

  4. Android开发学习笔记(二)——编译和运行原理(1)

    http://www.cnblogs.com/Pickuper/archive/2011/06/14/2078969.html 接着上一篇的内容,继续从全局了解Android.在清楚了Android的 ...

  5. Shell基础快速入门 了解shell运行原理

    Shell简介 Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁.Shell 既是一种命令语言,又是一种程序设计语言. Shell 是指一种应用程序,这个应用程序提供了一个界 ...

  6. iis6.0与asp.net的运行原理

    这几天上网翻阅了不少前辈们的关于iis和asp.net运行原理的博客,学的有点零零散散,花了好长时间做了一个小结(虽然文字不多,但也花了不少时间呢),鄙人不才,难免有理解不道的地方,还望前辈们不吝赐教 ...

  7. ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件)

    ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件) Startup Class 1.Startup Constructor(构造函数) 2.Configure ...

  8. ASP.NET Core 运行原理剖析1:初始化WebApp模版并运行

    ASP.NET Core 运行原理剖析1:初始化WebApp模版并运行 核心框架 ASP.NET Core APP 创建与运行 总结 之前两篇文章简析.NET Core 以及与 .NET Framew ...

  9. 场景9 深入RAC运行原理

    场景9 深入RAC运行原理 OPS(Oracle Parallel Server)通过磁盘的节点判定数据是否最新   —>   Data Guard   —>    RAC(Real Ap ...

随机推荐

  1. [Linux]实际操作中命令 su 与 sudo 的区别

    ------------------------------------------------------------------------------------------------ 首先我 ...

  2. GIS案例学习笔记-水文分析河网提取地理建模

    GIS案例学习笔记-水文分析河网提取地理建模 联系方式:谢老师,135-4855-4328,xiexiaokui#qq.com 目的:针对数字高程模型,通过水文分析,提取河网 操作时间:25分钟 数据 ...

  3. mysql 数据导出

    windowos: select * from pj_zzspdz_fpmx  order by kprq desc LIMIT 0,1000000 into outfile 'd:\fpmx.xls ...

  4. 对于低版本IE,ajax的设置处理

    !(function() {     var timeout = 16000;     //设置ajax请求的setting配置----start     jQuery.support.cors = ...

  5. php优化-》常用到的部分优化

    1.循环内部尽可能不要声明变量: 2.在可以用PHP内部字符串操作函数的情况下,尽量不要用正则表达式: 3.foreach效率更高,尽量用foreach代替while和for循环: 4.用单引号替代双 ...

  6. shiro 基于springmvc中做登陆功能

    1.添加依赖 <!-- shiro --> <dependency> <groupId>org.apache.shiro</groupId> <a ...

  7. git flow分支管理

    阅读目录 两种核心分支 三种临时分支 Git Flow流程示例代码 Git Flow工具 分支命名规范 总结 git flow是Vincent Driessen提出了一个分支管理的策略,非常值得借鉴. ...

  8. Ambient Light

    [Ambient Light] Ambient light is light that is present all around the scene and doesn’t come from an ...

  9. 给tbody加垂直滚动条的具体思路

    [给tbody加垂直滚动条的具体思路] 给tbody加垂直滚动条的思路就是把tbody设置成display:block,然后就对其高度设置一个固定值,overflow设置成auto即可 参考:http ...

  10. tomcat实现https

    第一步:生成key文件: C:\>keytool -genkey -alias tomcat -keyalg RSA -keystore C:\tomcat.key 密码最好设置默认change ...