1
- import { unmarshalPrivateKey } from '@libp2p/crypto/keys'
1
+ import { publicKeyToProtobuf } from '@libp2p/crypto/keys'
2
2
import { logger } from '@libp2p/logger'
3
- import errCode from 'err-code'
4
- import { Key } from 'interface-datastore/key'
5
- import { base32upper } from 'multiformats/bases/base32'
6
- import * as Digest from 'multiformats/hashes/digest'
7
- import { identity } from 'multiformats/hashes/identity'
3
+ import { type Key } from 'interface-datastore/key'
8
4
import NanoDate from 'timestamp-nano'
9
- import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
10
5
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
11
- import * as ERRORS from './errors.js'
6
+ import { SignatureCreationError } from './errors.js'
12
7
import { IpnsEntry } from './pb/ipns.js'
13
8
import { createCborData , ipnsRecordDataForV1Sig , ipnsRecordDataForV2Sig , normalizeValue } from './utils.js'
14
- import type { PrivateKey , PeerId } from '@libp2p/interface'
9
+ import type { PrivateKey , PublicKey } from '@libp2p/interface'
15
10
import type { CID } from 'multiformats/cid'
16
11
17
12
const log = logger ( 'ipns' )
18
- const ID_MULTIHASH_CODE = identity . code
19
13
const DEFAULT_TTL_NS = 60 * 60 * 1e+9 // 1 Hour or 3600 Seconds
20
14
21
15
export const namespace = '/ipns/'
@@ -157,22 +151,22 @@ const defaultCreateOptions: CreateOptions = {
157
151
* * PeerIDs will create recursive records, eg. the record value will be `/ipns/${cidV1Libp2pKey}`
158
152
* * String paths will be stored in the record as-is, but they must start with `"/"`
159
153
*
160
- * @param {PeerId } peerId - peer id containing private key for signing the record.
161
- * @param {CID | PeerId | string } value - content to be stored in the record.
154
+ * @param {PrivateKey } privateKey - the private key for signing the record.
155
+ * @param {CID | PublicKey | string } value - content to be stored in the record.
162
156
* @param {number | bigint } seq - number representing the current version of the record.
163
157
* @param {number } lifetime - lifetime of the record (in milliseconds).
164
158
* @param {CreateOptions } options - additional create options.
165
159
*/
166
- export async function create ( peerId : PeerId , value : CID | PeerId | string , seq : number | bigint , lifetime : number , options ?: CreateV2OrV1Options ) : Promise < IPNSRecordV1V2 >
167
- export async function create ( peerId : PeerId , value : CID | PeerId | string , seq : number | bigint , lifetime : number , options : CreateV2Options ) : Promise < IPNSRecordV2 >
168
- export async function create ( peerId : PeerId , value : CID | PeerId | string , seq : number | bigint , lifetime : number , options : CreateOptions ) : Promise < IPNSRecordV1V2 >
169
- export async function create ( peerId : PeerId , value : CID | PeerId | string , seq : number | bigint , lifetime : number , options : CreateOptions = defaultCreateOptions ) : Promise < IPNSRecord > {
160
+ export async function createIPNSRecord ( privateKey : PrivateKey , value : CID | PublicKey | string , seq : number | bigint , lifetime : number , options ?: CreateV2OrV1Options ) : Promise < IPNSRecordV1V2 >
161
+ export async function createIPNSRecord ( privateKey : PrivateKey , value : CID | PublicKey | string , seq : number | bigint , lifetime : number , options : CreateV2Options ) : Promise < IPNSRecordV2 >
162
+ export async function createIPNSRecord ( privateKey : PrivateKey , value : CID | PublicKey | string , seq : number | bigint , lifetime : number , options : CreateOptions ) : Promise < IPNSRecordV1V2 >
163
+ export async function createIPNSRecord ( privateKey : PrivateKey , value : CID | PublicKey | string , seq : number | bigint , lifetime : number , options : CreateOptions = defaultCreateOptions ) : Promise < IPNSRecord > {
170
164
// Validity in ISOString with nanoseconds precision and validity type EOL
171
165
const expirationDate = new NanoDate ( Date . now ( ) + Number ( lifetime ) )
172
166
const validityType = IpnsEntry . ValidityType . EOL
173
167
const ttlNs = BigInt ( options . ttlNs ?? DEFAULT_TTL_NS )
174
168
175
- return _create ( peerId , value , seq , validityType , expirationDate . toString ( ) , ttlNs , options )
169
+ return _create ( privateKey , value , seq , validityType , expirationDate . toString ( ) , ttlNs , options )
176
170
}
177
171
178
172
/**
@@ -185,47 +179,37 @@ export async function create (peerId: PeerId, value: CID | PeerId | string, seq:
185
179
* * PeerIDs will create recursive records, eg. the record value will be `/ipns/${cidV1Libp2pKey}`
186
180
* * String paths will be stored in the record as-is, but they must start with `"/"`
187
181
*
188
- * @param {PeerId } peerId - PeerId containing private key for signing the record.
189
- * @param {CID | PeerId | string } value - content to be stored in the record.
182
+ * @param {PrivateKey } privateKey - the private key for signing the record.
183
+ * @param {CID | PublicKey | string } value - content to be stored in the record.
190
184
* @param {number | bigint } seq - number representing the current version of the record.
191
185
* @param {string } expiration - expiration datetime for record in the [RFC3339]{@link https://www.ietf.org/rfc/rfc3339.txt} with nanoseconds precision.
192
186
* @param {CreateOptions } options - additional creation options.
193
187
*/
194
- export async function createWithExpiration ( peerId : PeerId , value : CID | PeerId | string , seq : number | bigint , expiration : string , options ?: CreateV2OrV1Options ) : Promise < IPNSRecordV1V2 >
195
- export async function createWithExpiration ( peerId : PeerId , value : CID | PeerId | string , seq : number | bigint , expiration : string , options : CreateV2Options ) : Promise < IPNSRecordV2 >
196
- export async function createWithExpiration ( peerId : PeerId , value : CID | PeerId | string , seq : number | bigint , expiration : string , options : CreateOptions ) : Promise < IPNSRecordV1V2 >
197
- export async function createWithExpiration ( peerId : PeerId , value : CID | PeerId | string , seq : number | bigint , expiration : string , options : CreateOptions = defaultCreateOptions ) : Promise < IPNSRecord > {
188
+ export async function createIPNSRecordWithExpiration ( privateKey : PrivateKey , value : CID | PublicKey | string , seq : number | bigint , expiration : string , options ?: CreateV2OrV1Options ) : Promise < IPNSRecordV1V2 >
189
+ export async function createIPNSRecordWithExpiration ( privateKey : PrivateKey , value : CID | PublicKey | string , seq : number | bigint , expiration : string , options : CreateV2Options ) : Promise < IPNSRecordV2 >
190
+ export async function createIPNSRecordWithExpiration ( privateKey : PrivateKey , value : CID | PublicKey | string , seq : number | bigint , expiration : string , options : CreateOptions ) : Promise < IPNSRecordV1V2 >
191
+ export async function createIPNSRecordWithExpiration ( privateKey : PrivateKey , value : CID | PublicKey | string , seq : number | bigint , expiration : string , options : CreateOptions = defaultCreateOptions ) : Promise < IPNSRecord > {
198
192
const expirationDate = NanoDate . fromString ( expiration )
199
193
const validityType = IpnsEntry . ValidityType . EOL
200
194
const ttlNs = BigInt ( options . ttlNs ?? DEFAULT_TTL_NS )
201
195
202
- return _create ( peerId , value , seq , validityType , expirationDate . toString ( ) , ttlNs , options )
196
+ return _create ( privateKey , value , seq , validityType , expirationDate . toString ( ) , ttlNs , options )
203
197
}
204
198
205
- const _create = async ( peerId : PeerId , value : CID | PeerId | string , seq : number | bigint , validityType : IpnsEntry . ValidityType , validity : string , ttl : bigint , options : CreateOptions = defaultCreateOptions ) : Promise < IPNSRecord > => {
199
+ const _create = async ( privateKey : PrivateKey , value : CID | PublicKey | string , seq : number | bigint , validityType : IpnsEntry . ValidityType , validity : string , ttl : bigint , options : CreateOptions = defaultCreateOptions ) : Promise < IPNSRecord > => {
206
200
seq = BigInt ( seq )
207
201
const isoValidity = uint8ArrayFromString ( validity )
208
202
const normalizedValue = normalizeValue ( value )
209
203
const encodedValue = uint8ArrayFromString ( normalizedValue )
210
-
211
- if ( peerId . privateKey == null ) {
212
- throw errCode ( new Error ( 'Missing private key' ) , ERRORS . ERR_MISSING_PRIVATE_KEY )
213
- }
214
-
215
- const privateKey = await unmarshalPrivateKey ( peerId . privateKey )
216
204
const data = createCborData ( encodedValue , validityType , isoValidity , seq , ttl )
217
205
const sigData = ipnsRecordDataForV2Sig ( data )
218
206
const signatureV2 = await privateKey . sign ( sigData )
219
207
let pubKey : Uint8Array | undefined
220
208
221
209
// if we cannot derive the public key from the PeerId (e.g. RSA PeerIDs),
222
210
// we have to embed it in the IPNS record
223
- if ( peerId . publicKey != null ) {
224
- const digest = Digest . decode ( peerId . toBytes ( ) )
225
-
226
- if ( digest . code !== ID_MULTIHASH_CODE || ! uint8ArrayEquals ( peerId . publicKey , digest . digest ) ) {
227
- pubKey = peerId . publicKey
228
- }
211
+ if ( privateKey . type === 'RSA' ) {
212
+ pubKey = publicKeyToProtobuf ( privateKey . publicKey )
229
213
}
230
214
231
215
if ( options . v1Compatible === true ) {
@@ -266,24 +250,13 @@ const _create = async (peerId: PeerId, value: CID | PeerId | string, seq: number
266
250
}
267
251
}
268
252
269
- /**
270
- * rawStdEncoding with RFC4648
271
- */
272
- const rawStdEncoding = ( key : Uint8Array ) : string => base32upper . encode ( key ) . slice ( 1 )
273
-
274
- /**
275
- * Get key for storing the record locally.
276
- * Format: /ipns/${base32(<HASH>)}
277
- *
278
- * @param {Uint8Array } key - peer identifier object.
279
- */
280
- export const getLocalKey = ( key : Uint8Array ) : Key => new Key ( `/ipns/${ rawStdEncoding ( key ) } ` )
281
-
282
- export { unmarshal } from './utils.js'
283
- export { marshal } from './utils.js'
284
- export { peerIdToRoutingKey } from './utils.js'
285
- export { peerIdFromRoutingKey } from './utils.js'
286
- export { extractPublicKey } from './utils.js'
253
+ export { unmarshalIPNSRecord } from './utils.js'
254
+ export { marshalIPNSRecord } from './utils.js'
255
+ export { multihashToIPNSRoutingKey } from './utils.js'
256
+ export { multihashFromIPNSRoutingKey } from './utils.js'
257
+ export { publicKeyToIPNSRoutingKey } from './utils.js'
258
+ export { publicKeyFromIPNSRoutingKey } from './utils.js'
259
+ export { extractPublicKeyFromIPNSRecord } from './utils.js'
287
260
288
261
/**
289
262
* Sign ipns record data using the legacy V1 signature scheme
@@ -295,6 +268,6 @@ const signLegacyV1 = async (privateKey: PrivateKey, value: Uint8Array, validityT
295
268
return await privateKey . sign ( dataForSignature )
296
269
} catch ( error : any ) {
297
270
log . error ( 'record signature creation failed' , error )
298
- throw errCode ( new Error ( 'record signature creation failed') , ERRORS . ERR_SIGNATURE_CREATION )
271
+ throw new SignatureCreationError ( 'Record signature creation failed')
299
272
}
300
273
}
0 commit comments