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
Prev Previous commit
Next Next commit
chore: rename app nostr_pubkey to app_pubkey, extract app consumers i…
…nto separate files
  • Loading branch information
rolznz committed Oct 29, 2024
commit a1dc936dcf17a502486044f503c8b4703eea52fb
10 changes: 5 additions & 5 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func (api *api) CreateApp(createAppRequest *CreateAppRequest) (*CreateAppRespons
responseBody := &CreateAppResponse{}
responseBody.Id = app.ID
responseBody.Name = createAppRequest.Name
responseBody.Pubkey = app.NostrPubkey
responseBody.Pubkey = app.AppPubkey
responseBody.PairingSecret = pairingSecretKey

lightningAddress, err := api.albyOAuthSvc.GetLightningAddress()
Expand All @@ -105,7 +105,7 @@ func (api *api) CreateApp(createAppRequest *CreateAppRequest) (*CreateAppRespons
if err == nil {
query := returnToUrl.Query()
query.Add("relay", relayUrl)
query.Add("pubkey", app.WalletPubkey)
query.Add("pubkey", *app.WalletPubkey)
if lightningAddress != "" && !app.Isolated {
query.Add("lud16", lightningAddress)
}
Expand All @@ -118,7 +118,7 @@ func (api *api) CreateApp(createAppRequest *CreateAppRequest) (*CreateAppRespons
if lightningAddress != "" && !app.Isolated {
lud16 = fmt.Sprintf("&lud16=%s", lightningAddress)
}
responseBody.PairingUri = fmt.Sprintf("nostr+walletconnect://%s?relay=%s&secret=%s%s", app.WalletPubkey, relayUrl, pairingSecretKey, lud16)
responseBody.PairingUri = fmt.Sprintf("nostr+walletconnect://%s?relay=%s&secret=%s%s", *app.WalletPubkey, relayUrl, pairingSecretKey, lud16)

return responseBody, nil
}
Expand Down Expand Up @@ -262,7 +262,7 @@ func (api *api) GetApp(dbApp *db.App) *App {
Description: dbApp.Description,
CreatedAt: dbApp.CreatedAt,
UpdatedAt: dbApp.UpdatedAt,
NostrPubkey: dbApp.NostrPubkey,
AppPubkey: dbApp.AppPubkey,
ExpiresAt: expiresAt,
MaxAmountSat: maxAmount,
Scopes: requestMethods,
Expand Down Expand Up @@ -313,7 +313,7 @@ func (api *api) ListApps() ([]App, error) {
Description: dbApp.Description,
CreatedAt: dbApp.CreatedAt,
UpdatedAt: dbApp.UpdatedAt,
NostrPubkey: dbApp.NostrPubkey,
AppPubkey: dbApp.AppPubkey,
Isolated: dbApp.Isolated,
}

Expand Down
2 changes: 1 addition & 1 deletion api/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ type App struct {
ID uint `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
NostrPubkey string `json:"nostrPubkey"`
AppPubkey string `json:"appPubkey"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
LastEventAt *time.Time `json:"lastEventAt"`
Expand Down
8 changes: 4 additions & 4 deletions apps/apps_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func (svc *appsService) CreateApp(name string, pubkey string, maxAmountSat uint6
}
}

app := db.App{Name: name, NostrPubkey: pairingPublicKey, Isolated: isolated, Metadata: datatypes.JSON(metadataBytes)}
app := db.App{Name: name, AppPubkey: pairingPublicKey, Isolated: isolated, Metadata: datatypes.JSON(metadataBytes)}

err := svc.db.Transaction(func(tx *gorm.DB) error {
err := tx.Save(&app).Error
Expand All @@ -92,17 +92,17 @@ func (svc *appsService) CreateApp(name string, pubkey string, maxAmountSat uint6
}
}

appWalletPrivKey, err := svc.keys.GetAppWalletKey(uint32(app.ID))
appWalletPrivKey, err := svc.keys.GetAppWalletKey(app.ID)
if err != nil {
return fmt.Errorf("error generating wallet child private key: %w", err)
}

app.WalletPubkey, err = nostr.GetPublicKey(appWalletPrivKey)
appWalletPubkey, err := nostr.GetPublicKey(appWalletPrivKey)
if err != nil {
return fmt.Errorf("error generating wallet child public key: %w", err)
}

err = tx.Model(&db.App{}).Where("id", app.ID).Update("wallet_pubkey", app.WalletPubkey).Error
err = tx.Model(&db.App{}).Where("id", app.ID).Update("wallet_pubkey", appWalletPubkey).Error
if err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions db/migrations/202410141503_add_wallet_pubkey.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ var _202410141503_add_wallet_pubkey = &gormigrate.Migration{

if err := tx.Exec(`
ALTER TABLE apps ADD COLUMN wallet_pubkey TEXT;
ALTER TABLE apps RENAME COLUMN nostr_pubkey TO app_pubkey;
`).Error; err != nil {
return err
}
Expand Down
11 changes: 5 additions & 6 deletions db/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,11 @@ type UserConfig struct {
}

type App struct {
ID uint
Name string `validate:"required"`
Description string
// TODO rename to AppPubKey
NostrPubkey string `validate:"required"`
WalletPubkey string
ID uint
Name string `validate:"required"`
Description string
AppPubkey string `validate:"required"`
WalletPubkey *string
CreatedAt time.Time
UpdatedAt time.Time
Isolated bool
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/TransactionItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ function TransactionItem({ tx }: Props) {
{app && (
<div className="mt-8">
<p>App</p>
<Link to={`/apps/${app.nostrPubkey}`}>
<Link to={`/apps/${app.appPubkey}`}>
<p className="font-semibold">
{app.name === "getalby.com" ? "Alby Account" : app.name}
</p>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/connections/AlbyConnectionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ function AlbyConnectionCard({ connection }: { connection?: App }) {
{connection && (
<div className="slashed-zero">
<Link
to={`/apps/${connection.nostrPubkey}?edit=true`}
to={`/apps/${connection.appPubkey}?edit=true`}
className="absolute top-0 right-0"
>
<EditIcon className="w-4 h-4 hidden group-hover:inline text-muted-foreground hover:text-card-foreground" />
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/connections/AppCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export default function AppCard({ app }: Props) {
return (
<Card
className="flex flex-col group cursor-pointer"
onClick={() => navigate(`/apps/${app.nostrPubkey}`)}
onClick={() => navigate(`/apps/${app.appPubkey}`)}
>
<CardHeader>
<CardTitle className="relative">
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/connections/AppCardConnectionInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export function AppCardConnectionInfo({
? dayjs(connection.lastEventAt).fromNow()
: "Never"}
</div>
<Link to={`/apps/${connection.nostrPubkey}?edit=true`}>
<Link to={`/apps/${connection.appPubkey}?edit=true`}>
<Button variant="outline">
<PlusCircle className="w-4 h-4 mr-2" />
Set Budget
Expand Down Expand Up @@ -151,7 +151,7 @@ export function AppCardConnectionInfo({
: "Never"}
</div>
<Link
to={`/apps/${connection.nostrPubkey}?edit=true`}
to={`/apps/${connection.appPubkey}?edit=true`}
onClick={(e) => e.stopPropagation()}
>
<Button variant="outline">
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/connections/AppCardNotice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export function AppCardNotice({ app }: AppCardNoticeProps) {
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Link to={`/apps/${app.nostrPubkey}`}>
<Link to={`/apps/${app.appPubkey}`}>
<Badge variant="destructive">
<CalendarClock className="w-3 h-3 mr-2" />
Expired
Expand All @@ -44,7 +44,7 @@ export function AppCardNotice({ app }: AppCardNoticeProps) {
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Link to={`/apps/${app.nostrPubkey}`}>
<Link to={`/apps/${app.appPubkey}`}>
<Badge variant="outline">
<CalendarClock className="w-3 h-3 mr-2" />
Expires Soon
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/screens/apps/AppCreated.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ function AppCreatedInternal() {
Optional: Increase isolated balance (
{new Intl.NumberFormat().format(Math.floor(app.balance / 1000))}{" "}
sats){" "}
<IsolatedAppTopupDialog appPubkey={app.nostrPubkey}>
<IsolatedAppTopupDialog appPubkey={app.appPubkey}>
<Button size="sm" variant="secondary">
Increase
</Button>
Expand Down Expand Up @@ -168,7 +168,7 @@ export function ConnectAppCard({
{timeout && (
<div className="text-sm flex flex-col gap-2 items-center text-center">
Connecting is taking longer than usual.
<Link to={`/apps/${app?.nostrPubkey}`}>
<Link to={`/apps/${app?.appPubkey}`}>
<Button variant="secondary">Continue anyway</Button>
</Link>
</div>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/screens/apps/AppList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ function AppList() {

const albyConnection = apps.find((x) => x.name === albyConnectionName);
const otherApps = apps
.filter((x) => x.nostrPubkey !== albyConnection?.nostrPubkey)
.filter((x) => x.appPubkey !== albyConnection?.appPubkey)
.sort(
(a, b) =>
new Date(b.lastEventAt ?? 0).getTime() -
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/screens/apps/ShowApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ function AppInternal({ app, refetchApp, capabilities }: AppInternalProps) {
maxAmount: permissions.maxAmount,
};

await request(`/api/apps/${app.nostrPubkey}`, {
await request(`/api/apps/${app.appPubkey}`, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
Expand Down Expand Up @@ -208,7 +208,7 @@ function AppInternal({ app, refetchApp, capabilities }: AppInternalProps) {
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={() => deleteApp(app.nostrPubkey)}
onClick={() => deleteApp(app.appPubkey)}
disabled={isDeleting}
>
Continue
Expand All @@ -235,7 +235,7 @@ function AppInternal({ app, refetchApp, capabilities }: AppInternalProps) {
<TableRow>
<TableCell className="font-medium">Public Key</TableCell>
<TableCell className="text-muted-foreground break-all">
{app.nostrPubkey}
{app.appPubkey}
</TableCell>
</TableRow>
{app.isolated && (
Expand All @@ -246,7 +246,7 @@ function AppInternal({ app, refetchApp, capabilities }: AppInternalProps) {
Math.floor(app.balance / 1000)
)}{" "}
sats{" "}
<IsolatedAppTopupDialog appPubkey={app.nostrPubkey}>
<IsolatedAppTopupDialog appPubkey={app.appPubkey}>
<Button size="sm" variant="secondary">
Increase
</Button>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export interface App {
id: number;
name: string;
description: string;
nostrPubkey: string;
appPubkey: string;
createdAt: string;
updatedAt: string;
lastEventAt?: string;
Expand Down
8 changes: 4 additions & 4 deletions nip47/event_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,14 @@ func (svc *nip47Service) HandleEvent(ctx context.Context, relay nostrmodels.Rela
}
app := db.App{}
err = svc.db.First(&app, &db.App{
NostrPubkey: event.PubKey,
AppPubkey: event.PubKey,
}).Error

appWalletPrivKey := svc.keys.GetNostrSecretKey()

if app.WalletPubkey != "" {
if app.WalletPubkey != nil {
// This is a new child key derived from master using app ID as index
appWalletPrivKey, err = svc.keys.GetAppWalletKey(uint32(app.ID))
appWalletPrivKey, err = svc.keys.GetAppWalletKey(app.ID)
if err != nil {
logger.Logger.WithFields(logrus.Fields{
"appId": app.ID,
Expand Down Expand Up @@ -173,7 +173,7 @@ func (svc *nip47Service) HandleEvent(ctx context.Context, relay nostrmodels.Rela
}).Debug("App found for nostr event")

//to be extra safe, decrypt using the key found from the app
ss, err = nip04.ComputeSharedSecret(app.NostrPubkey, appWalletPrivKey)
ss, err = nip04.ComputeSharedSecret(app.AppPubkey, appWalletPrivKey)
if err != nil {
logger.Logger.WithFields(logrus.Fields{
"requestEventNostrId": event.ID,
Expand Down
8 changes: 4 additions & 4 deletions nip47/notifications/nip47_notifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,11 @@ func (notifier *Nip47Notifier) notifySubscriber(ctx context.Context, app *db.App
}).Debug("Notifying subscriber")

appWalletPrivKey := notifier.keys.GetNostrSecretKey()
if app.WalletPubkey != "" {
appWalletPrivKey, _ = notifier.keys.GetAppWalletKey(uint32(app.ID))
if app.WalletPubkey != nil {
appWalletPrivKey, _ = notifier.keys.GetAppWalletKey(app.ID)
frnandu marked this conversation as resolved.
Show resolved Hide resolved
}

ss, err := nip04.ComputeSharedSecret(app.NostrPubkey, appWalletPrivKey)
ss, err := nip04.ComputeSharedSecret(app.AppPubkey, appWalletPrivKey)
frnandu marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
logger.Logger.WithFields(logrus.Fields{
"notification": notification,
Expand All @@ -139,7 +139,7 @@ func (notifier *Nip47Notifier) notifySubscriber(ctx context.Context, app *db.App
return
}

allTags := nostr.Tags{[]string{"p", app.NostrPubkey}}
allTags := nostr.Tags{[]string{"p", app.AppPubkey}}
allTags = append(allTags, tags...)

appWalletPubKey, _ := nostr.GetPublicKey(appWalletPrivKey)
Expand Down
2 changes: 1 addition & 1 deletion nip47/permissions/permissions.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func (svc *permissionsService) HasPermission(app *db.App, scope string) (result
"scope": scope,
"expiresAt": expiresAt.Unix(),
"appId": app.ID,
"pubkey": app.NostrPubkey,
"pubkey": app.AppPubkey,
}).Info("This pubkey is expired")

return false, constants.ERROR_EXPIRED, "This app has expired"
Expand Down
1 change: 1 addition & 0 deletions nip47/publish_nip47_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
)

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()
if len(lnClient.GetSupportedNIP47NotificationTypes()) > 0 {
capabilities = append(capabilities, "notifications")
Expand Down
52 changes: 52 additions & 0 deletions service/create_app_consumer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package service

import (
"context"

"github.com/getAlby/hub/events"
"github.com/getAlby/hub/logger"
"github.com/nbd-wtf/go-nostr"
"github.com/sirupsen/logrus"
)

type createAppConsumer struct {
events.EventSubscriber
svc *service
relay *nostr.Relay
}

// When a new app is created, subscribe to it on the relay
func (s *createAppConsumer) ConsumeEvent(ctx context.Context, event *events.Event, globalProperties map[string]interface{}) {
if event.Event != "app_created" {
return
}

properties, ok := event.Properties.(map[string]interface{})
if !ok {
logger.Logger.WithField("event", event).Error("Failed to cast event.Properties to map")
return
}
id, ok := properties["id"].(uint)
if !ok {
logger.Logger.WithField("event", event).Error("Failed to get app id")
return
}
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
}
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
}

go func() {
err = s.svc.startAppWalletSubscription(ctx, s.relay, walletPubKey, walletPrivKey)
if err != nil {
logger.Logger.WithError(err).WithFields(logrus.Fields{
"app_id": id}).Error("Failed to subscribe to wallet")
}
logger.Logger.WithFields(logrus.Fields{
"app_id": id}).Info("App Nostr Subscription ended")
}()
}
Loading
Loading