78 lines
3.1 KiB
Go
78 lines
3.1 KiB
Go
package sidechain
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/address"
|
|
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto"
|
|
"git.gammaspectra.live/P2Pool/p2pool-observer/types"
|
|
"github.com/floatdrop/lru"
|
|
"sync/atomic"
|
|
)
|
|
|
|
type deterministicTransactionCacheKey [crypto.PublicKeySize + types.HashSize]byte
|
|
type ephemeralPublicKeyCacheKey [crypto.PrivateKeySize + crypto.PublicKeySize*2 + 8]byte
|
|
|
|
type ephemeralPublicKeyWithViewTag struct {
|
|
PublicKey crypto.PublicKeyBytes
|
|
ViewTag uint8
|
|
}
|
|
|
|
type DerivationCache struct {
|
|
deterministicKeyCache atomic.Pointer[lru.LRU[deterministicTransactionCacheKey, *crypto.KeyPair]]
|
|
ephemeralPublicKeyCache atomic.Pointer[lru.LRU[ephemeralPublicKeyCacheKey, ephemeralPublicKeyWithViewTag]]
|
|
}
|
|
|
|
func NewDerivationCache() *DerivationCache {
|
|
d := &DerivationCache{}
|
|
d.Clear()
|
|
return d
|
|
}
|
|
|
|
func (d *DerivationCache) Clear() {
|
|
//keep a few recent blocks from the past few for uncles, and reused window miners
|
|
//~10s per share, keys change every Monero block (2m). around 2160 max shares per 6h (window), plus uncles. 6 shares per minute.
|
|
//each share can have up to 2160 outputs, plus uncles. each miner has its own private key per Monero block
|
|
|
|
const pplnsSize = 2160
|
|
const pplnsDurationInMinutes = 60 * 6
|
|
const sharesPerMinute = pplnsSize / pplnsDurationInMinutes
|
|
const cacheForNMinutesOfShares = sharesPerMinute * 5
|
|
const knownMinersPerPplns = pplnsSize / 4
|
|
const outputIdsPerMiner = 2
|
|
|
|
d.deterministicKeyCache.Store(lru.New[deterministicTransactionCacheKey, *crypto.KeyPair](cacheForNMinutesOfShares))
|
|
d.ephemeralPublicKeyCache.Store(lru.New[ephemeralPublicKeyCacheKey, ephemeralPublicKeyWithViewTag](pplnsSize * knownMinersPerPplns * outputIdsPerMiner))
|
|
}
|
|
|
|
func (d *DerivationCache) GetEphemeralPublicKey(a address.Interface, txKeySlice crypto.PrivateKeySlice, txKeyScalar *crypto.PrivateKeyScalar, outputIndex uint64) (crypto.PublicKeyBytes, uint8) {
|
|
var key ephemeralPublicKeyCacheKey
|
|
copy(key[:], txKeySlice)
|
|
copy(key[crypto.PrivateKeySize:], a.ToPackedAddress().Bytes())
|
|
binary.LittleEndian.PutUint64(key[crypto.PrivateKeySize+crypto.PublicKeySize*2:], outputIndex)
|
|
|
|
ephemeralPublicKeyCache := d.ephemeralPublicKeyCache.Load()
|
|
if ephemeralPubKey := ephemeralPublicKeyCache.Get(key); ephemeralPubKey == nil {
|
|
ephemeralPubKey, viewTag := address.GetEphemeralPublicKeyAndViewTag(a, txKeyScalar, outputIndex)
|
|
pKB := ephemeralPubKey.AsBytes()
|
|
ephemeralPublicKeyCache.Set(key, ephemeralPublicKeyWithViewTag{PublicKey: pKB, ViewTag: viewTag})
|
|
return pKB, viewTag
|
|
} else {
|
|
return ephemeralPubKey.PublicKey, ephemeralPubKey.ViewTag
|
|
}
|
|
}
|
|
|
|
func (d *DerivationCache) GetDeterministicTransactionKey(seed types.Hash, prevId types.Hash) *crypto.KeyPair {
|
|
var key deterministicTransactionCacheKey
|
|
copy(key[:], seed[:])
|
|
copy(key[types.HashSize:], prevId[:])
|
|
|
|
deterministicKeyCache := d.deterministicKeyCache.Load()
|
|
if kp := deterministicKeyCache.Get(key); kp == nil {
|
|
data := crypto.NewKeyPairFromPrivate(address.GetDeterministicTransactionPrivateKey(seed, prevId))
|
|
deterministicKeyCache.Set(key, data)
|
|
return data
|
|
} else {
|
|
return *kp
|
|
}
|
|
}
|