PoW原理介绍:
PoW是Proof of Work的缩写,即工作量证明。PoW主要是应用哈希函数的性质来保证区块链的安全性。哈希函数具有以下特性:
- 安全性:指在当前的计算资源下(包括时间、空间、资金等),找到两个不同的输入,通过哈希函数,得到相同的输出是不可能的。
- 雪崩特性:当输入变化很少(例如仅一个字符)时,通过哈希函数的输出变化却很大。详细的讲,当一个输入位发生改变时,输出的每一位都可能发生改变。
- 正向容易,逆向困难:通过输入得到输出应该很容易,通过输出逆推输入非常困难。
理解了哈希函数的特性之后,其实也就理解了PoW算法的原理。
- 区块链的挖矿过程是将交易数据和随机数等数据哈希,计算一个前导0个数满足要求的哈希值。其中前导0的个数就代表了困难度,要求的前导0个数越多,代表计算的难度就越大。
- 当节点成功的计算到满足要求的哈希值之后,会将数据进行打包,并全网广播。其它节点收到这个数据后,需要对其进行验证。
- 只有网络中最快计算出来满足要求的哈希值的节点所发送的数据会被添加到账本中,网络中其他节点都是简单的进行复制,由此可以保证区块链账本的唯一性。
- 如果人想通过节点作弊,其计算的哈希值在全网其他节点的校验是不会验证通过的,作弊节点的数据也就不会被记录在账本中,数据将被丢弃,作弊节点的计算资源的消耗就白白的浪费掉了。挖矿的成本使得旷工自愿遵守共识协议。
PoW简单实现:
先定义区块结构体、困难度固定为4、创世区块在这里写成了全局变量。
const (
ComputingDifficulty = 4
)
var (
genesisBlock *BlockBody
)
type BlockBody struct {
PreHash string // 上一个区块哈希
CurHash string // 当前区块哈希
Timestamp string // 时间戳
Index int // 区块索引
Data []byte // 交易数据
Difficulty int // 困难度
Nonce int // 随机数
}
生成创世区块时,由于没有前置区块,所以也就没有前置hash值。
func GenGenesisBlock(data []byte) *BlockBody {
if genesisBlock != nil {
return genesisBlock
}
genesisBlock = &BlockBody{
PreHash: "0", // 没有前置hash值,设置为“0”
Timestamp: time.Now().String(),
Index: 1,
Data: data,
Difficulty: ComputingDifficulty,
Nonce: 1,
}
genesisBlock.CurHash = HashBlock(genesisBlock) // 计算创世区块hash值
return genesisBlock
}
func HashBlock(block *BlockBody) string {
dataToHash := block.PreHash + block.Timestamp + strconv.Itoa(block.Index) + string(block.Data) + strconv.Itoa(block.Difficulty) + strconv.Itoa(block.Nonce)
hashArray := sha256.Sum256([]byte(dataToHash))
hashByte := hashArray[:]
return hex.EncodeToString(hashByte)
}
有了创世区块后,就可以根据业务需要,生成普通区块。
func GenCommonBlock(data []byte, preHash string, preIndex int) *BlockBody {
block := &BlockBody{
PreHash: preHash,
Timestamp: time.Now().String(),
Index: preIndex + 1,
Data: data,
Difficulty: ComputingDifficulty,
Nonce: 1,
}
PoW(block) // 填充当前区块hash值
return block
}
// PoW需要不断的计算寻找hash值,直到hash值满足困难度要求
func PoW(block *BlockBody) {
for {
if block.CurHash = HashBlock(block); strings.HasPrefix(block.CurHash, strings.Repeat("0", ComputingDifficulty)) {
return
} else {
block.Nonce++
}
}
}
测试:
func main() {
geneBlock := GenGenesisBlock([]byte("Hello, world."))
fmt.Println("创世区块哈希:", geneBlock.CurHash) // 创世区块哈希: 4f65f8b0fe1f7421db3712d3a01ce0f4245ab631aad0a9e6fd2aa37d1efc694c
commBlock := GenCommonBlock([]byte("hello, first block."), geneBlock.CurHash, geneBlock.Index)
fmt.Println("第二个区块哈希:", commBlock.CurHash) // 第二个区块哈希: 0000c277e042748315f319b1522b8c25f1ae5a0d71a9e9a7dadcfeb531c770b6
}
评论(0)