在上一篇文章中,我们构建了一个非常简单的数据结构,这是区块链数据库的本质。 而且我们可以用它们之间的链接向它添加区块:每个区块与前一个链接。 唉,然而在现实中添加一个区块添加到链是需要高成本的工作。
区块链的一个关键思想是,必须通过工作证明才能将数据放入其中。这是一个艰巨的工作,使块链安全和一致。此外,这笔辛苦的工作也得到了奖励(这是人们获得采矿硬币的方式)。
这种机制与现实生活中的机制非常相似:人们必须工作获酬劳励并维持生命。在网络中,网络的一些参与者(矿工)努力维持网络,为其添加新的块,并为他们的工作获得奖励。作为其工作的结果,块以安全的方式并入到块链中,这保持了整个块链数据库的稳定性。值得注意的是,完成工作的人必须证明这一点。
这个整体“努力工作和证明工作价值”机制被称为工作证明。这很难因为它需要很多的计算能力:即使是高性能的计算机也不能很快的完成。此外,这项工作的难度不时增加,以保持新的块率每小时大约6个块。在比特币,这样的工作的目标是找到一个块的哈希,满足一些要求。这是散列,作为证明。因此,找到证据是实际工作。
最后要注意的事情。工作证明算法必须满足要求:工作不易,证明容易。证明通常交给非工作者,所以对他们来说,验证它不应该花太多的时间。
在本文中,我们将讨论哈希算法。 如果你熟悉这个概念,你可以跳过这个部分。
哈希是获取指定数据的哈希值的过程。 哈希值是对其计算的数据的唯一表示。 哈希函数是一个获取任意大小的数据并产生固定大小的哈希的函数。 以下是哈希的一些主要功能:
Hashing functions are widely used to check the consistency of data. Some software providers publish checksums in addition to a software package. After downloading a file you can feed it to a hashing function and compare produced hash with the one provided by the software developer.
In blockchain, hashing is used to guarantee the consistency of a block. The input data for a hashing algorithm contains the hash of the previous block, thus making it impossible (or, at least, quite difficult) to modify a block in the chain: one has to recalculate its hash and hashes of all the blocks after it.
哈希函数被广泛用于检查数据的一致性。在区块链中,使用哈希来保证块的一致性。 哈希算法的输入数据包含前一个块的哈希值,从而使得已经生成的链难以修改之前产生的区块(或至少相当困难):篡改一个区块必须重新计算其前的所有块的哈希值。
比特币使用Hashcash,哈希现金的发明最初是为防止电子邮件垃圾邮件而开发的。它可以分为以下几个步骤:
数据+计数器
组合的哈希值。因此,这是一个强力brute force算法:
1. 计算一个新的哈希2. 检查该哈希值3. 增加计数器
现在让我们看看一个哈希必须满足的要求。在原来的Hashcash实现中“哈希的前20位必须是零”。然而在比特币中,哈希要求是不时进行调整的,因为尽管计算能力随着时间的推移而增加,越来越多的矿工加入网络,因此设计必须每10分钟生成一个块。
程序员小提醒:go和python都是不用加分号的语言
好的,我们完成了理论,让我们编写代码! 首先,我们来定义挖掘难度:
const targetBits = 24
在比特币中,“目标位(target bit)”是存储块被挖掘的困难的头部数据。 我们现在不会实现目标调整算法,所以我们可以将难度定义为全局常数。
24是一个任意数字,我们的目标是在内存中占用少于256位的目标。 而且我们希望差异足够大,但不要太大,因为差异越大,找到合适的哈希越难。
// 工作证明type ProofOfWork struct { block *Block target *big.Int //定义目标位}// 新的工作证明func NewProofOfWork(b *Block) *ProofOfWork { target := big.NewInt(1) target.Lsh(target, uint(256-targetBits)) //用于随机产生target,目标数值!!!这里从数学上保证了 // Lsh: local sensitivity hashing //左移256个 target bits位 pow := &ProofOfWork{b, target} return pow}
这里创建工作证明结构中保存指向区块的指针的和指向target的指针。 “target”是上一段所述要求的另一个名称。 我们使用一个大整数,因为我们将哈希与目标进行比较:我们将哈希转换为一个大整数,并检查它是否小于target。
在新的工作证明的函数中,我们初始化一个值为1的big.Int,并将其左移256个 - targetBits位。 256是SHA-256哈希的长度,以比特为单位,它是我们要使用的SHA-256散列算法。 目标的十六进制表示为:
0x10000000000000000000000000000000000000000000000000000000000
它在内存中占用29个字节。 这是与以前的例子中的哈希的比较:
0fac49161af82ed938add1d8725835cc123a1a87b1b196488360e58d4bfb51e300000100000000000000000000000000000000000000000000000000000000000000008b0f41ec78bab747864db66bcb9fb89920ee75f43fdaaeb5544f7f76ca
第一个哈希(以“我喜欢甜甜圈”计算)大于目标,因此它不是有效的工作证明。 第二个哈希(以“我喜欢甜甜圈ca07ca”计算)小于目标,因此这是一个有效的证明。
您可以将目标视为范围的上限:如果数字(哈希)低于边界,则它是有效的,反之亦然。 降低边界将导致有效数量减少,因此找到有效数量所需的工作更加困难。
//准备数据,加入targetBits和noncefunc (pow *ProofOfWork) prepareData(nonce int) []byte { data := bytes.Join( [][]byte{ pow.block.PrevBlockHash, pow.block.Data, IntToHex(pow.block.Timestamp), IntToHex(int64(targetBits)), IntToHex(int64(nonce)), }, []byte{}, ) return data}
func (pow *ProofOfWork) Run() (int, []byte) { var hashInt big.Int var hash [32]byte nonce := 0 fmt.Printf("Mining the block containing /"%s/"/n", pow.block.Data) // nounce 是counter for nonce < maxNonce { // maxNounce被设置成math.MaxInt64,防止溢出 data := pow.prepareData(nonce) // 1. prepare data hash = sha256.Sum256(data) // 2. sha256 hash:https://golang.org/pkg/crypto/sha256/#Sum256 fmt.Printf("/r%x", hash) hashInt.SetBytes(hash[:]) // 3. 从hexidecimal 转换成 big INT //执行这个for loop直到找到hashInt和target相等 if hashInt.Cmp(pow.target) == -1 { // 4. Compare integer with the target break } else { nonce++ } } fmt.Print("/n/n") return nonce, hash[:]}
NewBlock()
加入nounce和工作证明移除SetHash,并更改NewBlock:
func NewBlock(data string, prevBlockHash []byte) *Block { block := &Block{time.Now().Unix(), []byte(data), prevBlockHash, []byte{}, 0} // 工作证明 pow := NewProofOfWork(block) nonce, hash := pow.Run() block.Hash = hash[:] block.Nonce = nonce return block}
nonce
被加入到Block结构中
type Block struct { Timestamp int64 Data []byte PrevBlockHash []byte Hash []byte Nonce int // 用于验证}
func (pow *ProofOfWork) Validate() bool { var hashInt big.Int data := pow.prepareData(pow.block.Nonce) // 验证 hash := sha256.Sum256(data) hashInt.SetBytes(hash[:]) isValid := hashInt.Cmp(pow.target) == -1 //检查产生的Big Int hashInt是否和target相当 return isValid}
func main() { ... for _, block := range bc.blocks { ... pow := NewProofOfWork(block) fmt.Printf("PoW: %s/n", strconv.FormatBool(pow.Validate())) //验证工作证明 fmt.Println() }}
Output:
...Prev. hash:Data: Genesis BlockHash: 00000093253acb814afb942e652a84a8f245069a67b5eaa709df8ac612075038PoW: truePrev. hash: 00000093253acb814afb942e652a84a8f245069a67b5eaa709df8ac612075038Data: Send 1 BTC to IvanHash: 0000003eeb3743ee42020e4a15262fd110a72823d804ce8e49643b5fd9d1062bPoW: truePrev. hash: 0000003eeb3743ee42020e4a15262fd110a72823d804ce8e49643b5fd9d1062bData: Send 2 more BTC to IvanHash: 000000e42afddf57a3daa11b43b2e0923f23e894f96d1f24bfd9b8d2d494c57aPoW: true
我们的块链是一个更接近其实际架构的一步:添加块现在需要工作证明,因此mining是可能的。但是它仍然缺乏一些关键的特征:块链数据库不是持久性数据,没有钱包,地址,交易,没有共识机制。所有这些我们将在以后的文章中实现。
persistence refers to the characteristic of state that outlives the process that created it. This is achieved in practice by storing the state as data in computer data storage .
Links:
不同branches中保存着各个阶段的代码
新闻热点
疑难解答