使用 Go 语言打造区块链(二)

关于 第一篇: 参见笑来老师翻译的版本:
lixiaolai.com/2017/09/28/building-blockchain-in-go-part-1/

  1. 介绍
  2. 工作证明
  3. 哈希算法
  4. 如何实施

1. 介绍

在上一篇文章中,我们构建了一个非常简单的数据结构,这是区块链数据库的本质。 而且我们可以用它们之间的链接向它添加区块:每个区块与前一个链接。 唉,然而在现实中添加一个区块添加到链是需要高成本的工作。

2. 工作证明

区块链的一个关键思想是,必须通过工作证明才能将数据放入其中。这是一个艰巨的工作,使块链安全和一致。此外,这笔辛苦的工作也得到了奖励(这是人们获得采矿硬币的方式)。

这种机制与现实生活中的机制非常相似:人们必须工作获酬劳励并维持生命。在网络中,网络的一些参与者(矿工)努力维持网络,为其添加新的块,并为他们的工作获得奖励。作为其工作的结果,块以安全的方式并入到块链中,这保持了整个块链数据库的稳定性。值得注意的是,完成工作的人必须证明这一点。

这个整体“努力工作和证明工作价值”机制被称为工作证明。这很难因为它需要很多的计算能力:即使是高性能的计算机也不能很快的完成。此外,这项工作的难度不时增加,以保持新的块率每小时大约6个块。在比特币,这样的工作的目标是找到一个块的哈希,满足一些要求。这是散列,作为证明。因此,找到证据是实际工作。

最后要注意的事情。工作证明算法必须满足要求:工作不易,证明容易。证明通常交给非工作者,所以对他们来说,验证它不应该花太多的时间。

3. 哈希算法

在本文中,我们将讨论哈希算法。 如果你熟悉这个概念,你可以跳过这个部分。

哈希是获取指定数据的哈希值的过程。 哈希值是对其计算的数据的唯一表示。 哈希函数是一个获取任意大小的数据并产生固定大小的哈希的函数。 以下是哈希的一些主要功能:

  • 原始数据无法从哈希值恢复。 因此,哈希过程不是加密。
  • 数据只能有一个与之对应的哈希值,因此哈希是唯一的。
  • 更改输入数据中的一个字节将导致完全不同的散列。

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.

哈希函数被广泛用于检查数据的一致性。在区块链中,使用哈希来保证块的一致性。 哈希算法的输入数据包含前一个块的哈希值,从而使得已经生成的链难以修改之前产生的区块(或至少相当困难):篡改一个区块必须重新计算其前的所有块的哈希值。

译者注: genesis block的previous block是空

哈希现金

比特币使用Hashcash,哈希现金的发明最初是为防止电子邮件垃圾邮件而开发的。它可以分为以下几个步骤:

  1. 获取公开的数据(在电子邮件的情况下,它是接收者的电子邮件地址;在比特币的情况下,它是块标题)。
  2. 添加一个计数器。计数器从0开始。
  3. 获取数据+计数器组合的哈希值
  4. 检查哈希值是否符合要求。
    1. 如果满足要求,结束过程。
    2. 如果不满足要求,增加计数器并重复步骤3和4。

因此,这是一个强力brute force算法:

1. 计算一个新的哈希
2. 检查该哈希值
3. 增加计数器

现在让我们看看一个哈希必须满足的要求。在原来的Hashcash实现中“哈希的前20位必须是零”。然而在比特币中,哈希要求是不时进行调整的,因为尽管计算能力随着时间的推移而增加,越来越多的矿工加入网络,因此设计必须每10分钟生成一个块

4. 编写代码

程序员小提醒:go和python都是不用加分号的语言

好的,我们完成了理论,让我们编写代码! 首先,我们来定义挖掘难度

const targetBits = 24

4.1 目标位

在比特币中,“目标位(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
}

译者注:这里从数学上保证了miner必须使用brute force的方式最终发现target:https://github.com/ekzhu/lsh

这里创建工作证明结构中保存指向区块的指针的和指向target的指针。 “target”是上一段所述要求的另一个名称。 我们使用一个大整数,因为我们将哈希与目标进行比较:我们将哈希转换为一个大整数,并检查它是否小于target。

big: https://golang.org/pkg/math/big/

在新的工作证明的函数中,我们初始化一个值为1的big.Int,并将其左移256个 - targetBits位。 256是SHA-256哈希的长度,以比特为单位,它是我们要使用的SHA-256散列算法。 目标的十六进制表示为:

0x10000000000000000000000000000000000000000000000000000000000

它在内存中占用29个字节。 这是与以前的例子中的哈希的比较:

0fac49161af82ed938add1d8725835cc123a1a87b1b196488360e58d4bfb51e3
0000010000000000000000000000000000000000000000000000000000000000
0000008b0f41ec78bab747864db66bcb9fb89920ee75f43fdaaeb5544f7f76ca

第一个哈希(以“我喜欢甜甜圈”计算)大于目标,因此它不是有效的工作证明。 第二个哈希(以“我喜欢甜甜圈ca07ca”计算)小于目标,因此这是一个有效的证明。

您可以将目标视为范围的上限:如果数字(哈希)低于边界,则它是有效的,反之亦然。 降低边界将导致有效数量减少,因此找到有效数量所需的工作更加困难。

4.2 准备数据

//准备数据,加入targetBits和nonce
func (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
}

4.3 工作证明

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[:]
}

4.4. 给NewBlock() 加入nounce和工作证明

移除SetHash,并更改NewBlock:

  1. 产生新区块
  2. 工作证明
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 // 用于验证
}

4.5. 验证工作证明 validate()

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 Block
Hash: 00000093253acb814afb942e652a84a8f245069a67b5eaa709df8ac612075038
PoW: true

Prev. hash: 00000093253acb814afb942e652a84a8f245069a67b5eaa709df8ac612075038
Data: Send 1 BTC to Ivan
Hash: 0000003eeb3743ee42020e4a15262fd110a72823d804ce8e49643b5fd9d1062b
PoW: true

Prev. hash: 0000003eeb3743ee42020e4a15262fd110a72823d804ce8e49643b5fd9d1062b
Data: Send 2 more BTC to Ivan
Hash: 000000e42afddf57a3daa11b43b2e0923f23e894f96d1f24bfd9b8d2d494c57a
PoW: true

5. 总结

我们的块链是一个更接近其实际架构的一步:添加块现在需要工作证明,因此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:

  1. Full source codes
  2. Blockchain hashing algorithm
  3. Proof of work
  4. Hashcash

番外

不同branches中保存着各个阶段的代码


学习更多golang的知识: https://golang.org/
关于如何Compile一个golang project

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • 老妈最近常跟我说,她跟一起跳广场舞的阿姨,不能愉快地玩耍了。我问她是因为什么。她跟我说了很多细节,让我不禁对这样的...
    阳光鹜舞阅读 280评论 0 0
  • 庞边的Adrian阅读 103评论 0 0
  • 忘了谁说过类似这样一句话,原话忘记了大概意思就是无论和谁生活在一起最幸福的事就是可以在午夜梦醒时分仍可以陪你宽宽心...
    无敌之盾阅读 245评论 1 1
  • 《想象》 想象,可以弥补一切 你 很丑 但要想的美 《还有你》 我不仅有诗和远方 还有眼前的苟且 更重要的是,还...
    石石头阅读 373评论 0 7