@@ -76,6 +76,7 @@ export function id(){
7676 return sign ( 'id' ) ;
7777}
7878
79+ // Check if given key is valid, and if it is, return its type
7980export function validate ( key , silent = true ) {
8081 const sig = key . substring ( 0 , sigLen ) ;
8182 const hash = key . substring ( sigLen ) ;
@@ -89,18 +90,33 @@ export function validate(key, silent=true){
8990 }
9091}
9192
93+ // Assert if given key is of the given type (private | public)
94+ // This is computationally favorable to the Boolean: validate(key) === type
95+ export function assert ( key , type ) {
96+ const sig = key . substring ( 0 , sigLen ) ;
97+ const hash = key . substring ( sigLen ) ;
98+ return sig === sign ( hash + type ) ;
99+ }
100+
92101export function genPublicKey ( privateOrPublicKey ) {
93- if ( validate ( privateOrPublicKey ) === 'public' ) return privateOrPublicKey ;
102+ if ( assert ( privateOrPublicKey , 'public' ) ) return privateOrPublicKey ;
94103 const privateKey = privateOrPublicKey ;
95104 const privateHash = privateKey . substring ( sigLen ) ;
96105 const publicHash = hash ( privateHash ) ;
97106 const publicKey = sign ( publicHash + 'public' ) + publicHash ;
98107 return publicKey ;
99108}
100109
110+ export function genKeyPair ( seed = randomUUID ( ) ) {
111+ const privateHash = hash ( seed ) ;
112+ const privateKey = sign ( privateHash + 'private' ) + privateHash ;
113+ const publicKey = genPublicKey ( privateKey ) ;
114+ return { private : privateKey , public : publicKey } ;
115+ }
116+
101117export function cacheSet ( privateKey , obj ) {
102118 const publicKey = genPublicKey ( privateKey ) ;
103- const dbKey = dbKeyPrefix . cache + publicKey ;
119+ const dbKey = dbKeyPrefix . cache + publicKey . substring ( sigLen ) ;
104120 // Promise.all below enables both commands to be executed in a single http request (using same pipeline)
105121 // As Redis is single-threaded, the commands are executed in order
106122 // See https://upstash.com/docs/redis/sdks/ts/pipelining/auto-pipeline
@@ -111,23 +127,16 @@ export function cacheSet(privateKey, obj){
111127}
112128
113129export function cacheGet ( publicKey , key ) {
114- const dbKey = dbKeyPrefix . cache + publicKey ;
130+ const dbKey = dbKeyPrefix . cache + publicKey . substring ( sigLen ) ;
115131 return redisRateLimit . hget ( dbKey , key ) ;
116132}
117133
118134export function cacheDel ( privateOrPublicKey , key ) {
119135 const publicKey = genPublicKey ( privateOrPublicKey ) ;
120- const dbKey = dbKeyPrefix . cache + publicKey ;
136+ const dbKey = dbKeyPrefix . cache + publicKey . substring ( sigLen ) ;
121137 return redisRateLimit . hdel ( dbKey , key ) ;
122138}
123139
124- export function genKeyPair ( seed = randomUUID ( ) ) {
125- const privateHash = hash ( seed ) ;
126- const privateKey = sign ( privateHash + 'private' ) + privateHash ;
127- const publicKey = genPublicKey ( privateKey ) ;
128- return { private : privateKey , public : publicKey } ;
129- }
130-
131140// Add metadata to payload (which must be a JSON object)
132141// Some metadata, such as `id` to uniquely identify a payload and timestamp, are generated
133142// Other metadata may be provided as the `fields` JSON object.
@@ -151,7 +160,7 @@ export function unlockJSON(json, passwd){
151160}
152161
153162export async function publicProduce ( publicKey , data ) {
154- const dbKey = dbKeyPrefix . manyToOne + publicKey ;
163+ const dbKey = dbKeyPrefix . manyToOne + publicKey . substring ( sigLen ) ;
155164 return Promise . all ( [
156165 redisData . rpush ( dbKey , data ) ,
157166 redisData . expire ( dbKey , ttl )
@@ -160,7 +169,7 @@ export async function publicProduce(publicKey, data){
160169
161170export async function privateConsume ( privateKey ) {
162171 const publicKey = genPublicKey ( privateKey ) ;
163- const dbKey = dbKeyPrefix . manyToOne + publicKey ;
172+ const dbKey = dbKeyPrefix . manyToOne + publicKey . substring ( sigLen ) ;
164173 const atomicTransaction = redisData . multi ( ) ;
165174 atomicTransaction . lrange ( dbKey , 0 , - 1 ) ;
166175 atomicTransaction . del ( dbKey ) ;
@@ -170,26 +179,26 @@ export async function privateConsume(privateKey){
170179
171180export async function privateProduce ( privateKey , data ) {
172181 const publicKey = genPublicKey ( privateKey ) ;
173- const dbKey = dbKeyPrefix . oneToMany + publicKey ;
182+ const dbKey = dbKeyPrefix . oneToMany + publicKey . substring ( sigLen ) ;
174183 return redisData . set ( dbKey , data , { ex : ttl } ) ;
175184}
176185
177186export async function privateDelete ( privateKey ) {
178187 const publicKey = genPublicKey ( privateKey ) ;
179- const dbKey = dbKeyPrefix . oneToMany + publicKey ;
188+ const dbKey = dbKeyPrefix . oneToMany + publicKey . substring ( sigLen ) ;
180189 return redisData . del ( dbKey ) ;
181190}
182191
183192export async function privateRefresh ( privateKey ) {
184193 const publicKey = genPublicKey ( privateKey ) ;
185- const dbKey = dbKeyPrefix . oneToMany + publicKey ;
194+ const dbKey = dbKeyPrefix . oneToMany + publicKey . substring ( sigLen ) ;
186195 return redisData . expire ( dbKey , ttl ) ;
187196}
188197
189198export async function privateStats ( privateKey ) {
190199 const publicKey = genPublicKey ( privateKey ) ;
191- const dbKeyConsume = dbKeyPrefix . manyToOne + publicKey ;
192- const dbKeyPublish = dbKeyPrefix . oneToMany + publicKey ;
200+ const dbKeyConsume = dbKeyPrefix . manyToOne + publicKey . substring ( sigLen ) ;
201+ const dbKeyPublish = dbKeyPrefix . oneToMany + publicKey . substring ( sigLen ) ;
193202 const [ countConsume , ttlConsume ] = await Promise . all ( [
194203 redisData . llen ( dbKeyConsume ) ,
195204 redisData . ttl ( dbKeyConsume )
@@ -202,7 +211,7 @@ export async function privateStats(privateKey){
202211
203212// Demand for data also refreshes its expiry
204213export async function publicConsume ( publicKey ) {
205- const dbKey = dbKeyPrefix . oneToMany + publicKey ;
214+ const dbKey = dbKeyPrefix . oneToMany + publicKey . substring ( sigLen ) ;
206215 // Ideally there should be getex() in Upstash's Redis SDK.
207216 // Until it's available, we make do with pipelining as follows.
208217 const [ data , _ ] = await Promise . all ( [
@@ -214,16 +223,18 @@ export async function publicConsume(publicKey){
214223
215224export async function oneToOneProduce ( privateKey , key , data ) {
216225 const publicKey = genPublicKey ( privateKey ) ;
217- const dbKey = dbKeyPrefix . oneToOne + publicKey ;
226+ const dbKey = dbKeyPrefix . oneToOne + publicKey . substring ( sigLen ) ;
218227 const field = { [ hash ( key ) ] : data } ;
228+ // Ideally there should be hexpire() in Upstash's Redis SDK.
229+ // Until it's available, we expire the containing key as follows.
219230 return Promise . all ( [
220231 redisData . hset ( dbKey , field ) ,
221232 redisData . expire ( dbKey , ttl )
222233 ] )
223234}
224235
225236export async function oneToOneConsume ( publicKey , key ) {
226- const dbKey = dbKeyPrefix . oneToOne + publicKey ;
237+ const dbKey = dbKeyPrefix . oneToOne + publicKey . substring ( sigLen ) ;
227238 const field = hash ( key ) ;
228239 const atomicTransaction = redisData . multi ( ) ;
229240 atomicTransaction . hget ( dbKey , field ) ;
@@ -234,8 +245,10 @@ export async function oneToOneConsume(publicKey, key){
234245
235246export async function oneToOneTTL ( privateKey , key ) {
236247 const publicKey = genPublicKey ( privateKey ) ;
237- const dbKey = dbKeyPrefix . oneToOne + publicKey ;
248+ const dbKey = dbKeyPrefix . oneToOne + publicKey . substring ( sigLen ) ;
238249 const field = hash ( key ) ;
250+ // Ideally there should be httl() in Upstash's Redis SDK.
251+ // Until it's available, we use ttl of the containing key as follows.
239252 const [ bool , ttl ] = await Promise . all ( [
240253 redisData . hexists ( dbKey , field ) ,
241254 redisData . ttl ( dbKey )
@@ -244,13 +257,14 @@ export async function oneToOneTTL(privateKey, key){
244257}
245258
246259// Tokens are stored in LIFO stacks. Old and unused tokens are trimmed.
260+ // Timestamps are stored with the tokens using string concatenation
247261export async function streamToken ( privateOrPublicKey , receive = true ) {
248262 const type = validate ( privateOrPublicKey , false ) ;
249263 const typeComplement = ( type == 'private' ) ? 'public' : 'private' ;
250264 const publicKey = genPublicKey ( privateOrPublicKey ) ;
251265 const mode = receive ? "receive" : "send" ;
252266 const modeComplement = receive ? "send" : "receive" ;
253- const existing = await redisData . lpop ( dbKeyPrefix . stream [ typeComplement ] [ modeComplement ] + publicKey ) ;
267+ const existing = await redisData . lpop ( dbKeyPrefix . stream [ typeComplement ] [ modeComplement ] + publicKey . substring ( sigLen ) ) ;
254268 const timeNow = Math . round ( Date . now ( ) / 1000 ) ;
255269 if ( existing ) {
256270 const [ token , timestamp ] = existing . split ( '@' ) ;
@@ -259,7 +273,7 @@ export async function streamToken(privateOrPublicKey, receive=true){
259273 if ( ( timeNow - timestamp ) < streamTimeout ) return token ;
260274 }
261275 const token = randStr ( ) ;
262- const dbKey = dbKeyPrefix . stream [ type ] [ mode ] + publicKey ;
276+ const dbKey = dbKeyPrefix . stream [ type ] [ mode ] + publicKey . substring ( sigLen ) ;
263277 await Promise . all ( [
264278 redisData . lpush ( dbKey , token + '@' + timeNow ) ,
265279 redisData . expire ( dbKey , streamTimeout ) ,
0 commit comments