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  }