Reduce allocations on ephemeral public key verification
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
0d7c208e06
commit
6a690ad278
|
@ -7,6 +7,7 @@ import (
|
|||
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto"
|
||||
p2poolcrypto "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/crypto"
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/types"
|
||||
"hash"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
@ -36,13 +37,28 @@ func getEphemeralPublicKeyInline(spendPub, viewPub *edwards25519.Point, txKey *e
|
|||
}
|
||||
|
||||
func GetEphemeralPublicKeyAndViewTag(a Interface, txKey crypto.PrivateKey, outputIndex uint64) (crypto.PublicKey, uint8) {
|
||||
pK, viewTag := crypto.GetDerivationSharedDataAndViewTagForOutputIndexNoAllocate(txKey.GetDerivationCofactor(a.ViewPublicKey()), outputIndex)
|
||||
return GetPublicKeyForSharedData(a, crypto.PrivateKeyFromScalar(&pK)), viewTag
|
||||
pK, viewTag := crypto.GetDerivationSharedDataAndViewTagForOutputIndex(txKey.GetDerivationCofactor(a.ViewPublicKey()), outputIndex)
|
||||
return GetPublicKeyForSharedData(a, pK), viewTag
|
||||
}
|
||||
|
||||
func GetEphemeralPublicKeyAndViewTagNoAllocate(a Interface, txKey crypto.PrivateKey, outputIndex uint64) (crypto.PublicKeyBytes, uint8) {
|
||||
pK, viewTag := crypto.GetDerivationSharedDataAndViewTagForOutputIndexNoAllocate(txKey.GetDerivationCofactor(a.ViewPublicKey()), outputIndex)
|
||||
return GetPublicKeyForSharedData(a, crypto.PrivateKeyFromScalar(&pK)).AsBytes(), viewTag
|
||||
// GetEphemeralPublicKeyAndViewTagNoAllocate Special version of GetEphemeralPublicKeyAndViewTag
|
||||
func GetEphemeralPublicKeyAndViewTagNoAllocate(a *PackedAddress, txKey *crypto.PrivateKeyScalar, outputIndex uint64, hasher hash.Hash) (crypto.PublicKeyBytes, uint8) {
|
||||
scalar := txKey.Scalar()
|
||||
var spendPublicKeyPoint, viewPublicKeyPoint, point, cofactor, intermediatePublicKey, ephemeralPublicKey edwards25519.Point
|
||||
_, _ = spendPublicKeyPoint.SetBytes((*a)[0][:])
|
||||
_, _ = viewPublicKeyPoint.SetBytes((*a)[1][:])
|
||||
point.ScalarMult(scalar, &viewPublicKeyPoint)
|
||||
cofactor.MultByCofactor(&point)
|
||||
|
||||
pK, viewTag := crypto.GetDerivationSharedDataAndViewTagForOutputIndexNoAllocate(crypto.PublicKeyBytes(cofactor.Bytes()), outputIndex, hasher)
|
||||
|
||||
intermediatePublicKey.ScalarBaseMult(&pK)
|
||||
ephemeralPublicKey.Add(&intermediatePublicKey, &spendPublicKeyPoint)
|
||||
|
||||
var ephemeralPublicKeyBytes crypto.PublicKeyBytes
|
||||
copy(ephemeralPublicKeyBytes[:], ephemeralPublicKey.Bytes())
|
||||
|
||||
return ephemeralPublicKeyBytes, viewTag
|
||||
}
|
||||
|
||||
func GetTxProofV2(a Interface, txId types.Hash, txKey crypto.PrivateKey, message string) string {
|
||||
|
|
|
@ -3,6 +3,8 @@ package crypto
|
|||
import (
|
||||
"encoding/binary"
|
||||
"filippo.io/edwards25519"
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/types"
|
||||
"hash"
|
||||
)
|
||||
|
||||
func GetDerivationSharedDataForOutputIndex(derivation PublicKey, outputIndex uint64) PrivateKey {
|
||||
|
@ -11,10 +13,12 @@ func GetDerivationSharedDataForOutputIndex(derivation PublicKey, outputIndex uin
|
|||
return PrivateKeyFromScalar(HashToScalar(k[:], varIntBuf[:binary.PutUvarint(varIntBuf[:], outputIndex)]))
|
||||
}
|
||||
|
||||
var viewTagDomain = []byte("view_tag")
|
||||
|
||||
func GetDerivationViewTagForOutputIndex(derivation PublicKey, outputIndex uint64) uint8 {
|
||||
var k = derivation.AsBytes()
|
||||
var varIntBuf [binary.MaxVarintLen64]byte
|
||||
return PooledKeccak256([]byte("view_tag"), k[:], varIntBuf[:binary.PutUvarint(varIntBuf[:], outputIndex)])[0]
|
||||
return PooledKeccak256(viewTagDomain, k[:], varIntBuf[:binary.PutUvarint(varIntBuf[:], outputIndex)])[0]
|
||||
}
|
||||
|
||||
func GetDerivationSharedDataAndViewTagForOutputIndex(derivation PublicKey, outputIndex uint64) (PrivateKey, uint8) {
|
||||
|
@ -23,16 +27,30 @@ func GetDerivationSharedDataAndViewTagForOutputIndex(derivation PublicKey, outpu
|
|||
|
||||
n := binary.PutUvarint(varIntBuf[:], outputIndex)
|
||||
pK := PrivateKeyFromScalar(HashToScalar(k[:], varIntBuf[:n]))
|
||||
return pK, PooledKeccak256([]byte("view_tag"), k[:], varIntBuf[:n])[0]
|
||||
return pK, PooledKeccak256(viewTagDomain, k[:], varIntBuf[:n])[0]
|
||||
}
|
||||
|
||||
func GetDerivationSharedDataAndViewTagForOutputIndexNoAllocate(derivation PublicKey, outputIndex uint64) (edwards25519.Scalar, uint8) {
|
||||
// GetDerivationSharedDataAndViewTagForOutputIndexNoAllocate Special version of GetDerivationSharedDataAndViewTagForOutputIndex
|
||||
func GetDerivationSharedDataAndViewTagForOutputIndexNoAllocate(k PublicKeyBytes, outputIndex uint64, hasher hash.Hash) (edwards25519.Scalar, uint8) {
|
||||
var buf [PublicKeySize + binary.MaxVarintLen64]byte
|
||||
var k = derivation.AsBytes()
|
||||
copy(buf[:], k[:])
|
||||
|
||||
n := binary.PutUvarint(buf[PublicKeySize:], outputIndex)
|
||||
return HashToScalarNoAllocateSingle(buf[:PublicKeySize+n]), Keccak256([]byte("view_tag"), buf[:PublicKeySize+n])[0]
|
||||
var h types.Hash
|
||||
hasher.Reset()
|
||||
hasher.Write(buf[:PublicKeySize+n])
|
||||
HashFastSum(hasher, h[:])
|
||||
scReduce32(h[:])
|
||||
|
||||
var c edwards25519.Scalar
|
||||
_, _ = c.SetCanonicalBytes(h[:])
|
||||
|
||||
hasher.Reset()
|
||||
hasher.Write(viewTagDomain)
|
||||
hasher.Write(buf[:PublicKeySize+n])
|
||||
HashFastSum(hasher, h[:])
|
||||
|
||||
return c, h[0]
|
||||
}
|
||||
|
||||
func GetKeyImage(pair *KeyPair) PublicKey {
|
||||
|
|
|
@ -63,6 +63,7 @@ func (p *PrivateKeyScalar) GetDerivation(public PublicKey) PublicKey {
|
|||
func (p *PrivateKeyScalar) GetDerivationCofactor(public PublicKey) PublicKey {
|
||||
return deriveKeyExchangeSecretCofactor(p, public.AsPoint())
|
||||
}
|
||||
|
||||
func (p *PrivateKeyScalar) String() string {
|
||||
return hex.EncodeToString(p.Scalar().Bytes())
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto"
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/types"
|
||||
"github.com/floatdrop/lru"
|
||||
"hash"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
|
@ -18,7 +19,7 @@ type ephemeralPublicKeyWithViewTag struct {
|
|||
}
|
||||
|
||||
type DerivationCacheInterface interface {
|
||||
GetEphemeralPublicKey(a address.Interface, txKeySlice crypto.PrivateKeySlice, txKeyScalar *crypto.PrivateKeyScalar, outputIndex uint64) (crypto.PublicKeyBytes, uint8)
|
||||
GetEphemeralPublicKey(a *address.PackedAddress, txKeySlice crypto.PrivateKeySlice, txKeyScalar *crypto.PrivateKeyScalar, outputIndex uint64, hasher hash.Hash) (crypto.PublicKeyBytes, uint8)
|
||||
GetDeterministicTransactionKey(seed types.Hash, prevId types.Hash) *crypto.KeyPair
|
||||
}
|
||||
|
||||
|
@ -42,7 +43,7 @@ func (d *DerivationCache) Clear() {
|
|||
d.ephemeralPublicKeyCache.Store(lru.New[ephemeralPublicKeyCacheKey, ephemeralPublicKeyWithViewTag](2000))
|
||||
}
|
||||
|
||||
func (d *DerivationCache) GetEphemeralPublicKey(a address.Interface, txKeySlice crypto.PrivateKeySlice, txKeyScalar *crypto.PrivateKeyScalar, outputIndex uint64) (crypto.PublicKeyBytes, uint8) {
|
||||
func (d *DerivationCache) GetEphemeralPublicKey(a *address.PackedAddress, txKeySlice crypto.PrivateKeySlice, txKeyScalar *crypto.PrivateKeyScalar, outputIndex uint64, hasher hash.Hash) (crypto.PublicKeyBytes, uint8) {
|
||||
var key ephemeralPublicKeyCacheKey
|
||||
copy(key[:], txKeySlice)
|
||||
copy(key[crypto.PrivateKeySize:], a.ToPackedAddress().Bytes())
|
||||
|
@ -51,7 +52,7 @@ func (d *DerivationCache) GetEphemeralPublicKey(a address.Interface, txKeySlice
|
|||
ephemeralPublicKeyCache := d.ephemeralPublicKeyCache.Load()
|
||||
if ephemeralPubKey := ephemeralPublicKeyCache.Get(key); ephemeralPubKey == nil {
|
||||
d.ephemeralPublicKeyCacheMisses.Add(1)
|
||||
pKB, viewTag := address.GetEphemeralPublicKeyAndViewTagNoAllocate(a, txKeyScalar, outputIndex)
|
||||
pKB, viewTag := address.GetEphemeralPublicKeyAndViewTagNoAllocate(a, txKeyScalar, outputIndex, hasher)
|
||||
ephemeralPublicKeyCache.Set(key, ephemeralPublicKeyWithViewTag{PublicKey: pKB, ViewTag: viewTag})
|
||||
return pKB, viewTag
|
||||
} else {
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/address"
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto"
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/types"
|
||||
"hash"
|
||||
)
|
||||
|
||||
type NilDerivationCache struct {
|
||||
|
@ -13,8 +14,8 @@ func (d *NilDerivationCache) Clear() {
|
|||
|
||||
}
|
||||
|
||||
func (d *NilDerivationCache) GetEphemeralPublicKey(a address.Interface, _ crypto.PrivateKeySlice, txKeyScalar *crypto.PrivateKeyScalar, outputIndex uint64) (crypto.PublicKeyBytes, uint8) {
|
||||
ephemeralPubKey, viewTag := address.GetEphemeralPublicKeyAndViewTag(a, txKeyScalar, outputIndex)
|
||||
func (d *NilDerivationCache) GetEphemeralPublicKey(a *address.PackedAddress, _ crypto.PrivateKeySlice, txKeyScalar *crypto.PrivateKeyScalar, outputIndex uint64, hasher hash.Hash) (crypto.PublicKeyBytes, uint8) {
|
||||
ephemeralPubKey, viewTag := address.GetEphemeralPublicKeyAndViewTagNoAllocate(a, txKeyScalar, outputIndex, hasher)
|
||||
|
||||
return ephemeralPubKey.AsBytes(), viewTag
|
||||
}
|
||||
|
|
|
@ -7,12 +7,14 @@ import (
|
|||
"git.gammaspectra.live/P2Pool/p2pool-observer/monero"
|
||||
mainblock "git.gammaspectra.live/P2Pool/p2pool-observer/monero/block"
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/client"
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto"
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/randomx"
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/transaction"
|
||||
p2pooltypes "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/types"
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/types"
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/utils"
|
||||
"golang.org/x/exp/slices"
|
||||
"hash"
|
||||
"log"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
@ -549,19 +551,30 @@ func (c *SideChain) verifyBlock(block *PoolBlock) (verification error, invalid e
|
|||
txPrivateKeySlice := block.Side.CoinbasePrivateKey.AsSlice()
|
||||
txPrivateKeyScalar := block.Side.CoinbasePrivateKey.AsScalar()
|
||||
|
||||
var hashers []hash.Hash
|
||||
|
||||
results := utils.SplitWork(-2, uint64(len(rewards)), func(workIndex uint64, workerIndex int) error {
|
||||
out := block.Main.Coinbase.Outputs[workIndex]
|
||||
if rewards[workIndex] != out.Reward {
|
||||
return fmt.Errorf("has invalid reward at index %d, got %d, expected %d", workIndex, out.Reward, rewards[workIndex])
|
||||
}
|
||||
|
||||
if ephPublicKey, viewTag := c.derivationCache.GetEphemeralPublicKey(&c.sharesCache[workIndex].Address, txPrivateKeySlice, txPrivateKeyScalar, workIndex); ephPublicKey != out.EphemeralPublicKey {
|
||||
if ephPublicKey, viewTag := c.derivationCache.GetEphemeralPublicKey(&c.sharesCache[workIndex].Address, txPrivateKeySlice, txPrivateKeyScalar, workIndex, hashers[workerIndex]); ephPublicKey != out.EphemeralPublicKey {
|
||||
return fmt.Errorf("has incorrect eph_public_key at index %d, got %s, expected %s", workIndex, out.EphemeralPublicKey.String(), ephPublicKey.String())
|
||||
} else if out.Type == transaction.TxOutToTaggedKey && viewTag != out.ViewTag {
|
||||
return fmt.Errorf("has incorrect view tag at index %d, got %d, expected %d", workIndex, out.ViewTag, viewTag)
|
||||
}
|
||||
return nil
|
||||
}, nil)
|
||||
}, func(routines, routineIndex int) error {
|
||||
hashers = append(hashers, crypto.GetKeccak256Hasher())
|
||||
return nil
|
||||
})
|
||||
|
||||
defer func() {
|
||||
for _, h := range hashers {
|
||||
crypto.PutKeccak256Hasher(h)
|
||||
}
|
||||
}()
|
||||
|
||||
for i := range results {
|
||||
if results[i] != nil {
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"git.gammaspectra.live/P2Pool/p2pool-observer/types"
|
||||
"git.gammaspectra.live/P2Pool/p2pool-observer/utils"
|
||||
"golang.org/x/exp/slices"
|
||||
"hash"
|
||||
"log"
|
||||
"lukechampine.com/uint128"
|
||||
"math"
|
||||
|
@ -42,18 +43,29 @@ func CalculateOutputs(block *PoolBlock, consensus *Consensus, difficultyByHeight
|
|||
txPrivateKeySlice := block.Side.CoinbasePrivateKey.AsSlice()
|
||||
txPrivateKeyScalar := block.Side.CoinbasePrivateKey.AsScalar()
|
||||
|
||||
var hashers []hash.Hash
|
||||
|
||||
_ = utils.SplitWork(-2, n, func(workIndex uint64, workerIndex int) error {
|
||||
output := transaction.Output{
|
||||
Index: workIndex,
|
||||
Type: txType,
|
||||
}
|
||||
output.Reward = tmpRewards[output.Index]
|
||||
output.EphemeralPublicKey, output.ViewTag = derivationCache.GetEphemeralPublicKey(&tmpShares[output.Index].Address, txPrivateKeySlice, txPrivateKeyScalar, output.Index)
|
||||
output.EphemeralPublicKey, output.ViewTag = derivationCache.GetEphemeralPublicKey(&tmpShares[output.Index].Address, txPrivateKeySlice, txPrivateKeyScalar, output.Index, hashers[workerIndex])
|
||||
|
||||
outputs[output.Index] = output
|
||||
|
||||
return nil
|
||||
}, nil)
|
||||
}, func(routines, routineIndex int) error {
|
||||
hashers = append(hashers, crypto.GetKeccak256Hasher())
|
||||
return nil
|
||||
})
|
||||
|
||||
defer func() {
|
||||
for _, h := range hashers {
|
||||
crypto.PutKeccak256Hasher(h)
|
||||
}
|
||||
}()
|
||||
|
||||
return outputs, bottomHeight
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue