使用go语言实现简易版的区块链

使用go语言实现简易版的区块链

区块链概念

区块链(Blockchain),是比特币的一个重要概念,它本质上是一个去中心化的数据库,同时作为比特币的底层技术,是一串使用密码学方法相关联产生的数据块,每一个数据块中包含了一批次比特币网络交易的信息,用于验证其信息的有效性(防伪)和生成下一个区块。(百度百科)

区块里的数据

// 块数据
type Block struct {
    Hash        string // 当前块的hash
    BlockNumber int64  // 当前块的高度,是递增的
    PreHash     string // 上个区块的hash
    Timestamp   int64  // 生成当前区块的高度
    Data        string // 区块交易(上链数据) 这里应该是slice,Data里会有很多笔的交易记录
}

其中hash和preHash是区块 “链”起来的关键。这里hash使用了sha256(所有的参数拼接成字符串)。

生成hash算法

hash具有唯一性,是区块的唯一标识。

// 生成hash
func generateHash(b Block) string {
    str := fmt.Sprintf("%d,%d,%s,%s", b.BlockNumber, b.Timestamp, b.PreHash, b.Data) // 简单的拼接。实际区块链生成hash比这要复杂的多
    hash := sha256.New()
    hash.Write([]byte(str))
    bytes := hash.Sum(nil)
    return hex.EncodeToString(bytes)
}

创世区块

创世块(Genesis Block)作为比特币区块链的第一个区块是设定为不可交易的。与其他包含交易的区块链中不同,创世纪区块不包含任何交易,该区块中的币也不能用于交易中。

// 创建创世区块
func genesisBlock() Block {
    b := Block{}
    b.BlockNumber = 0
    b.PreHash = ""
    b.Timestamp = time.Now().Unix()
    b.Data = ""
    b.Hash = generateHash(b)
    return b
}

生成区块

使用上个区块的数据,和当前区块的交易数据构建成新的区块数据。
注意,必须用上一个区块的哈希值来初始化本区块的previousHash,否则区块之间的联系就断了。
假如我们修改创世块的信息,那么所有的区块的hash值都会发生很大的变化,这就是为什么在区块上作弊是非常困难的一件事情。

// 创建区块
func generateBlock(oldBlock Block, data string) Block {
    var b Block
    b.BlockNumber = oldBlock.BlockNumber + 1
    b.PreHash = oldBlock.Hash
    b.Timestamp = time.Now().Unix()
    b.Data = data
    b.Hash = generateHash(b)
    return b
}

到这里所有关于区块的部分就已经结束了。接下来就是如何把这些区块给串起来,形成链式结构了。

区块的 “链”

我们创建的是一个非常简单基础的链,很显然,既然对于每个节点,我们都能计算出一个hash值,并且这个hash值是唯一的。那么们就可以通过块的hash值来定位到这个块的后一个节点是谁,因为我们每个块除了包含交易信息以外,还保存了上一个区块的hash值。
使用这些块组合在一起就成一个 就形成了 区块链

// 区块"链"
type BlockChain struct {
    ChainData []Block
}

验证新增区块是否合法

通过上面的介绍,我们知道,块与块之间是通过 hash 来关联的,而块的高度是递增的。以及新增的块的数据,重新计算hash,判断和自身hash是否一致。
至此我们就能去验证新增的区块是否合法


// 数据区块数据是否合法
func (bc *BlockChain) verifyBlock(newBlock Block, oldBlock Block) bool {
    // 对比前后高度是否一致
    if newBlock.BlockNumber != oldBlock.BlockNumber+1 {
        return false
    }
    // 对比前后hash是否一致
    if newBlock.PreHash != oldBlock.Hash {
        return false
    }

    // 对比生成的hash是否一致
    if newBlock.Hash != generateHash(newBlock) {
        return false
    }
    return true
}

新 “块” 上链

拿到新的交易数据data,并对数据进行打包,得到newBlock, 并对新旧区块进行验证。

// 数据上链
func (bc *BlockChain) uploadChain(data string) bool {
    num := len(bc.ChainData)
    if num == 0 { // 还未产生块
        block := genesisBlock()
        bc.ChainData = append(bc.ChainData, block)
    } else {
        oldBlock := bc.ChainData[len(bc.ChainData)-1]
        var newBlock = generateBlock(oldBlock, data)
        if bc.verifyBlock(newBlock, oldBlock) {
            bc.ChainData = append(bc.ChainData, newBlock)
        } else {
            return false
        }
    }
    return true
}

打印出整个区块链的信息

// 输出区块数据记录
func (bc *BlockChain) getChainData() {
    for _, v := range bc.ChainData {
        fmt.Println("hash:", v.Hash)
        fmt.Println("blockNum:", v.BlockNumber)
        fmt.Println("timestamp:", v.Timestamp)
        fmt.Println("preHash:", v.PreHash)
        fmt.Println("data:", v.Data)
        fmt.Println("-----------------分割线------------------")
    }
}

测试

func TestBlockChain(t *testing.T) {
    blockchain := BlockChain{}
    for i := 0; i < 5; i++ {
        b := blockchain.uploadChain("上链数据:" + strconv.Itoa(i))
        if !b {
            fmt.Println("验证区块错误")
            return
        }
        time.Sleep(time.Second * 1)
    }
    // 打印链数据
    blockchain.getChainData()
}

输出结果:

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

推荐阅读更多精彩内容