mirror of
https://github.com/nbd-wtf/go-nostr.git
synced 2025-03-18 13:53:03 +01:00
162 lines
3.7 KiB
Go
162 lines
3.7 KiB
Go
package nip60
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"slices"
|
|
|
|
"github.com/elnosh/gonuts/cashu"
|
|
"github.com/elnosh/gonuts/cashu/nuts/nut10"
|
|
"github.com/nbd-wtf/go-nostr"
|
|
"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")
|
|
}
|
|
|
|
rs := receiveSettings{}
|
|
for _, opt := range opts {
|
|
opt(&rs)
|
|
}
|
|
|
|
source, _ := nostr.NormalizeHTTPURL(mint)
|
|
destination := rs.intoMint
|
|
|
|
swapSettings := swapSettings{}
|
|
|
|
for i, proof := range proofs {
|
|
if proof.Secret != "" {
|
|
nut10Secret, err := nut10.DeserializeSecret(proof.Secret)
|
|
if err == nil {
|
|
switch nut10Secret.Kind {
|
|
case nut10.P2PK:
|
|
swapSettings.mustSignOutputs = true
|
|
|
|
proofs[i].Witness, err = signInput(w.PrivateKey, proof)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to sign locked proof %d: %w", i, err)
|
|
}
|
|
case nut10.HTLC:
|
|
return fmt.Errorf("HTLC token not supported yet")
|
|
case nut10.AnyoneCanSpend:
|
|
// ok
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sourceKeysets, err := client.GetAllKeysets(ctx, source)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get %s keysets: %w", source, err)
|
|
}
|
|
|
|
// get new proofs
|
|
newProofs, _, err := w.swapProofs(ctx, source, proofs, proofs.Amount(), swapSettings)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
newMint := source // if we don't have to do a lightning swap then new mint will be the same as old mint
|
|
|
|
// 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
|
|
lightningSwap := slices.Contains(destination, source)
|
|
if lightningSwap {
|
|
for _, targetMint := range destination {
|
|
swappedProofs, err, status := lightningMeltMint(
|
|
ctx,
|
|
newProofs,
|
|
source,
|
|
sourceKeysets,
|
|
targetMint,
|
|
)
|
|
if err != nil {
|
|
if status == tryAnotherTargetMint {
|
|
continue
|
|
}
|
|
if status == manualActionRequired {
|
|
return fmt.Errorf("failed to swap (needs manual action): %w", err)
|
|
}
|
|
if status == nothingCanBeDone {
|
|
return fmt.Errorf("failed to swap (nothing can be done, we probably lost the money): %w", err)
|
|
}
|
|
|
|
// if we get here that means we still have our proofs from the untrusted mint, so save those
|
|
goto saveproofs
|
|
} else {
|
|
// everything went well
|
|
newProofs = swappedProofs
|
|
newMint = targetMint
|
|
goto saveproofs
|
|
}
|
|
}
|
|
|
|
// if we got here that means we ran out of our trusted mints to swap to, so save the untrusted proofs
|
|
goto saveproofs
|
|
}
|
|
|
|
saveproofs:
|
|
newToken := Token{
|
|
Mint: newMint,
|
|
Proofs: newProofs,
|
|
mintedAt: nostr.Now(),
|
|
event: &nostr.Event{},
|
|
}
|
|
if err := newToken.toEvent(ctx, w.kr, newToken.event); err != nil {
|
|
return fmt.Errorf("failed to make new token: %w", err)
|
|
}
|
|
|
|
he := HistoryEntry{
|
|
event: &nostr.Event{},
|
|
TokenReferences: []TokenRef{
|
|
{
|
|
EventID: newToken.event.ID,
|
|
Created: true,
|
|
IsNutzap: rs.isNutzap,
|
|
},
|
|
},
|
|
createdAt: nostr.Now(),
|
|
In: true,
|
|
Amount: newToken.Proofs.Amount(),
|
|
}
|
|
|
|
w.Lock()
|
|
w.PublishUpdate(*newToken.event, nil, &newToken, nil, false)
|
|
if err := he.toEvent(ctx, w.kr, he.event); err == nil {
|
|
w.PublishUpdate(*he.event, nil, nil, nil, true)
|
|
}
|
|
w.Unlock()
|
|
|
|
w.tokensMu.Lock()
|
|
w.Tokens = append(w.Tokens, newToken)
|
|
w.tokensMu.Unlock()
|
|
|
|
return nil
|
|
}
|