Proof of PIN(PoP)是 MetaID 中的一个有趣概念,我们参考了比特币的挖矿原理制定了 PoP 机制。PoP值反映的是用户在MetaID 世界的“工作量”证明。在 MetaID 世界中,用户最小的“工作量”是发送一条 PIN,因此每条 PIN 都有一个哈希值,并且该将用户的PIN与其所在的区块难度结合。PoP 值反映了如下两个维度:
PoP = hash(PIN_ID + Merkle_Root) * Block_Hash
PoP值的难度级别是类似区块哈希,以前缀 "0" 的位数定义的。预设的 "0"数量越多,表示该PoP的难度级别越高。任何有效的 PoP 至少需要包含21个前缀 "0"。
对于难度等级,按照比特币目前的算力推算MetaID PoP 可分为 了11个等级。从等级1开始,PoP值前缀需要拥有22个 "0" 才能满足等级1,需要23个 "0" 才能达到等级2,以此类推,数量的增加直接反映在 PoP 稀有度等级的提升。
package pop
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"math/big"
"strconv"
)
func CalculateHash(pinid string, merkleRoot string) string {
h := sha256.New()
h.Write([]byte(pinid + merkleRoot))
return hex.EncodeToString(h.Sum(nil))
}
func CalculateProductToHexStr(blockhash string, pinHash string) string {
blockhashByte, _ := hex.DecodeString(blockhash)
blockhashInt, _ := new(big.Int).SetString(blockhash, 16)
pinHashByte, _ := hex.DecodeString(pinHash)
pinHashInt, _ := new(big.Int).SetString(pinHash, 16)
popByte := new(big.Int).Mul(blockhashInt, pinHashInt).Bytes()
//计算总位数:32+32=64
totalLen := len(blockhashByte) + len(pinHashByte)
//需要补0的位数
remainingLen := totalLen - len(popByte)
for i := 0; i < remainingLen; i++ {
popByte = append([]byte{0}, popByte...)
}
return hex.EncodeToString(popByte)
}
func ConvertToOctalHex(productHex string) (string, int64) {
productByte, _ := hex.DecodeString(productHex)
//转二进制
bList := make([]string, 0)
for _, b := range productByte {
binaryB := fmt.Sprintf("%b", b)
bList = append(bList, fmt.Sprintf("%08s", binaryB))
}
productBinaryStr := ""
for _, b := range bList {
productBinaryStr += b
}
productBinaryStr = productBinaryStr[:510]
bCount := int64(0)
for _, b := range productBinaryStr {
if b == '0' {
bCount++
} else {
break
}
}
//二进制str转8进制str
octal := ""
for i := 0; i < len(productBinaryStr); i += 3 {
binaryStr := productBinaryStr[i : i+3]
num, err := strconv.ParseInt(binaryStr, 2, 64)
if err != nil {
fmt.Println("ParseInt error:", err)
return "", 0
}
octal += strconv.FormatInt(num, 8)
}
return octal, bCount
}
func GenPop(pinid, merkleRoot, blockHash string) (string, int64) {
//计算pinHash
pinHash := CalculateHash(pinid, merkleRoot)
//blockhash * pinHash
productHexStr := CalculateProductToHexStr(blockHash, pinHash)
//转8进制
octal, bCount := ConvertToOctalHex(productHexStr)
return octal, bCount
}
func Test_pop(t *testing.T) {
pinid := "77aac2ae323748dee3b8b1ae6b7c33c1c4466f568c572ea488f584f041f0de4ei0" // 64 char hash
merkleRoot := "e56011a241cb196fc4efbeafef051ca901761ffb569a43146582f9133bfd41d2" // 64 char hash
blockhash := "000000000000000004c2db0441a47fd3574992d508b8d9d866a789d371aa5060" // real block hash
pop, bCountZero := GenPop(mockPinId, merkleRoot, blockHash)
fmt.Println("POP:", octal)
fmt.Println("POP-0:", bCountZero)
}