Skip to content

Commit

Permalink
feat(nip13): add nip13, pow. (#85)
Browse files Browse the repository at this point in the history
  • Loading branch information
kehiy authored Nov 3, 2024
1 parent 463dc51 commit 765cba2
Show file tree
Hide file tree
Showing 8 changed files with 139 additions and 41 deletions.
24 changes: 12 additions & 12 deletions config/parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,18 +89,18 @@ func (c *Config) LoadParameters(db *database.Database) error {
Icon: "https://raw.githubusercontent.com/dezh-tech/immortal/refs/heads/main/assets/images/immortal.png",
WebsocketServer: &websocket.Config{
Limitation: &websocket.Limitation{
MaxMessageLength: 8192, // Maximum length of a single message (in bytes or characters)
MaxSubscriptions: 20, // Maximum number of concurrent subscriptions a client can create
MaxFilters: 20, // Maximum number of filters a client can apply in a subscription
MaxSubidLength: 256, // Maximum length of a subscription identifier
MinPowDifficulty: 0, // Minimum proof-of-work difficulty for publishing events
AuthRequired: false, // Whether authentication is required for writes
PaymentRequired: false, // Whether payment is required to interact with the relay
RestrictedWrites: false, // Whether writes are restricted to authenticated or paying users
MaxEventTags: 1000, // Maximum number of tags allowed in a single event
MaxContentLength: 4096, // Maximum content length of an event (in bytes)
CreatedAtLowerLimit: 0, // Earliest timestamp allowed for event creation
CreatedAtUpperLimit: 0, // Latest timestamp allowed for event creation (0 for no limit)
MaxMessageLength: 8192, // Maximum length of a single message (in bytes or characters)
MaxSubscriptions: 20, // Maximum number of concurrent subscriptions a client can create
MaxFilters: 20, // Maximum number of filters a client can apply in a subscription
MaxSubidLength: 256, // Maximum length of a subscription identifier
MinPowDifficulty: 0, // Minimum proof-of-work difficulty for publishing events
AuthRequired: false, // Whether authentication is required for writes
PaymentRequired: false, // Whether payment is required to interact with the relay
RestrictedWrites: false, // Whether writes are restricted to authenticated or paying users
MaxEventTags: 1000, // Maximum number of tags allowed in a single event
MaxContentLength: 4096, // Maximum content length of an event (in bytes)
CreatedAtLowerLimit: 0, // Earliest timestamp allowed for event creation
CreatedAtUpperLimit: 2_147_483_647, // Latest timestamp allowed for event creation
},
},
Handler: &handler.Config{
Expand Down
2 changes: 1 addition & 1 deletion documents/NIPs.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ The Immortal follows [NIPs](https://github.com/nostr-protocol/nips) and tries to
- [X] **NIP-01**: Basic Protocol Flow Description
- [ ] **NIP-09**: Event Deletion Request
- [X] **NIP-11**: Relay Information Document
- [ ] **NIP-13**: Proof of Work
- [X] **NIP-13**: Proof of Work
- [ ] **NIP-40**: Expiration Timestamp
- [ ] **NIP-42**: Authentication of Clients to Relays
- [ ] **NIP-50**: Search Capability
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ require (
github.com/joho/godotenv v1.5.1
github.com/mailru/easyjson v0.7.7
github.com/prometheus/client_golang v1.20.4
github.com/redis/go-redis/v9 v9.6.2
github.com/redis/go-redis/v9 v9.7.0
github.com/stretchr/testify v1.9.0
github.com/tidwall/gjson v1.18.0
go.mongodb.org/mongo-driver v1.17.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ github.com/prometheus/common v0.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJN
github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/redis/go-redis/v9 v9.6.2 h1:w0uvkRbc9KpgD98zcvo5IrVUsn0lXpRMuhNgiHDJzdk=
github.com/redis/go-redis/v9 v9.6.2/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA=
github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
Expand Down
24 changes: 12 additions & 12 deletions server/websocket/config.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
package websocket

type Limitation struct {
MaxMessageLength int `bson:"max_message_length" json:"max_message_length"` // todo.
MaxSubscriptions int `bson:"max_subscriptions" json:"max_subscriptions"`
MaxFilters int `bson:"max_filters" json:"max_filters"`
MaxSubidLength int `bson:"max_subid_length" json:"max_subid_length"`
MinPowDifficulty int `bson:"min_pow_difficulty" json:"min_pow_difficulty"` // todo.
AuthRequired bool `bson:"auth_required" json:"auth_required"` // todo.
PaymentRequired bool `bson:"payment_required" json:"payment_required"` // todo.
RestrictedWrites bool `bson:"restricted_writes" json:"restricted_writes"` // todo.
MaxEventTags int `bson:"max_event_tags" json:"max_event_tags"` // todo.
MaxContentLength int `bson:"max_content_length" json:"max_content_length"`
CreatedAtLowerLimit int `bson:"created_at_lower_limit" json:"created_at_lower_limit"` // todo.
CreatedAtUpperLimit int `bson:"created_at_upper_limit" json:"created_at_upper_limit"` // todo.
MaxMessageLength int `bson:"max_message_length" json:"max_message_length"` // todo?.
MaxSubscriptions int `bson:"max_subscriptions" json:"max_subscriptions"`
MaxFilters int `bson:"max_filters" json:"max_filters"`
MaxSubidLength int `bson:"max_subid_length" json:"max_subid_length"`
MinPowDifficulty int `bson:"min_pow_difficulty" json:"min_pow_difficulty"`
AuthRequired bool `bson:"auth_required" json:"auth_required"` // todo.
PaymentRequired bool `bson:"payment_required" json:"payment_required"` // todo.
RestrictedWrites bool `bson:"restricted_writes" json:"restricted_writes"` // todo.
MaxEventTags int `bson:"max_event_tags" json:"max_event_tags"`
MaxContentLength int `bson:"max_content_length" json:"max_content_length"`
CreatedAtLowerLimit int64 `bson:"created_at_lower_limit" json:"created_at_lower_limit"`
CreatedAtUpperLimit int64 `bson:"created_at_upper_limit" json:"created_at_upper_limit"`
}

type Config struct {
Expand Down
67 changes: 54 additions & 13 deletions server/websocket/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,19 +266,6 @@ func (s *Server) handleEvent(conn *websocket.Conn, m message.Message) {
return
}

if len(msg.Event.Content) > s.config.Limitation.MaxContentLength {
okm := message.MakeOK(false,
"",
fmt.Sprintf("error: max limit of message length is %d", s.config.Limitation.MaxContentLength),
)

_ = conn.WriteMessage(1, okm)

status = limitsFail

return
}

eID := msg.Event.GetRawID()

qCtx, cancel := context.WithTimeout(context.Background(), s.redis.QueryTimeout)
Expand Down Expand Up @@ -309,6 +296,60 @@ func (s *Server) handleEvent(conn *websocket.Conn, m message.Message) {
return
}

if len(msg.Event.Content) > s.config.Limitation.MaxContentLength {
okm := message.MakeOK(false,
"",
fmt.Sprintf("error: max limit of content length is %d", s.config.Limitation.MaxContentLength),
)

_ = conn.WriteMessage(1, okm)

status = limitsFail

return
}

if msg.Event.Difficulty() < s.config.Limitation.MinPowDifficulty {
okm := message.MakeOK(false,
"",
fmt.Sprintf("error: min pow required is %d", s.config.Limitation.MinPowDifficulty),
)

_ = conn.WriteMessage(1, okm)

status = limitsFail

return
}

if len(msg.Event.Tags) < s.config.Limitation.MaxEventTags {
okm := message.MakeOK(false,
"",
fmt.Sprintf("error: max limit of tags count is %d", s.config.Limitation.MaxEventTags),
)

_ = conn.WriteMessage(1, okm)

status = limitsFail

return
}

if msg.Event.CreatedAt < s.config.Limitation.CreatedAtLowerLimit ||
msg.Event.CreatedAt > s.config.Limitation.CreatedAtUpperLimit {
okm := message.MakeOK(false,
"",
fmt.Sprintf("error: created at must be as least %d and at most %d",
s.config.Limitation.CreatedAtLowerLimit, s.config.Limitation.CreatedAtUpperLimit),
)

_ = conn.WriteMessage(1, okm)

status = limitsFail

return
}

if !msg.Event.Kind.IsEphemeral() {
err := s.handlers.HandleEvent(msg.Event)
if err != nil {
Expand Down
30 changes: 30 additions & 0 deletions types/event/nip13.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package event

import (
"encoding/hex"
"math/bits"
)

// Difficulty returns the leading zeros of event id in base-2.
func (e *Event) Difficulty() int {
var zeros int
var b [1]byte

for i := 0; i < 64; i += 2 {
if e.ID[i:i+2] == "00" {
zeros += 8

continue
}

if _, err := hex.Decode(b[:], []byte{e.ID[i], e.ID[i+1]}); err != nil {
return -1
}

zeros += bits.LeadingZeros8(b[0])

break
}

return zeros
}
27 changes: 27 additions & 0 deletions types/event/nip13_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package event_test

import (
"testing"

"github.com/dezh-tech/immortal/types/event"
"github.com/stretchr/testify/assert"
)

func TestDifficulty(t *testing.T) {
testCases := []struct {
result int
id string
}{
{36, "000000000e9d97a1ab09fc381030b346cdd7a142ad57e6df0b46dc9bef6c7e2d"},
{22, "0000024d38993bae75a61e82710842305fac9cda280f541476c31426c42ca81a"},
{0, "f2775f4eeaa0aa45f66440490b45d6aede8d1ceb7ac443e6328763db5ce8d6e3"},
{7, "010b807e82a1417588be0bcd7606b2aae4163a365afe1f6c97404b17fc56d30b"},
{21, "000004f20d022b65dd961cbbdc157347dbd37ca375899fe38b40d174cccd8ee3"},
{18, "00003db72b8385511ef2c1dd5fb3a43988269c9d7f51986f03c1aecd675dc506"},
}

for _, tc := range testCases {
e := event.Event{ID: tc.id}
assert.Equal(t, tc.result, e.Difficulty())
}
}

0 comments on commit 765cba2

Please sign in to comment.