Upgrade to go-randomx v3.1.0 with full JIT and full mode support
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
DataHoarder 2024-04-23 14:51:33 +02:00
parent 137356ee20
commit 4fb641265d
Signed by: DataHoarder
SSH key fingerprint: SHA256:OLTRf6Fl87G52SiR7sWLGNzlJt4WOX+tfI2yxo0z7xk
7 changed files with 184 additions and 82 deletions

View file

@ -46,10 +46,11 @@ steps:
from_secret: MONEROD_RPC_URL
MONEROD_ZMQ_URL:
from_secret: MONEROD_ZMQ_URL
CGO_ENABLED: "1"
commands:
- apk update
- apk add --no-cache git gcc g++ musl-dev pkgconfig
- go list -f '{{.Dir}}/...' -m | xargs -n 1 sh -c 'go test -p 1 -failfast -timeout 20m -cover -gcflags=-d=checkptr -v $0 || exit 255'
- go list -f '{{.Dir}}/...' -m | xargs -n 1 sh -c 'go test -tags enable_randomx_library -p 1 -failfast -timeout 20m -cover -gcflags=-d=checkptr -v $0 || exit 255'
- name: test-go-asm
image: golang:1.22-alpine3.19
depends_on:
@ -125,10 +126,11 @@ steps:
from_secret: MONEROD_RPC_URL
MONEROD_ZMQ_URL:
from_secret: MONEROD_ZMQ_URL
CGO_ENABLED: "1"
commands:
- apk update
- apk add --no-cache git gcc g++ musl-dev pkgconfig
- go list -f '{{.Dir}}/...' -m | xargs -n 1 sh -c 'go test -p 1 -failfast -timeout 20m -cover -gcflags=-d=checkptr -v $0 || exit 255'
- go list -f '{{.Dir}}/...' -m | xargs -n 1 sh -c 'go test -tags enable_randomx_library -p 1 -failfast -timeout 20m -cover -gcflags=-d=checkptr -v $0 || exit 255'
- name: test-go
image: golang:1.22-alpine3.19
depends_on:

View file

@ -21,9 +21,9 @@ You can also use the OpenAlias `p2pool.observer` directly on the GUI.
### Development notes
Requires using CGO when running the main modes where RandomX hashes are used, but can be used with `CGO_ENABLED=0` specifically as a library.
This library supports both [Golang RandomX library](https://git.gammaspectra.live/P2Pool/go-randomx) and the [C++ RandomX counterpart](https://github.com/tevador/RandomX).
You can install the RandomX dependency via this command:
By default, the Golang library will be used. You can enable the C++ library if by using CGO and the compile tag `enable_randomx_library` and have it installed via the command below:
```bash
$ git clone --depth 1 --branch master https://github.com/tevador/RandomX.git /tmp/RandomX && cd /tmp/RandomX && \
mkdir build && cd build && \

2
go.mod
View file

@ -5,7 +5,7 @@ go 1.22
require (
git.gammaspectra.live/P2Pool/edwards25519 v0.0.0-20240405085108-e2f706cb5c00
git.gammaspectra.live/P2Pool/go-json v0.99.0
git.gammaspectra.live/P2Pool/go-randomx/v2 v2.2.0
git.gammaspectra.live/P2Pool/go-randomx/v3 v3.1.0
git.gammaspectra.live/P2Pool/monero-base58 v1.0.0
git.gammaspectra.live/P2Pool/randomx-go-bindings v1.0.0
git.gammaspectra.live/P2Pool/sha3 v0.17.0

4
go.sum
View file

@ -2,8 +2,8 @@ git.gammaspectra.live/P2Pool/edwards25519 v0.0.0-20240405085108-e2f706cb5c00 h1:
git.gammaspectra.live/P2Pool/edwards25519 v0.0.0-20240405085108-e2f706cb5c00/go.mod h1:FZsrMWGucMP3SZamzrd7m562geIs5zp1O/9MGoiAKH0=
git.gammaspectra.live/P2Pool/go-json v0.99.0 h1:TbFOEWbbDLFzm1fM/2+WPUhhzSwJ501+otfrQ8jCP84=
git.gammaspectra.live/P2Pool/go-json v0.99.0/go.mod h1:X9PvT0fmWrU1I+BiDUjMypUWdsWFm24QCW1sWxbzT8w=
git.gammaspectra.live/P2Pool/go-randomx/v2 v2.2.0 h1:ABLqnlKrv0pkSXyEWZ6C4PzLvQp/lkhTwKM21nZbN4Q=
git.gammaspectra.live/P2Pool/go-randomx/v2 v2.2.0/go.mod h1:eYjslaVjiP4C3mYGKgkjpseLgsD5kKBuOcZJy9KPJ78=
git.gammaspectra.live/P2Pool/go-randomx/v3 v3.1.0 h1:FEVDm+kMhiz/zEkjPcc6b/Pp9sKGR1KoAoejT1qgaXY=
git.gammaspectra.live/P2Pool/go-randomx/v3 v3.1.0/go.mod h1:esQjh/AvuhmoMeBv9MCZ2SqgU0vgWOk1u0l0zCTMiP0=
git.gammaspectra.live/P2Pool/monero-base58 v1.0.0 h1:s8LZxVNc93YEs2NCCNWZ7CKr8RbEb031y6Wkvhn+TS4=
git.gammaspectra.live/P2Pool/monero-base58 v1.0.0/go.mod h1:WWEJy/AdWKxKAruvlKI82brw+DtVlePy0ct3ZiBlc68=
git.gammaspectra.live/P2Pool/randomx-go-bindings v1.0.0 h1:tajr4QFSPrb8NtHmU14JaXdhr+z+0RbOBLIgUDI5Tow=

View file

@ -1,6 +1,8 @@
package randomx
import (
"crypto/subtle"
"git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto"
"git.gammaspectra.live/P2Pool/consensus/v3/types"
)
@ -35,3 +37,26 @@ const (
SeedHashEpochLag = 64
SeedHashEpochBlocks = 2048
)
func consensusHash(scratchpad []byte) types.Hash {
// Intentionally not a power of 2
const ScratchpadSize = 1009
const RandomxArgonMemory = 262144
n := RandomxArgonMemory * 1024
const Vec128Size = 128 / 8
cachePtr := scratchpad[ScratchpadSize*Vec128Size:]
scratchpadTopPtr := scratchpad[:ScratchpadSize*Vec128Size]
for i := ScratchpadSize * Vec128Size; i < n; i += ScratchpadSize * Vec128Size {
stride := ScratchpadSize * Vec128Size
if stride > len(cachePtr) {
stride = len(cachePtr)
}
subtle.XORBytes(scratchpadTopPtr, scratchpadTopPtr, cachePtr[:stride])
cachePtr = cachePtr[stride:]
}
return crypto.Keccak256(scratchpadTopPtr)
}

View file

@ -1,12 +1,10 @@
//go:build cgo && !disable_randomx_library && !purego
//go:build cgo && enable_randomx_library && !purego
package randomx
import (
"bytes"
"crypto/subtle"
"errors"
"git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto"
"git.gammaspectra.live/P2Pool/consensus/v3/types"
"git.gammaspectra.live/P2Pool/consensus/v3/utils"
"git.gammaspectra.live/P2Pool/randomx-go-bindings"
@ -104,29 +102,12 @@ func ConsensusHash(buf []byte) types.Hash {
defer randomx.ReleaseCache(cache)
randomx.InitCache(cache, buf)
// Intentionally not a power of 2
const ScratchpadSize = 1009
const RandomxArgonMemory = 262144
n := RandomxArgonMemory * 1024
const Vec128Size = 128 / 8
type Vec128 [Vec128Size]byte
scratchpad := unsafe.Slice((*byte)(randomx.GetCacheMemory(cache)), n)
cachePtr := scratchpad[ScratchpadSize*Vec128Size:]
scratchpadTopPtr := scratchpad[:ScratchpadSize*Vec128Size]
for i := ScratchpadSize * Vec128Size; i < n; i += ScratchpadSize * Vec128Size {
stride := ScratchpadSize * Vec128Size
if stride > len(cachePtr) {
stride = len(cachePtr)
}
subtle.XORBytes(scratchpadTopPtr, scratchpadTopPtr, cachePtr[:stride])
cachePtr = cachePtr[stride:]
}
return crypto.Keccak256(scratchpadTopPtr)
return consensusHash(scratchpad)
}
func NewRandomX(n int, flags ...Flag) (Hasher, error) {

View file

@ -1,89 +1,183 @@
//go:build !cgo || disable_randomx_library || purego
//go:build !cgo || !enable_randomx_library || purego
package randomx
import (
"bytes"
"crypto/subtle"
"git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto"
"errors"
"git.gammaspectra.live/P2Pool/consensus/v3/types"
"git.gammaspectra.live/P2Pool/go-randomx/v2"
"git.gammaspectra.live/P2Pool/consensus/v3/utils"
"git.gammaspectra.live/P2Pool/go-randomx/v3"
fasthex "github.com/tmthrgd/go-hex"
"runtime"
"slices"
"sync"
"unsafe"
)
type hasher struct {
cache *randomx.Randomx_Cache
lock sync.Mutex
type hasherCollection struct {
lock sync.RWMutex
index int
flags []Flag
cache []*hasherState
}
key []byte
func (h *hasherCollection) Hash(key []byte, input []byte) (types.Hash, error) {
if hash, err := func() (types.Hash, error) {
h.lock.RLock()
defer h.lock.RUnlock()
for _, c := range h.cache {
if len(c.key) > 0 && bytes.Compare(c.key, key) == 0 {
return c.Hash(input), nil
}
}
return types.ZeroHash, errors.New("no hasher")
}(); err == nil && hash != types.ZeroHash {
return hash, nil
} else {
h.lock.Lock()
defer h.lock.Unlock()
index := h.index
h.index = (h.index + 1) % len(h.cache)
if err = h.cache[index].Init(key); err != nil {
return types.ZeroHash, err
}
return h.cache[index].Hash(input), nil
}
}
func (h *hasherCollection) initStates(size int) (err error) {
for _, c := range h.cache {
c.Close()
}
h.cache = make([]*hasherState, size)
for i := range h.cache {
if h.cache[i], err = newRandomXState(h.flags...); err != nil {
return err
}
}
return nil
}
func (h *hasherCollection) OptionFlags(flags ...Flag) error {
h.lock.Lock()
defer h.lock.Unlock()
if slices.Compare(h.flags, flags) != 0 {
h.flags = flags
return h.initStates(len(h.cache))
}
return nil
}
func (h *hasherCollection) OptionNumberOfCachedStates(n int) error {
h.lock.Lock()
defer h.lock.Unlock()
if len(h.cache) != n {
return h.initStates(n)
}
return nil
}
func (h *hasherCollection) Close() {
h.lock.Lock()
defer h.lock.Unlock()
for _, c := range h.cache {
c.Close()
}
}
type hasherState struct {
lock sync.Mutex
cache *randomx.Cache
dataset randomx.Dataset
vm *randomx.VM
flags uint64
key []byte
}
func ConsensusHash(buf []byte) types.Hash {
cache := randomx.Randomx_alloc_cache(0)
cache := randomx.NewCache(0)
defer cache.Close()
cache.Init(buf)
scratchpad := unsafe.Slice((*byte)(unsafe.Pointer(unsafe.SliceData(cache.Blocks))), len(cache.Blocks)*len(cache.Blocks[0])*int(unsafe.Sizeof(uint64(0))))
defer runtime.KeepAlive(cache)
// Intentionally not a power of 2
const ScratchpadSize = 1009
const RandomxArgonMemory = 262144
n := RandomxArgonMemory * 1024
const Vec128Size = 128 / 8
type Vec128 [Vec128Size]byte
cachePtr := scratchpad[ScratchpadSize*Vec128Size:]
scratchpadTopPtr := scratchpad[:ScratchpadSize*Vec128Size]
for i := ScratchpadSize * Vec128Size; i < n; i += ScratchpadSize * Vec128Size {
stride := ScratchpadSize * Vec128Size
if stride > len(cachePtr) {
stride = len(cachePtr)
}
subtle.XORBytes(scratchpadTopPtr, scratchpadTopPtr, cachePtr[:stride])
cachePtr = cachePtr[stride:]
}
return crypto.Keccak256(scratchpadTopPtr)
}
func (h *hasher) OptionFlags(flags ...Flag) error {
return nil
}
func (h *hasher) OptionNumberOfCachedStates(n int) error {
return nil
return consensusHash(scratchpad)
}
func NewRandomX(n int, flags ...Flag) (Hasher, error) {
return &hasher{
collection := &hasherCollection{
flags: flags,
cache: randomx.Randomx_alloc_cache(randomx.RANDOMX_FLAG_JIT),
}, nil
}
if err := collection.initStates(n); err != nil {
return nil, err
}
return collection, nil
}
func (h *hasher) Hash(key []byte, input []byte) (output types.Hash, err error) {
vm := func() *randomx.VM {
h.lock.Lock()
defer h.lock.Unlock()
func newRandomXState(flags ...Flag) (*hasherState, error) {
if h.key == nil || bytes.Compare(h.key, key) != 0 {
h.key = make([]byte, len(key))
copy(h.key, key)
h.cache.Init(h.key)
applyFlags := randomx.GetFlags()
for _, f := range flags {
if f == FlagLargePages {
applyFlags |= randomx.RANDOMX_FLAG_LARGE_PAGES
} else if f == FlagFullMemory {
applyFlags |= randomx.RANDOMX_FLAG_FULL_MEM
} else if f == FlagSecure {
applyFlags |= randomx.RANDOMX_FLAG_SECURE
}
return h.cache.VM_Initialize()
}()
}
h := &hasherState{
flags: applyFlags,
}
h.cache = randomx.NewCache(h.flags)
vm.CalculateHash(input, (*[32]byte)(&output))
if dataset := randomx.NewDataset(h.cache); dataset == nil {
h.cache.Close()
return nil, errors.New("couldn't initialize dataset")
} else {
h.dataset = dataset
}
if vm := randomx.NewVM(h.dataset); vm == nil {
h.cache.Close()
return nil, errors.New("couldn't initialize dataset")
} else {
h.vm = vm
}
return h, nil
}
func (h *hasherState) Init(key []byte) (err error) {
h.lock.Lock()
defer h.lock.Unlock()
h.key = make([]byte, len(key))
copy(h.key, key)
utils.Logf("RandomX", "Initializing to seed %s", fasthex.EncodeToString(h.key))
h.cache.Init(h.key)
randomx.InitDatasetParallel(h.dataset, runtime.NumCPU())
utils.Logf("RandomX", "Initialized to seed %s", fasthex.EncodeToString(h.key))
return nil
}
func (h *hasherState) Hash(input []byte) (output types.Hash) {
h.lock.Lock()
defer h.lock.Unlock()
h.vm.CalculateHash(input, (*[32]byte)(&output))
runtime.KeepAlive(input)
return
}
func (h *hasher) Close() {
func (h *hasherState) Close() {
h.lock.Lock()
defer h.lock.Unlock()
h.vm.Close()
h.cache.Close()
}