feat(consensus): PoW difficulty check with RandomX
CheckDifficulty compares a 256-bit LE hash against a difficulty target. CheckPoWHash computes RandomX hash and checks against difficulty. Co-Authored-By: Charon <charon@lethean.io>
This commit is contained in:
parent
4fe3fdfbd2
commit
c2888c487b
2 changed files with 80 additions and 0 deletions
55
consensus/pow.go
Normal file
55
consensus/pow.go
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
// Copyright (c) 2017-2026 Lethean (https://lt.hn)
|
||||
//
|
||||
// Licensed under the European Union Public Licence (EUPL) version 1.2.
|
||||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
package consensus
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math/big"
|
||||
|
||||
"forge.lthn.ai/core/go-blockchain/crypto"
|
||||
"forge.lthn.ai/core/go-blockchain/types"
|
||||
)
|
||||
|
||||
// maxTarget is 2^256, used for difficulty comparison.
|
||||
var maxTarget = new(big.Int).Lsh(big.NewInt(1), 256)
|
||||
|
||||
// CheckDifficulty returns true if hash meets the given difficulty target.
|
||||
// The hash (interpreted as a 256-bit little-endian number) must be less
|
||||
// than maxTarget / difficulty.
|
||||
func CheckDifficulty(hash types.Hash, difficulty uint64) bool {
|
||||
if difficulty == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
// Convert hash to big.Int (little-endian as per CryptoNote convention).
|
||||
// Reverse to big-endian for big.Int.
|
||||
var be [32]byte
|
||||
for i := 0; i < 32; i++ {
|
||||
be[i] = hash[31-i]
|
||||
}
|
||||
hashInt := new(big.Int).SetBytes(be[:])
|
||||
|
||||
target := new(big.Int).Div(maxTarget, new(big.Int).SetUint64(difficulty))
|
||||
|
||||
return hashInt.Cmp(target) < 0
|
||||
}
|
||||
|
||||
// CheckPoWHash computes the RandomX hash of a block header hash + nonce
|
||||
// and checks it against the difficulty target.
|
||||
func CheckPoWHash(headerHash types.Hash, nonce, difficulty uint64) (bool, error) {
|
||||
// Build input: header_hash (32 bytes) || nonce (8 bytes LE).
|
||||
var input [40]byte
|
||||
copy(input[:32], headerHash[:])
|
||||
binary.LittleEndian.PutUint64(input[32:], nonce)
|
||||
|
||||
key := []byte("LetheanRandomXv1")
|
||||
powHash, err := crypto.RandomXHash(key, input[:])
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return CheckDifficulty(types.Hash(powHash), difficulty), nil
|
||||
}
|
||||
25
consensus/pow_test.go
Normal file
25
consensus/pow_test.go
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
//go:build !integration
|
||||
|
||||
package consensus
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"forge.lthn.ai/core/go-blockchain/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCheckDifficulty_Good(t *testing.T) {
|
||||
// A zero hash meets any difficulty.
|
||||
hash := types.Hash{}
|
||||
assert.True(t, CheckDifficulty(hash, 1))
|
||||
}
|
||||
|
||||
func TestCheckDifficulty_Bad(t *testing.T) {
|
||||
// Max hash (all 0xFF) should fail high difficulty.
|
||||
hash := types.Hash{}
|
||||
for i := range hash {
|
||||
hash[i] = 0xFF
|
||||
}
|
||||
assert.False(t, CheckDifficulty(hash, ^uint64(0)))
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue