Skip to content

Commit

Permalink
vuuuu
Browse files Browse the repository at this point in the history
  • Loading branch information
hacdias committed Aug 2, 2023
1 parent 15c245e commit 430cd08
Show file tree
Hide file tree
Showing 8 changed files with 173 additions and 184 deletions.
61 changes: 25 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
[![codecov](https://img.shields.io/codecov/c/github/ipfs/js-ipns.svg?style=flat-square)](https://codecov.io/gh/ipfs/js-ipns)
[![CI](https://img.shields.io/github/actions/workflow/status/ipfs/js-ipns/js-test-and-release.yml?branch=master\&style=flat-square)](https://github.com/ipfs/js-ipns/actions/workflows/js-test-and-release.yml?query=branch%3Amaster)

> ipns record definitions
> IPNS Record definitions.
## Table of contents <!-- omit in toc -->

Expand Down Expand Up @@ -54,15 +54,15 @@ This module contains all the necessary code for creating, understanding and vali
```js
import * as ipns from 'ipns'

const entryData = await ipns.create(privateKey, value, sequenceNumber, lifetime)
const ipnsRecord = await ipns.create(privateKey, value, sequenceNumber, lifetime)
```

### Validate record

```js
import * as ipns from 'ipns'

await ipns.validate(publicKey, ipnsEntry)
await ipns.validate(publicKey, ipnsRecord)
// if no error thrown, the record is valid
```

Expand All @@ -71,15 +71,15 @@ await ipns.validate(publicKey, ipnsEntry)
```js
import * as ipns from 'ipns'

const ipnsEntryWithEmbedPublicKey = await ipns.embedPublicKey(publicKey, ipnsEntry)
const ipnsRecordWithEmbeddedPublicKey = await ipns.embedPublicKey(publicKey, ipnsRecord)
```

### Extract public key from record

```js
import * as ipns from 'ipns'

const publicKey = await ipns.extractPublicKey(peerId, ipnsEntry)
const publicKey = await ipns.extractPublicKey(peerId, ipnsRecord)
```

### Datastore key
Expand All @@ -90,7 +90,7 @@ import * as ipns from 'ipns'
ipns.getLocalKey(peerId)
```

Returns a key to be used for storing the ipns entry locally, that is:
Returns a key to be used for storing the IPNS record locally, that is:

/ipns/${base32(<HASH>)}

Expand All @@ -99,23 +99,23 @@ Returns a key to be used for storing the ipns entry locally, that is:
```js
import * as ipns from 'ipns'

const entryData = await ipns.create(privateKey, value, sequenceNumber, lifetime)
const ipnsRecord = await ipns.create(privateKey, value, sequenceNumber, lifetime)
// ...
const marshalledData = ipns.marshal(entryData)
const marshalledData = ipns.marshal(ipnsRecord)
// ...
```

Returns the entry data serialized.
Returns the record data serialized.

### Unmarshal data from proto buffer

```js
import * as ipns from 'ipns'

const data = ipns.unmarshal(storedData)
const ipnsRecord = ipns.unmarshal(storedData)
```

Returns the entry data structure after being serialized.
Returns the `IPNSRecord` after being serialized.

### Validator

Expand All @@ -129,15 +129,15 @@ Contains an object with `validate (marshalledData, key)` and `select (dataA, dat

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).

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`.
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`.

## API

### Create record

```js

ipns.create(privateKey, value, sequenceNumber, lifetime)
ipns.create(privateKey, value, sequenceNumber, lifetime, options)
```

Create an IPNS record for being stored in a protocol buffer.
Expand All @@ -146,64 +146,53 @@ Create an IPNS record for being stored in a protocol buffer.
- `value` (Uint8Array): ipfs path of the object to be published.
- `sequenceNumber` (Number): number representing the current version of the record.
- `lifetime` (Number): lifetime of the record (in milliseconds).
- `options` (CreateOptions): additional creation options.

Returns a `Promise` that resolves to an object with the entry's properties eg:

```js
{
value: Uint8Array,
signature: Uint8Array, // V1 (legacy, ignored)
validityType: 0,
validity: Uint8Array,
sequence: 2,
signatureV2: Uint8Array, // V2 signature of data field
data: Uint8Array // DAG-CBOR that was signed
}
```
Returns a `Promise` that resolves to an object with a `IPNSRecord`.

### Validate record

```js
ipns.validate(publicKey, ipnsEntry)
ipns.validate(publicKey, ipnsRecord)
```

Validate an IPNS record previously stored in a protocol buffer.

- `publicKey` (`PubKey` [RSA Instance](https://github.com/libp2p/js-libp2p-crypto/blob/master/src/keys/rsa-class.js)): key to be used for cryptographic operations.
- `ipnsEntry` (Object): ipns entry record (obtained using the create function).
- `ipnsRecord` (`IPNSRecord`): ipns record record (obtained using the create function).

Returns a `Promise`, which may be rejected if the validation was not successful.

### Marshal data with proto buffer

```js
const marshalledData = ipns.marshal(entryData)
const marshalledData = ipns.marshal(ipnsRecord)
```

Returns the entry data serialized.
Returns the serialized IPNS record.

- `entryData` (Object): ipns entry record (obtained using the create function).
- `ipnsRecord` (`IPNSRecord`): ipns record (obtained using the create function).

### Unmarshal data from proto buffer

```js
const data = ipns.unmarshal(storedData)
```

Returns the entry data structure after being serialized.
Returns a `IPNSRecord` after being serialized.

- `storedData` (Uint8Array): ipns entry record serialized.
- `storedData` (Uint8Array): ipns record serialized.

### Extract public key from record

```js
const publicKey = await ipns.extractPublicKey(peerId, ipnsEntry)
const publicKey = await ipns.extractPublicKey(peerId, ipnsRecord)
```

Extract a public key from an IPNS entry.
Extract a public key from an IPNS record.

- `peerId` (`PeerId` [Instance](https://github.com/libp2p/js-libp2p-peer-id/tree/master/packages/libp2p-peer-id)): peer identifier object.
- `ipnsEntry` (Object): ipns entry record (obtained using the create function).
- `ipnsRecord` (`IPNSRecord`): ipns record (obtained using the create function).

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.

Expand Down
32 changes: 16 additions & 16 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
import * as ERRORS from './errors.js'
import { IpnsEntry } from './pb/ipns.js'
import { createCborData, ipnsEntryDataForV1Sig, ipnsEntryDataForV2Sig, parseRFC3339 } from './utils.js'
import { createCborData, ipnsRecordDataForV1Sig, ipnsRecordDataForV2Sig, parseRFC3339 } from './utils.js'
import type { PrivateKey } from '@libp2p/interface-keys'
import type { PeerId } from '@libp2p/interface-peer-id'

Expand Down Expand Up @@ -83,8 +83,8 @@ const defaultCreateOptions: CreateOptions = {
}

/**
* Creates a new ipns entry and signs it with the given private key.
* The ipns entry validity should follow the [RFC3339]{@link https://www.ietf.org/rfc/rfc3339.txt} with nanoseconds precision.
* Creates a new IPNS record and signs it with the given private key.
* The IPNS Record validity should follow the [RFC3339]{@link https://www.ietf.org/rfc/rfc3339.txt} with nanoseconds precision.
* Note: This function does not embed the public key. If you want to do that, use `EmbedPublicKey`.
*
* @param {PeerId} peerId - peer id containing private key for signing the record.
Expand All @@ -111,7 +111,7 @@ export const create = async (peerId: PeerId, value: Uint8Array, seq: number | bi
* @param {Uint8Array} value - value to be stored in the record.
* @param {number | bigint} seq - number representing the current version of the record.
* @param {string} expiration - expiration datetime for record in the [RFC3339]{@link https://www.ietf.org/rfc/rfc3339.txt} with nanoseconds precision.
* @param {CreateOptions} options - additional create options.
* @param {CreateOptions} options - additional creation options.
*/
export const createWithExpiration = async (peerId: PeerId, value: Uint8Array, seq: number | bigint, expiration: string, options: CreateOptions = defaultCreateOptions): Promise<IPNSRecord> => {
const expirationDate = NanoDate.fromString(expiration)
Expand All @@ -133,22 +133,22 @@ const _create = async (peerId: PeerId, value: Uint8Array, seq: number | bigint,

const privateKey = await unmarshalPrivateKey(peerId.privateKey)
const data = createCborData(value, isoValidity, validityType, seq, ttl)
const sigData = ipnsEntryDataForV2Sig(data)
const sigData = ipnsRecordDataForV2Sig(data)
const signatureV2 = await privateKey.sign(sigData)

const entry: IpnsEntry = {
const pb: IpnsEntry = {
signatureV2,
data
}

if (options.v1Compatible === true) {
const signatureV1 = await signLegacyV1(privateKey, value, validityType, isoValidity)
entry.value = value
entry.validity = isoValidity
entry.validityType = validityType
entry.signature = signatureV1
entry.sequence = seq
entry.ttl = ttl
pb.value = value
pb.validity = isoValidity
pb.validityType = validityType
pb.signature = signatureV1
pb.sequence = seq
pb.ttl = ttl
}

// if we cannot derive the public key from the PeerId (e.g. RSA PeerIDs),
Expand All @@ -157,12 +157,12 @@ const _create = async (peerId: PeerId, value: Uint8Array, seq: number | bigint,
const digest = Digest.decode(peerId.toBytes())

if (digest.code !== ID_MULTIHASH_CODE || !uint8ArrayEquals(peerId.publicKey, digest.digest)) {
entry.pubKey = peerId.publicKey
pb.pubKey = peerId.publicKey
}
}

log('ipns entry for %b created', value)
return new IPNSRecord(entry)
log('ipns record for %b created', value)
return new IPNSRecord(pb)
}

/**
Expand All @@ -189,7 +189,7 @@ export { extractPublicKey } from './utils.js'
*/
const signLegacyV1 = async (privateKey: PrivateKey, value: Uint8Array, validityType: IpnsEntry.ValidityType, validity: Uint8Array): Promise<Uint8Array> => {
try {
const dataForSignature = ipnsEntryDataForV1Sig(value, validityType, validity)
const dataForSignature = ipnsRecordDataForV1Sig(value, validityType, validity)

return await privateKey.sign(dataForSignature)
} catch (error: any) {
Expand Down
18 changes: 9 additions & 9 deletions src/selector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@ import type { SelectFn } from '@libp2p/interface-dht'

export const ipnsSelector: SelectFn = (key, data) => {
const entries = data.map((buf, index) => ({
entry: unmarshal(buf),
record: unmarshal(buf),
index
}))

entries.sort((a, b) => {
// having a newer signature version is better than an older signature version
if (a.entry.pb.signatureV2 != null && b.entry.pb.signatureV2 == null) {
if (a.record.pb.signatureV2 != null && b.record.pb.signatureV2 == null) {
return -1
} else if (a.entry.pb.signatureV2 == null && b.entry.pb.signatureV2 != null) {
} else if (a.record.pb.signatureV2 == null && b.record.pb.signatureV2 != null) {
return 1
}

const aSeq = a.entry.sequence()
const bSeq = b.entry.sequence()
const aSeq = a.record.sequence()
const bSeq = b.record.sequence()

// choose later sequence number
if (aSeq > bSeq) {
Expand All @@ -26,14 +26,14 @@ export const ipnsSelector: SelectFn = (key, data) => {
}

// choose longer lived record if sequence numbers the same
const entryAValidityDate = a.entry.validity()
const entryBValidityDate = b.entry.validity()
const recordAValidityDate = a.record.validity()
const recordBValidityDate = b.record.validity()

if (entryAValidityDate.getTime() > entryBValidityDate.getTime()) {
if (recordAValidityDate.getTime() > recordBValidityDate.getTime()) {
return -1
}

if (entryAValidityDate.getTime() < entryBValidityDate.getTime()) {
if (recordAValidityDate.getTime() < recordBValidityDate.getTime()) {
return 1
}

Expand Down
14 changes: 7 additions & 7 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ export function parseRFC3339 (time: string): Date {
* Extracts a public key from the passed PeerId, falling
* back to the pubKey embedded in the ipns record
*/
export const extractPublicKey = async (peerId: PeerId, entry: IPNSRecord): Promise<PublicKey> => {
if (entry == null || peerId == null) {
export const extractPublicKey = async (peerId: PeerId, record: IPNSRecord): Promise<PublicKey> => {
if (record == null || peerId == null) {
const error = new Error('one or more of the provided parameters are not defined')

log.error(error)
Expand All @@ -75,15 +75,15 @@ export const extractPublicKey = async (peerId: PeerId, entry: IPNSRecord): Promi

let pubKey: PublicKey | undefined

if (entry.pb.pubKey != null) {
if (record.pb.pubKey != null) {
try {
pubKey = unmarshalPublicKey(entry.pb.pubKey)
pubKey = unmarshalPublicKey(record.pb.pubKey)
} catch (err) {
log.error(err)
throw err
}

const otherId = await peerIdFromKeys(entry.pb.pubKey)
const otherId = await peerIdFromKeys(record.pb.pubKey)

if (!otherId.equals(peerId)) {
throw errCode(new Error('Embedded public key did not match PeerID'), ERRORS.ERR_INVALID_EMBEDDED_KEY)
Expand All @@ -102,7 +102,7 @@ export const extractPublicKey = async (peerId: PeerId, entry: IPNSRecord): Promi
/**
* Utility for creating the record data for being signed
*/
export const ipnsEntryDataForV1Sig = (value: Uint8Array, validityType: IpnsEntry.ValidityType, validity: Uint8Array): Uint8Array => {
export const ipnsRecordDataForV1Sig = (value: Uint8Array, validityType: IpnsEntry.ValidityType, validity: Uint8Array): Uint8Array => {
const validityTypeBuffer = uint8ArrayFromString(validityType)

return uint8ArrayConcat([value, validity, validityTypeBuffer])
Expand All @@ -111,7 +111,7 @@ export const ipnsEntryDataForV1Sig = (value: Uint8Array, validityType: IpnsEntry
/**
* Utility for creating the record data for being signed
*/
export const ipnsEntryDataForV2Sig = (data: Uint8Array): Uint8Array => {
export const ipnsRecordDataForV2Sig = (data: Uint8Array): Uint8Array => {
const entryData = uint8ArrayFromString('ipns-signature:')

return uint8ArrayConcat([entryData, data])
Expand Down
Loading

0 comments on commit 430cd08

Please sign in to comment.