区块链的大致概念。
按照个人理解来说,区块,就是保存一个一个数据的模块,然后区块链,是每个区块相连的链表那种,同时区块链实现之所以公平,是采用加密,且不可倒置等的机制,也就是,数据只要成功加载到区块链上,没有篡改、消除的可能性。
图中所示,就是区块链的实现机制,每个区块保存有上个区块的Hash值,同时生成自己的Hash给下个区块来保存。
具体的关于区块的知识,在Here有讲解,关于私链、公链,以及白皮书等,有兴趣可以看看。
接下来开始今天的主题。
首先今天的任务呢,就以下六个:
- 创建自己的区块链
- 了解哈希如何维护区块链的完整性
- 查看如何添加新块
- 查看多个节点生成块时如何解决决胜局
- 在网络浏览器中查看您的区块链
- 写新区块
首先需要下载代码中所需的第三方包
这个项目中所需要使用到的第三方包有三个:
go get github.com/davecgh/go-spew/spew
spew可以在控制台可视化structs和slices。
go get github.com/gorilla/mux
gorilla / mux是编写Web处理程序的流行软件包。
go get github.com/joho/godotenv
godotenv允许我们从.env保存在目录根目录中的文件中进行读取,因此无需对诸如HTTP端口之类的内容进行硬编码。
第一步我们在.env目录的根目录中创建一个文件,定义将为http请求提供服务的端口。只需向此文件添加一行:
PORT=8080
如果觉得细讲有点啰嗦,可直接跳到下面看完整代码!
创建好就是这样子:
接下来创建一个main.go文件。从现在开始,所有内容都将写入此文件,由于实现简单,暂无分模块处理,也是便于各位能看懂代码逻辑。
创建结构体
type Block struct {
Index int //数据记录在区块中的位置
Timestamp string //数据写入时间
BPM int //插入区块的频率
Hash string //此条数据的SHA256标识符
PrevHash string //上一个区块的标识符
}
根据结构体创建接下来的Blockchain
var Blockchain []Block
接下来就是生成区块本身的Hash:
//用于生成区块的SHA256哈希值
func calculateHash(block Block) string {
record := string(block.Index) + block.Timestamp + string(block.BPM) + block.PrevHash
h := sha256.New()
h.Write([]byte(record))
hashed := h.Sum(nil)
//返回生成的Hash
return hex.EncodeToString(hashed)
}
然后就是完整的新的区块的生成
//新区块的生成
func generateBlock(oldBlock Block, BPM int) (Block, error) {
var newBlock Block
t := time.Now()
newBlock.Index = oldBlock.Index + 1
newBlock.Timestamp = t.String()
newBlock.BPM = BPM
newBlock.PrevHash = oldBlock.Hash
newBlock.Hash = calculateHash(newBlock)
return newBlock, nil
}
再然后就是确认区块是否被更改
我们检查Index以确保它们已按预期增加。同时检查PrevHash的确与Hash上一个块的相同。最后,我们要通过在当前块上calculateHash再次运行该函数来仔细检查当前块的哈希。
//验证区块是否被篡改
func isBlockValid(newBlock, oldBlock Block) bool {
if oldBlock.Index+1 != newBlock.Index {
return false
}
if oldBlock.Hash != newBlock.PrevHash {
return false
}
if calculateHash(newBlock) != newBlock.Hash {
return false
}
return true
}
抛出一个问题
如果我们的区块链生态系统的两个节点都向其链中添加了区块,而我们都收到了它们。选择的话,我们选择最长的链条。
两个节点可能只是具有不同的链长,因此自然而然地,较长的节点将是最新的,并具有最新的块。因此,要确保我们要接收的新链比当前的链更长。如果是这样的话,我们可以使用具有新块的新链覆盖我们的链。
如图:
那我们解决这个问题的话,只需要我们比较链条的长度即可完成此操作:
//两个区块竞争,长的链优先
func replaceChain(newBlocks []Block) {
if len(newBlocks) > len(Blockchain) {
Blockchain = newBlocks
}
}
到了这里,基本关于区块的操作已经完成,接下来就是处理路由,然后就大功告成。
首先思考,设置路由,我们用最简单的方式实现,Get来获取区块的数据,Post来插入自己的新的区块。
首先是Get:
//查看区块数据
func handleGetBlockchain(w http.ResponseWriter, r *http.Request) {
//以json格式写会区块的数据
bytes, err := json.