组内技术分享的内容,目前网上相关资料不少,但读起来都不太合本身的习惯,因而本身整理并编写一篇简洁并便于(本身)理解和分享的文章。
由于以前对密码学没有专门研究,本身的体会或理解会特别标注为“我的理解”,请注意甄别,若有必要能够自行查证。
阅读前须要树立一种观点:大部分场景都是基于几率的大小而言的,好比SHA256安全性、区块链不可更改性等。html
区块链的基础算法之一,在其中用于区块hash计算方法。算法
是SHA-2下的一个算法标准,而SHA-2全称安全散列算法2,即Secure Hash Algorithm 2,属于SHA算法之一,是SHA-1的后继者,一种密码散列函数算法标准,由美国国家安全局研发,由美国国家标准与技术研究院(NIST)在2001年发布。其下一共分六个不一样的算法标准:SHA-22四、SHA-25六、SHA-38四、SHA-5十二、SHA-512/22四、SHA-512/256。数据库
散列(hash)函数须要知足如下性质才是安全的:
1. 不能从散列结果获得原始信息,也就是密码学中的原像问题。
2. 不能出现两个不一样的原始信息,可是hash结果同样,即碰撞问题。
3. 最好由原信息的微小变更能让hash结果面目全非,即雪崩效应。安全
SHA-256的压缩函数知足Merkle-Damgard结构,此时能够保证压缩函数在知足散列函数安全性的前提下,在分组后作压缩时仍能保证。关于Merkle-Damgard结构,能够参考《密码学原理与实践》第四章及文末参考资料[2]。
// 全部变量均为无符号32位整型,计算时以2^32为模 // 1. 变量初始化 // 1.1 初始化变量第一部分:前8个质数(2, 3, 5 ... 19)的平方根的前32位 h0 := 0x6a09e667 h1 := 0xbb67ae85 h2 := 0x3c6ef372 h3 := 0xa54ff53a h4 := 0x510e527f h5 := 0x9b05688c h6 := 0x1f83d9ab h7 := 0x5be0cd19 // 1.2 初始化变量第二部分:前64个质数(2, 3, ..., 311)的立方根的前32位 k[0..63] := 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 // 2. 预处理 // 2.1 给原始消息m末尾增长(接续)一个二进制1 m0 = add1bit(m) // 2.2 给m0末尾增长连续k个0,直到其长度对 512取模后余数为448 // k >= 0 m1 = add0bitsWhenMod512Equals448(m0) // 2.3 给m1末尾增长64bit表示的m1的长度, 以大端表示 m2 = addLengthBigEndian(m1) // 3. 将m1拆分红l个以512bit为长度的块 sub_ms[l] = splitBy512bit(m1) // 3.1 子块处理 for(sub_m : sub_ms ) { // 对每一个512bit的子块,进一步拆成16个32bit的子块 w[0...15] = splitBy32bit(sub_m) for(i from 16 to 63) { // 计算w[16]到w[63] //rightrotate 循环右移x位, rightshift右移x位高位补0 s0 := (w[i-15] rightrotate 7) xor (w[i-15] rightrotate 18) xor(w[i-15] rightshift 3) s1 := (w[i-2] rightrotate 17) xor (w[i-2] rightrotate 19) xor(w[i-2] rightshift 10) w[i] := w[i-16] + s0 + w[i-7] + s1 } // 局部变量初始化 a := h0 b := h1 c := h2 d := h3 e := h4 f := h5 g := h6 h := h7 // 子块内主循环 for i from 0 to 63 { s0 := (a rightrotate 2) xor (a rightrotate 13) xor(a rightrotate 22) maj := (a and b) xor (a and c) xor(b and c) t2 := s0 + maj s1 := (e rightrotate 6) xor (e rightrotate 11) xor(e rightrotate 25) ch := (e and f) xor ((not e) and g) t1 := h + s1 + ch + k[i] + w[i] h := g g := f f := e e := d + t1 d := c c := b b := a a := t1 + t2 } // 子块结果更新到全局变量 h0 := h0 + a h1 := h1 + b h2 := h2 + c h3 := h3 + d h4 := h4 + e h5 := h5 + f h6 := h6 + g h7 := h7 + h } // 3.2 最终的摘要结果计算, append即位拼接 digest = hash = h0 append h1 append h2 append h3 append h4 append h5 append h6 append h7
去中心的分布式数据库。数据结构
伪码以下app
// 区块与前个区块相连。 /** * 区块 */ class Block { /** 区块头 */ BlockHead head; /** 区块体 */ BlockBody body; } /** * 区块头 */ class BlockHead { /** 生成时间 */ Date gmtGenerate; /** 本区块体的hash值 */ String bodyHashCode; /** 上个区块的hash值 */ String prevHashCode; /** 难度系数 */ int difficulty; /** 随机数, 32位*/ int nonce; } /** * 区块体 */ class BlockBody { /** 数据 */ byte[] data; }
区块的hash=SHA256(区块头),区块头包含上一个区块hash及本区块体的hash。所以,当前区块(不管区块头和区块体)或上一个区块发生变化,都会致使区块的hash变化。这个关系能够用公式表达为:分布式
blockHash = SHA256(block.header) = SHA256(prevBlockHash + block.body + other)
所以,修改一个区块会致使断链,必须连锁地修改后续全部区块。因为后面会提到计算hash很慢的缘由,几乎不可能,除非拥有全网51%计算能力——区块链所以得名。函数
*__我的理解__为何是全网51%计算的能力?即在全网中全部计算能力都在挖矿时,修改的速度超过建立的速度。假设当前全网只有1%的计算能力在挖矿,那么你只要这1%的51%——全网的0.51%足矣。区块链
全部节点都会建立新的区块。为了保证节点同步,区块添加的速度不能太快。同步的速度由发明者中本聪设置为平均10分钟1次,即1小时6个。控制速度的方式是,建立区块须要大量的计算才能得到当前块的hash,此后才能添加新的块。这个海量计算过程相似于从大量的沙子中获取有用的一粒金子,所以被比喻为“挖矿”。可是“金子”并不彻底是指这粒“合适的hash”,下文会提到。3d
只有符合要求的hash才被区块连接受,这个要求是:hash < 常量targetMax / 当前区块difficulty。
大部分计算的hash是不符合条件的。为了使hash发生变化,须要改动区块头。为了让区块头产生变化,中本聪在区块头增长了随机值Nonce。从0开始计算到2^32位找到Nonce具体值的过程,就是挖矿的过程,所以Nonce和对应的hash合起来才是金子,而计算得到的hash只是须要作进一步确认金子的原石。
存在全部值都不是符合要求的Nonce的状况,此时协议容许矿工改变区块体,从新计算。
固然也存在符合条件的hash值有多个的状况,为了下降这种状况的几率,能够调节difficulty值。
根据当前的建立速度,能够动态地更改difficulty的大小来调节,确保近似平均10分钟1次。随着运算能力提高,difficulty会愈来愈大,致使挖矿难度愈来愈高。
若是同时提交两个区块,二者链接了同一个区块,那么区块链采用哪个?
新节点老是采用最长的区块链;哪一个分支先达到6个新区块(“6次确认”),区块链就会采用那条链。6次确认只须要1个小时就会完成。可见,取决于哪条分支拥有更多的计算速度。
被丢弃的短链,相关计算是彻底没有价值的。在比特币中是这样,以太坊则会受到必定的补偿。
8年的持续运行证明了其可行性。可是代价是:10分钟同步一次;大量无心义计算。所以使用场景有限:
目前的应用:比特币;电子证书等。