package nip13 import ( "context" "errors" "fmt" "strconv" "testing" "time" nostr "github.com/nbd-wtf/go-nostr" "github.com/stretchr/testify/require" ) func TestCheck(t *testing.T) { const eventID = "000000000e9d97a1ab09fc381030b346cdd7a142ad57e6df0b46dc9bef6c7e2d" tests := []struct { minDifficulty int wantErr error }{ {-1, nil}, {0, nil}, {1, nil}, {35, nil}, {36, nil}, {37, ErrDifficultyTooLow}, {42, ErrDifficultyTooLow}, } for i, tc := range tests { if err := Check(eventID, tc.minDifficulty); err != tc.wantErr { t.Errorf("%d: Check(%q, %d) returned %v; want err: %v", i, eventID, tc.minDifficulty, err, tc.wantErr) } } } func TestCommittedDifficulty(t *testing.T) { tests := []struct { result int id string tags nostr.Tags }{ {18, "000000000e9d97a1ab09fc381030b346cdd7a142ad57e6df0b46dc9bef6c7e2d", nostr.Tags{{"-"}, {"nonce", "654", "18"}}}, {36, "000000000e9d97a1ab09fc381030b346cdd7a142ad57e6df0b46dc9bef6c7e2d", nostr.Tags{{"nonce", "12315", "36"}}}, {0, "000000000e9d97a1ab09fc381030b346cdd7a142ad57e6df0b46dc9bef6c7e2d", nostr.Tags{{"nonce", "12315", "37"}}}, {0, "000000000e9d97a1ab09fc381030b346cdd7a142ad57e6df0b46dc9bef6c7e2d", nostr.Tags{{"nonce", "654", "64"}, {"t", "spam"}}}, {0, "000000000e9d97a1ab09fc381030b346cdd7a142ad57e6df0b46dc9bef6c7e2d", nostr.Tags{}}, } for i, tc := range tests { t.Run(strconv.Itoa(i), func(t *testing.T) { work := CommittedDifficulty(&nostr.Event{ID: tc.id, Tags: tc.tags}) require.Equal(t, tc.result, work) }) } } func TestDoWorkShort(t *testing.T) { event := nostr.Event{ Kind: nostr.KindTextNote, Content: "It's just me mining my own business", PubKey: "a48380f4cfcc1ad5378294fcac36439770f9c878dd880ffa94bb74ea54a6f243", } pow, err := DoWork(context.Background(), event, 2) if err != nil { t.Fatal(err) } testNonceTag(t, pow, 2) } func TestDoWorkLong(t *testing.T) { if testing.Short() { t.Skip("too consuming for short mode") } for _, difficulty := range []int{8, 16} { difficulty := difficulty t.Run(fmt.Sprintf("%dbits", difficulty), func(t *testing.T) { t.Parallel() event := nostr.Event{ Kind: nostr.KindTextNote, Content: "It's just me mining my own business", PubKey: "a48380f4cfcc1ad5378294fcac36439770f9c878dd880ffa94bb74ea54a6f243", } ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() pow, err := DoWork(ctx, event, difficulty) if err != nil { t.Fatal(err) } event.Tags = append(event.Tags, pow) if err := Check(event.GetID(), difficulty); err != nil { t.Error(err) } testNonceTag(t, pow, difficulty) }) } } func testNonceTag(t *testing.T, tag nostr.Tag, commitment int) { t.Helper() if tag[0] != "nonce" { t.Errorf("tag[0] = %q; want 'nonce'", tag[0]) } if n, err := strconv.ParseInt(tag[1], 10, 64); err != nil || n < 1 { t.Errorf("tag[1] = %q; want an int greater than 0", tag[1]) } if n, err := strconv.Atoi(tag[2]); err != nil || n != commitment { t.Errorf("tag[2] = %q; want %d", tag[2], commitment) } } func TestDoWorkTimeout(t *testing.T) { event := nostr.Event{ Kind: nostr.KindTextNote, Content: "It's just me mining my own business", PubKey: "a48380f4cfcc1ad5378294fcac36439770f9c878dd880ffa94bb74ea54a6f243", } done := make(chan error) go func() { ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond) defer cancel() _, err := DoWork(ctx, event, 256) done <- err }() select { case <-time.After(time.Second): t.Error("DoWork took too long to timeout") case err := <-done: if !errors.Is(err, ErrGenerateTimeout) { t.Errorf("DoWork returned %v; want ErrDoWorkTimeout", err) } } } func BenchmarkDoWork(b *testing.B) { if testing.Short() { b.Skip("too consuming for short mode") } for _, difficulty := range []int{8, 16, 24} { difficulty := difficulty b.Run(fmt.Sprintf("%dbits", difficulty), func(b *testing.B) { for i := 0; i < b.N; i++ { event := &nostr.Event{ Kind: nostr.KindTextNote, Content: "It's just me mining my own business", PubKey: "a48380f4cfcc1ad5378294fcac36439770f9c878dd880ffa94bb74ea54a6f243", } ctx, cancel := context.WithTimeout(context.Background(), time.Second*60) defer cancel() if _, err := DoWork(ctx, *event, difficulty); err != nil { b.Fatal(err) } } }) } }