Skip to content

Commit

Permalink
Merge 301a64b into 79f15d5
Browse files Browse the repository at this point in the history
  • Loading branch information
yusefnapora committed Feb 2, 2017
2 parents 79f15d5 + 301a64b commit b11e475
Show file tree
Hide file tree
Showing 23 changed files with 732 additions and 283 deletions.
2 changes: 1 addition & 1 deletion .flowconfig
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@

[options]
suppress_comment= \\(.\\|\n\\)*\\$FlowIssue

unsafe.enable_getters_and_setters=true
6 changes: 3 additions & 3 deletions integration-test/push_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const { getTestNodeId } = require('../test/util')
const { MediachainNode: AlephNode } = require('../src/peer/node')
const { concatNodeClient, concatNodePeerInfo } = require('./util')
const { PublisherId } = require('../src/peer/identity')
const { makeSimpleStatement } = require('../src/metadata/statement')
const { Statement } = require('../src/model/statement')

const TEST_NAMESPACE = 'scratch.push-test'
const UNAUTHORIZED_NAMESPACE = 'scratch.unauthorized-push-test'
Expand Down Expand Up @@ -38,14 +38,14 @@ function preparePartiallyValidStatements (alephNode: AlephNode, numValid: number
.then(([object]) => {
const promises = []
for (let i = 0; i < numValid; i++) {
promises.push(makeSimpleStatement(alephNode.publisherId, TEST_NAMESPACE, {
promises.push(Statement.createSimple(alephNode.publisherId, TEST_NAMESPACE, {
object,
refs: [`test:${i.toString()}`]
},
alephNode.statementCounter))
}
// add a statement with an invalid object reference
promises.push(makeSimpleStatement(alephNode.publisherId, TEST_NAMESPACE, {
promises.push(Statement.createSimple(alephNode.publisherId, TEST_NAMESPACE, {
object: 'QmNLftPEMzsadpbTsGaVP3haETYJb4GfnCgQiaFj5Red9G', refs: [], deps: [], tags: []
}))
return Promise.all(promises)
Expand Down
2 changes: 1 addition & 1 deletion integration-test/query_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describe('Query', () => {
assert(results != null && results.length > 0, 'query returned no results')

// unpack query results and compare to seed statements
const resultStatements = results.map(r => r.value.simple.stmt.body.simple)
const resultStatements = results.map(r => r.body.toProtobuf())
assert.deepEqual(seedStatements, resultStatements, 'query returned unexpected results')
})
})
Expand Down
44 changes: 36 additions & 8 deletions src/common/util.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @flow

const _ = require('lodash')
const Multihashing = require('multihashing')
import type { Writable, Readable } from 'stream'
import type { WriteStream } from 'tty'
Expand Down Expand Up @@ -49,19 +50,31 @@ function flatMap<T, U> (array: Array<T>, f: (x: T) => Array<U>): Array<U> {
}
/**
* Given two `Set`s, return a new `Set` that contains all elements from both.
* @param {Set} a
* @param {Set} b
* Given any number of `Set`s, return a new `Set` that contains all elements combined.
* @param
* @returns {Set} - the union of `a` and `b`
*/
function setUnion<T> (a: Set<T>, b: Set<T>): Set<T> {
const u = new Set(a)
for (const elem of b) {
u.add(elem)
function setUnion<T> (...sets: Array<Set<T>>): Set<T> {
const u = new Set()
for (const s of sets) {
for (const elem of s) {
u.add(elem)
}
}
return u
}

/**
* Returns true if Set `a` and Set `b` contain the same members, using strict (shallow) equality (the `===` operator)
*/
function setEquals<T> (a: Set<T>, b: Set<T>): boolean {
if (a.size !== b.size) return false
for (const elem of a.values()) {
if (!b.has(elem)) return false
}
return true
}

/**
* Returns base58-encoded sha256 multihash for the given Buffer
* @param buf - a `Buffer` you want to hash
Expand Down Expand Up @@ -132,15 +145,30 @@ function consumeStream (stream: Readable): Promise<string> {
})
}

/**
* Returns a clone of `obj` with all `Buffer` objects replaced with their base64-encoded string equivalents
*/
function stringifyNestedBuffers (obj: Object): Object {
const replacer = obj => {
if (obj instanceof Buffer) {
return obj.toString('base64')
}
}

return (_.cloneDeepWith(obj, replacer): any)
}

module.exports = {
promiseHash,
promiseTimeout,
flatMap,
setUnion,
setEquals,
b58MultihashForBuffer,
isB58Multihash,
writeln,
println,
printlnErr,
consumeStream
consumeStream,
stringifyNestedBuffers
}
31 changes: 19 additions & 12 deletions src/metadata/signatures.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,44 +3,51 @@
const { omit, cloneDeep } = require('lodash')
const pb = require('../protobuf')
const { PublicSigningKey } = require('../peer/identity')
const { Statement } = require('../model/statement')

import type { PublisherId } from '../peer/identity'
import type { StatementMsg } from '../protobuf/types'

function signStatement (stmt: StatementMsg, publisherId: PublisherId): Promise<StatementMsg> {
function signStatement (stmt: StatementMsg | Statement, publisherId: PublisherId): Promise<StatementMsg> {
// clone the original message, removing any existing signature
const result = omit(cloneDeep(stmt), 'signature')
return calculateSignature(result, publisherId).then((sig) => {
result.signature = sig
return result
const msg: Object = (stmt instanceof Statement) ? stmt.toProtobuf() : cloneDeep(stmt)
msg.signature = undefined

return calculateSignature(msg, publisherId).then((sig) => {
msg.signature = sig
return msg
})
}

function calculateSignature (stmt: StatementMsg, publisherId: PublisherId): Promise<Buffer> {
function calculateSignature (stmt: StatementMsg | Statement, publisherId: PublisherId): Promise<Buffer> {
const msg: StatementMsg = (stmt instanceof Statement) ? stmt.toProtobuf() : stmt

return Promise.resolve().then(() => {
const bytes = pb.stmt.Statement.encode(stmt)
const bytes = pb.stmt.Statement.encode(msg)
// sign the encoded statement message and set the signature
return publisherId.sign(bytes)
})
}

function verifyStatement (stmt: StatementMsg): Promise<boolean> {
function verifyStatement (stmt: StatementMsg | Statement): Promise<boolean> {
return Promise.resolve()
.then(() => PublicSigningKey.fromB58String(stmt.publisher))
.then(pubKey => verifyStatementSignature(stmt, pubKey))
}

function verifyStatementSignature (stmt: StatementMsg, publicKey: PublicSigningKey): Promise<boolean> {
function verifyStatementSignature (stmt: StatementMsg | Statement, publicKey: PublicSigningKey): Promise<boolean> {
const msg: StatementMsg = (stmt instanceof Statement) ? stmt.toProtobuf() : stmt

return Promise.resolve()
.then(() => {
const sig = stmt.signature
const withoutSig = omit(cloneDeep(stmt), 'signature')
const sig = msg.signature
const withoutSig = omit(cloneDeep(msg), 'signature')
const bytes = pb.stmt.Statement.encode(withoutSig)
return publicKey.verify(bytes, sig)
})
}

function verifyStatementWithKeyCache (stmt: StatementMsg, cache: Map<string, PublicSigningKey>): Promise<boolean> {
function verifyStatementWithKeyCache (stmt: StatementMsg | Statement, cache: Map<string, PublicSigningKey>): Promise<boolean> {
return Promise.resolve()
.then(() => {
const maybeKey = cache.get(stmt.publisher)
Expand Down
93 changes: 0 additions & 93 deletions src/metadata/statement.js

This file was deleted.

76 changes: 76 additions & 0 deletions src/model/query_result.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// @flow

const { Statement, StatementBody } = require('./statement')
import type { QueryResultMsg, QueryResultValueMsg, SimpleValueMsg, CompoundValueMsg } from '../protobuf/types'

export type QueryResult = QueryResultValue | Error
export type QueryResultValue = SimpleQueryResultValue | CompoundQueryResultValue
export type SimpleQueryResultValue = number | string | Statement | StatementBody

function unpackQueryResultProtobuf (msg: QueryResultMsg): QueryResult {
if (msg.error != null) {
const errorMsg = msg.error.error || 'Unknown error'
return new Error(errorMsg)
}
if (msg.value != null) {
return unpackQueryResultValueProtobuf((msg.value: any))
}
throw new Error('Unexpected query result: ' + JSON.stringify(msg))
}

function unpackQueryResultValueProtobuf (msg: QueryResultValueMsg): QueryResultValue {
if (msg.simple != null) {
return unpackSimpleValue((msg.simple: any))
}
if (msg.compound != null) {
return CompoundQueryResultValue.fromProtobuf((msg.compound: any))
}
throw new Error('Unexpected query result value ' + JSON.stringify(msg))
}

function unpackSimpleValue (val: SimpleValueMsg): SimpleQueryResultValue {
if (val.stringValue != null) return (val.stringValue: any)
if (val.intValue != null) return (val.intValue: any)
if (val.stmt != null) return Statement.fromProtobuf((val.stmt: any))
if (val.stmtBody != null) return StatementBody.fromProtobuf((val.stmtBody: any))

throw new Error('Unexpected query result value: ' + JSON.stringify(val))
}

type KVPair = {key: string, value: SimpleQueryResultValue}
class CompoundQueryResultValue {
body: Array<KVPair>

constructor (body: Array<KVPair>) {
this.body = body
}

static fromProtobuf (msg: CompoundValueMsg): CompoundQueryResultValue {
return new CompoundQueryResultValue(
msg.body.map(kv => ({
key: kv.key,
value: unpackSimpleValue(kv.value)
}))
)
}

keys (): Array<string> {
return this.body.map(kv => kv.key)
}

values (): Array<SimpleQueryResultValue> {
return this.body.map(kv => kv.value)
}

statements (): Array<Statement> {
return this.values()
.filter(v => v instanceof Statement)
.map(v => (v: any)) // dammit flow
}
}

module.exports = {
unpackQueryResultProtobuf,
unpackQueryResultValueProtobuf,
CompoundQueryResultValue
}

0 comments on commit b11e475

Please sign in to comment.