problem: rocket is not updated with valid merit transaction

This commit is contained in:
gsovereignty
2024-08-11 16:34:03 +08:00
parent 91c4446c30
commit 1c482ace47
6 changed files with 268 additions and 75 deletions

123
package-lock.json generated
View File

@@ -12,6 +12,7 @@
"@mempool/mempool.js": "^2.3.0",
"@nostr-dev-kit/ndk": "^2.8.2",
"@nostr-dev-kit/ndk-cache-dexie": "^2.4.2",
"@nostr-dev-kit/ndk-cache-nostr": "^0.1.0",
"@nostr-dev-kit/ndk-svelte": "^2.2.15",
"@nostr-dev-kit/ndk-svelte-components": "^2.2.16",
"@sveltejs/adapter-static": "^3.0.1",
@@ -21,6 +22,8 @@
"cmdk-sv": "^0.0.17",
"embla-carousel-svelte": "^8.1.3",
"formsnap": "^1.0.0",
"immutable": "^4.3.7",
"js-sha256": "^0.11.0",
"lucide-svelte": "^0.383.0",
"mode-watcher": "^0.3.0",
"paneforge": "^0.0.4",
@@ -797,6 +800,115 @@
}
}
},
"node_modules/@nostr-dev-kit/ndk-cache-nostr": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@nostr-dev-kit/ndk-cache-nostr/-/ndk-cache-nostr-0.1.0.tgz",
"integrity": "sha512-NqQ9RWp9z21Tyrs8GoGrdAjzbEyJktkCQt/+rx6AexR4Gb3StkmlBpAaNrKib3l7ePHPJQWKYPSdR0pWqvnGrw==",
"dependencies": {
"@nostr-dev-kit/ndk": "2.10.0",
"debug": "^4.3.4",
"typescript": "^5.4.4",
"websocket-polyfill": "^0.0.3"
}
},
"node_modules/@nostr-dev-kit/ndk-cache-nostr/node_modules/@noble/ciphers": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.5.3.tgz",
"integrity": "sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w==",
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@nostr-dev-kit/ndk-cache-nostr/node_modules/@noble/hashes": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz",
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@nostr-dev-kit/ndk-cache-nostr/node_modules/@nostr-dev-kit/ndk": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/@nostr-dev-kit/ndk/-/ndk-2.10.0.tgz",
"integrity": "sha512-TqCAAo6ylORraAXrzRkCGFN2xTMiFbdER8Y8CtUT0HwOpFG/Wn+PBNeDeDmqkl/6LaPdeyXmVwCWj2KcUjIwYA==",
"dependencies": {
"@noble/curves": "^1.4.0",
"@noble/hashes": "^1.3.1",
"@noble/secp256k1": "^2.0.0",
"@scure/base": "^1.1.1",
"debug": "^4.3.4",
"light-bolt11-decoder": "^3.0.0",
"node-fetch": "^3.3.1",
"nostr-tools": "^2.7.1",
"tseep": "^1.1.1",
"typescript-lru-cache": "^2.0.0",
"utf8-buffer": "^1.0.0",
"websocket-polyfill": "^0.0.3"
},
"engines": {
"node": ">=16"
}
},
"node_modules/@nostr-dev-kit/ndk-cache-nostr/node_modules/@scure/base": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz",
"integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==",
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
]
},
"node_modules/@nostr-dev-kit/ndk-cache-nostr/node_modules/nostr-tools": {
"version": "2.7.2",
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-2.7.2.tgz",
"integrity": "sha512-Bq3Ug0SZFtgtL1+0wCnAe8AJtI7yx/00/a2nUug9SkhfOwlKS92Tef12iCK9FdwXw+oFZWMtRnSwcLayQso+xA==",
"dependencies": {
"@noble/ciphers": "^0.5.1",
"@noble/curves": "1.2.0",
"@noble/hashes": "1.3.1",
"@scure/base": "1.1.1",
"@scure/bip32": "1.3.1",
"@scure/bip39": "1.2.1"
},
"optionalDependencies": {
"nostr-wasm": "v0.1.0"
},
"peerDependencies": {
"typescript": ">=5.0.0"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/@nostr-dev-kit/ndk-cache-nostr/node_modules/nostr-tools/node_modules/@noble/curves": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz",
"integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
"dependencies": {
"@noble/hashes": "1.3.2"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@nostr-dev-kit/ndk-cache-nostr/node_modules/nostr-tools/node_modules/@noble/curves/node_modules/@noble/hashes": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
"integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@nostr-dev-kit/ndk-svelte": {
"version": "2.2.15",
"resolved": "https://registry.npmjs.org/@nostr-dev-kit/ndk-svelte/-/ndk-svelte-2.2.15.tgz",
@@ -2963,6 +3075,11 @@
"url": "https://github.com/sponsors/typicode"
}
},
"node_modules/immutable": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz",
"integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw=="
},
"node_modules/import-fresh": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@@ -3168,6 +3285,11 @@
"@sideway/pinpoint": "^2.0.0"
}
},
"node_modules/js-sha256": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.11.0.tgz",
"integrity": "sha512-6xNlKayMZvds9h1Y1VWc0fQHQ82BxTXizWPEtEeGvmOUYpBRy4gbWroHLpzowe6xiQhHpelCQiE7HEdznyBL9Q=="
},
"node_modules/json-schema-to-ts": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.0.tgz",
@@ -5423,7 +5545,6 @@
"version": "5.4.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
"integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
"devOptional": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"

View File

@@ -42,6 +42,7 @@
"@mempool/mempool.js": "^2.3.0",
"@nostr-dev-kit/ndk": "^2.8.2",
"@nostr-dev-kit/ndk-cache-dexie": "^2.4.2",
"@nostr-dev-kit/ndk-cache-nostr": "^0.1.0",
"@nostr-dev-kit/ndk-svelte": "^2.2.15",
"@nostr-dev-kit/ndk-svelte-components": "^2.2.16",
"@sveltejs/adapter-static": "^3.0.1",
@@ -51,6 +52,8 @@
"cmdk-sv": "^0.0.17",
"embla-carousel-svelte": "^8.1.3",
"formsnap": "^1.0.0",
"immutable": "^4.3.7",
"js-sha256": "^0.11.0",
"lucide-svelte": "^0.383.0",
"mode-watcher": "^0.3.0",
"paneforge": "^0.0.4",

View File

@@ -54,6 +54,8 @@
</script>
<Heading title="Sponsor a Contributor" />
Contributors who need Sats are able to list their Merits for sale, to sponsor them simply buy some of
their Merits.
<Card.Root>
<Card.Header><Card.Title>Your Bitcoin Addresses</Card.Title></Card.Header>
<Card.Content>

View File

@@ -69,7 +69,7 @@ export class MeritRequest {
}
constructor(request: NDKEvent | string) {
if (typeof request == 'string') {
console.log(69);
throw new Error('implement me');
} else {
this.LeadTime = 0;
this.LastLTUpdate = 0;

View File

@@ -2,7 +2,9 @@ import { getAuthorizedZapper } from '@/helpers';
import { BitcoinTipTag, txs } from '@/stores/bitcoin';
import { NDKEvent, type NDKTag } from '@nostr-dev-kit/ndk';
import validate from 'bitcoin-address-validation';
import { sha256 } from 'js-sha256';
import { MapOfVotes, MeritRequest, Votes } from './merits';
import * as immutable from 'immutable';
export class Rocket {
Event: NDKEvent;
@@ -39,15 +41,57 @@ export class Rocket {
}
return a;
}
UpsertMeritTransfer(): NDKEvent | undefined {
UpsertLeadTime(event: NDKEvent): NDKEvent {
//todo: validate that there are no current auctions that include this AMR
return new NDKEvent();
}
UpsertMeritTransfer(request: MeritPurchase): NDKEvent | undefined {
let event: NDKEvent | undefined = undefined;
this.PrepareForUpdate();
event = new NDKEvent(this.Event.ndk, this.Event.rawEvent());
event.created_at = Math.floor(new Date().getTime() / 1000);
event.tags.push(['address', `${association.Pubkey}:${association.Address}`]);
event.tags.push(['proof_full', JSON.stringify(association.Event.rawEvent())]);
updateIgnitionAndParentTag(event);
updateBitcoinTip(event);
let fatal = false;
if (this.PendingAMRAuctionsMap().get(request.auction.ID())) {
this.PrepareForUpdate();
let _event = new NDKEvent(this.Event.ndk, this.Event.rawEvent());
_event.created_at = Math.floor(new Date().getTime() / 1000);
//delete the auction
let auctionID = request.auction.ID();
let existing = _event.getMatchingTags('amr_auction');
_event.removeTag('amr_auction');
for (let t of existing) {
let amr = AMRAuctionFromTag(t, this.Event);
if (amr.ID() != auctionID) {
_event.tags.push(t);
}
}
_event.tags.push(['proof_raw', `txid:${request.txid}`]);
let modifiedMerits: Map<string, RocketAMR> = new Map();
for (let id of request.auction.AMRIDs) {
let amr = this.ApprovedMeritRequests().get(id);
if (!amr) {
return event;
}
if (amr.LeadTime > 0) {
return event;
}
amr.Pubkey = request.buyer;
modifiedMerits.set(amr.ID, amr);
}
let existingMerits = this.ApprovedMeritRequests();
for (let [id, m] of modifiedMerits) {
existingMerits.set(id, m);
}
_event.removeTag('merit');
for (let [id, m] of existingMerits) {
_event.tags.push(m.Tag());
}
_event.tags.push([
'swap',
`${request.auction.Merits}:${request.sats}:${Math.floor(new Date().getTime() / 1000)}`
]);
updateIgnitionAndParentTag(_event);
updateBitcoinTip(_event);
event = _event;
}
return event;
}
@@ -194,47 +238,38 @@ export class Rocket {
}
return event;
}
PendingAMRAuctions(): AMRAuction[] {
let auctions: AMRAuction[] = [];
PendingAMRAuctionsMap(): Map<string, AMRAuction> {
let m = new Map<string, AMRAuction>();
for (let t of this.Event.getMatchingTags('amr_auction')) {
if (t.length == 2) {
let items = t[1].split(':');
if (items.length == 6) {
let a = new AMRAuction(this.Event);
a.RxAddress = items[0];
a.StartPrice = parseInt(items[2], 10);
a.EndPrice = parseInt(items[3], 10);
a.Merits = parseInt(items[4], 10);
let ids = items[5].match(/.{1,64}/g);
if (ids) {
for (let id of ids) {
a.AMRIDs.push(id);
}
}
let amrs = this.ApprovedMeritRequests();
let failed = false;
for (let id of a.AMRIDs) {
let amr = amrs.get(id);
if (!amr) {
failed = true;
} else {
if (!a.Owner) {
a.Owner = amr.Pubkey;
} else if (a.Owner != amr.Pubkey) {
failed = true;
}
}
}
if (!failed) {
auctions.push(a);
let auction = AMRAuctionFromTag(t, this.Event);
if (auction.Validate()) {
let amrs = this.ApprovedMeritRequests();
let failed = false;
for (let id of auction.AMRIDs) {
let amr = amrs.get(id);
if (!amr) {
failed = true;
} else {
throw new Error('this should not happen, bug!');
if (!auction.Owner) {
auction.Owner = amr.Pubkey;
} else if (auction.Owner != amr.Pubkey) {
failed = true;
}
}
}
if (!failed) {
m.set(auction.ID(), auction);
} else {
throw new Error('this should not happen, bug!');
}
}
}
return auctions;
return m;
}
PendingAMRAuctions(): AMRAuction[] {
return Array.from(this.PendingAMRAuctionsMap(), ([_, amr]) => {
return amr;
});
}
CanThisAMRBeSold(amr: string): boolean {
let valid = true;
@@ -433,7 +468,14 @@ export class RocketAMR {
LeadTimeUpdate: number;
Merits: number;
Extra: { eventAMR: AMRAuction };
Tag(): NDKTag {
return [
'merit',
`${this.Pubkey}:${this.ID}:${this.LeadTime}:${this.LeadTimeUpdate}:${this.Merits}`
];
}
SatsOwed(): number {
//if rocket creator is acting as custodian instead of using a cashu mint
return 0;
}
SatsPaid(): number {
@@ -648,8 +690,12 @@ export class AMRAuction {
RocketD: string;
RocketP: string;
Merits: number;
Event: NDKEvent;
Event: NDKEvent | undefined;
Extra: { rocket: Rocket };
ID(): string {
this.AMRIDs.sort();
return sha256(''.concat(...this.AMRIDs).trim());
}
Status(rocket: Rocket, bitcoinTip: number, transactions?: txs): AMRAuctionStatus {
let status: AMRAuctionStatus = 'PENDING';
if (transactions && transactions.Address != this.RxAddress) {
@@ -731,12 +777,14 @@ export class AMRAuction {
Validate(): boolean {
let valid = true;
if (
this.Owner?.length != 64 ||
(this.Owner && this.Owner.length != 64) ||
!this.StartPrice ||
!this.EndPrice ||
!validate(this.RxAddress) ||
this.RocketP.length != 64
this.RocketP.length != 64 ||
!this.Merits
) {
//console.log(780, this, (this.Owner && this.Owner.length != 64), !this.StartPrice, !this.EndPrice, !validate(this.RxAddress), this.RocketP.length != 64, !this.Merits)
valid = false;
}
for (let id of this.AMRIDs) {
@@ -816,7 +864,6 @@ export class BitcoinAssociation {
Event: NDKEvent;
Balance: number;
Validate(): boolean {
console.log(819, this);
let valid = true;
if (this.Pubkey.length != 64) {
valid = false;
@@ -903,3 +950,43 @@ export class Product {
this.Event = event;
}
}
export class MeritPurchase {
auction: AMRAuction;
buyer: string;
txid: string;
sats: number;
rocket: Rocket;
Validate(): boolean {
//todo: at least validate the utxo format
return true;
}
constructor(rocket: Rocket, auction: AMRAuction, buyer: string, txid: string, sats: number) {
this.rocket = rocket;
this.auction = auction;
this.buyer = buyer;
this.txid = txid;
this.sats = sats;
}
}
function AMRAuctionFromTag(t: NDKTag, rocket: NDKEvent): AMRAuction {
let a = new AMRAuction(rocket);
if (t.length == 2) {
let items = t[1].split(':');
if (items.length == 6) {
a.RxAddress = items[0];
a.StartPrice = parseInt(items[2], 10);
a.EndPrice = parseInt(items[3], 10);
a.Merits = parseInt(items[4], 10);
let ids = items[5].match(/.{1,64}/g);
if (ids) {
for (let id of ids) {
a.AMRIDs.push(id);
}
}
}
}
return a;
}

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import Button from '@/components/ui/button/button.svelte';
import * as Table from '@/components/ui/table';
import { AMRAuction, Rocket } from '@/event_helpers/rockets';
import { AMRAuction, MeritPurchase, Rocket } from '@/event_helpers/rockets';
import { ndk } from '@/ndk';
import { bitcoinTip, getIncomingTransactions, txs } from '@/stores/bitcoin';
import { currentUser } from '@/stores/session';
@@ -89,12 +89,7 @@
for (let [address, txo] of txs.From()) {
for (let [_, ba] of r.BitcoinAssociations()) {
if (ba.Address == txo.From) {
return {
auction: amrAuction,
buyer: ba.Pubkey,
txid: txo.ID,
sats: txo.Amount
};
return new MeritPurchase(r, amrAuction, ba.Pubkey, txo.ID, txo.Amount);
}
}
}
@@ -108,7 +103,10 @@
);
nextSoldButNotInState.subscribe((t) => {
if (t) console.log(t);
if (t) {
console.log(t.rocket.UpsertMeritTransfer(t)?.rawEvent());
//t.rocket.UpsertMeritTransfer(t)?.publish().then(x=>{console.log(goto(...))})
}
});
let nostrocket = derived(rockets, ($rockets) => {
@@ -128,24 +126,6 @@
});
transactions.subscribe((t) => {});
// let noAssociatedBitcoinAddress = derived(
// [currentUser, pendingSales],
// ([$currentUser, $pendingSales]) => {
// let show = false;
// if ($currentUser) {
// for (let [r, a] of $pendingSales) {
// if (a.length > 0) {
// let show = true
// for (let [_, ba] of r.BitcoinAssociations()) {
// }
// }
// }
// }
// return show;
// }
// );
</script>
{#if $nostrocket}<AssociateBitcoinAddress rocket={$nostrocket} />