Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: app child key derived from wallet master key #736

Merged
merged 55 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
83de003
feat(appwalletKey): add GetBIP32ChildKey
frnandu Oct 14, 2024
546322c
feat: fix interface
frnandu Oct 14, 2024
8cbe566
feat: adding subscription WiP
frnandu Oct 15, 2024
853beed
feat: handle nostr subscriptions for lifecycle of apps
frnandu Oct 18, 2024
8cb135d
Delete .idea/.gitignore
frnandu Oct 18, 2024
616a69b
Delete .idea/hub.iml
frnandu Oct 18, 2024
55113ce
Delete .idea/modules.xml
frnandu Oct 18, 2024
78c2faa
Delete .idea/vcs.xml
frnandu Oct 18, 2024
e7ae82c
fix: remove unnecessary
frnandu Oct 18, 2024
dc404dd
fix: missing handling legacy app
frnandu Oct 18, 2024
b145c41
fix: review fixes
frnandu Oct 21, 2024
23b35c8
fix: use app.ID for key calculation instead of passing in event
frnandu Oct 21, 2024
b64c8cf
fix: add TODO
frnandu Oct 21, 2024
d0b0354
fix: remove unnecessary check
frnandu Oct 25, 2024
0c5949c
fix: improve err handling and remove check
frnandu Oct 25, 2024
324f113
Merge remote-tracking branch 'origin/master' into feat/wallet-child-k…
rolznz Oct 28, 2024
b4d1f3e
chore: store master nostr key to avoid deriving each time
rolznz Oct 29, 2024
a1dc936
chore: rename app nostr_pubkey to app_pubkey, extract app consumers i…
rolznz Oct 29, 2024
bedc82d
chore: finish renaming
rolznz Oct 29, 2024
200c172
fix: update app wallet pubkey on app creation
rolznz Oct 29, 2024
aef3142
fix: not NULL check
rolznz Oct 29, 2024
96a71bc
fix: fix HandleEvent
frnandu Oct 29, 2024
b359339
fix: error handling
frnandu Oct 29, 2024
aa2c3c1
fix: error handling
frnandu Oct 29, 2024
9f69b1a
fix: move StartSubscription to start.go
frnandu Oct 29, 2024
0d61bb2
fix: remove duplicated error check
frnandu Oct 29, 2024
b530632
fix: make tests use AppsService for creating apps
frnandu Oct 29, 2024
d4c6a60
chore: remove unused code
rolznz Oct 29, 2024
63c3fd9
chore: minor event handler improvements
rolznz Oct 29, 2024
1d79b8a
fix: add event_handler tests for legacy app
frnandu Oct 29, 2024
55d9515
chore: add comment about legacy apps in deleteAppConsumer
rolznz Oct 29, 2024
b1bcc58
Merge branch 'feat/wallet-child-key-per-connection' of github.com:get…
rolznz Oct 29, 2024
c56d1d1
fix: remove unused app from tests
rolznz Oct 29, 2024
bc923eb
fix: error handling in startAppWalletSubscription
rolznz Oct 29, 2024
606fb32
fix: only create event info and nostr subscription for master key if …
frnandu Oct 30, 2024
932a9fb
fix: add legacy tests
frnandu Oct 31, 2024
17c9c0b
fix: move fetching of Nip47 event info to deleteAppConsumer
frnandu Oct 31, 2024
8127fea
fix: fixed arguments
frnandu Oct 31, 2024
7cff2a7
fix: use require instead of assert
frnandu Nov 1, 2024
339d420
Merge branch 'master' into feat/wallet-child-key-per-connection
frnandu Nov 1, 2024
606c33b
fix: adapt GetAppWalletKey to use DeriveKey with path 1'
frnandu Nov 1, 2024
877509e
fix: for backends that don't use a mnemonic, create appKey from nostr…
frnandu Nov 4, 2024
c47d923
fix: cleanup eventPublisher Subscribers when relay reconnects
frnandu Nov 4, 2024
7bfd140
fix: bip32.FirstHardenedChild + appID
frnandu Nov 6, 2024
ce4c5cf
fix: remove unused env vars
frnandu Nov 6, 2024
8c1ef4b
fix: generate new mnemonic if empty
frnandu Nov 6, 2024
aea59f0
fix: add tests.CreateTestServiceWithMnemonic to fix TestEncryptedBackup
frnandu Nov 6, 2024
78f59bc
fix: handle both relay and main ctx Done
frnandu Nov 6, 2024
b322f4c
Merge branch 'master' into feat/wallet-child-key-per-connection
rolznz Nov 7, 2024
b4bf53f
chore: add keys tests
rolznz Nov 7, 2024
caed3b3
chore: add extra assertions to keys test
rolznz Nov 7, 2024
2b14d3c
chore: log when legacy app subscription is created
rolznz Nov 7, 2024
dc54213
chore: remove unnecessary break
rolznz Nov 7, 2024
0be7e32
chore: add log when relay is successfully connected
rolznz Nov 7, 2024
287607d
fix: only auto-start node if it has been started before
rolznz Nov 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions nip47/nip47_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type Nip47Service interface {
events.EventSubscriber
StartNotifier(ctx context.Context, relay *nostr.Relay, lnClient lnclient.LNClient)
HandleEvent(ctx context.Context, relay nostrmodels.Relay, event *nostr.Event, lnClient lnclient.LNClient)
GetNip47Info(ctx context.Context, relay *nostr.Relay, appWalletPubKey string) (*nostr.Event, error)
PublishNip47Info(ctx context.Context, relay nostrmodels.Relay, appWalletPubKey string, appWalletPrivKey string, lnClient lnclient.LNClient) (*nostr.Event, error)
PublishNip47InfoDeletion(ctx context.Context, relay nostrmodels.Relay, appWalletPubKey string, appWalletPrivKey string, infoEventId string) error
im-adithya marked this conversation as resolved.
Show resolved Hide resolved
CreateResponse(initialEvent *nostr.Event, content interface{}, tags nostr.Tags, ss []byte, walletPrivKey string) (result *nostr.Event, err error)
Expand Down
9 changes: 8 additions & 1 deletion nip47/notifications/nip47_notifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,14 @@ func (notifier *Nip47Notifier) notifySubscriber(ctx context.Context, app *db.App
allTags := nostr.Tags{[]string{"p", app.AppPubkey}}
allTags = append(allTags, tags...)

appWalletPubKey, _ := nostr.GetPublicKey(appWalletPrivKey)
appWalletPubKey, err := nostr.GetPublicKey(appWalletPrivKey)
if err != nil {
logger.Logger.WithFields(logrus.Fields{
"notification": notification,
"appId": app.ID,
}).WithError(err).Error("Failed to calculate app wallet pub key")
return
}

event := &nostr.Event{
PubKey: appWalletPubKey,
Expand Down
19 changes: 19 additions & 0 deletions nip47/publish_nip47_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,25 @@ import (
"github.com/nbd-wtf/go-nostr"
)

func (svc *nip47Service) GetNip47Info(ctx context.Context, relay *nostr.Relay, appWalletPubKey string) (*nostr.Event, error) {
filter := nostr.Filter{
Kinds: []int{models.INFO_EVENT_KIND},
Authors: []string{appWalletPubKey},
Limit: 1,
}

events, err := relay.QuerySync(ctx, filter)
if err != nil {
return nil, err
}

if len(events) == 0 {
return nil, nil
}

return events[0], nil
}

func (svc *nip47Service) PublishNip47Info(ctx context.Context, relay nostrmodels.Relay, appWalletPubKey string, appWalletPrivKey string, lnClient lnclient.LNClient) (*nostr.Event, error) {
rolznz marked this conversation as resolved.
Show resolved Hide resolved
// TODO: should the capabilities be based on the app permissions? (for non-legacy apps)
frnandu marked this conversation as resolved.
Show resolved Hide resolved
capabilities := lnClient.GetSupportedNIP47Methods()
Expand Down
8 changes: 7 additions & 1 deletion service/create_app_consumer.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,20 @@ func (s *createAppConsumer) ConsumeEvent(ctx context.Context, event *events.Even
walletPrivKey, err := s.svc.keys.GetAppWalletKey(id)
if err != nil {
logger.Logger.WithError(err).Error("Failed to calculate app wallet priv key")
frnandu marked this conversation as resolved.
Show resolved Hide resolved
return
}
walletPubKey, err := nostr.GetPublicKey(walletPrivKey)
if err != nil {
logger.Logger.WithError(err).Error("Failed to calculate app wallet pub key")
frnandu marked this conversation as resolved.
Show resolved Hide resolved
return
}

go func() {
err = s.svc.startAppWalletSubscription(ctx, s.relay, walletPubKey, walletPrivKey)
nip47EventInfo, err := s.svc.GetNip47Service().PublishNip47Info(ctx, s.relay, walletPubKey, walletPrivKey, s.svc.lnClient)
if err != nil {
logger.Logger.WithError(err).Error("Could not publish NIP47 info")
}
err = s.svc.startAppWalletSubscription(ctx, s.relay, walletPubKey, nip47EventInfo)
if err != nil {
logger.Logger.WithError(err).WithFields(logrus.Fields{
"app_id": id}).Error("Failed to subscribe to wallet")
Expand Down
23 changes: 17 additions & 6 deletions service/delete_app_consumer.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type deleteAppConsumer struct {
relay *nostr.Relay
nostrSubscription *nostr.Subscription
svc *service
infoEventId string
nip47InfoEvent *nostr.Event
}

// When an app is deleted, unsubscribe from events for that app on the relay
Expand All @@ -28,20 +28,31 @@ func (s *deleteAppConsumer) ConsumeEvent(ctx context.Context, event *events.Even
logger.Logger.WithField("event", event).Error("Failed to cast event.Properties to map")
return
}
id, _ := properties["id"].(uint)
id, ok := properties["id"].(uint)
if !ok {
logger.Logger.WithField("event", event).Error("missing id in properties event")
return
}

walletPrivKey, err := s.svc.keys.GetAppWalletKey(id)
if err != nil {
logger.Logger.WithError(err).WithField("id", id).Error("Failed to calculate app wallet priv key")
frnandu marked this conversation as resolved.
Show resolved Hide resolved
return
}
walletPubKey, err := nostr.GetPublicKey(walletPrivKey)
if err != nil {
logger.Logger.WithError(err).WithField("id", id).Error("Failed to calculate app wallet pub key")
return
}
walletPubKey, _ := nostr.GetPublicKey(walletPrivKey)
// Note: for legacy apps this check will always return false as the wallet pubkey
// generated by the id will not match the master key which is used for all legacy apps
if s.walletPubkey == walletPubKey {
im-adithya marked this conversation as resolved.
Show resolved Hide resolved
s.nostrSubscription.Unsub()
err := s.svc.nip47Service.PublishNip47InfoDeletion(ctx, s.relay, walletPubKey, walletPrivKey, s.infoEventId)
if err != nil {
logger.Logger.WithError(err).WithField("event", event).Error("Failed to publish nip47 info deletion")
if s.nip47InfoEvent != nil {
err := s.svc.nip47Service.PublishNip47InfoDeletion(ctx, s.relay, walletPubKey, walletPrivKey, s.nip47InfoEvent.ID)
if err != nil {
logger.Logger.WithError(err).WithField("event", event).Error("Failed to publish nip47 info deletion")
}
}
}
}
73 changes: 54 additions & 19 deletions service/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,32 @@ func (svc *service) startNostr(ctx context.Context, encryptionKey string) error
// register a subscriber for events of "app_created" which handles creation of nostr subscription for new app
svc.eventPublisher.RegisterSubscriber(&createAppConsumer{svc: svc, relay: relay})

// legacy single wallet subscription - only subscribe once for all legacy apps
// to ensure we do not get duplicate events
err = svc.startAppWalletSubscription(ctx, relay, svc.keys.GetNostrPublicKey(), svc.keys.GetNostrSecretKey())
if err != nil {
//err being non-nil means that we have an error on the websocket error channel. In this case we just try to reconnect.
logger.Logger.WithError(err).Error("Got an error from the relay while listening to subscription.")
continue
// check if there are still legacy apps in DB
var legacyAppCount int64
result := svc.db.Model(&db.App{}).Where("wallet_pubkey IS NULL").Count(&legacyAppCount)
if result.Error != nil {
logger.Logger.WithError(result.Error).Error("Failed to count Legacy Apps")
return
}
if legacyAppCount > 0 {
// re-publish single NIP47 event info for legacy apps
_, err := svc.GetNip47Service().PublishNip47Info(ctx, relay, svc.keys.GetNostrPublicKey(), svc.keys.GetNostrSecretKey(), svc.lnClient)
if err != nil {
logger.Logger.WithError(err).Error("Could not publish NIP47 info for legacy apps")
continue
}

// legacy single wallet subscription - only subscribe once for all legacy apps
// to ensure we do not get duplicate events
err = svc.startAppWalletSubscription(ctx, relay, svc.keys.GetNostrPublicKey(), nil)
if err != nil {
//err being non-nil means that we have an error on the websocket error channel. In this case we just try to reconnect.
logger.Logger.WithError(err).Error("Got an error from the relay while listening to subscription.")
continue
}
} else {
rolznz marked this conversation as resolved.
Show resolved Hide resolved
// there is only new apps, so no single master key nostr subscription needed
<-ctx.Done()
}
//err being nil means that the context was canceled and we should exit the program.
break
Expand All @@ -126,25 +145,41 @@ func (svc *service) startAllExistingAppsWalletSubscriptions(ctx context.Context,

for _, app := range apps {
go func(app db.App) {
err := svc.startAppWalletSubscription(ctx, relay, *app.WalletPubkey, "")

// get nip47 event info for this app wallet key
nip47InfoEvent, err := svc.GetNip47Service().GetNip47Info(ctx, relay, *app.WalletPubkey)
frnandu marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
logger.Logger.WithError(err).Error("Could not get nip47 info event")
return
}
if nip47InfoEvent == nil {
// nip47 info event missing for this app, re-publish it
walletPrivKey, err := svc.keys.GetAppWalletKey(app.ID)
if err != nil {
logger.Logger.WithError(err).WithFields(logrus.Fields{
"app_id": app.ID}).Error("Failed to calculate app wallet priv key")
return
}

nip47InfoEvent, err = svc.GetNip47Service().PublishNip47Info(ctx, relay, *app.WalletPubkey, walletPrivKey, svc.lnClient)
if err != nil {
logger.Logger.WithError(err).WithFields(logrus.Fields{
"app_id": app.ID}).Error("Could not re-publish NIP47 info for app")
return
}
}

err = svc.startAppWalletSubscription(ctx, relay, *app.WalletPubkey, nip47InfoEvent)
if err != nil {
logger.Logger.WithError(err).WithFields(logrus.Fields{
"app_id": app.ID}).Error("Failed to subscribe to wallet")
return
}
}(app)
}
}

func (svc *service) startAppWalletSubscription(ctx context.Context, relay *nostr.Relay, appWalletPubKey string, appWalletPrivKey string) error {
var infoEventId string
if appWalletPrivKey != "" {
infoEvent, err := svc.GetNip47Service().PublishNip47Info(ctx, relay, appWalletPubKey, appWalletPrivKey, svc.lnClient)
if err != nil {
logger.Logger.WithError(err).Error("Could not publish NIP47 info")
return err
}
infoEventId = infoEvent.ID
}
func (svc *service) startAppWalletSubscription(ctx context.Context, relay *nostr.Relay, appWalletPubKey string, nip47InfoEvent *nostr.Event) error {

logger.Logger.Info("Subscribing to events for wallet ", appWalletPubKey)
sub, err := relay.Subscribe(ctx, svc.createFilters(appWalletPubKey))
Expand All @@ -154,7 +189,7 @@ func (svc *service) startAppWalletSubscription(ctx context.Context, relay *nostr
}

// register a subscriber for "app_deleted" events, which handles nostr subscription cancel and nip47 info event deletion
svc.eventPublisher.RegisterSubscriber(&deleteAppConsumer{nostrSubscription: sub, walletPubkey: appWalletPubKey, svc: svc, relay: relay, infoEventId: infoEventId})
svc.eventPublisher.RegisterSubscriber(&deleteAppConsumer{nostrSubscription: sub, walletPubkey: appWalletPubKey, svc: svc, relay: relay, nip47InfoEvent: nip47InfoEvent})

err = svc.StartSubscription(sub.Context, sub)
frnandu marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
Expand Down
Loading