From eb6fdfdd395624d3820bd903054ea074d0704432 Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Thu, 27 Nov 2025 06:40:23 -0300 Subject: [PATCH] view: nsec, ncryptsec, event signing things. --- go.mod | 1 - go.sum | 2 -- view/event.go | 31 ++++++++++++++++--- view/helpers.go | 3 +- view/main.go | 82 +++++++++++++++++++++++++++++++++++++++++++------ 5 files changed, 100 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index 374c8f6..ac72d82 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,6 @@ require ( github.com/mattn/go-isatty v0.0.20 github.com/mattn/go-tty v0.0.7 github.com/mdp/qrterminal/v3 v3.2.1 - github.com/mitchellh/go-homedir v1.1.0 github.com/puzpuzpuz/xsync/v3 v3.5.1 github.com/stretchr/testify v1.10.0 github.com/therecipe/qt v0.0.0-20200904063919-c0c124a5770d diff --git a/go.sum b/go.sum index f54b5ac..66e11b5 100644 --- a/go.sum +++ b/go.sum @@ -163,8 +163,6 @@ github.com/mdp/qrterminal/v3 v3.2.1 h1:6+yQjiiOsSuXT5n9/m60E54vdgFsw0zhADHhHLrFe github.com/mdp/qrterminal/v3 v3.2.1/go.mod h1:jOTmXvnBsMy5xqLniO0R++Jmjs2sTm9dFSuQ5kpz/SU= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= diff --git a/view/event.go b/view/event.go index f12ed8e..26d026e 100644 --- a/view/event.go +++ b/view/event.go @@ -48,19 +48,40 @@ func updateEvent() { } } - final := nostr.Event{ + result := nostr.Event{ Kind: kind, Content: event.contentEdit.ToPlainText(), CreatedAt: nostr.Timestamp(event.createdAtEdit.DateTime().ToMSecsSinceEpoch() / 1000), Tags: tags, } - if currentKeyer != nil { - currentKeyer.SignEvent(context.Background(), &final) + finalize := func() { + jsonBytes, _ := json.MarshalIndent(result, "", " ") + event.outputEdit.SetPlainText(string(jsonBytes)) } - jsonBytes, _ := json.MarshalIndent(event, "", " ") - event.outputEdit.SetPlainText(string(jsonBytes)) + if currentKeyer != nil { + signAndFinalize := func() { + if currentKeyer != nil { + if err := currentKeyer.SignEvent(context.Background(), &result); err == nil { + finalize() + } else { + statusLabel.SetText("failed to sign: " + err.Error()) + } + } + } + + if currentSec == [32]byte{} { + // empty key, we must have a bunker + debounced.Call(signAndFinalize) + } else { + // we have a key, can sign immediately + signAndFinalize() + return + } + } + + finalize() } func setupEventTab() *widgets.QWidget { diff --git a/view/helpers.go b/view/helpers.go index 7d27bbf..46147eb 100644 --- a/view/helpers.go +++ b/view/helpers.go @@ -27,7 +27,8 @@ func handleSecretKeyOrBunker(sec string) (nostr.SecretKey, nostr.Keyer, error) { } if prefix, ski, err := nip19.Decode(sec); err == nil && prefix == "nsec" { - return ski.(nostr.SecretKey), nil, nil + sk := ski.(nostr.SecretKey) + return sk, keyer.NewPlainKeySigner(sk), nil } sk, err := nostr.SecretKeyFromHex(sec) diff --git a/view/main.go b/view/main.go index cf12214..6275b9d 100644 --- a/view/main.go +++ b/view/main.go @@ -1,9 +1,15 @@ package main import ( + "encoding/hex" "os" + "strings" + "time" + "fiatjaf.com/lib/debouncer" "fiatjaf.com/nostr" + "fiatjaf.com/nostr/nip19" + "fiatjaf.com/nostr/nip49" "github.com/therecipe/qt/widgets" ) @@ -11,6 +17,7 @@ var ( currentSec nostr.SecretKey currentKeyer nostr.Keyer statusLabel *widgets.QLabel + debounced = debouncer.New(800 * time.Millisecond) ) func main() { @@ -29,33 +36,87 @@ func main() { // private key input secLabel := widgets.NewQLabel2("private key (hex or nsec):", nil, 0) mainLayout.AddWidget(secLabel, 0, 0) - secEdit := widgets.NewQLineEdit(nil) - mainLayout.AddWidget(secEdit, 0, 0) - secEdit.ConnectTextChanged(func(text string) { + secHBox := widgets.NewQHBoxLayout() + mainLayout.AddLayout(secHBox, 0) + secEdit := widgets.NewQLineEdit(nil) + secHBox.AddWidget(secEdit, 0, 0) + generateButton := widgets.NewQPushButton2("generate", nil) + secHBox.AddWidget(generateButton, 0, 0) + + // password input + passwordHBox := widgets.NewQHBoxLayout() + passwordWidget := widgets.NewQWidget(nil, 0) + passwordWidget.SetLayout(passwordHBox) + passwordWidget.SetVisible(false) + mainLayout.AddWidget(passwordWidget, 0, 0) + passwordLabel := widgets.NewQLabel2("password:", nil, 0) + passwordHBox.AddWidget(passwordLabel, 0, 0) + secPasswordEdit := widgets.NewQLineEdit(nil) + secPasswordEdit.SetEchoMode(widgets.QLineEdit__Password) + passwordHBox.AddWidget(secPasswordEdit, 0, 0) + keyChanged := func(text string) { + text = strings.TrimSpace(text) + + var sk nostr.SecretKey + var keyer nostr.Keyer + var err error + if text == "" { - currentSec = nostr.SecretKey{} - currentKeyer = nil - statusLabel.SetText("") - return + passwordWidget.SetVisible(false) + goto empty } - sk, bunker, err := handleSecretKeyOrBunker(text) + + if strings.HasPrefix(text, "ncryptsec1") { + passwordWidget.SetVisible(true) + password := secPasswordEdit.Text() + if password != "" { + sk, err = nip49.Decrypt(text, password) + if err != nil { + statusLabel.SetText("decryption failed: " + err.Error()) + goto empty + } + text = hex.EncodeToString(sk[:]) + } else { + goto empty + } + } else { + passwordWidget.SetVisible(false) + } + + sk, keyer, err = handleSecretKeyOrBunker(text) if err != nil { statusLabel.SetText(err.Error()) currentSec = nostr.SecretKey{} currentKeyer = nil return } + currentSec = sk - currentKeyer = bunker + currentKeyer = keyer statusLabel.SetText("") updateEvent() + return + + empty: + currentSec = nostr.SecretKey{} + currentKeyer = nil + statusLabel.SetText("") + return + } + secEdit.ConnectTextChanged(keyChanged) + secPasswordEdit.ConnectTextChanged(keyChanged) + generateButton.ConnectClicked(func(bool) { + sk := nostr.Generate() + nsec := nip19.EncodeNsec(sk) + secEdit.SetText(nsec) + keyChanged(nsec) }) tabWidget := widgets.NewQTabWidget(nil) eventTab := setupEventTab() - reqTab := widgets.NewQWidget(nil, 0) + reqTab := setupReqTab() tabWidget.AddTab(eventTab, "event") tabWidget.AddTab(reqTab, "req") @@ -67,6 +128,7 @@ func main() { // initial render updateEvent() + updateReq() window.Show() app.Exec()