mirror of
https://github.com/nbd-wtf/go-nostr.git
synced 2025-03-19 06:12:44 +01:00
154 lines
3.0 KiB
Go
154 lines
3.0 KiB
Go
package nip60
|
|
|
|
import (
|
|
"context"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"sync"
|
|
|
|
"github.com/btcsuite/btcd/btcec/v2"
|
|
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
|
"github.com/nbd-wtf/go-nostr"
|
|
)
|
|
|
|
type Wallet struct {
|
|
wl *WalletStash
|
|
|
|
Identifier string
|
|
Description string
|
|
Name string
|
|
PrivateKey *btcec.PrivateKey
|
|
PublicKey *btcec.PublicKey
|
|
Relays []string
|
|
Mints []string
|
|
Tokens []Token
|
|
History []HistoryEntry
|
|
|
|
tokensMu sync.Mutex
|
|
|
|
event *nostr.Event
|
|
tokensPendingDeletion []string
|
|
}
|
|
|
|
func (w *Wallet) Balance() uint64 {
|
|
var sum uint64
|
|
for _, token := range w.Tokens {
|
|
sum += token.Proofs.Amount()
|
|
}
|
|
return sum
|
|
}
|
|
|
|
func (w *Wallet) DisplayName() string {
|
|
if w.Name != "" {
|
|
return fmt.Sprintf("%s (%s)", w.Name, w.Identifier)
|
|
}
|
|
return w.Identifier
|
|
}
|
|
|
|
func (w *Wallet) toEvent(ctx context.Context, kr nostr.Keyer, evt *nostr.Event) error {
|
|
evt.CreatedAt = nostr.Now()
|
|
evt.Kind = 37375
|
|
evt.Tags = make(nostr.Tags, 0, 7)
|
|
|
|
pk, err := kr.GetPublicKey(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
evt.Content, err = kr.Encrypt(
|
|
ctx,
|
|
fmt.Sprintf(`[["privkey","%x"]]`, w.PrivateKey.Serialize()),
|
|
pk,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
evt.Tags = append(evt.Tags,
|
|
nostr.Tag{"d", w.Identifier},
|
|
nostr.Tag{"unit", "sat"},
|
|
)
|
|
if w.Name != "" {
|
|
evt.Tags = append(evt.Tags, nostr.Tag{"name", w.Name})
|
|
}
|
|
if w.Description != "" {
|
|
evt.Tags = append(evt.Tags, nostr.Tag{"description", w.Description})
|
|
}
|
|
for _, relay := range w.Relays {
|
|
evt.Tags = append(evt.Tags, nostr.Tag{"relay", relay})
|
|
}
|
|
for _, mint := range w.Mints {
|
|
evt.Tags = append(evt.Tags, nostr.Tag{"mint", mint})
|
|
}
|
|
|
|
err = kr.SignEvent(ctx, evt)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (w *Wallet) parse(ctx context.Context, kr nostr.Keyer, evt *nostr.Event) error {
|
|
w.Tokens = make([]Token, 0, 128)
|
|
w.History = make([]HistoryEntry, 0, 128)
|
|
w.event = evt
|
|
|
|
pk, err := kr.GetPublicKey(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
jsonb, err := kr.Decrypt(ctx, evt.Content, pk)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var tags nostr.Tags
|
|
if len(jsonb) > 0 {
|
|
tags = make(nostr.Tags, 0, 7)
|
|
if err := json.Unmarshal([]byte(jsonb), &tags); err != nil {
|
|
return err
|
|
}
|
|
tags = append(tags, evt.Tags...)
|
|
}
|
|
|
|
essential := 0
|
|
for _, tag := range tags {
|
|
if len(tag) < 2 {
|
|
continue
|
|
}
|
|
switch tag[0] {
|
|
case "d":
|
|
essential++
|
|
w.Identifier = tag[1]
|
|
case "name":
|
|
w.Name = tag[1]
|
|
case "description":
|
|
w.Description = tag[1]
|
|
case "unit":
|
|
essential++
|
|
if tag[1] != "sat" {
|
|
return fmt.Errorf("only 'sat' wallets are supported")
|
|
}
|
|
case "relay":
|
|
w.Relays = append(w.Relays, tag[1])
|
|
case "mint":
|
|
w.Mints = append(w.Mints, tag[1])
|
|
case "privkey":
|
|
essential++
|
|
skb, err := hex.DecodeString(tag[1])
|
|
if err != nil {
|
|
return fmt.Errorf("failed to parse private key: %w", err)
|
|
}
|
|
w.PrivateKey = secp256k1.PrivKeyFromBytes(skb)
|
|
w.PublicKey = w.PrivateKey.PubKey()
|
|
}
|
|
}
|
|
|
|
if essential != 3 {
|
|
return fmt.Errorf("missing essential tags")
|
|
}
|
|
|
|
return nil
|
|
}
|