Skip to content
This repository has been archived by the owner on Aug 11, 2021. It is now read-only.

feat: add flow annotations #27

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than having this file (.babelrc) and .flowconfig, could we have these in a separate repository and import here? Would be a lot better, otherwise this is gonna be duplicated everywhere.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think ideally we should move these into AEgir

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sadly .flowconfig isn't js or json so you can't really pull it from elsewhere (unless you're thinking of git submodules which aren't worth the hassle IMO).

As of .babelrc indeed this probably should end up in AEgir.

"env": {
"development": {
"sourceMaps": "inline",
"comments": false,
"presets": [
[
"env",
{
"targets": {
"node": "current"
}
}
],
"flow-node"
]
},
"umd": {
"comments": false,
"presets": [
[
"env",
{
"modules": false,
"targets": {
"browsers": "last 2 versions"
}
}
],
"flow-node"
]
}
}
}
9 changes: 9 additions & 0 deletions .flowconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[ignore]
.*/node_modules/documentation/*

[libs]

[include]

[options]
suppress_comment= \\(.\\|\n\\)*\\@FlowIgnore
31 changes: 25 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,32 @@
"description": "multiple hash functions",
"main": "src/index.js",
"scripts": {
"lint": "aegir lint",
"build": "aegir build",
"test": "aegir test -t node -t browser -t webworker",
"lint": "npm run type-check && aegir lint",
"type-check": "flow check",
"build": "npm run build:node && BABEL_ENV=umd aegir build",
"test": "npm run build:node && aegir test -t node -t browser -t webworker",
"test:node": "aegir test -t node",
"test:browser": "aegir test -t browser",
"test:webworker": "aegir test -t webworker",
"release": "aegir release -t node -t browser",
"release-minor": "aegir release --type minor -t node -t browser",
"release-major": "aegir release --type major -t node -t browser",
"coverage": "aegir coverage",
"coverage-publish": "aegir coverage --provider coveralls"
"coverage-publish": "aegir coverage --provider coveralls",
"build:types": "flow-copy-source --verbose src lib",
"build:lib": "babel --out-dir lib src",
"build:node": "npm run build:types && npm run build:lib",
"start": "flow-copy-source --watch --verbose src lib & babel --watch --out-dir lib src"
},
"pre-commit": [
"lint",
"test"
],
"standard": {
"ignore": [
"dist"
]
},
"repository": {
"type": "git",
"url": "git://github.com/multiformats/js-multihashing.git"
Expand All @@ -33,16 +43,25 @@
"url": "https://github.com/multiformats/js-multihashing/issues"
},
"dependencies": {
"babel-cli": "^6.26.0",
"babel-core": "^6.26.0",
"babel-loader": "^7.1.4",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those 3 are devDependencies aren't they?

"blakejs": "^1.1.0",
"js-sha3": "^0.7.0",
"multihashes": "~0.4.12",
"webcrypto": "~0.1.1"
},
"devDependencies": {
"aegir": "^12.3.0",
"aegir": "git+https://github.com/ipfs/aegir.git#flow",
"babel-preset-flow-node": "^2.0.1",
"chai": "^4.1.2",
"dirty-chai": "^2.0.1",
"pre-commit": "^1.2.2"
"flow-bin": "^0.69.0",
"flow-copy-source": "^1.3.0",
"lint-staged": "^7.0.2",
"pre-commit": "^1.2.2",
"rollup.config.flow": "^1.0.0",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as multiformats/js-multihash#47 (review). No need for rollup I guess.

"source-map-support": "^0.5.4"
},
"homepage": "https://github.com/multiformats/js-multihashing",
"contributors": [
Expand Down
61 changes: 42 additions & 19 deletions src/blake.js
Original file line number Diff line number Diff line change
@@ -1,56 +1,79 @@
'use strict'
// @flow
import * as blake from 'blakejs'
import type { Hash, HashUpdate, HashTable, HashBuilder } from './types'
import type { Code } from 'multihashes/lib/constants'

const blake = require('blakejs')
const minB = 0xb201
const minS = 0xb241
const minB: Code = 0xb201
const minS: Code = 0xb241

var blake2b = {
type BlakeCtx = {
b: Uint8Array,
h: Uint32Array,
t: number,
c: number,
outlen: number
}
type Blake2Hash = Buffer
type BlakeHasher = {
init(size: number, key: ?number): BlakeCtx,
update(ctx: BlakeCtx, input: Uint8Array): void,
digest(ctx: BlakeCtx): Uint8Array
}

const blake2b: BlakeHasher = {
init: blake.blake2bInit,
update: blake.blake2bUpdate,
digest: blake.blake2bFinal
}

var blake2s = {
const blake2s: BlakeHasher = {
init: blake.blake2sInit,
update: blake.blake2sUpdate,
digest: blake.blake2sFinal
}

class B2Hash {
class B2Hash implements Hash {
ctx: BlakeCtx | null
hf: BlakeHasher

constructor (size, hashFunc) {
this.hf = hashFunc
this.ctx = this.hf.init(size, null)
}

update (buf) {
static new (size, hashFunc): HashUpdate {
return new B2Hash(size, hashFunc)
}

update (buf: Buffer): Hash {
if (this.ctx === null) {
throw new Error('blake2 context is null. (already called digest?)')
}
this.hf.update(this.ctx, buf)
return this
}

digest () {
digest (): Blake2Hash {
const ctx = this.ctx
this.ctx = null
if (ctx === null) {
throw Error('blake2 context is null. (already called digest?)')
}
return Buffer.from(this.hf.digest(ctx))
}
}

function addFuncs (table) {
function mkFunc (size, hashFunc) {
return () => new B2Hash(size, hashFunc)
export const addFuncs = (table: HashTable) => {
const mkFunc = (size: number, hashFunc: BlakeHasher): HashBuilder => {
return (): HashUpdate => B2Hash.new(size, hashFunc)
}

var i
// I don't like using any here but the only way I could get the types to work here.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More out of curiosity - what was the problem? Shouldn't Code be just an alias for number?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They were opaque type aliases which behave like nominal types. In that module context any number can be treated as Code but outside of that context Code is a subtype of number and only way to get hold of value of that tipe is by getting it from that module either from exposed constant annotated as Code or via function that returns Code. This gives you a way to say have parseCode : number -> ?Code function that can return passed value back but ensuring that it’s a valid Code. Kind of typically you’d have isCode function but such that type checker then enforce typings

let i
for (i = 0; i < 64; i++) {
table[minB + i] = mkFunc(i + 1, blake2b)
table[(minB + i: any)] = mkFunc(i + 1, blake2b)
}
for (i = 0; i < 32; i++) {
table[minS + i] = mkFunc(i + 1, blake2s)
table[(minS + i: any)] = mkFunc(i + 1, blake2s)
}
}

module.exports = {
addFuncs: addFuncs
}
51 changes: 32 additions & 19 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,34 @@
'use strict'
// @flow

const multihash = require('multihashes')
const blake = require('./blake')
const sha3 = require('./sha3')
const crypto = require('webcrypto')
import * as multihash from 'multihashes'
import type { Multihash } from 'multihashes'
import type { Name, Code } from 'multihashes/lib/constants'
import type { HashUpdate, HashTable } from './types'
import * as blake from './blake'
import * as sha3 from './sha3'
import * as crypto from 'webcrypto'

const mh = module.exports = Multihashing
const mh = Multihashing
export default mh

mh.Buffer = Buffer // for browser things

function Multihashing (buf, func, length) {
function Multihashing (
buf: Buffer,
func: Name | Code,
length: number
): Multihash {
return multihash.encode(mh.digest(buf, func, length), func, length)
}

// expose multihash itself, to avoid silly double requires.
mh.multihash = multihash

mh.digest = function (buf, func, length) {
let digest = mh.createHash(func).update(buf).digest()
mh.digest = function (buf: Buffer, func: Name | Code, length: ?number): Buffer {
let digest = mh
.createHash(func)
.update(buf)
.digest()

if (length) {
digest = digest.slice(0, length)
Expand All @@ -26,7 +37,7 @@ mh.digest = function (buf, func, length) {
return digest
}

mh.createHash = function (func, length) {
mh.createHash = function (func: Name | Code): HashUpdate {
func = multihash.coerceCode(func)
if (!mh.functions[func]) {
throw new Error('multihash function ' + func + ' not yet supported')
Expand All @@ -35,29 +46,31 @@ mh.createHash = function (func, length) {
return mh.functions[func]()
}

mh.verify = function (hash, buf) {
mh.verify = function verify (hash: Multihash, buf: Buffer): boolean {
const decoded = multihash.decode(hash)
const encoded = mh(buf, decoded.name, decoded.length)
return encoded.equals(hash)
}

mh.functions = {
0x11: gsha1,
0x12: gsha2256,
0x13: gsha2512
}
/* eslint-disable no-useless-computed-key */
mh.functions = ({
[0x11]: gsha1,
[0x12]: gsha2256,
[0x13]: gsha2512
}: HashTable)
/* eslint-enable no-useless-computed-key */

blake.addFuncs(mh.functions)
sha3.addFuncs(mh.functions)

function gsha1 () {
function gsha1 (): HashUpdate {
return crypto.createHash('sha1')
}

function gsha2256 () {
function gsha2256 (): HashUpdate {
return crypto.createHash('sha256')
}

function gsha2512 () {
function gsha2512 (): HashUpdate {
return crypto.createHash('sha512')
}
51 changes: 32 additions & 19 deletions src/sha3.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict'

const sha3 = require('js-sha3')
// @flow
import * as sha3 from 'js-sha3'
import type { Hash, HashTable, HashUpdate } from './types'

const functions = [
[0x14, sha3.sha3_512],
Expand All @@ -9,44 +9,57 @@ const functions = [
[0x17, sha3.sha3_224],
[0x18, sha3.shake128, 256],
[0x19, sha3.shake256, 512],
[0x1A, sha3.keccak224],
[0x1B, sha3.keccak256],
[0x1C, sha3.keccak384],
[0x1D, sha3.keccak512]
[0x1a, sha3.keccak224],
[0x1b, sha3.keccak256],
[0x1c, sha3.keccak384],
[0x1d, sha3.keccak512]
]

class Hasher {
constructor (hashFunc, arg) {
type HexString = string
type Sha3Hash = Buffer
type ShaHasher = (input: string | Buffer, length?: number) => HexString

class ShaHash implements Hash {
hf: ShaHasher
input: Buffer | null
arg: number

constructor (hashFunc, arg?: number) {
this.hf = hashFunc
this.arg = arg
if (arg) {
this.arg = arg
}
this.input = null
}

update (buf) {
static new (hashFunc, arg?: number): HashUpdate {
return new ShaHash(hashFunc, arg)
}

update (buf: Buffer): Hash {
this.input = buf
return this
}

digest () {
digest (): Sha3Hash {
if (!this.input) {
throw Error('Missing an input to hash')
}
const input = this.input
const arg = this.arg
return Buffer.from(this.hf(input, arg), 'hex')
}
}

function addFuncs (table) {
export const addFuncs = (table: HashTable) => {
for (const info of functions) {
const code = info[0]
const fn = info[1]

if (info.length === 3) {
table[code] = () => new Hasher(fn, info[2])
table[code] = () => ShaHash.new(fn, info[2])
} else {
table[code] = () => new Hasher(fn)
table[code] = () => ShaHash.new(fn)
}
}
}

module.exports = {
addFuncs: addFuncs
}
Loading