2022-11-03 11:32:07 +00:00
|
|
|
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"
|
|
|
|
)
|
|
|
|
|
2022-11-05 09:10:04 +00:00
|
|
|
type derivationCacheKey [crypto.PublicKeySize * 2]byte
|
|
|
|
type sharedDataCacheKey [crypto.PrivateKeySize + 8]byte
|
2022-11-03 11:32:07 +00:00
|
|
|
|
|
|
|
type sharedDataWithTag struct {
|
2022-11-05 05:29:12 +00:00
|
|
|
SharedData *crypto.PrivateKeyScalar
|
2022-11-03 11:32:07 +00:00
|
|
|
ViewTag uint8
|
|
|
|
}
|
|
|
|
|
|
|
|
type DerivationCache struct {
|
|
|
|
deterministicKeyCache *lru.LRU[derivationCacheKey, *crypto.KeyPair]
|
2022-11-05 05:29:12 +00:00
|
|
|
derivationCache *lru.LRU[derivationCacheKey, *crypto.PublicKeyPoint]
|
2022-11-03 11:32:07 +00:00
|
|
|
sharedDataCache *lru.LRU[sharedDataCacheKey, sharedDataWithTag]
|
2022-11-05 05:29:12 +00:00
|
|
|
ephemeralPublicKeyCache *lru.LRU[derivationCacheKey, crypto.PublicKeyBytes]
|
2022-11-03 11:32:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2022-11-05 09:10:04 +00:00
|
|
|
|
|
|
|
const pplnsSize = 2160
|
|
|
|
const pplnsDurationInMinutes = 60 * 6
|
|
|
|
const sharesPerMinute = pplnsSize / pplnsDurationInMinutes
|
|
|
|
const cacheForNMinutesOfShares = sharesPerMinute * 5
|
|
|
|
const knownMinersPerPplns = pplnsSize / 4
|
|
|
|
const outputIdsPerMiner = 2
|
|
|
|
|
|
|
|
d.deterministicKeyCache = lru.New[derivationCacheKey, *crypto.KeyPair](cacheForNMinutesOfShares)
|
|
|
|
d.derivationCache = lru.New[derivationCacheKey, *crypto.PublicKeyPoint](pplnsSize * knownMinersPerPplns)
|
|
|
|
d.sharedDataCache = lru.New[sharedDataCacheKey, sharedDataWithTag](pplnsSize * knownMinersPerPplns * outputIdsPerMiner)
|
|
|
|
d.ephemeralPublicKeyCache = lru.New[derivationCacheKey, crypto.PublicKeyBytes](pplnsSize * knownMinersPerPplns * outputIdsPerMiner)
|
2022-11-03 11:32:07 +00:00
|
|
|
}
|
|
|
|
|
2022-11-05 05:29:12 +00:00
|
|
|
func (d *DerivationCache) GetEphemeralPublicKey(a address.Interface, txKey crypto.PrivateKey, outputIndex uint64) (crypto.PublicKeyBytes, uint8) {
|
|
|
|
sharedData, viewTag := d.GetSharedData(a, txKey, outputIndex)
|
2022-11-03 11:32:07 +00:00
|
|
|
|
|
|
|
var key derivationCacheKey
|
2022-11-05 05:29:12 +00:00
|
|
|
copy(key[:], a.SpendPublicKey().AsSlice())
|
|
|
|
copy(key[types.HashSize:], sharedData.AsSlice())
|
2022-11-03 11:32:07 +00:00
|
|
|
if ephemeralPubKey := d.ephemeralPublicKeyCache.Get(key); ephemeralPubKey == nil {
|
2022-11-06 10:58:19 +00:00
|
|
|
ephemeralPubKey := address.GetPublicKeyForSharedData(a, sharedData).AsBytes()
|
|
|
|
d.ephemeralPublicKeyCache.Set(key, ephemeralPubKey)
|
|
|
|
return ephemeralPubKey, viewTag
|
2022-11-03 11:32:07 +00:00
|
|
|
} else {
|
|
|
|
return *ephemeralPubKey, viewTag
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-05 05:29:12 +00:00
|
|
|
func (d *DerivationCache) GetSharedData(a address.Interface, txKey crypto.PrivateKey, outputIndex uint64) (*crypto.PrivateKeyScalar, uint8) {
|
|
|
|
derivation := d.GetDerivation(a, txKey)
|
2022-11-03 11:32:07 +00:00
|
|
|
|
|
|
|
var key sharedDataCacheKey
|
2022-11-05 05:29:12 +00:00
|
|
|
copy(key[:], derivation.AsSlice())
|
2022-11-03 11:32:07 +00:00
|
|
|
binary.LittleEndian.PutUint64(key[types.HashSize:], outputIndex)
|
|
|
|
|
|
|
|
if sharedData := d.sharedDataCache.Get(key); sharedData == nil {
|
|
|
|
var data sharedDataWithTag
|
2022-11-05 05:29:12 +00:00
|
|
|
data.SharedData = crypto.GetDerivationSharedDataForOutputIndex(derivation, outputIndex).AsScalar()
|
2022-11-03 11:32:07 +00:00
|
|
|
data.ViewTag = crypto.GetDerivationViewTagForOutputIndex(derivation, outputIndex)
|
|
|
|
d.sharedDataCache.Set(key, data)
|
|
|
|
return data.SharedData, data.ViewTag
|
|
|
|
} else {
|
|
|
|
return sharedData.SharedData, sharedData.ViewTag
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-05 05:29:12 +00:00
|
|
|
func (d *DerivationCache) GetDeterministicTransactionKey(a address.Interface, prevId types.Hash) *crypto.KeyPair {
|
2022-11-03 11:32:07 +00:00
|
|
|
var key derivationCacheKey
|
2022-11-05 05:29:12 +00:00
|
|
|
copy(key[:], a.SpendPublicKey().AsSlice())
|
2022-11-03 11:32:07 +00:00
|
|
|
copy(key[types.HashSize:], prevId[:])
|
|
|
|
|
|
|
|
if kp := d.deterministicKeyCache.Get(key); kp == nil {
|
2022-11-05 05:29:12 +00:00
|
|
|
data := crypto.NewKeyPairFromPrivate(address.GetDeterministicTransactionPrivateKey(a, prevId))
|
2022-11-03 11:32:07 +00:00
|
|
|
d.deterministicKeyCache.Set(key, data)
|
|
|
|
return data
|
|
|
|
} else {
|
|
|
|
return *kp
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-05 05:29:12 +00:00
|
|
|
func (d *DerivationCache) GetDerivation(a address.Interface, txKey crypto.PrivateKey) *crypto.PublicKeyPoint {
|
2022-11-03 11:32:07 +00:00
|
|
|
var key derivationCacheKey
|
2022-11-05 05:29:12 +00:00
|
|
|
copy(key[:], a.ViewPublicKey().AsSlice())
|
|
|
|
copy(key[types.HashSize:], txKey.AsSlice())
|
2022-11-03 11:32:07 +00:00
|
|
|
|
2022-11-05 05:29:12 +00:00
|
|
|
if derivation := d.derivationCache.Get(key); derivation == nil {
|
2022-11-07 08:52:35 +00:00
|
|
|
data := txKey.GetDerivationCofactor(a.ViewPublicKey())
|
2022-11-05 05:29:12 +00:00
|
|
|
d.derivationCache.Set(key, data.AsPoint())
|
|
|
|
return data.AsPoint()
|
2022-11-03 11:32:07 +00:00
|
|
|
} else {
|
|
|
|
return *derivation
|
|
|
|
}
|
|
|
|
}
|