git.gammaspectra.live/P2Pool/consensus/v3@v3.8.0/p2pool/stratum/stratum_test.go (about) 1 package stratum 2 3 import ( 4 "compress/gzip" 5 "fmt" 6 "git.gammaspectra.live/P2Pool/consensus/v3/monero/address" 7 "git.gammaspectra.live/P2Pool/consensus/v3/monero/client" 8 "git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto" 9 "git.gammaspectra.live/P2Pool/consensus/v3/p2pool/sidechain" 10 p2pooltypes "git.gammaspectra.live/P2Pool/consensus/v3/p2pool/types" 11 "git.gammaspectra.live/P2Pool/consensus/v3/types" 12 "git.gammaspectra.live/P2Pool/consensus/v3/utils" 13 unsafeRandom "math/rand/v2" 14 "os" 15 "path" 16 "runtime" 17 "slices" 18 "testing" 19 "time" 20 _ "unsafe" 21 ) 22 23 var preLoadedMiniSideChain *sidechain.SideChain 24 25 var preLoadedPoolBlock *sidechain.PoolBlock 26 27 func init() { 28 utils.GlobalLogLevel = 0 29 30 _, filename, _, _ := runtime.Caller(0) 31 // The ".." may change depending on you folder structure 32 dir := path.Join(path.Dir(filename), "../..") 33 err := os.Chdir(dir) 34 if err != nil { 35 panic(err) 36 } 37 38 _ = sidechain.ConsensusDefault.InitHasher(2) 39 _ = sidechain.ConsensusMini.InitHasher(2) 40 client.SetDefaultClientSettings(os.Getenv("MONEROD_RPC_URL")) 41 } 42 43 func getMinerData() *p2pooltypes.MinerData { 44 if d, err := client.GetDefaultClient().GetMinerData(); err != nil { 45 return nil 46 } else { 47 return &p2pooltypes.MinerData{ 48 MajorVersion: d.MajorVersion, 49 Height: d.Height, 50 PrevId: d.PrevId, 51 SeedHash: d.SeedHash, 52 Difficulty: d.Difficulty, 53 MedianWeight: d.MedianWeight, 54 AlreadyGeneratedCoins: d.AlreadyGeneratedCoins, 55 MedianTimestamp: d.MedianTimestamp, 56 TimeReceived: time.Now(), 57 TxBacklog: nil, 58 } 59 } 60 } 61 62 func TestMain(m *testing.M) { 63 if buf, err := os.ReadFile("testdata/block.dat"); err != nil { 64 panic(err) 65 } else { 66 preLoadedPoolBlock = &sidechain.PoolBlock{} 67 if err = preLoadedPoolBlock.UnmarshalBinary(sidechain.ConsensusDefault, &sidechain.NilDerivationCache{}, buf); err != nil { 68 panic(err) 69 } 70 } 71 72 _ = sidechain.ConsensusMini.InitHasher(2) 73 client.SetDefaultClientSettings(os.Getenv("MONEROD_RPC_URL")) 74 75 preLoadedMiniSideChain = sidechain.NewSideChain(sidechain.GetFakeTestServer(sidechain.ConsensusMini)) 76 77 f, err := os.Open("testdata/sidechain_dump_mini.dat.gz") 78 if err != nil { 79 panic(err) 80 } 81 defer f.Close() 82 83 r, err := gzip.NewReader(f) 84 if err != nil { 85 panic(err) 86 } 87 defer r.Close() 88 89 if err = sidechain.LoadSideChainTestData(preLoadedMiniSideChain, r); err != nil { 90 panic(err) 91 } 92 93 code := m.Run() 94 95 os.Exit(code) 96 } 97 98 func TestStratumServer(t *testing.T) { 99 stratumServer := NewServer(preLoadedMiniSideChain, func(block *sidechain.PoolBlock) error { 100 return nil 101 }) 102 minerData := getMinerData() 103 tip := preLoadedMiniSideChain.GetChainTip() 104 stratumServer.HandleMinerData(minerData) 105 stratumServer.HandleTip(tip) 106 107 func() { 108 //Process all incoming changes first 109 for { 110 select { 111 case f := <-stratumServer.incomingChanges: 112 if f() { 113 stratumServer.Update() 114 } 115 default: 116 return 117 } 118 } 119 }() 120 121 tpl, _, _, seedHash, err := stratumServer.BuildTemplate(address.FromBase58(types.DonationAddress).ToPackedAddress(), false) 122 if err != nil { 123 t.Fatal(err) 124 } 125 126 if seedHash != minerData.SeedHash { 127 t.Fatal() 128 } 129 130 if tpl.MainHeight != minerData.Height { 131 t.Fatal() 132 } 133 134 if tpl.MainParent != minerData.PrevId { 135 t.Fatal() 136 } 137 138 if tpl.SideHeight != (tip.Side.Height + 1) { 139 t.Fatal() 140 } 141 142 if tpl.SideParent != tip.SideTemplateId(preLoadedMiniSideChain.Consensus()) { 143 t.Fatal() 144 } 145 } 146 147 func TestShuffleMapping(t *testing.T) { 148 const n = 16 149 const shareVersion = sidechain.ShareVersion_V2 150 var seed = zeroExtraBaseRCTHash 151 mappings := BuildShuffleMapping(n, shareVersion, seed) 152 153 seq := make([]int, n) 154 for i := range seq { 155 seq[i] = i 156 } 157 158 seq1 := slices.Clone(seq) 159 160 sidechain.ShuffleShares(seq1, shareVersion, seed) 161 seq2 := ApplyShuffleMapping(seq, mappings) 162 163 if slices.Compare(seq1, seq2) != 0 { 164 for i := range seq1 { 165 if seq1[i] != seq2[i] { 166 t.Logf("%d %d *** @ %d", seq1[i], seq2[i], i) 167 } else { 168 t.Logf("%d %d @ %d", seq1[i], seq2[i], i) 169 } 170 } 171 172 t.Fatal() 173 } 174 175 } 176 177 func BenchmarkServer_FillTemplate(b *testing.B) { 178 stratumServer := NewServer(preLoadedMiniSideChain, func(block *sidechain.PoolBlock) error { 179 return nil 180 }) 181 minerData := getMinerData() 182 tip := preLoadedMiniSideChain.GetChainTip() 183 stratumServer.minerData = minerData 184 stratumServer.tip = tip 185 186 b.ResetTimer() 187 188 b.Run("New", func(b *testing.B) { 189 for i := 0; i < b.N; i++ { 190 if err := stratumServer.fillNewTemplateData(minerData.Difficulty); err != nil { 191 b.Fatal(err) 192 } 193 } 194 b.ReportAllocs() 195 }) 196 197 b.Run("Cached", func(b *testing.B) { 198 for i := 0; i < b.N; i++ { 199 if err := stratumServer.fillNewTemplateData(types.ZeroDifficulty); err != nil { 200 b.Fatal(err) 201 } 202 } 203 b.ReportAllocs() 204 }) 205 206 } 207 208 func BenchmarkServer_BuildTemplate(b *testing.B) { 209 stratumServer := NewServer(preLoadedMiniSideChain, func(block *sidechain.PoolBlock) error { 210 return nil 211 }) 212 minerData := getMinerData() 213 tip := preLoadedMiniSideChain.GetChainTip() 214 stratumServer.minerData = minerData 215 stratumServer.tip = tip 216 217 if err := stratumServer.fillNewTemplateData(minerData.Difficulty); err != nil { 218 b.Fatal(err) 219 } 220 221 const randomPoolSize = 512 222 var randomKeys [randomPoolSize]address.PackedAddress 223 224 //generate random keys deterministically 225 for i := range randomKeys { 226 spendPriv, viewPriv := crypto.DeterministicScalar([]byte(fmt.Sprintf("BenchmarkBuildTemplate_%d_spend", i))), crypto.DeterministicScalar([]byte(fmt.Sprintf("BenchmarkBuildTemplate_%d_spend", i))) 227 randomKeys[i][0] = (*crypto.PrivateKeyScalar)(spendPriv).PublicKey().AsBytes() 228 randomKeys[i][1] = (*crypto.PrivateKeyScalar)(viewPriv).PublicKey().AsBytes() 229 } 230 231 b.ResetTimer() 232 b.ReportAllocs() 233 234 b.Run("Cached", func(b *testing.B) { 235 b.RunParallel(func(pb *testing.PB) { 236 counter := unsafeRandom.IntN(randomPoolSize) 237 for pb.Next() { 238 a := randomKeys[counter%randomPoolSize] 239 if _, _, _, _, err := stratumServer.BuildTemplate(a, false); err != nil { 240 b.Fatal(err) 241 } 242 counter++ 243 } 244 }) 245 b.ReportAllocs() 246 }) 247 248 b.Run("Forced", func(b *testing.B) { 249 b.RunParallel(func(pb *testing.PB) { 250 counter := unsafeRandom.IntN(randomPoolSize) 251 for pb.Next() { 252 a := randomKeys[counter%randomPoolSize] 253 if _, _, _, _, err := stratumServer.BuildTemplate(a, true); err != nil { 254 b.Fatal(err) 255 } 256 counter++ 257 } 258 }) 259 b.ReportAllocs() 260 }) 261 }