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)
}