有關PoP

概要

Proof of PIN(PoP)是 MetaID 中的一個有趣概念,我們參考了比特幣的挖礦原理制定了 PoP 機制。PoP 值反映的是用戶在 MetaID 世界的“工作量”證明。在 MetaID 世界中,用戶最小的“工作量”是發送一條 PIN,因此每條 PIN 都有一個哈希值,並且該將用戶的 PIN 與其所在的區塊難度結合。PoP 值反映了如下兩個維度:

  1. 用戶創建 MetaID 數據的數量概況:創建 MetaID PIN 越多,獲得高難度的 PoP 值的概率越高。

  2. 用戶創建 MetaID 數據的算力消耗:PoP 值和 MetaID 數據所在區塊鏈算力情況掛鉤,算力越高越容易獲得難度高的 PoP 值。

PIN 如同 SHA256 算力,PoP 如同難度哈希,算力越高則越大概率獲得高難度的哈希值。

通過驗證用戶相關的 PoP 值,我們可以快速評價一個用戶在 MetaID 世界的貢獻度,也讓 PIN 有了等級和稀有度的概念。

計算方法

PoP 是通過區塊的 MerkleRootPINID 用 SHA256 聯合哈希,然後再與 BlockHash 相乘,從而生成一個新的值。具體公式為:

PoP = hash(PIN_ID + Merkle_Root) * Block_Hash

得出的原始哈希值然後進行 8 進制轉換,轉換後的結果即為 PoP。

PoP 值的難度概念

PoP 值的難度級別類似區塊哈希,以前綴 "0" 的位數定義的。預設的 "0" 數量越多,表示該 PoP 的難度級別越高。任何有效的 PoP 至少需要包含 21 個前綴 "0"。

PoP 難度等級

對於難度等級,按照比特幣目前的算力推算 MetaID PoP 可分為 11 個等級。從等級 1 開始,PoP 值前綴需要擁有 22 個 "0" 才能滿足等級 1,需要 23 個 "0" 才能達到等級 2,以此類推,數量的增加直接反映在 PoP 稀有度等級的提升。

以下是計算 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
}

PoP 計算的測試用例:

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

1

Last updated