Skip to content

Commit b73dac4

Browse files
2colorSgtPooki
andauthored
docs: update api docs (#333)
* test: clarify that paths are allowed with test * docs: update based on curent API * Apply suggestions from code review Co-authored-by: Russell Dempsey <[email protected]> * docs: document difference between two validators --------- Co-authored-by: Daniel N <[email protected]> Co-authored-by: Russell Dempsey <[email protected]>
1 parent 35a97a5 commit b73dac4

File tree

5 files changed

+58
-111
lines changed

5 files changed

+58
-111
lines changed

README.md

+16-98
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,11 @@
1414
- [Usage](#usage)
1515
- [Create record](#create-record)
1616
- [Validate record](#validate-record)
17-
- [Embed public key to record](#embed-public-key-to-record)
1817
- [Extract public key from record](#extract-public-key-from-record)
1918
- [Marshal data with proto buffer](#marshal-data-with-proto-buffer)
2019
- [Unmarshal data from proto buffer](#unmarshal-data-from-proto-buffer)
21-
- [Validator](#validator)
22-
- [API](#api)
23-
- [Create record](#create-record-1)
24-
- [Validate record](#validate-record-1)
25-
- [Marshal data with proto buffer](#marshal-data-with-proto-buffer-1)
26-
- [Unmarshal data from proto buffer](#unmarshal-data-from-proto-buffer-1)
27-
- [Extract public key from record](#extract-public-key-from-record-1)
28-
- [Namespace](#namespace)
2920
- [API Docs](#api-docs)
21+
- [Namespace](#namespace)
3022
- [License](#license)
3123
- [Contribute](#contribute)
3224

@@ -53,42 +45,44 @@ This module contains all the necessary code for creating, understanding and vali
5345
```js
5446
import * as ipns from 'ipns'
5547

56-
const ipnsRecord = await ipns.create(privateKey, value, sequenceNumber, lifetime)
48+
const ipnsRecord = await ipns.createIPNSRecord(privateKey, value, sequenceNumber, lifetime)
5749
```
5850

59-
### Validate record
51+
### Validate record against public key
6052

6153
```js
62-
import * as ipns from 'ipns'
54+
import { validate } from 'ipns/validator'
6355

64-
await ipns.validate(publicKey, marshalledData)
56+
await validate(publicKey, marshalledRecord)
6557
// if no error thrown, the record is valid
6658
```
6759

68-
### Embed public key to record
60+
### Validate record against routing key
61+
62+
This is useful when validating IPNS names that use RSA keys, whose public key is embedded in the record (rather than in the routing key as with Ed25519).
6963

7064
```js
71-
import * as ipns from 'ipns'
65+
import { ipnsValidator } from 'ipns/validator'
7266

73-
const ipnsRecordWithEmbeddedPublicKey = await ipns.embedPublicKey(publicKey, ipnsRecord)
67+
await ipnsValidator(routingKey, marshalledRecord)
7468
```
7569

7670
### Extract public key from record
7771

7872
```js
7973
import * as ipns from 'ipns'
8074

81-
const publicKey = await ipns.extractPublicKey(peerId, ipnsRecord)
75+
const publicKey = await ipns.extractPublicKeyFromIPNSRecord(peerId, ipnsRecord)
8276
```
8377

8478
### Marshal data with proto buffer
8579

8680
```js
8781
import * as ipns from 'ipns'
8882

89-
const ipnsRecord = await ipns.create(privateKey, value, sequenceNumber, lifetime)
83+
const ipnsRecord = await ipns.createIPNSRecord(privateKey, value, sequenceNumber, lifetime)
9084
// ...
91-
const marshalledData = ipns.marshal(ipnsRecord)
85+
const marshalledData = ipns.marshalIPNSRecord(ipnsRecord)
9286
// ...
9387
```
9488

@@ -99,89 +93,16 @@ Returns the record data serialized.
9993
```js
10094
import * as ipns from 'ipns'
10195

102-
const ipnsRecord = ipns.unmarshal(storedData)
96+
const ipnsRecord = ipns.unmarshalIPNSRecord(storedData)
10397
```
10498

10599
Returns the `IPNSRecord` after being deserialized.
106100

107-
### Validator
108-
109-
```js
110-
import * as ipns from 'ipns'
111-
112-
const validator = ipns.validator
113-
```
114-
115-
Contains an object with `validate (marshalledData, key)` and `select (dataA, dataB)` functions.
116-
117-
The `validate` async function aims to verify if an IPNS record is valid. First the record is unmarshalled, then the public key is obtained and finally the record is validated (`signatureV2` of CBOR `data` is verified).
118-
119-
The `select` function is responsible for deciding which IPNS record is the best (newer) between two records. Both records are unmarshalled and their sequence numbers are compared. If the first record provided is the newer, the operation result will be `0`, otherwise the operation result will be `1`.
120-
121-
## API
122-
123-
### Create record
124-
125-
```js
126-
127-
ipns.create(privateKey, value, sequenceNumber, lifetime, options)
128-
```
129-
130-
Create an IPNS record for being stored in a protocol buffer.
131-
132-
- `privateKey` ([PrivateKey](https://libp2p.github.io/js-libp2p/interfaces/_libp2p_interface.keys.PrivateKey.html)): key to be used for cryptographic operations.
133-
- `value` (string): IPFS path of the object to be published.
134-
- `sequenceNumber` (Number): number representing the current version of the record.
135-
- `lifetime` (Number): lifetime of the record (in milliseconds).
136-
- `options` (CreateOptions): additional creation options.
137-
138-
Returns a `Promise` that resolves to an object with a `IPNSRecord`.
139-
140-
### Validate record
141-
142-
```js
143-
ipns.validate(publicKey, ipnsRecord)
144-
```
145-
146-
Validate an IPNS record previously stored in a protocol buffer.
147-
148-
- `publicKey` ([PublicKey](https://libp2p.github.io/js-libp2p/interfaces/_libp2p_interface.keys.PublicKey.html)): key to be used for cryptographic operations.
149-
- `ipnsRecord` (`IPNSRecord`): IPNS record (obtained using the create function).
150-
151-
Returns a `Promise`, which may be rejected if the validation was not successful.
152-
153-
### Marshal data with proto buffer
154-
155-
```js
156-
const marshalledData = ipns.marshal(ipnsRecord)
157-
```
158-
159-
Returns the serialized IPNS record.
160101

161-
- `ipnsRecord` (`IPNSRecord`): ipns record (obtained using the create function).
162-
163-
### Unmarshal data from proto buffer
164-
165-
```js
166-
const data = ipns.unmarshal(storedData)
167-
```
168-
169-
Returns a `IPNSRecord` after being serialized.
170-
171-
- `storedData` (Uint8Array): ipns record serialized.
172-
173-
### Extract public key from record
174-
175-
```js
176-
const publicKey = await ipns.extractPublicKey(peerId, ipnsRecord)
177-
```
178-
179-
Extract a public key from an IPNS record.
102+
## API Docs
180103

181-
- `peerId` ([PeerId](https://libp2p.github.io/js-libp2p/types/_libp2p_interface.peer_id.PeerId.html)): peer identifier object.
182-
- `ipnsRecord` (`IPNSRecord`): ipns record (obtained using the create function).
104+
- <https://ipfs.github.io/js-ipns>
183105

184-
Returns a `Promise` which resolves to public key ([`PublicKey`](https://github.com/libp2p/js-libp2p-interfaces/blob/master/packages/interface-keys/src/index.ts) ): may be used for cryptographic operations.
185106

186107
### Namespace
187108

@@ -199,9 +120,6 @@ ipns.namespaceLength
199120
// 6
200121
```
201122

202-
## API Docs
203-
204-
- <https://ipfs.github.io/js-ipns>
205123

206124
## License
207125

src/index.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -146,10 +146,10 @@ const defaultCreateOptions: CreateOptions = {
146146
* The IPNS Record validity should follow the [RFC3339]{@link https://www.ietf.org/rfc/rfc3339.txt} with nanoseconds precision.
147147
* Note: This function does not embed the public key. If you want to do that, use `EmbedPublicKey`.
148148
*
149-
* The passed value can be a CID, a PeerID or an arbitrary string path.
149+
* The passed value can be a CID, a PublicKey or an arbitrary string path e.g. `/ipfs/...` or `/ipns/...`.
150150
*
151151
* * CIDs will be converted to v1 and stored in the record as a string similar to: `/ipfs/${cid}`
152-
* * PeerIDs will create recursive records, eg. the record value will be `/ipns/${cidV1Libp2pKey}`
152+
* * PublicKeys will create recursive records, eg. the record value will be `/ipns/${cidV1Libp2pKey}`
153153
* * String paths will be stored in the record as-is, but they must start with `"/"`
154154
*
155155
* @param {PrivateKey} privateKey - the private key for signing the record.
@@ -174,10 +174,10 @@ export async function createIPNSRecord (privateKey: PrivateKey, value: CID | Pub
174174
* Same as create(), but instead of generating a new Date, it receives the intended expiration time
175175
* WARNING: nano precision is not standard, make sure the value in seconds is 9 orders of magnitude lesser than the one provided.
176176
*
177-
* The passed value can be a CID, a PeerID or an arbitrary string path.
177+
* The passed value can be a CID, a PublicKey or an arbitrary string path e.g. `/ipfs/...` or `/ipns/...`.
178178
*
179179
* * CIDs will be converted to v1 and stored in the record as a string similar to: `/ipfs/${cid}`
180-
* * PeerIDs will create recursive records, eg. the record value will be `/ipns/${cidV1Libp2pKey}`
180+
* * PublicKeys will create recursive records, eg. the record value will be `/ipns/${cidV1Libp2pKey}`
181181
* * String paths will be stored in the record as-is, but they must start with `"/"`
182182
*
183183
* @param {PrivateKey} privateKey - the private key for signing the record.

src/selector.ts

+11
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@ import NanoDate from 'timestamp-nano'
22
import { IpnsEntry } from './pb/ipns.js'
33
import { unmarshalIPNSRecord } from './utils.js'
44

5+
/**
6+
* Selects the latest valid IPNS record from an array of marshalled IPNS records.
7+
*
8+
* Records are sorted by:
9+
* 1. Sequence number (higher takes precedence)
10+
* 2. Validity time for EOL records with same sequence number (longer lived record takes precedence)
11+
*
12+
* @param key - The routing key for the IPNS record
13+
* @param data - Array of marshalled IPNS records to select from
14+
* @returns The index of the most valid record from the input array
15+
*/
516
export function ipnsSelector (key: Uint8Array, data: Uint8Array[]): number {
617
const entries = data.map((buf, index) => ({
718
record: unmarshalIPNSRecord(buf),

src/validator.ts

+17-9
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ const MAX_RECORD_SIZE = 1024 * 10
1818
* Validates the given IPNS Record against the given public key. We need a "raw"
1919
* record in order to be able to access to all of its fields.
2020
*/
21-
export const validate = async (publicKey: PublicKey, buf: Uint8Array): Promise<void> => {
21+
export const validate = async (publicKey: PublicKey, marshalledRecord: Uint8Array): Promise<void> => {
2222
// unmarshal ensures that (1) SignatureV2 and Data are present, (2) that ValidityType
2323
// and Validity are of valid types and have a value, (3) that CBOR data matches protobuf
2424
// if it's a V1+V2 record.
25-
const record = unmarshalIPNSRecord(buf)
25+
const record = unmarshalIPNSRecord(marshalledRecord)
2626

2727
// Validate Signature V2
2828
let isValid
@@ -53,13 +53,21 @@ export const validate = async (publicKey: PublicKey, buf: Uint8Array): Promise<v
5353
log('ipns record for %s is valid', record.value)
5454
}
5555

56-
export async function ipnsValidator (key: Uint8Array, marshalledData: Uint8Array): Promise<void> {
57-
if (marshalledData.byteLength > MAX_RECORD_SIZE) {
56+
/**
57+
* Validate the given IPNS record against the given routing key.
58+
*
59+
* @see https://specs.ipfs.tech/ipns/ipns-record/#routing-record for the binary format of the routing key
60+
*
61+
* @param routingKey - The routing key in binary format: binary(ascii(IPNS_PREFIX) + multihash(public key))
62+
* @param marshalledRecord - The marshalled record to validate.
63+
*/
64+
export async function ipnsValidator (routingKey: Uint8Array, marshalledRecord: Uint8Array): Promise<void> {
65+
if (marshalledRecord.byteLength > MAX_RECORD_SIZE) {
5866
throw new RecordTooLargeError('The record is too large')
5967
}
6068

6169
// try to extract public key from routing key
62-
const routingMultihash = multihashFromIPNSRoutingKey(key)
70+
const routingMultihash = multihashFromIPNSRoutingKey(routingKey)
6371
let routingPubKey: PublicKey | undefined
6472

6573
// identity hash
@@ -68,19 +76,19 @@ export async function ipnsValidator (key: Uint8Array, marshalledData: Uint8Array
6876
}
6977

7078
// extract public key from record
71-
const receivedRecord = unmarshalIPNSRecord(marshalledData)
79+
const receivedRecord = unmarshalIPNSRecord(marshalledRecord)
7280
const recordPubKey = extractPublicKeyFromIPNSRecord(receivedRecord) ?? routingPubKey
7381

7482
if (recordPubKey == null) {
7583
throw new InvalidEmbeddedPublicKeyError('Could not extract public key from IPNS record or routing key')
7684
}
7785

78-
const routingKey = multihashToIPNSRoutingKey(recordPubKey.toMultihash())
86+
const expectedRoutingKey = multihashToIPNSRoutingKey(recordPubKey.toMultihash())
7987

80-
if (!uint8ArrayEquals(key, routingKey)) {
88+
if (!uint8ArrayEquals(expectedRoutingKey, routingKey)) {
8189
throw new InvalidEmbeddedPublicKeyError('Embedded public key did not match routing key')
8290
}
8391

8492
// Record validation
85-
await validate(recordPubKey, marshalledData)
93+
await validate(recordPubKey, marshalledRecord)
8694
}

test/utils.spec.ts

+10
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,16 @@ describe('utils', () => {
2222
output: '/ipns/k73ap3wtp70r7cd9ofyhwgogv1j96huvtvfnsof5spyfaaopkxmonumi4fckgguqr'
2323
},
2424

25+
// path input
26+
'/ipfs/CID path': {
27+
input: '/ipfs/QmWEekX7EZLUd9VXRNMRXW3LXe4F6x7mB8oPxY5XLptrBq/docs/readme.md',
28+
output: '/ipfs/QmWEekX7EZLUd9VXRNMRXW3LXe4F6x7mB8oPxY5XLptrBq/docs/readme.md'
29+
},
30+
'/ipns/CID path': {
31+
input: '/ipns/k51qzi5uqu5djni72pr40dt64kxlh0zb8baat8h7dtdvkov66euc2lho0oidr3',
32+
output: '/ipns/k51qzi5uqu5djni72pr40dt64kxlh0zb8baat8h7dtdvkov66euc2lho0oidr3'
33+
},
34+
2535
// peer id input
2636
'Ed25519 PeerId': {
2737
input: peerIdFromString('12D3KooWKBpVwnRACfEsk6QME7dA5CZnFYVHQ7Zc927BEzuUekQe'),

0 commit comments

Comments
 (0)