go-nostr/nip13/nip13_test.go
alex 5bfb398f4d nip13: check and generate proof of work
implementation as per NIP-13
https://github.com/nostr-protocol/nips/blob/e79c84ae/13.md

some benchmark results:

    $ go test -bench . ./nip13
    goos: linux
    goarch: amd64
    pkg: github.com/nbd-wtf/go-nostr/nip13
    cpu: Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz
    BenchmarkCheck-8                        144905385                8.328 ns/op
    BenchmarkGenerateOneIteration-8           467874              2451 ns/op
    BenchmarkGenerate/8bits-8                   4045           1033196 ns/op
    BenchmarkGenerate/16bits-8                    16         101939954 ns/op
    BenchmarkGenerate/24bits-8                     1        11411513764 ns/op
    PASS
    ok      github.com/nbd-wtf/go-nostr/nip13       22.220s
2022-12-26 08:05:40 -03:00

150 lines
3.7 KiB
Go

package nip13
import (
"errors"
"fmt"
"strconv"
"testing"
"time"
nostr "github.com/nbd-wtf/go-nostr"
)
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 TestGenerateShort(t *testing.T) {
event := &nostr.Event{
Kind: 1,
Content: "It's just me mining my own business",
PubKey: "a48380f4cfcc1ad5378294fcac36439770f9c878dd880ffa94bb74ea54a6f243",
}
pow, err := Generate(event, 0, 3*time.Second)
if err != nil {
t.Fatal(err)
}
testNonceTag(t, pow, 0)
}
func TestGenerateLong(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: 1,
Content: "It's just me mining my own business",
PubKey: "a48380f4cfcc1ad5378294fcac36439770f9c878dd880ffa94bb74ea54a6f243",
}
pow, err := Generate(event, difficulty, time.Minute)
if err != nil {
t.Fatal(err)
}
if err := Check(pow.GetID(), difficulty); err != nil {
t.Error(err)
}
testNonceTag(t, pow, difficulty)
})
}
}
func testNonceTag(t *testing.T, event *nostr.Event, commitment int) {
t.Helper()
tagptr := event.Tags.GetFirst([]string{"nonce"})
if tagptr == nil {
t.Fatal("no nonce tag")
}
tag := *tagptr
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 TestGenerateTimeout(t *testing.T) {
event := &nostr.Event{
Kind: 1,
Content: "It's just me mining my own business",
PubKey: "a48380f4cfcc1ad5378294fcac36439770f9c878dd880ffa94bb74ea54a6f243",
}
done := make(chan error)
go func() {
_, err := Generate(event, 256, time.Millisecond)
done <- err
}()
select {
case <-time.After(time.Second):
t.Error("Generate took too long to timeout")
case err := <-done:
if !errors.Is(err, ErrGenerateTimeout) {
t.Errorf("Generate returned %v; want ErrGenerateTimeout", err)
}
}
}
func BenchmarkCheck(b *testing.B) {
for i := 0; i < b.N; i++ {
Check("000000000e9d97a1ab09fc381030b346cdd7a142ad57e6df0b46dc9bef6c7e2d", 36)
}
}
func BenchmarkGenerateOneIteration(b *testing.B) {
for i := 0; i < b.N; i++ {
event := &nostr.Event{
Kind: 1,
Content: "It's just me mining my own business",
PubKey: "a48380f4cfcc1ad5378294fcac36439770f9c878dd880ffa94bb74ea54a6f243",
}
if _, err := Generate(event, 0, time.Minute); err != nil {
b.Fatal(err)
}
}
}
func BenchmarkGenerate(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: 1,
Content: "It's just me mining my own business",
PubKey: "a48380f4cfcc1ad5378294fcac36439770f9c878dd880ffa94bb74ea54a6f243",
}
if _, err := Generate(event, difficulty, time.Minute); err != nil {
b.Fatal(err)
}
}
})
}
}