mirror of
https://github.com/nbd-wtf/go-nostr.git
synced 2025-08-27 14:22:20 +02:00
nip60: make client better, fixes to receive flow, wallet helper methods.
This commit is contained in:
@@ -19,40 +19,17 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func GetMintInfo(ctx context.Context, mintURL string) (*nut06.MintInfo, error) {
|
func GetMintInfo(ctx context.Context, mintURL string) (*nut06.MintInfo, error) {
|
||||||
resp, err := httpGet(ctx, mintURL+"/v1/info")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var mintInfo nut06.MintInfo
|
var mintInfo nut06.MintInfo
|
||||||
if err := json.Unmarshal(body, &mintInfo); err != nil {
|
if err := httpGet(ctx, mintURL+"/v1/info", &mintInfo); err != nil {
|
||||||
return nil, fmt.Errorf("error reading response from mint: %v", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &mintInfo, nil
|
return &mintInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetActiveKeyset(ctx context.Context, mintURL string) (*nut01.Keyset, error) {
|
func GetActiveKeyset(ctx context.Context, mintURL string) (*nut01.Keyset, error) {
|
||||||
resp, err := httpGet(ctx, mintURL+"/v1/keys")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var keysetRes nut01.GetKeysResponse
|
var keysetRes nut01.GetKeysResponse
|
||||||
if err := json.Unmarshal(body, &keysetRes); err != nil {
|
if err := httpGet(ctx, mintURL+"/v1/keys", &keysetRes); err != nil {
|
||||||
return nil, fmt.Errorf("error reading response from mint: %w", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, keyset := range keysetRes.Keysets {
|
for _, keyset := range keysetRes.Keysets {
|
||||||
@@ -65,42 +42,18 @@ func GetActiveKeyset(ctx context.Context, mintURL string) (*nut01.Keyset, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetAllKeysets(ctx context.Context, mintURL string) ([]nut02.Keyset, error) {
|
func GetAllKeysets(ctx context.Context, mintURL string) ([]nut02.Keyset, error) {
|
||||||
resp, err := httpGet(ctx, mintURL+"/v1/keysets")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var keysetsRes nut02.GetKeysetsResponse
|
var keysetsRes nut02.GetKeysetsResponse
|
||||||
if err := json.Unmarshal(body, &keysetsRes); err != nil {
|
if err := httpGet(ctx, mintURL+"/v1/keysets", &keysetsRes); err != nil {
|
||||||
return nil, fmt.Errorf("error reading response from mint: %v", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return keysetsRes.Keysets, nil
|
return keysetsRes.Keysets, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetKeysetById(ctx context.Context, mintURL, id string) (map[uint64]string, error) {
|
func GetKeysetById(ctx context.Context, mintURL, id string) (map[uint64]string, error) {
|
||||||
resp, err := httpGet(ctx, mintURL+"/v1/keys/"+id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var keysetRes nut01.GetKeysResponse
|
var keysetRes nut01.GetKeysResponse
|
||||||
if err := json.Unmarshal(body, &keysetRes); err != nil || len(keysetRes.Keysets) != 1 {
|
if err := httpGet(ctx, mintURL+"/v1/keys/"+id, &keysetRes); err != nil {
|
||||||
return nil, fmt.Errorf("error reading response from mint: %w", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return keysetRes.Keysets[0].Keys, nil
|
return keysetRes.Keysets[0].Keys, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,42 +62,18 @@ func PostMintQuoteBolt11(
|
|||||||
mintURL string,
|
mintURL string,
|
||||||
mintQuoteRequest nut04.PostMintQuoteBolt11Request,
|
mintQuoteRequest nut04.PostMintQuoteBolt11Request,
|
||||||
) (*nut04.PostMintQuoteBolt11Response, error) {
|
) (*nut04.PostMintQuoteBolt11Response, error) {
|
||||||
resp, err := httpPost(ctx, mintURL+"/v1/mint/quote/bolt11", mintQuoteRequest)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var reqMintResponse nut04.PostMintQuoteBolt11Response
|
var reqMintResponse nut04.PostMintQuoteBolt11Response
|
||||||
if err := json.Unmarshal(body, &reqMintResponse); err != nil {
|
if err := httpPost(ctx, mintURL+"/v1/mint/quote/bolt11", mintQuoteRequest, &reqMintResponse); err != nil {
|
||||||
return nil, fmt.Errorf("error reading response from mint: %w", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &reqMintResponse, nil
|
return &reqMintResponse, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetMintQuoteState(ctx context.Context, mintURL, quoteId string) (*nut04.PostMintQuoteBolt11Response, error) {
|
func GetMintQuoteState(ctx context.Context, mintURL, quoteId string) (*nut04.PostMintQuoteBolt11Response, error) {
|
||||||
resp, err := httpGet(ctx, mintURL+"/v1/mint/quote/bolt11/"+quoteId)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var mintQuoteResponse nut04.PostMintQuoteBolt11Response
|
var mintQuoteResponse nut04.PostMintQuoteBolt11Response
|
||||||
if err := json.Unmarshal(body, &mintQuoteResponse); err != nil {
|
if err := httpGet(ctx, mintURL+"/v1/mint/quote/bolt11/"+quoteId, &mintQuoteResponse); err != nil {
|
||||||
return nil, fmt.Errorf("error reading response from mint: %w", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &mintQuoteResponse, nil
|
return &mintQuoteResponse, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,42 +82,18 @@ func PostMintBolt11(
|
|||||||
mintURL string,
|
mintURL string,
|
||||||
mintRequest nut04.PostMintBolt11Request,
|
mintRequest nut04.PostMintBolt11Request,
|
||||||
) (*nut04.PostMintBolt11Response, error) {
|
) (*nut04.PostMintBolt11Response, error) {
|
||||||
resp, err := httpPost(ctx, mintURL+"/v1/mint/bolt11", mintRequest)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var reqMintResponse nut04.PostMintBolt11Response
|
var reqMintResponse nut04.PostMintBolt11Response
|
||||||
if err := json.Unmarshal(body, &reqMintResponse); err != nil {
|
if err := httpPost(ctx, mintURL+"/v1/mint/bolt11", mintRequest, &reqMintResponse); err != nil {
|
||||||
return nil, fmt.Errorf("error reading response from mint: %w", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &reqMintResponse, nil
|
return &reqMintResponse, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func PostSwap(ctx context.Context, mintURL string, swapRequest nut03.PostSwapRequest) (*nut03.PostSwapResponse, error) {
|
func PostSwap(ctx context.Context, mintURL string, swapRequest nut03.PostSwapRequest) (*nut03.PostSwapResponse, error) {
|
||||||
resp, err := httpPost(ctx, mintURL+"/v1/swap", swapRequest)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var swapResponse nut03.PostSwapResponse
|
var swapResponse nut03.PostSwapResponse
|
||||||
if err := json.Unmarshal(body, &swapResponse); err != nil {
|
if err := httpPost(ctx, mintURL+"/v1/swap", swapRequest, &swapResponse); err != nil {
|
||||||
return nil, fmt.Errorf("error reading response from mint: %w", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &swapResponse, nil
|
return &swapResponse, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,42 +102,18 @@ func PostMeltQuoteBolt11(
|
|||||||
mintURL string,
|
mintURL string,
|
||||||
meltQuoteRequest nut05.PostMeltQuoteBolt11Request,
|
meltQuoteRequest nut05.PostMeltQuoteBolt11Request,
|
||||||
) (*nut05.PostMeltQuoteBolt11Response, error) {
|
) (*nut05.PostMeltQuoteBolt11Response, error) {
|
||||||
resp, err := httpPost(ctx, mintURL+"/v1/melt/quote/bolt11", meltQuoteRequest)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var meltQuoteResponse nut05.PostMeltQuoteBolt11Response
|
var meltQuoteResponse nut05.PostMeltQuoteBolt11Response
|
||||||
if err := json.Unmarshal(body, &meltQuoteResponse); err != nil {
|
if err := httpPost(ctx, mintURL+"/v1/melt/quote/bolt11", meltQuoteRequest, &meltQuoteResponse); err != nil {
|
||||||
return nil, fmt.Errorf("error reading response from mint: %w", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &meltQuoteResponse, nil
|
return &meltQuoteResponse, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetMeltQuoteState(ctx context.Context, mintURL, quoteId string) (*nut05.PostMeltQuoteBolt11Response, error) {
|
func GetMeltQuoteState(ctx context.Context, mintURL, quoteId string) (*nut05.PostMeltQuoteBolt11Response, error) {
|
||||||
resp, err := httpGet(ctx, mintURL+"/v1/melt/quote/bolt11/"+quoteId)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var meltQuoteResponse nut05.PostMeltQuoteBolt11Response
|
var meltQuoteResponse nut05.PostMeltQuoteBolt11Response
|
||||||
if err := json.Unmarshal(body, &meltQuoteResponse); err != nil {
|
if err := httpGet(ctx, mintURL+"/v1/melt/quote/bolt11/"+quoteId, &meltQuoteResponse); err != nil {
|
||||||
return nil, fmt.Errorf("error reading response from mint: %w", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &meltQuoteResponse, nil
|
return &meltQuoteResponse, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,22 +122,10 @@ func PostMeltBolt11(
|
|||||||
mintURL string,
|
mintURL string,
|
||||||
meltRequest nut05.PostMeltBolt11Request,
|
meltRequest nut05.PostMeltBolt11Request,
|
||||||
) (*nut05.PostMeltQuoteBolt11Response, error) {
|
) (*nut05.PostMeltQuoteBolt11Response, error) {
|
||||||
resp, err := httpPost(ctx, mintURL+"/v1/melt/bolt11", meltRequest)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var meltResponse nut05.PostMeltQuoteBolt11Response
|
var meltResponse nut05.PostMeltQuoteBolt11Response
|
||||||
if err := json.Unmarshal(body, &meltResponse); err != nil {
|
if err := httpPost(ctx, mintURL+"/v1/melt/bolt11", meltRequest, &meltResponse); err != nil {
|
||||||
return nil, fmt.Errorf("error reading response from mint: %w", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &meltResponse, nil
|
return &meltResponse, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,22 +134,10 @@ func PostCheckProofState(
|
|||||||
mintURL string,
|
mintURL string,
|
||||||
stateRequest nut07.PostCheckStateRequest,
|
stateRequest nut07.PostCheckStateRequest,
|
||||||
) (*nut07.PostCheckStateResponse, error) {
|
) (*nut07.PostCheckStateResponse, error) {
|
||||||
resp, err := httpPost(ctx, mintURL+"/v1/checkstate", stateRequest)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var stateResponse nut07.PostCheckStateResponse
|
var stateResponse nut07.PostCheckStateResponse
|
||||||
if err := json.Unmarshal(body, &stateResponse); err != nil {
|
if err := httpPost(ctx, mintURL+"/v1/checkstate", stateRequest, &stateResponse); err != nil {
|
||||||
return nil, fmt.Errorf("error reading response from mint: %v", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &stateResponse, nil
|
return &stateResponse, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,74 +146,71 @@ func PostRestore(
|
|||||||
mintURL string,
|
mintURL string,
|
||||||
restoreRequest nut09.PostRestoreRequest,
|
restoreRequest nut09.PostRestoreRequest,
|
||||||
) (*nut09.PostRestoreResponse, error) {
|
) (*nut09.PostRestoreResponse, error) {
|
||||||
resp, err := httpPost(ctx, mintURL+"/v1/restore", restoreRequest)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var restoreResponse nut09.PostRestoreResponse
|
var restoreResponse nut09.PostRestoreResponse
|
||||||
if err := json.Unmarshal(body, &restoreResponse); err != nil {
|
if err := httpPost(ctx, mintURL+"/v1/restore", restoreRequest, &restoreResponse); err != nil {
|
||||||
return nil, fmt.Errorf("error reading response from mint: %v", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &restoreResponse, nil
|
return &restoreResponse, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func httpGet(ctx context.Context, url string) (*http.Response, error) {
|
func httpGet(ctx context.Context, url string, dst any) error {
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := http.DefaultClient.Do(req)
|
resp, err := http.DefaultClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return parse(resp)
|
return parse(resp, dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
func httpPost(ctx context.Context, url string, data any) (*http.Response, error) {
|
func httpPost(ctx context.Context, url string, data any, dst any) error {
|
||||||
r, w := io.Pipe()
|
r, w := io.Pipe()
|
||||||
json.NewEncoder(w).Encode(data)
|
go func() {
|
||||||
|
json.NewEncoder(w).Encode(data)
|
||||||
|
w.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, "POST", url, r)
|
req, err := http.NewRequestWithContext(ctx, "POST", url, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
resp, err := http.DefaultClient.Do(req)
|
resp, err := http.DefaultClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
return parse(resp)
|
return parse(resp, dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parse(response *http.Response) (*http.Response, error) {
|
func parse(response *http.Response, dst any) error {
|
||||||
if response.StatusCode == 400 {
|
if response.StatusCode == 400 {
|
||||||
var errResponse cashu.Error
|
var errResponse cashu.Error
|
||||||
err := json.NewDecoder(response.Body).Decode(&errResponse)
|
err := json.NewDecoder(response.Body).Decode(&errResponse)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not decode error response from mint: %v", err)
|
return fmt.Errorf("could not decode error response from mint: %v", err)
|
||||||
}
|
}
|
||||||
return nil, errResponse
|
return errResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.StatusCode != 200 {
|
if response.StatusCode != 200 {
|
||||||
body, err := io.ReadAll(response.Body)
|
body, err := io.ReadAll(response.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("%s", body)
|
return fmt.Errorf("%s", body)
|
||||||
}
|
}
|
||||||
|
|
||||||
return response, nil
|
err := json.NewDecoder(response.Body).Decode(dst)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not decode response from mint: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
66
nip60/lib.go
66
nip60/lib.go
@@ -3,9 +3,11 @@ package nip60
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"iter"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/btcec/v2"
|
||||||
"github.com/nbd-wtf/go-nostr"
|
"github.com/nbd-wtf/go-nostr"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,6 +19,40 @@ type WalletStash struct {
|
|||||||
pendingHistory map[string][]HistoryEntry // history entries not yet assigned to a wallet
|
pendingHistory map[string][]HistoryEntry // history entries not yet assigned to a wallet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (wl *WalletStash) EnsureWallet(id string) *Wallet {
|
||||||
|
wl.Lock()
|
||||||
|
defer wl.Unlock()
|
||||||
|
if w, ok := wl.wallets[id]; ok {
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
sk, err := btcec.NewPrivateKey()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
w := &Wallet{
|
||||||
|
Identifier: id,
|
||||||
|
PrivateKey: sk,
|
||||||
|
PublicKey: sk.PubKey(),
|
||||||
|
}
|
||||||
|
wl.wallets[id] = w
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wl *WalletStash) Wallets() iter.Seq[*Wallet] {
|
||||||
|
return func(yield func(*Wallet) bool) {
|
||||||
|
wl.Lock()
|
||||||
|
defer wl.Unlock()
|
||||||
|
|
||||||
|
for _, w := range wl.wallets {
|
||||||
|
if !yield(w) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func NewStash() *WalletStash {
|
func NewStash() *WalletStash {
|
||||||
return &WalletStash{
|
return &WalletStash{
|
||||||
wallets: make(map[string]*Wallet, 1),
|
wallets: make(map[string]*Wallet, 1),
|
||||||
@@ -28,7 +64,7 @@ func NewStash() *WalletStash {
|
|||||||
func LoadStash(
|
func LoadStash(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
kr nostr.Keyer,
|
kr nostr.Keyer,
|
||||||
events <-chan *nostr.Event,
|
events <-chan nostr.RelayEvent,
|
||||||
errors chan<- error,
|
errors chan<- error,
|
||||||
) *WalletStash {
|
) *WalletStash {
|
||||||
wl := &WalletStash{
|
wl := &WalletStash{
|
||||||
@@ -38,15 +74,15 @@ func LoadStash(
|
|||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for evt := range events {
|
for ie := range events {
|
||||||
wl.Lock()
|
wl.Lock()
|
||||||
switch evt.Kind {
|
switch ie.Event.Kind {
|
||||||
case 37375:
|
case 37375:
|
||||||
wallet := &Wallet{}
|
wallet := &Wallet{}
|
||||||
if err := wallet.parse(ctx, kr, evt); err != nil {
|
if err := wallet.parse(ctx, kr, ie.Event); err != nil {
|
||||||
wl.Unlock()
|
wl.Unlock()
|
||||||
if errors != nil {
|
if errors != nil {
|
||||||
errors <- fmt.Errorf("event %s failed: %w", evt, err)
|
errors <- fmt.Errorf("event %s failed: %w", ie.Event, err)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -61,11 +97,11 @@ func LoadStash(
|
|||||||
wl.wallets[wallet.Identifier] = wallet
|
wl.wallets[wallet.Identifier] = wallet
|
||||||
|
|
||||||
case 7375: // token
|
case 7375: // token
|
||||||
ref := evt.Tags.GetFirst([]string{"a", ""})
|
ref := ie.Event.Tags.GetFirst([]string{"a", ""})
|
||||||
if ref == nil {
|
if ref == nil {
|
||||||
wl.Unlock()
|
wl.Unlock()
|
||||||
if errors != nil {
|
if errors != nil {
|
||||||
errors <- fmt.Errorf("event %s missing 'a' tag", evt)
|
errors <- fmt.Errorf("event %s missing 'a' tag", ie.Event)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -73,16 +109,16 @@ func LoadStash(
|
|||||||
if len(spl) < 3 {
|
if len(spl) < 3 {
|
||||||
wl.Unlock()
|
wl.Unlock()
|
||||||
if errors != nil {
|
if errors != nil {
|
||||||
errors <- fmt.Errorf("event %s invalid 'a' tag", evt)
|
errors <- fmt.Errorf("event %s invalid 'a' tag", ie.Event)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
token := Token{}
|
token := Token{}
|
||||||
if err := token.parse(ctx, kr, evt); err != nil {
|
if err := token.parse(ctx, kr, ie.Event); err != nil {
|
||||||
wl.Unlock()
|
wl.Unlock()
|
||||||
if errors != nil {
|
if errors != nil {
|
||||||
errors <- fmt.Errorf("event %s failed: %w", evt, err)
|
errors <- fmt.Errorf("event %s failed: %w", ie.Event, err)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -94,11 +130,11 @@ func LoadStash(
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 7376: // history
|
case 7376: // history
|
||||||
ref := evt.Tags.GetFirst([]string{"a", ""})
|
ref := ie.Event.Tags.GetFirst([]string{"a", ""})
|
||||||
if ref == nil {
|
if ref == nil {
|
||||||
wl.Unlock()
|
wl.Unlock()
|
||||||
if errors != nil {
|
if errors != nil {
|
||||||
errors <- fmt.Errorf("event %s missing 'a' tag", evt)
|
errors <- fmt.Errorf("event %s missing 'a' tag", ie.Event)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -106,16 +142,16 @@ func LoadStash(
|
|||||||
if len(spl) < 3 {
|
if len(spl) < 3 {
|
||||||
wl.Unlock()
|
wl.Unlock()
|
||||||
if errors != nil {
|
if errors != nil {
|
||||||
errors <- fmt.Errorf("event %s invalid 'a' tag", evt)
|
errors <- fmt.Errorf("event %s invalid 'a' tag", ie.Event)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
he := HistoryEntry{}
|
he := HistoryEntry{}
|
||||||
if err := he.parse(ctx, kr, evt); err != nil {
|
if err := he.parse(ctx, kr, ie.Event); err != nil {
|
||||||
wl.Unlock()
|
wl.Unlock()
|
||||||
if errors != nil {
|
if errors != nil {
|
||||||
errors <- fmt.Errorf("event %s failed: %w", evt, err)
|
errors <- fmt.Errorf("event %s failed: %w", ie.Event, err)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@@ -116,11 +116,11 @@ func TestWalletRoundtrip(t *testing.T) {
|
|||||||
|
|
||||||
for _, allEvents := range [][]nostr.Event{allEvents, reversedAllEvents} {
|
for _, allEvents := range [][]nostr.Event{allEvents, reversedAllEvents} {
|
||||||
// create channel and feed events into it
|
// create channel and feed events into it
|
||||||
eventChan := make(chan *nostr.Event)
|
eventChan := make(chan nostr.RelayEvent)
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
for _, evt := range allEvents {
|
for _, evt := range allEvents {
|
||||||
eventChan <- &evt
|
eventChan <- nostr.RelayEvent{Event: &evt}
|
||||||
}
|
}
|
||||||
close(eventChan)
|
close(eventChan)
|
||||||
done <- struct{}{}
|
done <- struct{}{}
|
||||||
|
@@ -28,20 +28,19 @@ func (w *Wallet) ReceiveToken(ctx context.Context, serializedToken string) error
|
|||||||
for i, proof := range proofs {
|
for i, proof := range proofs {
|
||||||
if proof.Secret != "" {
|
if proof.Secret != "" {
|
||||||
nut10Secret, err := nut10.DeserializeSecret(proof.Secret)
|
nut10Secret, err := nut10.DeserializeSecret(proof.Secret)
|
||||||
if err != nil {
|
if err == nil {
|
||||||
return fmt.Errorf("invalid nip10 secret at %d: %w", i, err)
|
switch nut10Secret.Kind {
|
||||||
}
|
case nut10.P2PK:
|
||||||
switch nut10Secret.Kind {
|
isp2pk = true
|
||||||
case nut10.P2PK:
|
proofs[i].Witness, err = signInput(w.PrivateKey, w.PublicKey, proof, nut10Secret)
|
||||||
isp2pk = true
|
if err != nil {
|
||||||
proofs[i].Witness, err = signInput(w.PrivateKey, w.PublicKey, proof, nut10Secret)
|
return fmt.Errorf("failed to sign locked proof %d: %w", i, err)
|
||||||
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
|
||||||
}
|
}
|
||||||
case nut10.HTLC:
|
|
||||||
return fmt.Errorf("HTLC token not supported yet")
|
|
||||||
case nut10.AnyoneCanSpend:
|
|
||||||
// ok
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -90,7 +89,7 @@ func (w *Wallet) ReceiveToken(ctx context.Context, serializedToken string) error
|
|||||||
|
|
||||||
res, err := client.PostSwap(ctx, source, req)
|
res, err := client.PostSwap(ctx, source, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to swap %s->%s: %w", source, w.Mints[0], err)
|
return fmt.Errorf("failed to claim received tokens at %s: %w", source, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
newProofs, err := constructProofs(res.Signatures, req.Outputs, secrets, rs, sourceActiveKeys)
|
newProofs, err := constructProofs(res.Signatures, req.Outputs, secrets, rs, sourceActiveKeys)
|
||||||
|
@@ -34,7 +34,18 @@ func (w Wallet) Balance() uint64 {
|
|||||||
return sum
|
return sum
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w Wallet) ToPublishableEvents(ctx context.Context, kr nostr.Keyer, skipExisting bool) ([]nostr.Event, error) {
|
func (w Wallet) DisplayName() string {
|
||||||
|
if w.Name != "" {
|
||||||
|
return fmt.Sprintf("%s (%s)", w.Name, w.Identifier)
|
||||||
|
}
|
||||||
|
return w.Identifier
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w Wallet) ToPublishableEvents(
|
||||||
|
ctx context.Context,
|
||||||
|
kr nostr.Keyer,
|
||||||
|
skipExisting bool,
|
||||||
|
) ([]nostr.Event, error) {
|
||||||
evt := nostr.Event{
|
evt := nostr.Event{
|
||||||
CreatedAt: nostr.Now(),
|
CreatedAt: nostr.Now(),
|
||||||
Kind: 37375,
|
Kind: 37375,
|
||||||
@@ -162,7 +173,6 @@ func (w *Wallet) parse(ctx context.Context, kr nostr.Keyer, evt *nostr.Event) er
|
|||||||
case "relay":
|
case "relay":
|
||||||
w.Relays = append(w.Relays, tag[1])
|
w.Relays = append(w.Relays, tag[1])
|
||||||
case "mint":
|
case "mint":
|
||||||
essential++
|
|
||||||
w.Mints = append(w.Mints, tag[1])
|
w.Mints = append(w.Mints, tag[1])
|
||||||
case "privkey":
|
case "privkey":
|
||||||
essential++
|
essential++
|
||||||
@@ -187,8 +197,8 @@ func (w *Wallet) parse(ctx context.Context, kr nostr.Keyer, evt *nostr.Event) er
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if essential < 4 {
|
if essential != 3 {
|
||||||
return fmt.Errorf("missing essential tags %s", evt)
|
return fmt.Errorf("missing essential tags")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
Reference in New Issue
Block a user