allow using libsecp256k1 for signature verification in subscriptions.
This commit is contained in:
@@ -1,4 +1,8 @@
|
|||||||
This is faster than the pure Go version:
|
This wraps [libsecp256k1](https://github.com/bitcoin-core/secp256k1) with `cgo`.
|
||||||
|
|
||||||
|
It doesn't embed the library or anything smart like that because I don't know how to do it, so you must have it installed in your system.
|
||||||
|
|
||||||
|
It is faster than the pure Go version:
|
||||||
|
|
||||||
```
|
```
|
||||||
goos: linux
|
goos: linux
|
||||||
@@ -8,3 +12,22 @@ cpu: AMD Ryzen 3 3200G with Radeon Vega Graphics
|
|||||||
BenchmarkSignatureVerification/btcec-4 145 7873130 ns/op 127069 B/op 579 allocs/op
|
BenchmarkSignatureVerification/btcec-4 145 7873130 ns/op 127069 B/op 579 allocs/op
|
||||||
BenchmarkSignatureVerification/libsecp256k1-4 502 2314573 ns/op 112241 B/op 392 allocs/op
|
BenchmarkSignatureVerification/libsecp256k1-4 502 2314573 ns/op 112241 B/op 392 allocs/op
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To use it manually, just import. To use it inside the automatic verification that happens for subscriptions, set it up with a `SimplePool`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
pool := nostr.NewSimplePool()
|
||||||
|
pool.SignatureChecker = func (evt nostr.Event) bool {
|
||||||
|
ok, _ := libsecp256k1.CheckSignature(evt)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Or directly to the `Relay`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
relay := nostr.RelayConnect(context.Background(), "wss://relay.nostr.com", nostr.WithSignatureChecker(func (evt nostr.Event) bool {
|
||||||
|
ok, _ := libsecp256k1.CheckSignature(evt)
|
||||||
|
return ok
|
||||||
|
}))
|
||||||
|
```
|
||||||
|
|||||||
11
pool.go
11
pool.go
@@ -22,6 +22,9 @@ type SimplePool struct {
|
|||||||
|
|
||||||
authHandler func(*Event) error
|
authHandler func(*Event) error
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
|
|
||||||
|
// custom things not often used
|
||||||
|
SignatureChecker func(Event) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type DirectedFilters struct {
|
type DirectedFilters struct {
|
||||||
@@ -85,7 +88,13 @@ func (pool *SimplePool) EnsureRelay(url string) (*Relay, error) {
|
|||||||
// we use this ctx here so when the pool dies everything dies
|
// we use this ctx here so when the pool dies everything dies
|
||||||
ctx, cancel := context.WithTimeout(pool.Context, time.Second*15)
|
ctx, cancel := context.WithTimeout(pool.Context, time.Second*15)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
if relay, err = RelayConnect(ctx, nm); err != nil {
|
|
||||||
|
opts := make([]RelayOption, 0, 1)
|
||||||
|
if pool.SignatureChecker != nil {
|
||||||
|
opts = append(opts, WithSignatureChecker(pool.SignatureChecker))
|
||||||
|
}
|
||||||
|
|
||||||
|
if relay, err = RelayConnect(ctx, nm, opts...); err != nil {
|
||||||
return nil, fmt.Errorf("failed to connect: %w", err)
|
return nil, fmt.Errorf("failed to connect: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
47
relay.go
47
relay.go
@@ -38,6 +38,7 @@ type Relay struct {
|
|||||||
okCallbacks *xsync.MapOf[string, func(bool, string)]
|
okCallbacks *xsync.MapOf[string, func(bool, string)]
|
||||||
writeQueue chan writeRequest
|
writeQueue chan writeRequest
|
||||||
subscriptionChannelCloseQueue chan *Subscription
|
subscriptionChannelCloseQueue chan *Subscription
|
||||||
|
signatureChecker func(Event) bool
|
||||||
|
|
||||||
// custom things that aren't often used
|
// custom things that aren't often used
|
||||||
//
|
//
|
||||||
@@ -60,18 +61,14 @@ func NewRelay(ctx context.Context, url string, opts ...RelayOption) *Relay {
|
|||||||
okCallbacks: xsync.NewMapOf[string, func(bool, string)](),
|
okCallbacks: xsync.NewMapOf[string, func(bool, string)](),
|
||||||
writeQueue: make(chan writeRequest),
|
writeQueue: make(chan writeRequest),
|
||||||
subscriptionChannelCloseQueue: make(chan *Subscription),
|
subscriptionChannelCloseQueue: make(chan *Subscription),
|
||||||
|
signatureChecker: func(e Event) bool {
|
||||||
|
ok, _ := e.CheckSignature()
|
||||||
|
return ok
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
switch o := opt.(type) {
|
opt.ApplyRelayOption(r)
|
||||||
case WithNoticeHandler:
|
|
||||||
r.notices = make(chan string)
|
|
||||||
go func() {
|
|
||||||
for notice := range r.notices {
|
|
||||||
o(notice)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return r
|
return r
|
||||||
@@ -89,16 +86,34 @@ func RelayConnect(ctx context.Context, url string, opts ...RelayOption) (*Relay,
|
|||||||
// When instantiating relay connections, some options may be passed.
|
// When instantiating relay connections, some options may be passed.
|
||||||
// RelayOption is the type of the argument passed for that.
|
// RelayOption is the type of the argument passed for that.
|
||||||
type RelayOption interface {
|
type RelayOption interface {
|
||||||
IsRelayOption()
|
ApplyRelayOption(*Relay)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ RelayOption = (WithNoticeHandler)(nil)
|
||||||
|
_ RelayOption = (WithSignatureChecker)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
// WithNoticeHandler just takes notices and is expected to do something with them.
|
// WithNoticeHandler just takes notices and is expected to do something with them.
|
||||||
// when not given, defaults to logging the notices.
|
// when not given, defaults to logging the notices.
|
||||||
type WithNoticeHandler func(notice string)
|
type WithNoticeHandler func(notice string)
|
||||||
|
|
||||||
func (_ WithNoticeHandler) IsRelayOption() {}
|
func (nh WithNoticeHandler) ApplyRelayOption(r *Relay) {
|
||||||
|
r.notices = make(chan string)
|
||||||
|
go func() {
|
||||||
|
for notice := range r.notices {
|
||||||
|
nh(notice)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
var _ RelayOption = (WithNoticeHandler)(nil)
|
// WithSignatureChecker must be a function that checks the signature of an
|
||||||
|
// event and returns true or false.
|
||||||
|
type WithSignatureChecker func(Event) bool
|
||||||
|
|
||||||
|
func (sc WithSignatureChecker) ApplyRelayOption(r *Relay) {
|
||||||
|
r.signatureChecker = sc
|
||||||
|
}
|
||||||
|
|
||||||
// String just returns the relay URL.
|
// String just returns the relay URL.
|
||||||
func (r *Relay) String() string {
|
func (r *Relay) String() string {
|
||||||
@@ -237,12 +252,8 @@ func (r *Relay) ConnectWithTLS(ctx context.Context, tlsConfig *tls.Config) error
|
|||||||
|
|
||||||
// check signature, ignore invalid, except from trusted (AssumeValid) relays
|
// check signature, ignore invalid, except from trusted (AssumeValid) relays
|
||||||
if !r.AssumeValid {
|
if !r.AssumeValid {
|
||||||
if ok, err := env.Event.CheckSignature(); !ok {
|
if ok := r.signatureChecker(env.Event); !ok {
|
||||||
errmsg := ""
|
InfoLogger.Printf("{%s} bad signature on %s\n", r.URL, env.Event.ID)
|
||||||
if err != nil {
|
|
||||||
errmsg = err.Error()
|
|
||||||
}
|
|
||||||
InfoLogger.Printf("{%s} bad signature on %s; %s\n", r.URL, env.Event.ID, errmsg)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user