@ -203,8 +203,12 @@ import "leaflet/dist/leaflet.css";
import { AxiosError , AxiosRequestHeaders } from "axios" ;
import { DateTime } from "luxon" ;
import { hexToBytes } from "@noble/hashes/utils" ;
import type { EventTemplate , VerifiedEvent } from "nostr-tools/lib/types/core" ;
import { accountFromSeedWords } from "nostr-tools/nip06" ;
/ / t h e s e c o r e i m p o r t s c o u l d a l s o b e i n c l u d e d a s " i m p o r t t y p e . . . "
import { EventTemplate , UnsignedEvent , VerifiedEvent } from "nostr-tools/core" ;
import {
accountFromExtendedKey ,
extendedKeysFromSeedWords ,
} from "nostr-tools/nip06" ;
import { finalizeEvent , serializeEvent } from "nostr-tools/pure" ;
import { Component , Vue } from "vue-facing-decorator" ;
import { LMap , LMarker , LTileLayer } from "@vue-leaflet/vue-leaflet" ;
@ -225,7 +229,6 @@ import {
} from "@/libs/endorserServer" ;
import {
retrieveAccountCount ,
retrieveAccountMetadata ,
retrieveFullyDecryptedAccount ,
} from "@/libs/util" ;
@ -472,31 +475,45 @@ export default class NewEditProjectView extends Vue {
try {
const resp = await this . axios . post ( url , payload , { headers } ) ;
if ( resp . data ? . success ? . handleId ) {
this . $notify (
{
group : "alert" ,
type : "success" ,
title : "Saved" ,
text : "The project was saved successfully." ,
} ,
3000 ,
) ;
this . errorMessage = "" ;
const projectPath = encodeURIComponent ( resp . data . success . handleId ) ;
if ( this . sendToTrustroots || this . sendToTripHopping ) {
if ( this . latitude && this . longitude ) {
let signedPayload : VerifiedEvent | undefined ; / / s i g n s o m e t h i n g t o p r o v e o w n e r s h i p o f p u b k e y
let payloadAndKey ; / / s i g n s o m e t h i n g t o p r o v e o w n e r s h i p o f p u b k e y
if ( this . sendToTrustroots ) {
signedPayload = await this . signPayload ( ) ;
payloadAndKey = await this . signSomePayload ( ) ;
/ / n o t g o i n g t o a w a i t . . . t h e s a v e w a s s u c c e s s f u l , s o w e ' l l c o n t i n u e t o t h e n e x t p a g e
this . sendToNostrPartner (
"NOSTR-EVENT-TRUSTROOTS" ,
"Trustroots" ,
resp . data . success . claimId ,
signedPayload ,
payloadAndKey . signedEvent ,
payloadAndKey . publicExtendedKey ,
) ;
}
if ( this . sendToTripHopping ) {
if ( ! signedPayload ) {
signedPayload = await this . signPayload ( ) ;
if ( ! payloadAndKey ) {
payloadAndKey = await this . signSome Payload ( ) ;
}
/ / n o t g o i n g t o a w a i t . . . t h e s a v e w a s s u c c e s s f u l , s o w e ' l l c o n t i n u e t o t h e n e x t p a g e
this . sendToNostrPartner (
"NOSTR-EVENT-TRIPHOPPING" ,
"TripHopping" ,
resp . data . success . claimId ,
signedPayload ,
payloadAndKey . signedEvent ,
payloadAndKey . publicExtendedKey ,
) ;
}
} else {
@ -576,19 +593,28 @@ export default class NewEditProjectView extends Vue {
}
}
private async signPayload ( ) : Promise < VerifiedEvent > {
/ * *
* @ return a signed payload and an extended public key for later transmission
* /
private async signSomePayload ( ) : Promise < {
signedEvent : VerifiedEvent ;
publicExtendedKey : string ;
} > {
const account = await retrieveFullyDecryptedAccount ( this . activeDid ) ;
/ / g e t t h e l a s t n u m b e r o f t h e d e r i v a t i o n P a t h
const finalDerNum = account ? . derivationPath ? . split ? . ( "/" ) ? . reverse ( ) [ 0 ] ;
/ / r e m o v e a n y t r a i l i n g '
const finalDerNumNoApostrophe = finalDerNum ? . replace ( /'/g , "" ) ;
const accountNum = Number ( finalDerNumNoApostrophe || 0 ) ;
const pubPri = account FromSeedWords(
const extPubPri = extendedKeys FromSeedWords(
account ? . mnemonic as string ,
"" ,
accountNum ,
) ;
const privateBytes = hexToBytes ( pubPri ? . privateKey ) ;
const publicExtendedKey : string = extPubPri ? . publicExtendedKey ;
const privateExtendedKey = extPubPri ? . privateExtendedKey ;
const privateKey = accountFromExtendedKey ( privateExtendedKey ) . privateKey ;
const privateBytes = hexToBytes ( privateKey ) ;
/ / N o r e a l c o n t e n t i s n e c e s s a r y , w e j u s t w a n t s o m e t h i n g s i g n e d ,
/ / s o w e m i g h t a s w e l l u s e n o s t r l i b s f o r n o s t r f u n c t i o n s .
/ / B e s i d e s : s o m e d a y w e m a y c r e a t e r e a l c o n t e n t t h a t w e c a n r e l a y .
@ -598,9 +624,12 @@ export default class NewEditProjectView extends Vue {
content : "" ,
created_at : 0 ,
} ;
/ / W h y d o e s I n t e l l i J n o t s e e m a t c h i n g t y p e s ?
const signedEvent = finalizeEvent ( event , privateBytes ) ;
return signedEvent ;
const signedEvent : VerifiedEvent = finalizeEvent (
/ / W h y d o e s I n t e l l i J n o t s e e m a t c h i n g t y p e s ?
event as EventTemplate ,
privateBytes ,
) as VerifiedEvent ;
return { signedEvent , publicExtendedKey } ;
}
private async sendToNostrPartner (
@ -608,41 +637,37 @@ export default class NewEditProjectView extends Vue {
serviceName : string ,
jwtId : string ,
signedPayload : VerifiedEvent ,
publicExtendedKey : string ,
) {
/ / f i r s t , g e t t h e p u b l i c k e y f o r n o s t r
const account = await retrieveAccountMetadata ( this . activeDid ) ;
/ / g e t t h e l a s t n u m b e r o f t h e d e r i v a t i o n P a t h
const finalDerNum = account ? . derivationPath ? . split ? . ( "/" ) ? . reverse ( ) [ 0 ] ;
/ / r e m o v e a n y t r a i l i n g '
const finalDerNumNoApostrophe = finalDerNum ? . replace ( /'/g , "" ) ;
const accountNum = Number ( finalDerNumNoApostrophe || 0 ) ;
const pubPri = accountFromSeedWords (
account ? . mnemonic as string ,
"" ,
accountNum ,
) ;
const nostrPubKey = pubPri ? . publicKey ;
let partnerServer = DEFAULT_PARTNER_API_SERVER ;
const settings = await retrieveSettingsForActiveAccount ( ) ;
if ( settings . partnerApiServer ) {
partnerServer = settings . partnerApiServer ;
}
const endorserPartnerUrl = partnerServer + "/api/partner/link" ;
const timeSafariUrl = window . location . origin + "/claim/" + jwtId ;
const content = this . fullClaim . name + " - see " + timeSafariUrl ;
/ / W h y d o e s I n t e l l i J n o t s e e m a t c h i n g t y p e s ?
const payload = serializeEvent ( signedPayload ) ;
const partnerParams = {
jwtId : jwtId ,
linkCode : linkCode ,
inputJson : JSON . stringify ( content ) ,
pubKeyHex : nostrPubKey ,
pubKeyImage : payload ,
pubKeySigHex : signedPayload . sig ,
} ;
const headers = await getHeaders ( this . activeDid ) ;
try {
let partnerServer = DEFAULT_PARTNER_API_SERVER ;
const settings = await retrieveSettingsForActiveAccount ( ) ;
if ( settings . partnerApiServer ) {
partnerServer = settings . partnerApiServer ;
}
const endorserPartnerUrl = partnerServer + "/api/partner/link" ;
const timeSafariUrl = window . location . origin + "/claim/" + jwtId ;
const content = this . fullClaim . name + " - see " + timeSafariUrl ;
const publicKeyHex = accountFromExtendedKey ( publicExtendedKey ) . publicKey ;
const unsignedPayload : UnsignedEvent = {
/ / w h y d o e s n ' t " . . . s i g n e d P a y l o a d " w o r k ?
kind : signedPayload . kind ,
tags : signedPayload . tags ,
content : signedPayload . content ,
created_at : signedPayload . created_at ,
pubkey : publicKeyHex ,
} ;
/ / W h y d o e s I n t e l l i J n o t s e e m a t c h i n g t y p e s ?
const payload = serializeEvent ( unsignedPayload as UnsignedEvent ) ;
const partnerParams = {
jwtId : jwtId ,
linkCode : linkCode ,
inputJson : JSON . stringify ( content ) ,
pubKeyHex : publicKeyHex ,
pubKeyImage : payload ,
pubKeySigHex : signedPayload . sig ,
} ;
const headers = await getHeaders ( this . activeDid ) ;
const linkResp = await this . axios . post (
endorserPartnerUrl ,
partnerParams ,
@ -731,7 +756,7 @@ export default class NewEditProjectView extends Vue {
group : "alert" ,
type : "info" ,
title : "About Nostr Events" ,
text : "This will cause a submission to a partner on the nostr network. It will contain your public key data which may allow correlation, so don't allow this if you're not comfortable with that." ,
text : "This will submit this project to a partner on the nostr network. It will contain your public key data which may allow correlation, so don't allow this if you're not comfortable with that." ,
} ,
7000 ,
) ;