About PoP

Overview

Proof of PIN (PoP) is an interesting concept in MetaID. We have developed the PoP mechanism with reference to the mining principle of Bitcoin. The PoP value reflects the user's "proof of work" in the MetaID world. In the MetaID world, the minimum "work" of a user is to send a PIN, so each PIN has a hash value that combines the user's PIN with the difficulty of the block it is in. The PoP value reflects the following two dimensions:

  1. Overview of the amount of MetaID data created by the user: The more MetaID PINs created, the higher the probability of obtaining a high-difficulty PoP value.

  2. Hashrate consumption of the user's MetaID data creation: The PoP value is linked to the hashrate of the blockchain where the MetaID data is located. The higher the hashrate, the easier it is to obtain a high-difficulty PoP value.

PIN is similar to SHA256 hashrate, while PoP is similar to difficulty hash. The higher the hashrate, the greater the probability of obtaining a high-difficulty hash value.

By verifying the user's related PoP value, we can quickly evaluate a user's contribution in the MetaID world and give the concept of level and rarity to PINs.

Calculation Method

PoP is calculated by performing a SHA256 joint hash on the MerkleRoot and PINID, and then multiplying it by the BlockHash to generate a new value. The specific formula is:

PoP = hash(PIN_ID + Merkle_Root) * Block_Hash

The resulting original hash value is then converted to octal, and the converted result is the PoP.

Concept of PoP Difficulty

The difficulty level of a PoP value is similar to block hashes, defined by the number of leading zeros ("0"). The more leading zeros, the higher the difficulty level of the PoP. Any valid PoP must contain at least 21 leading zeros.

PoP Difficulty Levels

Regarding the difficulty levels, based on the current hashrate of Bitcoin, MetaID PoP can be divided into 11 levels. Starting from level 1, the PoP value prefix needs to have 22 "0"s to meet level 1, 23 "0"s to reach level 2, and so on. The increase in the number of "0"s directly reflects the improvement in the rarity level of PoP.

Here is the code:

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()
	// Calculate total length: 32+32=64
	totalLen := len(blockhashByte) + len(pinHashByte)
	// Number of leading zeros needed
	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)
	
	// Convert to binary
	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
		}
	}

	// Convert binary string to octal string
	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) {
	// Calculate pinHash
	pinHash := CalculateHash(pinid, merkleRoot)
	// Multiply blockhash by pinHash
	productHexStr := CalculateProductToHexStr(blockHash, pinHash)
	// Convert to octal
	octal, bCount := ConvertToOctalHex(productHexStr)
	
	return octal, bCount
}

PoP calculation test case:

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:", pop)
	fmt.Println("POP-0:", bCountZero)
}

Last updated