From 83de0038ca17b84a0e7b2287238bcdc838e92540 Mon Sep 17 00:00:00 2001 From: Fmar Date: Mon, 14 Oct 2024 17:22:14 +0200 Subject: [PATCH] feat(appwalletKey): add GetBIP32ChildKey --- api/api.go | 15 +++++++-- db/db_service.go | 2 +- .../202410141503_wallet_child_idx.go | 26 +++++++++++++++ db/migrations/migrate.go | 1 + db/models.go | 17 +++++----- go.mod | 3 ++ go.sum | 10 ++++++ nip47/event_handler.go | 30 ++++++++++++----- nip47/event_handler_test.go | 2 +- nip47/notifications/nip47_notifier.go | 19 +++++++++-- service/keys/keys.go | 32 +++++++++++++++++++ service/service.go | 2 +- 12 files changed, 135 insertions(+), 24 deletions(-) create mode 100644 db/migrations/202410141503_wallet_child_idx.go diff --git a/api/api.go b/api/api.go index ce4719e2..f19056ca 100644 --- a/api/api.go +++ b/api/api.go @@ -2,6 +2,7 @@ package api import ( "context" + "encoding/hex" "encoding/json" "errors" "fmt" @@ -29,6 +30,7 @@ import ( "github.com/getAlby/hub/service/keys" "github.com/getAlby/hub/utils" "github.com/getAlby/hub/version" + "github.com/nbd-wtf/go-nostr" ) type api struct { @@ -98,12 +100,21 @@ func (api *api) CreateApp(createAppRequest *CreateAppRequest) (*CreateAppRespons return nil, err } + appWalletKey, err := api.keys.GetBIP32ChildKey(uint32(app.ID)) + if err != nil { + fmt.Println("error creating child key: ", err) + return nil, err + } + fmt.Println("!+!+!+!+!+!+!+ app secret key: ", hex.EncodeToString(appWalletKey.Serialize())) + appWalletPubKey, _ := nostr.GetPublicKey(hex.EncodeToString(appWalletKey.Serialize())) + fmt.Println("!+!+!+!+!+!+!+ app public key: ", appWalletPubKey) + if createAppRequest.ReturnTo != "" { returnToUrl, err := url.Parse(createAppRequest.ReturnTo) if err == nil { query := returnToUrl.Query() query.Add("relay", relayUrl) - query.Add("pubkey", api.keys.GetNostrPublicKey()) + query.Add("pubkey", appWalletPubKey) if lightningAddress != "" && !app.Isolated { query.Add("lud16", lightningAddress) } @@ -116,7 +127,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", api.keys.GetNostrPublicKey(), relayUrl, pairingSecretKey, lud16) + responseBody.PairingUri = fmt.Sprintf("nostr+walletconnect://%s?relay=%s&secret=%s%s", appWalletPubKey, relayUrl, pairingSecretKey, lud16) return responseBody, nil } diff --git a/db/db_service.go b/db/db_service.go index 32753ba6..03a386ce 100644 --- a/db/db_service.go +++ b/db/db_service.go @@ -59,7 +59,7 @@ func (svc *dbService) CreateApp(name string, pubkey string, maxAmountSat uint64, } } - app := App{Name: name, NostrPubkey: pairingPublicKey, Isolated: isolated, Metadata: datatypes.JSON(metadataBytes)} + app := App{Name: name, NostrPubkey: pairingPublicKey, walletChildIdx: 2, Isolated: isolated, Metadata: datatypes.JSON(metadataBytes)} err := svc.db.Transaction(func(tx *gorm.DB) error { err := tx.Save(&app).Error diff --git a/db/migrations/202410141503_wallet_child_idx.go b/db/migrations/202410141503_wallet_child_idx.go new file mode 100644 index 00000000..2f795d71 --- /dev/null +++ b/db/migrations/202410141503_wallet_child_idx.go @@ -0,0 +1,26 @@ +package migrations + +import ( + _ "embed" + + "github.com/go-gormigrate/gormigrate/v2" + "gorm.io/gorm" +) + +var _202410141503_wallet_child_idx = &gormigrate.Migration{ + ID: "202410141503_wallet_child_idx", + Migrate: func(tx *gorm.DB) error { + + if err := tx.Exec(` + ALTER TABLE apps ADD COLUMN wallet_child_idx INTEGER; + CREATE UNIQUE INDEX idx_wallet_child_idx ON apps (wallet_child_idx); +`).Error; err != nil { + return err + } + + return nil + }, + Rollback: func(tx *gorm.DB) error { + return nil + }, +} diff --git a/db/migrations/migrate.go b/db/migrations/migrate.go index 00fd48bb..27f9db8a 100644 --- a/db/migrations/migrate.go +++ b/db/migrations/migrate.go @@ -22,6 +22,7 @@ func Migrate(gormDB *gorm.DB) error { _202408061737_add_boostagrams_and_use_json, _202408191242_transaction_failure_reason, _202408291715_app_metadata, + _202410141503_wallet_child_idx, }) return m.Migrate() diff --git a/db/models.go b/db/models.go index 3126ed8c..f93f267b 100644 --- a/db/models.go +++ b/db/models.go @@ -16,14 +16,15 @@ type UserConfig struct { } type App struct { - ID uint - Name string `validate:"required"` - Description string - NostrPubkey string `validate:"required"` - CreatedAt time.Time - UpdatedAt time.Time - Isolated bool - Metadata datatypes.JSON + ID uint + Name string `validate:"required"` + Description string + NostrPubkey string `validate:"required"` + walletChildIdx uint `validate:"required"` + CreatedAt time.Time + UpdatedAt time.Time + Isolated bool + Metadata datatypes.JSON } type AppPermission struct { diff --git a/go.mod b/go.mod index 2d6f6909..319a44fc 100644 --- a/go.mod +++ b/go.mod @@ -31,6 +31,8 @@ require ( github.com/BurntSushi/toml v1.2.1 // indirect github.com/DataDog/datadog-go/v5 v5.3.0 // indirect github.com/DataDog/gostackparse v0.7.0 // indirect + github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e // indirect + github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect @@ -168,6 +170,7 @@ require ( github.com/tidwall/pretty v1.2.1 // indirect github.com/tkrajina/go-reflector v0.5.6 // indirect github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 // indirect + github.com/tyler-smith/go-bip32 v1.0.0 // indirect github.com/tyler-smith/go-bip39 v1.1.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect diff --git a/go.sum b/go.sum index 169d9c05..fc854b7b 100644 --- a/go.sum +++ b/go.sum @@ -25,6 +25,10 @@ github.com/DataDog/gostackparse v0.7.0 h1:i7dLkXHvYzHV308hnkvVGDL3BR4FWl7IsXNPz/ github.com/DataDog/gostackparse v0.7.0/go.mod h1:lTfqcJKqS9KnXQGnyQMCugq3u1FP6UZMfWR0aitKFMM= github.com/DataDog/sketches-go v1.4.5 h1:ki7VfeNz7IcNafq7yI/j5U/YCkO3LJiMDtXz9OMQbyE= github.com/DataDog/sketches-go v1.4.5/go.mod h1:7Y8GN8Jf66DLyDhc94zuWA3uHEt/7ttt8jHOBWWrSOg= +github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e h1:ahyvB3q25YnZWly5Gq1ekg6jcmWaGj/vG/MhF4aisoc= +github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:kGUqhHd//musdITWjFvNTHn90WG9bMLBEPQZ17Cmlpw= +github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec h1:1Qb69mGp/UtRPn422BH4/Y4Q3SLUrD9KHuDkm8iodFc= +github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec/go.mod h1:CD8UlnlLDiqb36L110uqiP2iSflVjx9g/3U9hCI4q2U= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= @@ -106,6 +110,7 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:P13beTBKr5Q18lJe1rIoLUqjM+CB1zYrRg44ZqGuQSA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= @@ -601,6 +606,7 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.1.5-0.20170601210322-f6abca593680/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -634,6 +640,8 @@ github.com/tkrajina/go-reflector v0.5.6 h1:hKQ0gyocG7vgMD2M3dRlYN6WBBOmdoOzJ6njQ github.com/tkrajina/go-reflector v0.5.6/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk= +github.com/tyler-smith/go-bip32 v1.0.0 h1:sDR9juArbUgX+bO/iblgZnMPeWY1KZMUC2AFUJdv5KE= +github.com/tyler-smith/go-bip32 v1.0.0/go.mod h1:onot+eHknzV4BVPwrzqY5OoVpyCvnwD7lMawL5aQupE= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= @@ -720,6 +728,7 @@ go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +golang.org/x/crypto v0.0.0-20170613210332-850760c427c5/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180723164146-c126467f60eb/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -947,6 +956,7 @@ gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM= modernc.org/cc/v4 v4.20.0 h1:45Or8mQfbUqJOG9WaxvlFYOAQO0lQ5RvqBcFCXngjxk= modernc.org/cc/v4 v4.20.0/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= modernc.org/ccgo/v4 v4.16.0 h1:ofwORa6vx2FMm0916/CkZjpFPSR70VwTjUCe2Eg5BnA= diff --git a/nip47/event_handler.go b/nip47/event_handler.go index abd66897..c858301e 100644 --- a/nip47/event_handler.go +++ b/nip47/event_handler.go @@ -2,6 +2,7 @@ package nip47 import ( "context" + "encoding/hex" "encoding/json" "errors" "fmt" @@ -75,7 +76,7 @@ func (svc *nip47Service) HandleEvent(ctx context.Context, relay nostrmodels.Rela Message: fmt.Sprintf("Failed to save nostr event: %s", err.Error()), }, } - resp, err := svc.CreateResponse(event, nip47Response, nostr.Tags{}, ss) + resp, err := svc.CreateResponse(event, nip47Response, nostr.Tags{}, ss, svc.keys.GetNostrSecretKey()) if err != nil { logger.Logger.WithFields(logrus.Fields{ "requestEventNostrId": event.ID, @@ -87,9 +88,11 @@ func (svc *nip47Service) HandleEvent(ctx context.Context, relay nostrmodels.Rela } app := db.App{} + err = svc.db.First(&app, &db.App{ NostrPubkey: event.PubKey, }).Error + if err != nil { logger.Logger.WithFields(logrus.Fields{ "nostrPubkey": event.PubKey, @@ -101,7 +104,7 @@ func (svc *nip47Service) HandleEvent(ctx context.Context, relay nostrmodels.Rela Message: "The public key does not have a wallet connected.", }, } - resp, err := svc.CreateResponse(event, nip47Response, nostr.Tags{}, ss) + resp, err := svc.CreateResponse(event, nip47Response, nostr.Tags{}, ss, appWalletPrivKey) if err != nil { logger.Logger.WithFields(logrus.Fields{ "requestEventNostrId": event.ID, @@ -133,7 +136,7 @@ func (svc *nip47Service) HandleEvent(ctx context.Context, relay nostrmodels.Rela Message: fmt.Sprintf("Failed to save app to nostr event: %s", err.Error()), }, } - resp, err := svc.CreateResponse(event, nip47Response, nostr.Tags{}, ss) + resp, err := svc.CreateResponse(event, nip47Response, nostr.Tags{}, ss, svc.keys.GetNostrSecretKey()) if err != nil { logger.Logger.WithFields(logrus.Fields{ "requestEventNostrId": event.ID, @@ -159,8 +162,17 @@ func (svc *nip47Service) HandleEvent(ctx context.Context, relay nostrmodels.Rela "appId": app.ID, }).Debug("App found for nostr event") + appWalletPrivKeyBip32, err := svc.keys.GetBIP32ChildKey(uint32(app.ID)) + if err != nil { + logger.Logger.WithFields(logrus.Fields{ + "appId": app.ID, + }).WithError(err).Error("error deriving child key") + return + } + appWalletPrivKey, _ := nostr.GetPublicKey(hex.EncodeToString(appWalletPrivKeyBip32.Serialize())) + //to be extra safe, decrypt using the key found from the app - ss, err = nip04.ComputeSharedSecret(app.NostrPubkey, svc.keys.GetNostrSecretKey()) + ss, err = nip04.ComputeSharedSecret(app.NostrPubkey, appWalletPrivKey) if err != nil { logger.Logger.WithFields(logrus.Fields{ "requestEventNostrId": event.ID, @@ -225,7 +237,7 @@ func (svc *nip47Service) HandleEvent(ctx context.Context, relay nostrmodels.Rela // TODO: replace with a channel // TODO: update all previous occurences of svc.publishResponseEvent to also use the channel publishResponse := func(nip47Response *models.Response, tags nostr.Tags) { - resp, err := svc.CreateResponse(event, nip47Response, tags, ss) + resp, err := svc.CreateResponse(event, nip47Response, tags, ss, appWalletPrivKey) if err != nil { logger.Logger.WithFields(logrus.Fields{ "requestEventNostrId": event.ID, @@ -356,7 +368,7 @@ func (svc *nip47Service) HandleEvent(ctx context.Context, relay nostrmodels.Rela } } -func (svc *nip47Service) CreateResponse(initialEvent *nostr.Event, content interface{}, tags nostr.Tags, ss []byte) (result *nostr.Event, err error) { +func (svc *nip47Service) CreateResponse(initialEvent *nostr.Event, content interface{}, tags nostr.Tags, ss []byte, walletPrivKey string) (result *nostr.Event, err error) { payloadBytes, err := json.Marshal(content) if err != nil { return nil, err @@ -369,14 +381,16 @@ func (svc *nip47Service) CreateResponse(initialEvent *nostr.Event, content inter allTags := nostr.Tags{[]string{"p", initialEvent.PubKey}, []string{"e", initialEvent.ID}} allTags = append(allTags, tags...) + appWalletPubKey, _ := nostr.GetPublicKey(walletPrivKey) + resp := &nostr.Event{ - PubKey: svc.keys.GetNostrPublicKey(), + PubKey: appWalletPubKey, CreatedAt: nostr.Now(), Kind: models.RESPONSE_KIND, Tags: allTags, Content: msg, } - err = resp.Sign(svc.keys.GetNostrSecretKey()) + err = resp.Sign(walletPrivKey) if err != nil { return nil, err } diff --git a/nip47/event_handler_test.go b/nip47/event_handler_test.go index 114439d4..b95c7242 100644 --- a/nip47/event_handler_test.go +++ b/nip47/event_handler_test.go @@ -51,7 +51,7 @@ func TestCreateResponse(t *testing.T) { nip47svc := NewNip47Service(svc.DB, svc.Cfg, svc.Keys, svc.EventPublisher) - res, err := nip47svc.CreateResponse(reqEvent, nip47Response, nostr.Tags{}, ss) + res, err := nip47svc.CreateResponse(reqEvent, nip47Response, nostr.Tags{}, ss, svc.Keys.GetNostrSecretKey()) assert.NoError(t, err) assert.Equal(t, reqPubkey, res.Tags.GetFirst([]string{"p"}).Value()) assert.Equal(t, reqEvent.ID, res.Tags.GetFirst([]string{"e"}).Value()) diff --git a/nip47/notifications/nip47_notifier.go b/nip47/notifications/nip47_notifier.go index 8fccf3a0..f1974671 100644 --- a/nip47/notifications/nip47_notifier.go +++ b/nip47/notifications/nip47_notifier.go @@ -2,6 +2,7 @@ package notifications import ( "context" + "encoding/hex" "encoding/json" "github.com/getAlby/hub/config" @@ -108,7 +109,17 @@ func (notifier *Nip47Notifier) notifySubscriber(ctx context.Context, app *db.App "appId": app.ID, }).Debug("Notifying subscriber") - ss, err := nip04.ComputeSharedSecret(app.NostrPubkey, notifier.keys.GetNostrSecretKey()) + appWalletPrivKeyBIP32, err := notifier.keys.GetBIP32ChildKey(uint32(app.ID)) + if err != nil { + logger.Logger.WithFields(logrus.Fields{ + "notification": notification, + "appId": app.ID, + }).WithError(err).Error("error derivingchild key") + return + } + appWalletPrivKey := hex.EncodeToString(appWalletPrivKeyBIP32.Serialize()) + + ss, err := nip04.ComputeSharedSecret(app.NostrPubkey, appWalletPrivKey) if err != nil { logger.Logger.WithFields(logrus.Fields{ "notification": notification, @@ -137,14 +148,16 @@ func (notifier *Nip47Notifier) notifySubscriber(ctx context.Context, app *db.App allTags := nostr.Tags{[]string{"p", app.NostrPubkey}} allTags = append(allTags, tags...) + appWalletPubKey, _ := nostr.GetPublicKey(appWalletPrivKey) + event := &nostr.Event{ - PubKey: notifier.keys.GetNostrPublicKey(), + PubKey: appWalletPubKey, CreatedAt: nostr.Now(), Kind: models.NOTIFICATION_KIND, Tags: allTags, Content: msg, } - err = event.Sign(notifier.keys.GetNostrSecretKey()) + err = event.Sign(appWalletPrivKey) if err != nil { logger.Logger.WithFields(logrus.Fields{ "notification": notification, diff --git a/service/keys/keys.go b/service/keys/keys.go index 803f3846..e876f69a 100644 --- a/service/keys/keys.go +++ b/service/keys/keys.go @@ -1,9 +1,13 @@ package keys import ( + "encoding/hex" + + "github.com/btcsuite/btcd/btcec/v2" "github.com/getAlby/hub/config" "github.com/getAlby/hub/logger" "github.com/nbd-wtf/go-nostr" + "github.com/tyler-smith/go-bip32" ) type Keys interface { @@ -12,6 +16,8 @@ type Keys interface { GetNostrPublicKey() string // Wallet Service Nostr secret key GetNostrSecretKey() string + // GetBIP32ChildKey derives a BIP32 child key from the nostrSecretKey given a child key index + GetBIP32ChildKey(childIndex uint32) (*btcec.PrivateKey, error) } type keys struct { @@ -51,3 +57,29 @@ func (keys *keys) GetNostrPublicKey() string { func (keys *keys) GetNostrSecretKey() string { return keys.nostrSecretKey } + +func (keys *keys) GetBIP32ChildKey(childIndex uint32) (*btcec.PrivateKey, error) { + // Convert nostrSecretKey to btcec private key + privKeyBytes, err := hex.DecodeString(keys.nostrSecretKey) + if err != nil { + return nil, err + } + privKey, _ := btcec.PrivKeyFromBytes(privKeyBytes) + + // Create a BIP32 master key from the private key + masterKey, err := bip32.NewMasterKey(privKey.Serialize()) + if err != nil { + return nil, err + } + + // Derive child key + childKey, err := masterKey.NewChildKey(childIndex) + if err != nil { + return nil, err + } + + // Convert child key to btcec private key + childPrivKey, _ := btcec.PrivKeyFromBytes(childKey.Key) + + return childPrivKey, nil +} diff --git a/service/service.go b/service/service.go index 34cd0b10..351d000e 100644 --- a/service/service.go +++ b/service/service.go @@ -138,7 +138,7 @@ func NewService(ctx context.Context) (*service, error) { func (svc *service) createFilters(identityPubkey string) nostr.Filters { filter := nostr.Filter{ - Tags: nostr.TagMap{"p": []string{identityPubkey}}, + // Tags: nostr.TagMap{"p": []string{identityPubkey}}, Kinds: []int{models.REQUEST_KIND}, } return []nostr.Filter{filter}