mirror of
https://github.com/nbd-wtf/go-nostr.git
synced 2025-11-15 16:50:16 +01:00
nip60: make it work with emitting events to be published dynamically and stuff.
This commit is contained in:
@@ -98,11 +98,33 @@ func TestWalletRoundtrip(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// convert wallets to events
|
// convert wallets to events
|
||||||
events1, err := wallet1.ToPublishableEvents(ctx, kr, false)
|
events := [][]nostr.Event{
|
||||||
require.NoError(t, err)
|
make([]nostr.Event, 0, 4),
|
||||||
|
make([]nostr.Event, 0, 4),
|
||||||
|
}
|
||||||
|
|
||||||
events2, err := wallet2.ToPublishableEvents(ctx, kr, false)
|
for i, w := range []*Wallet{&wallet1, &wallet2} {
|
||||||
|
evt := nostr.Event{}
|
||||||
|
err := w.toEvent(ctx, kr, &evt)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
events[i] = append(events[i], evt)
|
||||||
|
|
||||||
|
for _, token := range w.Tokens {
|
||||||
|
evt = nostr.Event{}
|
||||||
|
err = token.toEvent(ctx, kr, w.Identifier, &evt)
|
||||||
|
require.NoError(t, err)
|
||||||
|
events[i] = append(events[i], evt)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, he := range w.History {
|
||||||
|
evt = nostr.Event{}
|
||||||
|
err = he.toEvent(ctx, kr, w.Identifier, &evt)
|
||||||
|
require.NoError(t, err)
|
||||||
|
events[i] = append(events[i], evt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
events1, events2 := events[0], events[1]
|
||||||
|
|
||||||
// combine all events
|
// combine all events
|
||||||
allEvents := append(events1, events2...)
|
allEvents := append(events1, events2...)
|
||||||
@@ -127,13 +149,12 @@ func TestWalletRoundtrip(t *testing.T) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
// load wallets from events
|
// load wallets from events
|
||||||
errorChan := make(chan error)
|
walletStash := loadStash(ctx, kr, eventChan, make(chan struct{}))
|
||||||
walletStash := LoadStash(ctx, kr, eventChan, errorChan)
|
|
||||||
|
|
||||||
var errorChanErr error
|
var errorChanErr error
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
errorChanErr = <-errorChan
|
errorChanErr = <-walletStash.Processed
|
||||||
if errorChanErr != nil {
|
if errorChanErr != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,13 +89,25 @@ func (w *Wallet) ReceiveToken(ctx context.Context, serializedToken string) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
saveproofs:
|
saveproofs:
|
||||||
w.tokensMu.Lock()
|
newToken := Token{
|
||||||
w.Tokens = append(w.Tokens, Token{
|
|
||||||
Mint: newMint,
|
Mint: newMint,
|
||||||
Proofs: newProofs,
|
Proofs: newProofs,
|
||||||
mintedAt: nostr.Now(),
|
mintedAt: nostr.Now(),
|
||||||
})
|
event: &nostr.Event{},
|
||||||
|
}
|
||||||
|
if err := newToken.toEvent(ctx, w.wl.kr, w.Identifier, newToken.event); err != nil {
|
||||||
|
return fmt.Errorf("failed to make new token: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.wl.Changes <- *newToken.event
|
||||||
|
|
||||||
|
w.tokensMu.Lock()
|
||||||
|
w.Tokens = append(w.Tokens, newToken)
|
||||||
w.tokensMu.Unlock()
|
w.tokensMu.Unlock()
|
||||||
|
|
||||||
|
wevt := nostr.Event{}
|
||||||
|
w.toEvent(ctx, w.wl.kr, &wevt)
|
||||||
|
w.wl.Changes <- wevt
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -130,18 +130,40 @@ found:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// delete spent tokens and save our change
|
// delete spent tokens and save our change
|
||||||
newTokens := make([]Token, 0, len(w.Tokens))
|
updatedTokens := make([]Token, 0, len(w.Tokens))
|
||||||
for i, token := range w.Tokens {
|
|
||||||
if slices.Contains(target.tokenIndexes, i) {
|
changeToken := Token{
|
||||||
continue
|
|
||||||
}
|
|
||||||
newTokens = append(newTokens, token)
|
|
||||||
}
|
|
||||||
w.Tokens = append(newTokens, Token{
|
|
||||||
mintedAt: nostr.Now(),
|
mintedAt: nostr.Now(),
|
||||||
Mint: target.mint,
|
Mint: target.mint,
|
||||||
Proofs: changeProofs,
|
Proofs: changeProofs,
|
||||||
})
|
Deleted: make([]string, 0, len(target.tokenIndexes)),
|
||||||
|
event: &nostr.Event{},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, token := range w.Tokens {
|
||||||
|
if slices.Contains(target.tokenIndexes, i) {
|
||||||
|
if token.event != nil {
|
||||||
|
token.Deleted = append(token.Deleted, token.event.ID)
|
||||||
|
|
||||||
|
deleteEvent := nostr.Event{
|
||||||
|
CreatedAt: nostr.Now(),
|
||||||
|
Kind: 5,
|
||||||
|
Tags: nostr.Tags{{"e", token.event.ID}},
|
||||||
|
}
|
||||||
|
w.wl.kr.SignEvent(ctx, &deleteEvent)
|
||||||
|
w.wl.Changes <- deleteEvent
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
updatedTokens = append(updatedTokens, token)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := changeToken.toEvent(ctx, w.wl.kr, w.Identifier, changeToken.event); err != nil {
|
||||||
|
return "", fmt.Errorf("failed to make change token: %w", err)
|
||||||
|
}
|
||||||
|
w.wl.Changes <- *changeToken.event
|
||||||
|
|
||||||
|
w.Tokens = append(updatedTokens, changeToken)
|
||||||
|
|
||||||
// serialize token we're sending out
|
// serialize token we're sending out
|
||||||
token, err := cashu.NewTokenV4(proofsToSend, target.mint, cashu.Sat, true)
|
token, err := cashu.NewTokenV4(proofsToSend, target.mint, cashu.Sat, true)
|
||||||
@@ -149,5 +171,9 @@ found:
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wevt := nostr.Event{}
|
||||||
|
w.toEvent(ctx, w.wl.kr, &wevt)
|
||||||
|
w.wl.Changes <- wevt
|
||||||
|
|
||||||
return token.Serialize()
|
return token.Serialize()
|
||||||
}
|
}
|
||||||
|
|||||||
330
nip60/stash.go
330
nip60/stash.go
@@ -15,8 +15,213 @@ type WalletStash struct {
|
|||||||
sync.Mutex
|
sync.Mutex
|
||||||
wallets map[string]*Wallet
|
wallets map[string]*Wallet
|
||||||
|
|
||||||
pendingTokens map[string][]Token // tokens not yet assigned to a wallet
|
|
||||||
pendingHistory map[string][]HistoryEntry // history entries not yet assigned to a wallet
|
pendingHistory map[string][]HistoryEntry // history entries not yet assigned to a wallet
|
||||||
|
pendingTokens map[string][]Token // tokens not yet assigned to a wallet
|
||||||
|
pendingDeletions []string // token events that should be deleted
|
||||||
|
|
||||||
|
kr nostr.Keyer
|
||||||
|
|
||||||
|
// Changes emits a stream of events that must be published whenever something changes
|
||||||
|
Changes chan nostr.Event
|
||||||
|
|
||||||
|
// Processed emits an error or nil every time an event is processed
|
||||||
|
Processed chan error
|
||||||
|
|
||||||
|
// Stable is closed when we have gotten an EOSE from all relays
|
||||||
|
Stable chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadStash(
|
||||||
|
ctx context.Context,
|
||||||
|
kr nostr.Keyer,
|
||||||
|
pool *nostr.SimplePool,
|
||||||
|
relays []string,
|
||||||
|
) *WalletStash {
|
||||||
|
pk, err := kr.GetPublicKey(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
eoseChan := make(chan struct{})
|
||||||
|
events := pool.SubManyNotifyEOSE(
|
||||||
|
ctx,
|
||||||
|
relays,
|
||||||
|
nostr.Filters{{Kinds: []int{5, 37375, 7375, 7376}, Authors: []string{pk}}},
|
||||||
|
eoseChan,
|
||||||
|
)
|
||||||
|
|
||||||
|
return loadStash(ctx, kr, events, eoseChan)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadStash(
|
||||||
|
ctx context.Context,
|
||||||
|
kr nostr.Keyer,
|
||||||
|
events chan nostr.RelayEvent,
|
||||||
|
eoseChan chan struct{},
|
||||||
|
) *WalletStash {
|
||||||
|
wl := &WalletStash{
|
||||||
|
wallets: make(map[string]*Wallet, 1),
|
||||||
|
pendingTokens: make(map[string][]Token),
|
||||||
|
pendingHistory: make(map[string][]HistoryEntry),
|
||||||
|
pendingDeletions: make([]string, 0, 128),
|
||||||
|
kr: kr,
|
||||||
|
Changes: make(chan nostr.Event),
|
||||||
|
Processed: make(chan error),
|
||||||
|
Stable: make(chan struct{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
eosed := false
|
||||||
|
go func() {
|
||||||
|
<-eoseChan
|
||||||
|
eosed = true
|
||||||
|
|
||||||
|
// check all pending deletions and delete stuff locally
|
||||||
|
for _, id := range wl.pendingDeletions {
|
||||||
|
wl.removeDeletedToken(id)
|
||||||
|
}
|
||||||
|
wl.pendingDeletions = nil
|
||||||
|
|
||||||
|
close(wl.Stable)
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for ie := range events {
|
||||||
|
wl.Lock()
|
||||||
|
switch ie.Event.Kind {
|
||||||
|
case 5:
|
||||||
|
if !eosed {
|
||||||
|
for _, tag := range ie.Event.Tags.All([]string{"e", ""}) {
|
||||||
|
wl.pendingDeletions = append(wl.pendingDeletions, tag[1])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, tag := range ie.Event.Tags.All([]string{"e", ""}) {
|
||||||
|
wl.removeDeletedToken(tag[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 37375:
|
||||||
|
wallet := &Wallet{
|
||||||
|
wl: wl,
|
||||||
|
}
|
||||||
|
if err := wallet.parse(ctx, kr, ie.Event); err != nil {
|
||||||
|
wl.Unlock()
|
||||||
|
wl.Processed <- fmt.Errorf("event %s failed: %w", ie.Event, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we already have a wallet with this identifier then we must be careful
|
||||||
|
if curr, ok := wl.wallets[wallet.Identifier]; ok {
|
||||||
|
// if the metadata we have is newer ignore this event
|
||||||
|
if curr.event.CreatedAt > ie.Event.CreatedAt {
|
||||||
|
wl.Unlock()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise transfer history events and tokens to the new wallet object
|
||||||
|
wallet.Tokens = curr.Tokens
|
||||||
|
wallet.History = curr.History
|
||||||
|
}
|
||||||
|
|
||||||
|
// get all pending stuff and assign them to this, then delete the pending stuff
|
||||||
|
for _, he := range wl.pendingHistory[wallet.Identifier] {
|
||||||
|
wallet.History = append(wallet.History, he)
|
||||||
|
}
|
||||||
|
delete(wl.pendingHistory, wallet.Identifier)
|
||||||
|
wallet.tokensMu.Lock()
|
||||||
|
for _, token := range wl.pendingTokens[wallet.Identifier] {
|
||||||
|
wallet.Tokens = append(wallet.Tokens, token)
|
||||||
|
}
|
||||||
|
delete(wl.pendingTokens, wallet.Identifier)
|
||||||
|
wallet.tokensMu.Unlock()
|
||||||
|
|
||||||
|
// finally save the new wallet object
|
||||||
|
wl.wallets[wallet.Identifier] = wallet
|
||||||
|
|
||||||
|
case 7375: // token
|
||||||
|
ref := ie.Event.Tags.GetFirst([]string{"a", ""})
|
||||||
|
if ref == nil {
|
||||||
|
wl.Unlock()
|
||||||
|
wl.Processed <- fmt.Errorf("event %s missing 'a' tag", ie.Event)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
spl := strings.SplitN((*ref)[1], ":", 3)
|
||||||
|
if len(spl) < 3 {
|
||||||
|
wl.Unlock()
|
||||||
|
wl.Processed <- fmt.Errorf("event %s invalid 'a' tag", ie.Event)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
token := Token{}
|
||||||
|
if err := token.parse(ctx, kr, ie.Event); err != nil {
|
||||||
|
wl.Unlock()
|
||||||
|
wl.Processed <- fmt.Errorf("event %s failed: %w", ie.Event, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if wallet, ok := wl.wallets[spl[2]]; ok {
|
||||||
|
wallet.tokensMu.Lock()
|
||||||
|
wallet.Tokens = append(wallet.Tokens, token)
|
||||||
|
wallet.tokensMu.Unlock()
|
||||||
|
} else {
|
||||||
|
wl.pendingTokens[spl[2]] = append(wl.pendingTokens[spl[2]], token)
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep track tokens that were deleted by this, if they exist
|
||||||
|
if !eosed {
|
||||||
|
for _, del := range token.Deleted {
|
||||||
|
wl.pendingDeletions = append(wl.pendingDeletions, del)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, del := range token.Deleted {
|
||||||
|
wl.removeDeletedToken(del)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case 7376: // history
|
||||||
|
ref := ie.Event.Tags.GetFirst([]string{"a", ""})
|
||||||
|
if ref == nil {
|
||||||
|
wl.Unlock()
|
||||||
|
wl.Processed <- fmt.Errorf("event %s missing 'a' tag", ie.Event)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
spl := strings.SplitN((*ref)[1], ":", 3)
|
||||||
|
if len(spl) < 3 {
|
||||||
|
wl.Unlock()
|
||||||
|
wl.Processed <- fmt.Errorf("event %s invalid 'a' tag", ie.Event)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
he := HistoryEntry{}
|
||||||
|
if err := he.parse(ctx, kr, ie.Event); err != nil {
|
||||||
|
wl.Unlock()
|
||||||
|
wl.Processed <- fmt.Errorf("event %s failed: %w", ie.Event, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if wallet, ok := wl.wallets[spl[2]]; ok {
|
||||||
|
wallet.History = append(wallet.History, he)
|
||||||
|
} else {
|
||||||
|
wl.pendingHistory[spl[2]] = append(wl.pendingHistory[spl[2]], he)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wl.Unlock()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return wl
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wl *WalletStash) removeDeletedToken(eventId string) {
|
||||||
|
for _, w := range wl.wallets {
|
||||||
|
for t := len(w.Tokens) - 1; t >= 0; t-- {
|
||||||
|
token := w.Tokens[t]
|
||||||
|
if token.event != nil && token.event.ID == eventId {
|
||||||
|
// swap delete
|
||||||
|
w.Tokens[t] = w.Tokens[len(w.Tokens)-1]
|
||||||
|
w.Tokens = w.Tokens[0 : len(w.Tokens)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wl *WalletStash) EnsureWallet(id string) *Wallet {
|
func (wl *WalletStash) EnsureWallet(id string) *Wallet {
|
||||||
@@ -35,6 +240,7 @@ func (wl *WalletStash) EnsureWallet(id string) *Wallet {
|
|||||||
Identifier: id,
|
Identifier: id,
|
||||||
PrivateKey: sk,
|
PrivateKey: sk,
|
||||||
PublicKey: sk.PubKey(),
|
PublicKey: sk.PubKey(),
|
||||||
|
wl: wl,
|
||||||
}
|
}
|
||||||
wl.wallets[id] = w
|
wl.wallets[id] = w
|
||||||
return w
|
return w
|
||||||
@@ -52,125 +258,3 @@ func (wl *WalletStash) Wallets() iter.Seq[*Wallet] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStash() *WalletStash {
|
|
||||||
return &WalletStash{
|
|
||||||
wallets: make(map[string]*Wallet, 1),
|
|
||||||
pendingTokens: make(map[string][]Token),
|
|
||||||
pendingHistory: make(map[string][]HistoryEntry),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func LoadStash(
|
|
||||||
ctx context.Context,
|
|
||||||
kr nostr.Keyer,
|
|
||||||
events <-chan nostr.RelayEvent,
|
|
||||||
errors chan<- error,
|
|
||||||
) *WalletStash {
|
|
||||||
wl := &WalletStash{
|
|
||||||
wallets: make(map[string]*Wallet, 1),
|
|
||||||
pendingTokens: make(map[string][]Token),
|
|
||||||
pendingHistory: make(map[string][]HistoryEntry),
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for ie := range events {
|
|
||||||
wl.Lock()
|
|
||||||
switch ie.Event.Kind {
|
|
||||||
case 37375:
|
|
||||||
wallet := &Wallet{}
|
|
||||||
if err := wallet.parse(ctx, kr, ie.Event); err != nil {
|
|
||||||
wl.Unlock()
|
|
||||||
if errors != nil {
|
|
||||||
errors <- fmt.Errorf("event %s failed: %w", ie.Event, err)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, he := range wl.pendingHistory[wallet.Identifier] {
|
|
||||||
wallet.History = append(wallet.History, he)
|
|
||||||
}
|
|
||||||
|
|
||||||
wallet.tokensMu.Lock()
|
|
||||||
for _, token := range wl.pendingTokens[wallet.Identifier] {
|
|
||||||
wallet.Tokens = append(wallet.Tokens, token)
|
|
||||||
}
|
|
||||||
wallet.tokensMu.Unlock()
|
|
||||||
|
|
||||||
wl.wallets[wallet.Identifier] = wallet
|
|
||||||
|
|
||||||
case 7375: // token
|
|
||||||
ref := ie.Event.Tags.GetFirst([]string{"a", ""})
|
|
||||||
if ref == nil {
|
|
||||||
wl.Unlock()
|
|
||||||
if errors != nil {
|
|
||||||
errors <- fmt.Errorf("event %s missing 'a' tag", ie.Event)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
spl := strings.SplitN((*ref)[1], ":", 3)
|
|
||||||
if len(spl) < 3 {
|
|
||||||
wl.Unlock()
|
|
||||||
if errors != nil {
|
|
||||||
errors <- fmt.Errorf("event %s invalid 'a' tag", ie.Event)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
token := Token{}
|
|
||||||
if err := token.parse(ctx, kr, ie.Event); err != nil {
|
|
||||||
wl.Unlock()
|
|
||||||
if errors != nil {
|
|
||||||
errors <- fmt.Errorf("event %s failed: %w", ie.Event, err)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if wallet, ok := wl.wallets[spl[2]]; ok {
|
|
||||||
wallet.tokensMu.Lock()
|
|
||||||
wallet.Tokens = append(wallet.Tokens, token)
|
|
||||||
wallet.tokensMu.Unlock()
|
|
||||||
} else {
|
|
||||||
wl.pendingTokens[spl[2]] = append(wl.pendingTokens[spl[2]], token)
|
|
||||||
}
|
|
||||||
|
|
||||||
case 7376: // history
|
|
||||||
ref := ie.Event.Tags.GetFirst([]string{"a", ""})
|
|
||||||
if ref == nil {
|
|
||||||
wl.Unlock()
|
|
||||||
if errors != nil {
|
|
||||||
errors <- fmt.Errorf("event %s missing 'a' tag", ie.Event)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
spl := strings.SplitN((*ref)[1], ":", 3)
|
|
||||||
if len(spl) < 3 {
|
|
||||||
wl.Unlock()
|
|
||||||
if errors != nil {
|
|
||||||
errors <- fmt.Errorf("event %s invalid 'a' tag", ie.Event)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
he := HistoryEntry{}
|
|
||||||
if err := he.parse(ctx, kr, ie.Event); err != nil {
|
|
||||||
wl.Unlock()
|
|
||||||
if errors != nil {
|
|
||||||
errors <- fmt.Errorf("event %s failed: %w", ie.Event, err)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if wallet, ok := wl.wallets[spl[2]]; ok {
|
|
||||||
wallet.History = append(wallet.History, he)
|
|
||||||
} else {
|
|
||||||
wl.pendingHistory[spl[2]] = append(wl.pendingHistory[spl[2]], he)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wl.Unlock()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return wl
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
type Token struct {
|
type Token struct {
|
||||||
Mint string `json:"mint"`
|
Mint string `json:"mint"`
|
||||||
Proofs cashu.Proofs `json:"proofs"`
|
Proofs cashu.Proofs `json:"proofs"`
|
||||||
|
Deleted []string `json:"del,omitempty"`
|
||||||
|
|
||||||
mintedAt nostr.Timestamp
|
mintedAt nostr.Timestamp
|
||||||
event *nostr.Event
|
event *nostr.Event
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Wallet struct {
|
type Wallet struct {
|
||||||
|
wl *WalletStash
|
||||||
|
|
||||||
Identifier string
|
Identifier string
|
||||||
Description string
|
Description string
|
||||||
Name string
|
Name string
|
||||||
@@ -26,6 +28,9 @@ type Wallet struct {
|
|||||||
|
|
||||||
temporaryBalance uint64
|
temporaryBalance uint64
|
||||||
tokensMu sync.Mutex
|
tokensMu sync.Mutex
|
||||||
|
|
||||||
|
event *nostr.Event
|
||||||
|
tokensPendingDeletion []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wallet) Balance() uint64 {
|
func (w *Wallet) Balance() uint64 {
|
||||||
@@ -43,20 +48,14 @@ func (w *Wallet) DisplayName() string {
|
|||||||
return w.Identifier
|
return w.Identifier
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wallet) ToPublishableEvents(
|
func (w *Wallet) toEvent(ctx context.Context, kr nostr.Keyer, evt *nostr.Event) error {
|
||||||
ctx context.Context,
|
evt.CreatedAt = nostr.Now()
|
||||||
kr nostr.Keyer,
|
evt.Kind = 37375
|
||||||
skipExisting bool,
|
evt.Tags = make(nostr.Tags, 0, 7)
|
||||||
) ([]nostr.Event, error) {
|
|
||||||
evt := nostr.Event{
|
|
||||||
CreatedAt: nostr.Now(),
|
|
||||||
Kind: 37375,
|
|
||||||
Tags: make(nostr.Tags, 0, 7),
|
|
||||||
}
|
|
||||||
|
|
||||||
pk, err := kr.GetPublicKey(ctx)
|
pk, err := kr.GetPublicKey(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
evt.Content, err = kr.Encrypt(
|
evt.Content, err = kr.Encrypt(
|
||||||
@@ -65,7 +64,7 @@ func (w *Wallet) ToPublishableEvents(
|
|||||||
pk,
|
pk,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
evt.Tags = append(evt.Tags,
|
evt.Tags = append(evt.Tags,
|
||||||
@@ -85,64 +84,23 @@ func (w *Wallet) ToPublishableEvents(
|
|||||||
evt.Tags = append(evt.Tags, nostr.Tag{"mint", mint})
|
evt.Tags = append(evt.Tags, nostr.Tag{"mint", mint})
|
||||||
}
|
}
|
||||||
|
|
||||||
err = kr.SignEvent(ctx, &evt)
|
err = kr.SignEvent(ctx, evt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
events := make([]nostr.Event, 0, 1+len(w.Tokens))
|
return nil
|
||||||
events = append(events, evt)
|
|
||||||
|
|
||||||
w.tokensMu.Lock()
|
|
||||||
for _, t := range w.Tokens {
|
|
||||||
var evt nostr.Event
|
|
||||||
|
|
||||||
if t.event != nil {
|
|
||||||
if skipExisting {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
evt = *t.event
|
|
||||||
} else {
|
|
||||||
err := t.toEvent(ctx, kr, w.Identifier, &evt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
events = append(events, evt)
|
|
||||||
}
|
|
||||||
w.tokensMu.Unlock()
|
|
||||||
|
|
||||||
for _, h := range w.History {
|
|
||||||
var evt nostr.Event
|
|
||||||
|
|
||||||
if h.event != nil {
|
|
||||||
if skipExisting {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
evt = *h.event
|
|
||||||
} else {
|
|
||||||
err := h.toEvent(ctx, kr, w.Identifier, &evt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
events = append(events, evt)
|
|
||||||
}
|
|
||||||
|
|
||||||
return events, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wallet) parse(ctx context.Context, kr nostr.Keyer, evt *nostr.Event) error {
|
func (w *Wallet) parse(ctx context.Context, kr nostr.Keyer, evt *nostr.Event) error {
|
||||||
w.Tokens = make([]Token, 0, 128)
|
w.Tokens = make([]Token, 0, 128)
|
||||||
w.History = make([]HistoryEntry, 0, 128)
|
w.History = make([]HistoryEntry, 0, 128)
|
||||||
|
w.event = evt
|
||||||
|
|
||||||
pk, err := kr.GetPublicKey(ctx)
|
pk, err := kr.GetPublicKey(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonb, err := kr.Decrypt(ctx, evt.Content, pk)
|
jsonb, err := kr.Decrypt(ctx, evt.Content, pk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
Reference in New Issue
Block a user