problem: can't get current Bitcoin block height and hash

This commit is contained in:
Bob
2024-08-03 13:00:58 +08:00
parent 601b6c4261
commit e182c0fb30
5 changed files with 87 additions and 11 deletions

View File

@@ -15,6 +15,7 @@
import type { NDKEventStore } from '@nostr-dev-kit/ndk-svelte'; import type { NDKEventStore } from '@nostr-dev-kit/ndk-svelte';
import { onDestroy } from 'svelte'; import { onDestroy } from 'svelte';
import { writable } from 'svelte/store'; import { writable } from 'svelte/store';
import { BitcoinTipTag } from '@/stores/bitcoin';
let rockets: NDKEventStore<NDKEvent> | undefined; let rockets: NDKEventStore<NDKEvent> | undefined;
const rocketsStore = writable<NDKEvent[]>([]); const rocketsStore = writable<NDKEvent[]>([]);
@@ -72,6 +73,7 @@
e.tags.push(['ruleset', '334000']); e.tags.push(['ruleset', '334000']);
e.tags.push(['ignition', 'this']); e.tags.push(['ignition', 'this']);
e.tags.push(['parent', 'this']); e.tags.push(['parent', 'this']);
e.tags.push(BitcoinTipTag());
e.publish().then((x) => { e.publish().then((x) => {
console.log(x); console.log(x);
goto(`${base}/rockets/${getRocketURL(e)}`); goto(`${base}/rockets/${getRocketURL(e)}`);

View File

@@ -2,6 +2,7 @@ import { NDKEvent, type NDKTag } from '@nostr-dev-kit/ndk';
import { MapOfVotes, MeritRequest, Votes } from './merits'; import { MapOfVotes, MeritRequest, Votes } from './merits';
import { getAuthorizedZapper } from '@/helpers'; import { getAuthorizedZapper } from '@/helpers';
import validate from 'bitcoin-address-validation'; import validate from 'bitcoin-address-validation';
import { BitcoinTipTag } from '@/stores/bitcoin';
export class Rocket { export class Rocket {
Event: NDKEvent; Event: NDKEvent;
@@ -117,11 +118,12 @@ export class Rocket {
event.tags.push(['merit', `${request.Pubkey}:${request.ID}:0:0:${request.Merits}`]); event.tags.push(['merit', `${request.Pubkey}:${request.ID}:0:0:${request.Merits}`]);
event.tags.push(['proof_full', JSON.stringify(signedProof.rawEvent())]); event.tags.push(['proof_full', JSON.stringify(signedProof.rawEvent())]);
updateIgnitionAndParentTag(event); updateIgnitionAndParentTag(event);
updateBitcionTip(event);
} }
return event; return event;
} }
PendingAMRAuctions(): AMRAuction[] { PendingAMRAuctions(): AMRAuction[] {
let auctions:AMRAuction[] = []; let auctions: AMRAuction[] = [];
for (let t of this.Event.getMatchingTags('amr_auction')) { for (let t of this.Event.getMatchingTags('amr_auction')) {
if (t.length == 2) { if (t.length == 2) {
let items = t[1].split(':'); let items = t[1].split(':');
@@ -135,7 +137,7 @@ export class Rocket {
let ids = items[5].match(/.{1,64}/g); let ids = items[5].match(/.{1,64}/g);
if (ids) { if (ids) {
for (let id of ids) { for (let id of ids) {
a.AMRIDs.push(id) a.AMRIDs.push(id);
} }
} }
let amrs = this.ApprovedMeritRequests() let amrs = this.ApprovedMeritRequests()
@@ -161,24 +163,24 @@ export class Rocket {
} }
} }
} }
return auctions return auctions;
} }
CanThisAMRBeSold(amr:string):boolean { CanThisAMRBeSold(amr: string): boolean {
let valid = true let valid = true;
let existing = this.ApprovedMeritRequests().get(amr) let existing = this.ApprovedMeritRequests().get(amr);
if (!existing) { if (!existing) {
valid = false valid = false;
} }
if (existing && existing.LeadTime > 0) { if (existing && existing.LeadTime > 0) {
valid = false valid = false;
} }
let pending = this.PendingAMRAuctions() let pending = this.PendingAMRAuctions();
for (let p of pending) { for (let p of pending) {
if (p.AMRIDs.includes(amr)) { if (p.AMRIDs.includes(amr)) {
valid = false valid = false;
} }
} }
return valid return valid;
} }
UpsertAMRAuction(request: AMRAuction): NDKEvent | undefined { UpsertAMRAuction(request: AMRAuction): NDKEvent | undefined {
//todo: validate that all items in the request exist and the total amount is correct, from same pubkey //todo: validate that all items in the request exist and the total amount is correct, from same pubkey
@@ -212,6 +214,7 @@ export class Rocket {
]); //<merit request ID:start price:end price:start height:rx address> ]); //<merit request ID:start price:end price:start height:rx address>
event.tags.push(['proof_full', JSON.stringify(request.Event!.rawEvent())]); event.tags.push(['proof_full', JSON.stringify(request.Event!.rawEvent())]);
updateIgnitionAndParentTag(event); updateIgnitionAndParentTag(event);
updateBitcionTip(event);
} }
if (invalid) { if (invalid) {
event = undefined; event = undefined;
@@ -235,6 +238,7 @@ export class Rocket {
purchases purchases
]); ]);
updateIgnitionAndParentTag(event); updateIgnitionAndParentTag(event);
updateBitcionTip(event);
return event; return event;
} }
UpdateMission(mission: string): NDKEvent { UpdateMission(mission: string): NDKEvent {
@@ -244,6 +248,7 @@ export class Rocket {
event.removeTag('mission'); event.removeTag('mission');
event.tags.push(['mission', mission]); event.tags.push(['mission', mission]);
updateIgnitionAndParentTag(event); updateIgnitionAndParentTag(event);
updateBitcionTip(event);
return event; return event;
} }
CurrentProducts(): Map<string, RocketProduct> { CurrentProducts(): Map<string, RocketProduct> {
@@ -330,6 +335,25 @@ function updateIgnitionAndParentTag(event: NDKEvent) {
event.tags.push(['parent', event.id]); event.tags.push(['parent', event.id]);
} }
function updateBitcionTip(event: NDKEvent) {
let existingBitcoinTip = event.getMatchingTags('bitcoin');
let existing = [];
for (let t of event.tags) {
existing.push(t);
}
event.tags = [];
for (let t of existing) {
if (t[0] !== 'bitcoin') {
event.tags.push(t);
}
}
if (existingBitcoinTip.length > 1) {
throw new Error('too many bitcoin tip tags!');
} else {
event.tags.push(BitcoinTipTag());
}
}
export class RocketAMR { export class RocketAMR {
//todo: also add a query for sats tags to find payments for this AMR //todo: also add a query for sats tags to find payments for this AMR
ID: string; ID: string;

View File

@@ -30,6 +30,10 @@ export function getMission(rocketEvent: NDKEvent): string {
return ''; return '';
} }
export function unixTimeNow() {
return Math.floor(new Date().getTime() / 1000);
}
export function unixToRelativeTime(timestamp: number): string { export function unixToRelativeTime(timestamp: number): string {
const currentTime = Date.now(); const currentTime = Date.now();
const secondsAgo = Math.floor((currentTime - timestamp) / 1000); const secondsAgo = Math.floor((currentTime - timestamp) / 1000);

32
src/lib/stores/bitcoin.ts Normal file
View File

@@ -0,0 +1,32 @@
import { get, writable } from 'svelte/store';
type BitcoinTip = {
height: number;
hash: string;
};
let _b: BitcoinTip = { hash: '', height: 0 };
export let bitcoinTip = writable(_b);
export function BitcoinTipTag(): string[] {
let tip = get(bitcoinTip);
let bths: string[] = ['bitcoin', ''];
if (tip.hash && tip.height) {
bths = ['bitcoin', tip.height.toString() + ':' + tip.hash];
}
return bths;
}
export async function getBitcoinTip() {
const response = await fetch('https://blockstream.info/api/blocks/tip');
const _json = await response.json();
if (_json[0]) {
let r: BitcoinTip = {
height: _json[0].height,
hash: _json[0].id
};
bitcoinTip.set(r);
return r;
}
return null;
}

View File

@@ -5,6 +5,8 @@
import { ndk } from '@/ndk'; import { ndk } from '@/ndk';
import type { NDKUser } from '@nostr-dev-kit/ndk'; import type { NDKUser } from '@nostr-dev-kit/ndk';
import { currentUser, prepareUserSession } from '@/stores/session'; import { currentUser, prepareUserSession } from '@/stores/session';
import { unixTimeNow } from '@/helpers';
import { getBitcoinTip } from '@/stores/bitcoin';
let sessionStarted = false; let sessionStarted = false;
let connected = false; let connected = false;
@@ -24,6 +26,18 @@
}); });
sessionStarted = true; sessionStarted = true;
} }
let lastRequestTime = 0;
$: {
if (unixTimeNow() > lastRequestTime + 30000) {
getBitcoinTip().then((x) => {
if (x) {
lastRequestTime = unixTimeNow();
}
});
}
}
</script> </script>
<ModeWatcher defaultMode="dark" /> <ModeWatcher defaultMode="dark" />