nip61 and nip60 improvements and fixes.

This commit is contained in:
fiatjaf
2025-02-04 13:43:18 -03:00
parent 48ce669a3d
commit 1e4848d84d
6 changed files with 180 additions and 21 deletions

View File

@@ -11,17 +11,47 @@ import (
"github.com/nbd-wtf/go-nostr/nip60/client"
)
type receiveSettings struct {
intoMint []string
isNutzap bool
}
type ReceiveOption func(*receiveSettings)
func WithMintDestination(url string) ReceiveOption {
return func(rs *receiveSettings) {
rs.intoMint = append(rs.intoMint, url)
}
}
func WithNutzap() ReceiveOption {
return func(rs *receiveSettings) {
rs.isNutzap = true
}
}
func (w *Wallet) Receive(
ctx context.Context,
proofs cashu.Proofs,
mint string,
opts ...ReceiveOption,
) error {
if w.PublishUpdate == nil {
return fmt.Errorf("can't do write operations: missing PublishUpdate function")
}
source := "http" + nostr.NormalizeURL(mint)[2:]
lightningSwap := slices.Contains(w.Mints, source)
rs := receiveSettings{}
for _, opt := range opts {
opt(&rs)
}
source, _ := nostr.NormalizeHTTPURL(mint)
destination := rs.intoMint
if len(destination) == 0 {
destination = w.Mints
}
lightningSwap := slices.Contains(destination, source)
swapOpts := make([]SwapOption, 0, 1)
for i, proof := range proofs {
@@ -60,7 +90,7 @@ func (w *Wallet) Receive(
// if we have to swap to our own mint we do it now by getting a bolt11 invoice from our mint
// and telling the current mint to pay it
if lightningSwap {
for _, targetMint := range w.Mints {
for _, targetMint := range destination {
swappedProofs, err, status := lightningMeltMint(
ctx,
newProofs,
@@ -110,7 +140,7 @@ saveproofs:
{
EventID: newToken.event.ID,
Created: true,
IsNutzap: false,
IsNutzap: rs.isNutzap,
},
},
createdAt: nostr.Now(),

View File

@@ -72,7 +72,7 @@ func (t *Token) parse(ctx context.Context, kr nostr.Keyer, evt *nostr.Event) err
return fmt.Errorf("failed to parse token content: %w", err)
}
t.Mint = "http" + nostr.NormalizeURL(t.Mint)[2:]
t.Mint, _ = nostr.NormalizeHTTPURL(t.Mint)
return nil
}

View File

@@ -24,6 +24,7 @@ type Wallet struct {
kr nostr.Keyer
// PublishUpdate must be set to a function that publishes event to the user relays
// (if all arguments are their zero values that means it is a wallet update event).
PublishUpdate func(
event nostr.Event,
deleted *Token,
@@ -78,7 +79,7 @@ func loadWalletFromPool(
kinds := []int{17375, 7375}
if withHistory {
kinds = append(kinds, 7375)
kinds = append(kinds, 7376)
}
eoseChan := make(chan struct{})
@@ -229,6 +230,89 @@ func (w *Wallet) Balance() uint64 {
return sum
}
func (w *Wallet) AddMint(ctx context.Context, urls ...string) error {
if w.PublishUpdate == nil {
return fmt.Errorf("can't do write operations: missing PublishUpdate function")
}
for _, url := range urls {
url, err := nostr.NormalizeHTTPURL(url)
if err != nil {
return err
}
if !slices.Contains(w.Mints, url) {
w.Mints = append(w.Mints, url)
}
}
evt := nostr.Event{}
if err := w.toEvent(ctx, w.kr, &evt); err != nil {
return err
}
w.Lock()
w.PublishUpdate(evt, nil, nil, nil, false)
w.Unlock()
return nil
}
func (w *Wallet) RemoveMint(ctx context.Context, urls ...string) error {
if w.PublishUpdate == nil {
return fmt.Errorf("can't do write operations: missing PublishUpdate function")
}
for _, url := range urls {
url, err := nostr.NormalizeHTTPURL(url)
if err != nil {
return err
}
if idx := slices.Index(w.Mints, url); idx != -1 {
w.Mints = slices.Delete(w.Mints, idx, idx+1)
}
}
evt := nostr.Event{}
if err := w.toEvent(ctx, w.kr, &evt); err != nil {
return err
}
w.Lock()
w.PublishUpdate(evt, nil, nil, nil, false)
w.Unlock()
return nil
}
func (w *Wallet) SetPrivateKey(ctx context.Context, privateKey string) error {
if w.PublishUpdate == nil {
return fmt.Errorf("can't do write operations: missing PublishUpdate function")
}
skb, err := hex.DecodeString(privateKey)
if err != nil {
return err
}
if len(skb) != 32 {
return fmt.Errorf("private key must be 32 bytes, got %d", len(skb))
}
w.PrivateKey, w.PublicKey = btcec.PrivKeyFromBytes(skb)
evt := nostr.Event{}
if err := w.toEvent(ctx, w.kr, &evt); err != nil {
return err
}
w.Lock()
w.PublishUpdate(evt, nil, nil, nil, false)
w.Unlock()
return nil
}
func (w *Wallet) toEvent(ctx context.Context, kr nostr.Keyer, evt *nostr.Event) error {
evt.CreatedAt = nostr.Now()
evt.Kind = 17375
@@ -239,12 +323,15 @@ func (w *Wallet) toEvent(ctx context.Context, kr nostr.Keyer, evt *nostr.Event)
return err
}
tags := make(nostr.Tags, 0, 1+len(w.Mints))
tags = append(tags, nostr.Tag{"privkey", hex.EncodeToString(w.PrivateKey.Serialize())})
for _, mint := range w.Mints {
tags = append(tags, nostr.Tag{"mint", mint})
encryptedTags := make(nostr.Tags, 0, 1+len(w.Mints))
if w.PrivateKey != nil {
encryptedTags = append(encryptedTags, nostr.Tag{"privkey", hex.EncodeToString(w.PrivateKey.Serialize())})
}
jtags, _ := json.Marshal(tags)
for _, mint := range w.Mints {
encryptedTags = append(encryptedTags, nostr.Tag{"mint", mint})
}
jtags, _ := json.Marshal(encryptedTags)
evt.Content, err = kr.Encrypt(
ctx,
string(jtags),
@@ -301,15 +388,12 @@ func (w *Wallet) parse(ctx context.Context, kr nostr.Keyer, evt *nostr.Event) er
}
}
if privateKey == nil {
return fmt.Errorf("missing wallet private key")
if privateKey != nil {
w.PrivateKey = privateKey
w.PublicKey = w.PrivateKey.PubKey()
}
// finally set these things when we know nothing will fail
w.Mints = mints
fmt.Println("mints", mints)
w.PrivateKey = privateKey
w.PublicKey = w.PrivateKey.PubKey()
return nil
}