@ -1,19 +1,31 @@
import { GroupPubkeyType , PubkeyType } from 'libsession_util_nodejs' ;
import { GroupPubkeyType , PubkeyType , WithGroupPubkey } from 'libsession_util_nodejs' ;
import { from_hex } from 'libsodium-wrappers-sumo' ;
import { isEmpty } from 'lodash' ;
import { concatUInt8Array } from '../../crypto' ;
import { StringUtils , UserUtils } from '../../utils' ;
import { GetNetworkTime } from './getNetworkTime' ;
import {
SnodeNamespaces ,
SnodeNamespacesGroup ,
SnodeNamespacesGroupConfig ,
UserConfigNamespaces ,
} from './namespaces' ;
import { SignedGroupHashesParams , SignedHashesParams } from './types' ;
export type SwarmForSubRequest = { method : 'get_swarm' ; params : { pubkey : string } } ;
import { SnodeGroupSignature } from './signature/groupSignature' ;
import { SnodeSignature } from './signature/snodeSignatures' ;
import {
SignedGroupHashesParams ,
SignedHashesParams ,
WithMessagesHashes ,
WithSecretKey ,
WithSignature ,
WithTimestamp ,
} from './types' ;
type WithRetrieveMethod = { method : 'retrieve' } ;
type WithMaxCountSize = { max_count? : number ; max_size? : number } ;
type WithPubkeyAsString = { pubkey : string } ;
type WithPubkeyAsGroupPubkey = { pubkey : GroupPubkeyType } ;
export type WithShortenOrExtend = { shortenOrExtend : 'shorten' | 'extend' | '' } ;
type RetrieveAlwaysNeeded = {
namespace : number ;
@ -23,12 +35,12 @@ type RetrieveAlwaysNeeded = {
export type RetrievePubkeySubRequestType = WithRetrieveMethod & {
params : {
signature : string ;
pubkey_ed25519 : string ;
namespace : number ;
} & RetrieveAlwaysNeeded &
WithMaxCountSize &
WithPubkeyAsString ;
WithPubkeyAsString &
WithSignature ;
} ;
/ * * T h o s e n a m e s p a c e s d o n o t r e q u i r e t o b e a u t h e n t i c a t e d f o r s t o r i n g m e s s a g e s .
@ -49,21 +61,21 @@ export type RetrieveLegacyClosedGroupSubRequestType = WithRetrieveMethod & {
export type RetrieveGroupAdminSubRequestType = WithRetrieveMethod & {
params : {
signature : string ;
namespace : SnodeNamespacesGroup ;
} & RetrieveAlwaysNeeded &
WithMaxCountSize ;
WithMaxCountSize &
WithSignature ;
} ;
export type RetrieveGroupSubAccountSubRequestType = WithRetrieveMethod & {
params : {
namespace : SnodeNamespacesGroup ;
signature : string ;
subaccount : string ;
subaccount_sig : string ;
} & RetrieveAlwaysNeeded &
WithMaxCountSize &
WithPubkeyAsGroupPubkey ;
WithPubkeyAsGroupPubkey &
WithSignature ;
} ;
export type RetrieveSubRequestType =
@ -74,36 +86,219 @@ export type RetrieveSubRequestType =
| UpdateExpiryOnNodeGroupSubRequest
| RetrieveGroupSubAccountSubRequestType ;
/ * *
* OXEND_REQUESTS
* /
export type OnsResolveSubRequest = {
method : 'oxend_request' ;
params : {
endpoint : 'ons_resolve' ;
params : {
type : 0 ;
name_hash : string ; // base64EncodedNameHash
abstract class SnodeAPISubRequest {
public abstract method : string ;
}
export class OnsResolveSubRequest extends SnodeAPISubRequest {
public method : string = 'oxend_request' ;
public readonly base64EncodedNameHash : string ;
constructor ( base64EncodedNameHash : string ) {
super ( ) ;
this . base64EncodedNameHash = base64EncodedNameHash ;
}
public build() {
return {
method : this.method ,
params : {
endpoint : 'ons_resolve' ,
params : {
type : 0 ,
name_hash : this.base64EncodedNameHash ,
} ,
} ,
} ;
} ;
} ;
}
}
export type GetServiceNodesSubRequest = {
method : 'oxend_request' ;
params : {
endpoint : 'get_service_nodes' ;
params : {
active_only : true ;
fields : {
public_ip : true ;
storage_port : true ;
pubkey_x25519 : true ;
pubkey_ed25519 : true ;
} ;
export class GetServiceNodesSubRequest extends SnodeAPISubRequest {
public method = 'oxend_request' as const ;
public build() {
return {
method : this.method ,
params : {
endpoint : 'get_service_nodes' as const ,
params : {
active_only : true ,
fields : {
public_ip : true ,
storage_port : true ,
pubkey_x25519 : true ,
pubkey_ed25519 : true ,
} ,
} ,
} ,
} ;
} ;
} ;
}
}
export class SwarmForSubRequest extends SnodeAPISubRequest {
public method = 'get_swarm' as const ;
public readonly pubkey ;
constructor ( pubkey : PubkeyType | GroupPubkeyType ) {
super ( ) ;
this . pubkey = pubkey ;
}
public build() {
return {
method : this.method ,
params : {
pubkey : this.pubkey ,
params : {
active_only : true ,
fields : {
public_ip : true ,
storage_port : true ,
pubkey_x25519 : true ,
pubkey_ed25519 : true ,
} ,
} ,
} ,
} as const ;
}
}
export class NetworkTimeSubRequest extends SnodeAPISubRequest {
public method = 'info' as const ;
public build() {
return {
method : this.method ,
params : { } ,
} as const ;
}
}
abstract class SubaccountRightsSubRequest extends SnodeAPISubRequest {
public readonly groupPk : GroupPubkeyType ;
public readonly timestamp : number ;
public readonly revokeTokenHex : Array < string > ;
protected readonly secretKey : Uint8Array ;
constructor ( {
groupPk ,
timestamp ,
revokeTokenHex ,
secretKey ,
} : WithGroupPubkey & WithTimestamp & WithSecretKey & { revokeTokenHex : Array < string > } ) {
super ( ) ;
this . groupPk = groupPk ;
this . timestamp = timestamp ;
this . revokeTokenHex = revokeTokenHex ;
this . secretKey = secretKey ;
}
public async sign() {
if ( ! this . secretKey ) {
throw new Error ( 'we need an admin secretkey' ) ;
}
const tokensBytes = from_hex ( this . revokeTokenHex . join ( '' ) ) ;
const prefix = new Uint8Array ( StringUtils . encode ( ` ${ this . method } ${ this . timestamp } ` , 'utf8' ) ) ;
const sigResult = await SnodeGroupSignature . signDataWithAdminSecret (
concatUInt8Array ( prefix , tokensBytes ) ,
{ secretKey : this.secretKey }
) ;
return sigResult . signature ;
}
}
export class SubaccountRevokeSubRequest extends SubaccountRightsSubRequest {
public method = 'revoke_subaccount' as const ;
public async buildAndSignParameters() {
const signature = await this . sign ( ) ;
return {
method : this.method ,
params : {
pubkey : this.groupPk ,
signature ,
revoke : this.revokeTokenHex ,
timestamp : this.timestamp ,
} ,
} ;
}
}
export class SubaccountUnrevokeSubRequest extends SubaccountRightsSubRequest {
public method = 'unrevoke_subaccount' as const ;
/ * *
* For Revoke / unrevoke , this needs an admin signature
* /
public async buildAndSignParameters() {
const signature = await this . sign ( ) ;
return {
method : this.method ,
params : {
pubkey : this.groupPk ,
signature ,
unrevoke : this.revokeTokenHex ,
timestamp : this.timestamp ,
} ,
} ;
}
}
/ * *
* The getExpiriies request can currently only be used for our own pubkey as we use it to fetch
* the expiries updated by another of our devices .
* /
export class GetExpiriesFromNodeSubRequest extends SnodeAPISubRequest {
public method = 'get_expiries' as const ;
pubkey : string ;
messageHashes : Array < string > ;
constructor ( args : WithMessagesHashes ) {
super ( ) ;
const ourPubKey = UserUtils . getOurPubKeyStrFromCache ( ) ;
if ( ! ourPubKey ) {
throw new Error ( '[GetExpiriesFromNodeSubRequest] No pubkey found' ) ;
}
this . pubkey = ourPubKey ;
this . messageHashes = args . messagesHashes ;
}
/ * *
* For Revoke / unrevoke , this needs an admin signature
* /
public async buildAndSignParameters() {
const timestamp = GetNetworkTime . now ( ) ;
const signResult = await SnodeSignature . generateGetExpiriesOurSignature ( {
timestamp ,
messageHashes : this.messageHashes ,
} ) ;
if ( ! signResult ) {
throw new Error (
` [GetExpiriesFromNodeSubRequest] SnodeSignature.generateUpdateExpirySignature returned an empty result ${ this . messageHashes } `
) ;
}
return {
method : this.method ,
params : {
pubkey : this.pubkey ,
pubkey_ed25519 : signResult.pubkey_ed25519.toUpperCase ( ) ,
signature : signResult.signature ,
messages : this.messageHashes ,
timestamp ,
} ,
} ;
}
}
/ * *
* STORE SUBREQUESTS
* /
type StoreOnNodeNormalParams = {
pubkey : string ;
ttl : number ;
@ -118,13 +313,14 @@ type StoreOnNodeNormalParams = {
type StoreOnNodeSubAccountParams = Pick <
StoreOnNodeNormalParams ,
'data' | 'namespace' | 'ttl' | 'timestamp'
> & {
pubkey : GroupPubkeyType ;
subaccount : string ;
subaccount_sig : string ;
namespace : SnodeNamespaces . ClosedGroupMessages ; // this can only be this one, subaccounts holder can not post to something else atm
signature : string ; // signature is mandatory for subaccount
} ;
> &
WithSignature & {
pubkey : GroupPubkeyType ;
subaccount : string ;
subaccount_sig : string ;
namespace : SnodeNamespaces . ClosedGroupMessages ; // this can only be this one, subaccounts holder can not post to something else atm
// signature is mandatory for subaccount
} ;
export type StoreOnNodeParams = StoreOnNodeNormalParams | StoreOnNodeSubAccountParams ;
@ -133,16 +329,6 @@ export type StoreOnNodeParamsNoSig = Pick<
'pubkey' | 'ttl' | 'timestamp' | 'ttl' | 'namespace'
> & { data64 : string } ;
export type DeleteFromNodeWithTimestampParams = {
timestamp : string | number ;
namespace : number | null | 'all' ;
} & ( DeleteSigUserParameters | DeleteSigGroupParameters ) ;
export type DeleteByHashesFromNodeParams = { messages : Array < string > } & (
| DeleteSigUserParameters
| DeleteSigGroupParameters
) ;
type StoreOnNodeShared = {
networkTimestamp : number ;
data : Uint8Array ;
@ -173,17 +359,28 @@ export type StoreOnNodeSubRequest = {
method : 'store' ;
params : StoreOnNodeParams | StoreOnNodeSubAccountParams ;
} ;
export type NetworkTimeSubRequest = { method : 'info' ; params : object } ;
type DeleteSigUserParameters = {
/ * *
* DELETE SUBREQUESTS
* /
type DeleteFromNodeWithTimestampParams = {
timestamp : string | number ;
namespace : number | null | 'all' ;
} & ( DeleteSigUserParameters | DeleteSigGroupParameters ) ;
export type DeleteByHashesFromNodeParams = { messages : Array < string > } & (
| DeleteSigUserParameters
| DeleteSigGroupParameters
) ;
type DeleteSigUserParameters = WithSignature & {
pubkey : PubkeyType ;
pubkey_ed25519 : string ;
signature : string ;
} ;
type DeleteSigGroupParameters = {
type DeleteSigGroupParameters = WithSignature & {
pubkey : GroupPubkeyType ;
signature : string ;
} ;
export type DeleteAllFromNodeSubRequest = {
@ -196,10 +393,9 @@ export type DeleteFromNodeSubRequest = {
params : DeleteByHashesFromNodeParams ;
} ;
type UpdateExpireAlwaysNeeded = {
type UpdateExpireAlwaysNeeded = WithSignature & {
messages : Array < string > ;
expiry : number ;
signature : string ;
extend? : boolean ;
shorten? : boolean ;
} ;
@ -225,66 +421,22 @@ type UpdateExpiryOnNodeSubRequest =
| UpdateExpiryOnNodeUserSubRequest
| UpdateExpiryOnNodeGroupSubRequest ;
type SignedRevokeSubaccountShared = {
pubkey : GroupPubkeyType ;
signature : string ;
timestamp : number ;
} ;
export type SignedRevokeSubaccountParams = SignedRevokeSubaccountShared & {
revoke : Array < string > ; // the subaccounts token to revoke in hex
} ;
export type SignedUnrevokeSubaccountParams = SignedRevokeSubaccountShared & {
unrevoke : Array < string > ; // the subaccounts token to unrevoke in hex
} ;
export type RevokeSubaccountParams = Omit < SignedRevokeSubaccountParams , ' timestamp ' | ' signature ' > ;
export type UnrevokeSubaccountParams = Omit <
SignedUnrevokeSubaccountParams ,
'timestamp' | 'signature'
> ;
export type RevokeSubaccountSubRequest = {
method : 'revoke_subaccount' ;
params : SignedRevokeSubaccountParams ;
} ;
export type UnrevokeSubaccountSubRequest = {
method : 'unrevoke_subaccount' ;
params : SignedUnrevokeSubaccountParams ;
} ;
export type GetExpiriesNodeParams = {
pubkey : string ;
pubkey_ed25519 : string ;
messages : Array < string > ;
timestamp : number ;
signature : string ;
} ;
export type GetExpiriesFromNodeSubRequest = {
method : 'get_expiries' ;
params : GetExpiriesNodeParams ;
} ;
// Until the next storage server release is released, we need to have at least 2 hashes in the list for the `get_expiries` AND for the `update_expiries`
export const fakeHash = '///////////////////////////////////////////' ;
export type OxendSubRequest = OnsResolveSubRequest | GetServiceNodesSubRequest ;
export type SnodeApiSubRequests =
| RetrieveSubRequestType
| SwarmForSubRequest
| OxendSubRequest
| ReturnType < SwarmForSubRequest [ ' build ' ] >
| ReturnType < OnsResolveSubRequest [ ' build ' ] >
| ReturnType < GetServiceNodesSubRequest [ ' build ' ] >
| StoreOnNodeSubRequest
| NetworkTimeSubRequest
| ReturnType < NetworkTimeSubRequest [ ' build ' ] >
| DeleteFromNodeSubRequest
| DeleteAllFromNodeSubRequest
| UpdateExpiryOnNodeSubRequest
| RevokeSubaccountSubRequest
| UnrevokeSubaccountSubRequest
| GetExpiriesFromNodeSubRequest;
| Awaited < ReturnType < SubaccountRevokeSubRequest [ ' buildAndSignParameters ' ] > >
| Awaited < ReturnType < SubaccountUnrevokeSubRequest [ ' buildAndSignParameters ' ] > >
| Awaited < ReturnType < GetExpiriesFromNodeSubRequest [ ' buildAndSignParameters ' ] > > ;
// eslint-disable-next-line @typescript-eslint/array-type
export type NonEmptyArray < T > = [ T , . . . T [ ] ] ;
@ -296,28 +448,14 @@ export type BatchResultEntry = {
export type NotEmptyArrayOfBatchResults = NonEmptyArray < BatchResultEntry > ;
export type WithShortenOrExtend = { shortenOrExtend : 'shorten' | 'extend' | '' } ;
export const MAX_SUBREQUESTS_COUNT = 20 ;
export type BatchStoreWithExtraParams =
| StoreOnNodeParams
| SignedGroupHashesParams
| SignedHashesParams
| RevokeSubaccountSubRequest
| UnrevokeSubaccountSubRequest ;
export function isUnrevokeRequest (
request : BatchStoreWithExtraParams
) : request is UnrevokeSubaccountSubRequest {
return ! isEmpty ( ( request as UnrevokeSubaccountSubRequest ) ? . params ? . unrevoke ) ;
}
export function isRevokeRequest (
request : BatchStoreWithExtraParams
) : request is RevokeSubaccountSubRequest {
return ! isEmpty ( ( request as RevokeSubaccountSubRequest ) ? . params ? . revoke ) ;
}
| SubaccountRevokeSubRequest
| SubaccountUnrevokeSubRequest ;
export function isDeleteByHashesParams (
request : BatchStoreWithExtraParams