Remove github.com/holiman/uint256 dependency on types.DifficultyFromPoW

Implement and compare native P2Pool check_pow

use uint128.Max on Difficulty PoW
This commit is contained in:
DataHoarder 2024-04-07 03:33:40 +02:00
parent eb372dc167
commit 44d969a218
Signed by: DataHoarder
SSH key fingerprint: SHA256:OLTRf6Fl87G52SiR7sWLGNzlJt4WOX+tfI2yxo0z7xk
5 changed files with 167 additions and 49 deletions

2
go.mod
View file

@ -13,8 +13,6 @@ require (
github.com/floatdrop/lru v1.3.0
github.com/go-zeromq/zmq4 v0.16.0
github.com/goccy/go-json v0.10.2
github.com/holiman/uint256 v1.2.4
github.com/jxskiss/base62 v1.1.0
github.com/stretchr/testify v1.8.1
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc
golang.org/x/sys v0.19.0

4
go.sum
View file

@ -27,10 +27,6 @@ github.com/go-zeromq/goczmq/v4 v4.2.2 h1:HAJN+i+3NW55ijMJJhk7oWxHKXgAuSBkoFfvr8b
github.com/go-zeromq/goczmq/v4 v4.2.2/go.mod h1:Sm/lxrfxP/Oxqs0tnHD6WAhwkWrx+S+1MRrKzcxoaYE=
github.com/go-zeromq/zmq4 v0.16.0 h1:D6oIPWSdkY/4DJu4tBUmo28P3WRq4F4Ji4/iQ/fJHc0=
github.com/go-zeromq/zmq4 v0.16.0/go.mod h1:8c3aXloJBRPba1AqWMJK4vypniM+yC+JKqi8KpRaDFc=
github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU=
github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
github.com/jxskiss/base62 v1.1.0 h1:A5zbF8v8WXx2xixnAKD2w+abC+sIzYJX+nxmhA6HWFw=
github.com/jxskiss/base62 v1.1.0/go.mod h1:HhWAlUXvxKThfOlZbcuFzsqwtF5TcqS9ru3y5GfjWAc=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=

View file

@ -1,11 +1,9 @@
package types
import (
"bytes"
"database/sql/driver"
"errors"
"git.gammaspectra.live/P2Pool/consensus/v3/utils"
"github.com/holiman/uint256"
fasthex "github.com/tmthrgd/go-hex"
"io"
"lukechampine.com/uint128"
@ -327,44 +325,3 @@ func (d *Difficulty) Scan(src any) error {
func (d *Difficulty) Value() (driver.Value, error) {
return d.Bytes(), nil
}
// TODO: remove uint256 dependency as it's unique to this section
var powBase = uint256.NewInt(0).SetBytes32(bytes.Repeat([]byte{0xff}, 32))
func DifficultyFromPoW(powHash Hash) Difficulty {
if powHash == ZeroHash {
return ZeroDifficulty
}
pow := uint256.NewInt(0).SetBytes32(powHash[:])
pow = &uint256.Int{bits.ReverseBytes64(pow[3]), bits.ReverseBytes64(pow[2]), bits.ReverseBytes64(pow[1]), bits.ReverseBytes64(pow[0])}
powResult := uint256.NewInt(0).Div(powBase, pow).Bytes32()
return DifficultyFromBytes(powResult[16:])
}
func (d Difficulty) CheckPoW(pow Hash) bool {
return DifficultyFromPoW(pow).Cmp(d) >= 0
}
// Target
// Finds a 64-bit target for mining (target = 2^64 / difficulty) and rounds up the result of division
// Because of that, there's a very small chance that miners will find a hash that meets the target but is still wrong (hash * difficulty >= 2^256)
// A proper difficulty check is in check_pow()
func (d Difficulty) Target() uint64 {
if d.Hi > 0 {
return 1
}
// Safeguard against division by zero (CPU will trigger it even if lo = 1 because result doesn't fit in 64 bits)
if d.Lo <= 1 {
return math.MaxUint64
}
q, rem := Difficulty{Hi: 1, Lo: 0}.QuoRem64(d.Lo)
if rem > 0 {
return q.Lo + 1
} else {
return q.Lo
}
}

89
types/pow.go Normal file
View file

@ -0,0 +1,89 @@
package types
import (
"encoding/binary"
"lukechampine.com/uint128"
"math"
"math/bits"
)
func DifficultyFromPoW(powHash Hash) Difficulty {
if powHash == ZeroHash {
return ZeroDifficulty
}
return Difficulty(uint128.Max.Div(uint128.FromBytes(powHash[16:])))
}
func (d Difficulty) CheckPoW(pow Hash) bool {
return DifficultyFromPoW(pow).Cmp(d) >= 0
}
func (d Difficulty) CheckPoW_Native(pow Hash) bool {
// P2Pool similar code
var result [6]uint64
var product [6]uint64
a := [4]uint64{
binary.LittleEndian.Uint64(pow[:]),
binary.LittleEndian.Uint64(pow[8:]),
binary.LittleEndian.Uint64(pow[16:]),
binary.LittleEndian.Uint64(pow[24:]),
}
if d.Hi == 0 {
for i := 3; i >= 0; i-- {
product[1], product[0] = bits.Mul64(a[i], d.Lo)
var carry uint64
for k, l := i, 0; k < 5; k, l = k+1, l+1 {
result[k], carry = bits.Add64(result[k], product[l], carry)
}
if result[4] > 0 {
return false
}
}
} else {
b := [2]uint64{d.Lo, d.Hi}
for i := 3; i >= 0; i-- {
for j := 1; j >= 0; j-- {
product[1], product[0] = bits.Mul64(a[i], b[j])
var carry uint64
for k, l := i+j, 0; k < 6; k, l = k+1, l+1 {
result[k], carry = bits.Add64(result[k], product[l], carry)
}
if result[4] > 0 || result[5] > 0 {
return false
}
}
}
}
return true
}
// Target
// Finds a 64-bit target for mining (target = 2^64 / difficulty) and rounds up the result of division
// Because of that, there's a very small chance that miners will find a hash that meets the target but is still wrong (hash * difficulty >= 2^256)
// A proper difficulty check is in check_pow()
func (d Difficulty) Target() uint64 {
if d.Hi > 0 {
return 1
}
// Safeguard against division by zero (CPU will trigger it even if lo = 1 because result doesn't fit in 64 bits)
if d.Lo <= 1 {
return math.MaxUint64
}
q, rem := Difficulty{Hi: 1, Lo: 0}.QuoRem64(d.Lo)
if rem > 0 {
return q.Lo + 1
} else {
return q.Lo
}
}

78
types/pow_test.go Normal file
View file

@ -0,0 +1,78 @@
package types
import (
"runtime"
"testing"
)
var (
powHash = MustHashFromString("abcf2c2ee4a64a683f24bedb2099dd16ae08c03a1ecc1208bf93a90200000000")
sidechainDifficulty = DifficultyFrom64(2062136440)
powDifficulty = DifficultyFrom64(412975968250)
moneroDifficulty = DifficultyFrom64(229654626174)
)
func TestDifficultyFromPoW(t *testing.T) {
diff := DifficultyFromPoW(powHash)
if !diff.Equals(powDifficulty) {
t.Errorf("%s does not equal %s", diff, powDifficulty)
}
}
func TestDifficulty_CheckPoW(t *testing.T) {
if !moneroDifficulty.CheckPoW(powHash) {
t.Errorf("%s does not pass PoW %s", powHash, moneroDifficulty)
}
if !sidechainDifficulty.CheckPoW(powHash) {
t.Errorf("%s does not pass PoW %s", powHash, sidechainDifficulty)
}
if !powDifficulty.CheckPoW(powHash) {
t.Errorf("%s does not pass PoW %s", powHash, powDifficulty)
}
powHash2 := powHash
powHash2[len(powHash2)-1]++
if moneroDifficulty.CheckPoW(powHash2) {
t.Errorf("%s does pass PoW %s incorrectly", powHash2, moneroDifficulty)
}
if sidechainDifficulty.CheckPoW(powHash2) {
t.Errorf("%s does pass PoW %s incorrectly", powHash2, sidechainDifficulty)
}
powHash3 := powHash
powHash3[len(powHash2)-9]++
if powDifficulty.CheckPoW(powHash3) {
t.Errorf("%s does pass PoW %s incorrectly", powHash3, powDifficulty)
}
}
func BenchmarkDifficulty_CheckPoW(b *testing.B) {
b.ReportAllocs()
b.RunParallel(func(pb *testing.PB) {
var result bool
for pb.Next() {
result = moneroDifficulty.CheckPoW(powHash)
}
runtime.KeepAlive(result)
})
}
func BenchmarkDifficulty_CheckPoW_Native(b *testing.B) {
b.ReportAllocs()
b.RunParallel(func(pb *testing.PB) {
var result bool
for pb.Next() {
result = moneroDifficulty.CheckPoW_Native(powHash)
}
runtime.KeepAlive(result)
})
}