package miner import ( "errors" "fmt" "strings" "testing" ) func TestNewEngineNotNil(t *testing.T) { e := NewEngine() if e == nil || e.cache == nil { t.Fatal("NewEngine should allocate cache") } } func TestEngineSetJobInvalidSeed(t *testing.T) { e := NewEngine() if err := e.SetJob("zz", strings.Repeat("00", 76)); err == nil { t.Fatal("invalid seed hex should error") } } func TestEngineSetJobInvalidBlob(t *testing.T) { e := NewEngine() seed := strings.Repeat("ab", 32) if err := e.SetJob(seed, "not-hex"); err == nil { t.Fatal("invalid blob hex should error") } } func TestEngineHashAtNonceNoVM(t *testing.T) { e := NewEngine() hash, blob, err := e.HashAtNonce(0) if !errors.Is(err, ErrEngineNotReady) { t.Fatalf("expected ErrEngineNotReady, got err=%v hash=%q blob=%q", err, hash, blob) } if hash != "" || blob != "" { t.Fatalf("unready VM should return empty strings, got hash=%q blob=%q", hash, blob) } } func TestEngineHashAtNonceShortBlob(t *testing.T) { e := NewEngine() seed := strings.Repeat("cd", 32) if err := e.SetJob(seed, strings.Repeat("00", 20)); err != nil { t.Fatal(err) } hash, blob, err := e.HashAtNonce(1) if !errors.Is(err, ErrBlobTooShort) { t.Fatalf("expected ErrBlobTooShort, got err=%v hash=%q blob=%q", err, hash, blob) } if hash != "" || blob != "" { t.Fatalf("short blob should return empty strings, got hash=%q blob=%q", hash, blob) } } // TestEngineFullHash verifies that HashAtNonce produces a valid 32-byte hash // when given a proper 76-byte Monero block header. This is the critical path // that was previously broken due to missing SuperScalar program initialization. func TestEngineFullHash(t *testing.T) { // Known test vector from go-randomx's own test suite. key := []byte("test key 000") input := []byte("This is a test") // Use the same seed the library uses: the raw key bytes hex-encoded. seedHex := strings.ToLower(fmt.Sprintf("%x", key)) // Build a synthetic 76-byte blob (the engine only needs seed + blob; we use // the "input" bytes zero-padded to 76 bytes so nonce sits at offset 39). blobBytes := make([]byte, 76) copy(blobBytes, input) blobHex := fmt.Sprintf("%x", blobBytes) e := NewEngine() if err := e.SetJob(seedHex, blobHex); err != nil { t.Fatalf("SetJob: %v", err) } hashHex, gotBlob, err := e.HashAtNonce(0) if err != nil { t.Fatalf("HashAtNonce: %v", err) } if len(hashHex) != 64 { t.Fatalf("expected 64-char hash hex, got %d chars: %q", len(hashHex), hashHex) } if len(gotBlob) != 152 { t.Fatalf("expected 152-char blob hex, got %d chars", len(gotBlob)) } // Ensure two sequential nonces produce different hashes. hashHex2, _, err := e.HashAtNonce(1) if err != nil { t.Fatalf("HashAtNonce(1): %v", err) } if hashHex == hashHex2 { t.Fatal("nonce 0 and nonce 1 produced identical hashes — nonce injection broken") } } // TestEngineReSeedChangesHash confirms the engine re-seeds when the seed changes. func TestEngineReSeedChangesHash(t *testing.T) { blobHex := strings.Repeat("0c", 76) seed1 := strings.Repeat("aa", 32) seed2 := strings.Repeat("bb", 32) e := NewEngine() if err := e.SetJob(seed1, blobHex); err != nil { t.Fatalf("SetJob seed1: %v", err) } h1, _, _ := e.HashAtNonce(0) if err := e.SetJob(seed2, blobHex); err != nil { t.Fatalf("SetJob seed2: %v", err) } h2, _, _ := e.HashAtNonce(0) if h1 == h2 { t.Fatal("different seeds produced identical hash — re-seed is broken") } }