Adding Flow files to dist

This commit is contained in:
Karel Bilek
2016-07-23 19:47:33 +02:00
parent 294862de89
commit f0366b7466
13 changed files with 1928 additions and 1 deletions

View File

@@ -7,5 +7,7 @@ node_modules:
build:
rm -rf dist
mkdir -p dist
cp -r src/ dist
find dist/ -type f ! -name '*.js' | xargs -I {} rm {}
find dist/ -name '*.js' | xargs -I {} mv {} {}.flow
`npm bin`/browserify src/index.js > dist/index.js

23
packages/transport/dist/defered.js.flow vendored Normal file
View File

@@ -0,0 +1,23 @@
/* @flow */
"use strict";
export type Defered = {
promise: Promise<void>;
resolve: () => void;
reject: (e: Error) => void;
};
export function create(): Defered {
let _resolve: () => void = () => {};
let _reject: (e: Error) => void = (e) => {};
const promise = new Promise((resolve, reject) => {
_resolve = resolve;
_reject = reject;
});
return {
promise,
resolve: _resolve,
reject: _reject,
};
}

View File

@@ -0,0 +1,217 @@
/* @flow */
"use strict";
import type {TrezorDeviceInfo, Transport} from '../transport';
import {create as createDefered} from '../defered';
import {parseConfigure} from '../protobuf/parse_protocol';
import {verifyHexBin} from './verify';
import {buildAndSend} from './send';
import {receiveAndParse} from './receive';
import type {Defered} from '../defered';
import type {Messages} from '../protobuf/messages';
export type MessageFromTrezor = {type: string, message: Object};
// eslint-disable-next-line quotes
const stringify = require('json-stable-stringify');
type TrezorDeviceInfoWithSession = TrezorDeviceInfo & {
session: ?string;
}
type InternalAcquireInput = {
path: string;
previous: ?string;
checkPrevious: boolean;
}
type AcquireInput = {
path: string;
previous: ?string;
} | string;
function parseAcquireInput(input: AcquireInput): InternalAcquireInput {
// eslint-disable-next-line quotes
if (typeof input !== 'string') {
const path = input.path.toString();
const previous = input.previous == null ? null : input.previous.toString();
return {
path,
previous,
checkPrevious: true,
};
} else {
const path = input.toString();
return {
path,
previous: null,
checkPrevious: false,
};
}
}
function compare(a: TrezorDeviceInfoWithSession, b: TrezorDeviceInfoWithSession): number {
if (!isNaN(a.path)) {
return parseInt(a.path) - parseInt(b.path);
} else {
return a.path < a.path ? -1 : (a.path > a.path ? 1 : 0);
}
}
function timeoutPromise(delay: number): Promise<void> {
return new Promise((resolve) => {
window.setTimeout(() => resolve(), delay);
});
}
const ITER_MAX = 60;
const ITER_DELAY = 500;
export class Handler {
transport: Transport;
_lock: Promise<any> = Promise.resolve();
// path => promise rejecting on release
deferedOnRelease: {[path: string]: Defered} = {};
// path => session
connections: {[path: string]: string} = {};
// session => path
reverse: {[session: string]: string} = {};
_messages: ?Messages;
constructor(transport: Transport) {
this.transport = transport;
}
lock<X>(fn: () => (X|Promise<X>)): Promise<X> {
const res = this._lock.then(() => fn());
this._lock = res.catch(() => {});
return res;
}
enumerate(): Promise<Array<TrezorDeviceInfoWithSession>> {
return this.lock((): Promise<Array<TrezorDeviceInfoWithSession>> => {
return this.transport.enumerate().then((devices) => devices.map(device => {
return {
...device,
session: this.connections[device.path],
};
})).then(devices => {
this._releaseDisconnected(devices);
return devices;
}).then(devices => {
return devices.sort(compare);
});
});
}
_releaseDisconnected(devices: Array<TrezorDeviceInfoWithSession>) {
}
_lastStringified: string = ``;
listen(old: ?Array<TrezorDeviceInfoWithSession>): Promise<Array<TrezorDeviceInfoWithSession>> {
const oldStringified = stringify(old);
const last = old == null ? this._lastStringified : oldStringified;
return this._runIter(0, last);
}
_runIter(iteration: number, oldStringified: string): Promise<Array<TrezorDeviceInfoWithSession>> {
return this.enumerate().then(devices => {
const stringified = stringify(devices);
if ((stringified !== oldStringified) || (iteration === ITER_MAX)) {
this._lastStringified = stringified;
return devices;
}
return timeoutPromise(ITER_DELAY).then(() => this._runIter(iteration + 1, stringified));
});
}
_checkAndReleaseBeforeAcquire(parsed: InternalAcquireInput): Promise<any> {
const realPrevious = this.connections[parsed.path];
if (parsed.checkPrevious) {
let error = false;
if (realPrevious == null) {
error = (parsed.previous != null);
} else {
error = (parsed.previous !== realPrevious);
}
if (error) {
throw new Error(`wrong previous session`);
}
}
if (realPrevious != null) {
const releasePromise: Promise<void> = this._realRelease(parsed.path, realPrevious);
return releasePromise;
} else {
return Promise.resolve();
}
}
acquire(input: AcquireInput): Promise<string> {
const parsed = parseAcquireInput(input);
return this.lock((): Promise<string> => {
return this._checkAndReleaseBeforeAcquire(parsed).then(() =>
this.transport.connect(parsed.path)
).then((session: string) => {
this.connections[parsed.path] = session;
this.reverse[session] = parsed.path;
this.deferedOnRelease[parsed.path] = createDefered();
return session;
});
});
}
release(session: string): Promise<void> {
const path = this.reverse[session];
return this.lock(() => this._realRelease(path, session));
}
_realRelease(path:string, session: string): Promise<void> {
return this.transport.disconnect(path, session).then(() => {
this._releaseCleanup(session);
});
}
_releaseCleanup(session: string) {
const path: string = this.reverse[session];
delete this.reverse[session];
delete this.connections[path];
this.deferedOnRelease[path].reject(new Error(`Device released or disconnected`));
return;
}
configure(signedData: string): Promise<void> {
return verifyHexBin(signedData).then((data: Buffer): Messages => {
return parseConfigure(data);
}).then((messages: Messages) => {
this._messages = messages;
return;
});
}
_sendTransport(session: string): (data: ArrayBuffer) => Promise<void> {
const path: string = this.reverse[session];
return (data) => this.transport.send(path, session, data);
}
_receiveTransport(session: string): () => Promise<ArrayBuffer> {
const path: string = this.reverse[session];
return () => this.transport.receive(path, session);
}
call(session: string, name: string, data: Object): Promise<MessageFromTrezor> {
if (this._messages == null) {
return Promise.reject(new Error(`Handler not configured.`));
}
const messages = this._messages;
return buildAndSend(messages, this._sendTransport(session), name, data).then(() => {
return receiveAndParse(messages, this._receiveTransport(session));
});
}
}

View File

@@ -0,0 +1,112 @@
/* @flow */
"use strict";
// Logic of recieving data from trezor
// Logic of "call" is broken to two parts - sending and recieving
import {MessageDecoder} from "../protobuf/message_decoder.js";
import {ByteBuffer} from "protobufjs";
import type {Messages} from "../protobuf/messages.js";
import type {MessageFromTrezor} from "./index";
const MESSAGE_HEADER_BYTE: number = 0x23;
// input that might or might not be fully parsed yet
class PartiallyParsedInput {
// Message type number
typeNumber: number;
// Expected length of the raq message, in bytes
expectedLength: number;
// Buffer with the beginning of message; can be non-complete and WILL be modified
// during the object's lifetime
buffer: ByteBuffer;
constructor(typeNumber: number, length: number) {
this.typeNumber = typeNumber;
this.expectedLength = length;
this.buffer = new ByteBuffer(length);
}
isDone(): boolean {
return (this.buffer.offset >= this.expectedLength);
}
append(buffer: ByteBuffer):void {
this.buffer.append(buffer);
}
arrayBuffer(): ArrayBuffer {
const byteBuffer: ByteBuffer = this.buffer;
byteBuffer.reset();
return byteBuffer.toArrayBuffer();
}
}
// Parses first raw input that comes from Trezor and returns some information about the whole message.
function parseFirstInput(bytes: ArrayBuffer): PartiallyParsedInput {
// convert to ByteBuffer so it's easier to read
const byteBuffer: ByteBuffer = ByteBuffer.concat([bytes]);
// checking first two bytes
const sharp1: number = byteBuffer.readByte();
const sharp2: number = byteBuffer.readByte();
if (sharp1 !== MESSAGE_HEADER_BYTE || sharp2 !== MESSAGE_HEADER_BYTE) {
throw new Error(`Didn't receive expected header signature.`);
}
// reading things from header
const type: number = byteBuffer.readUint16();
const length: number = byteBuffer.readUint32();
// creating a new buffer with the right size
const res: PartiallyParsedInput = new PartiallyParsedInput(type, length);
res.append(byteBuffer);
return res;
}
// If the whole message wasn't loaded in the first input, loads more inputs until everything is loaded.
// note: the return value is not at all important since it's still the same parsedinput
function receiveRest(
parsedInput: PartiallyParsedInput,
receiver: () => Promise<ArrayBuffer>
): Promise<void> {
if (parsedInput.isDone()) {
return Promise.resolve();
}
return receiver().then((data) => {
// sanity check
if (data == null) {
throw new Error(`Received no data.`);
}
parsedInput.append(data);
return receiveRest(parsedInput, receiver);
});
}
// Receives the whole message as a raw data buffer (but without headers or type info)
function receiveBuffer(
receiver: () => Promise<ArrayBuffer>
): Promise<PartiallyParsedInput> {
return receiver().then((data: ArrayBuffer) => {
const partialInput: PartiallyParsedInput = parseFirstInput(data);
return receiveRest(partialInput, receiver).then(() => {
return partialInput;
});
});
}
// Reads data from device and returns decoded message, that can be sent back to trezor.js
export function receiveAndParse(
messages: Messages,
receiver: () => Promise<ArrayBuffer>
): Promise<MessageFromTrezor> {
return receiveBuffer(receiver).then((received) => {
const typeId: number = received.typeNumber;
const buffer: ArrayBuffer = received.arrayBuffer();
const decoder: MessageDecoder = new MessageDecoder(messages, typeId, buffer);
return {
message: decoder.decodedJSON(),
type: decoder.messageName(),
};
});
}

View File

@@ -0,0 +1,152 @@
/* @flow */
"use strict";
// Logic of sending data to trezor
//
// Logic of "call" is broken to two parts - sending and recieving
import * as ProtoBuf from "protobufjs";
import {ByteBuffer} from "protobufjs";
import type {Messages} from "../protobuf/messages.js";
const HEADER_SIZE = 1 + 1 + 4 + 2;
const MESSAGE_HEADER_BYTE: number = 0x23;
const BUFFER_SIZE: number = 63;
// Sends more buffers to device.
function sendBuffers(
sender: (data: ArrayBuffer) => Promise<void>,
buffers: Array<ArrayBuffer>
): Promise<void> {
return buffers.reduce((prevPromise: Promise<void>, buffer: ArrayBuffer) => {
return prevPromise.then(() => {
return sender(buffer);
});
}, Promise.resolve());
}
// already built PB message
class BuiltMessage {
message: ProtoBuf.Builder.Message;
type: number;
constructor(messages: Messages, // Builders, generated by reading config
name: string, // Name of the message
data: Object // data as "pure" object, from trezor.js
) {
const Builder = messages.messagesByName[name];
if (Builder == null) {
throw new Error(`The message name ${name} is not found.`);
}
// cleans up stuff from angular and remove "null" that crashes in builder
cleanupInput(data);
if (data) {
this.message = new Builder(data);
} else {
this.message = new Builder();
}
this.type = messages.messageTypes[`MessageType_${name}`];
}
// encodes into "raw" data, but it can be too long and needs to be split into
// smaller buffers
_encodeLong(): Uint8Array {
const headerSize: number = HEADER_SIZE; // should be 8
const bytes: Uint8Array = new Uint8Array(this.message.encodeAB());
const fullSize: number = headerSize + bytes.length;
const encodedByteBuffer = new ByteBuffer(fullSize);
// first encode header
// 2*1 byte
encodedByteBuffer.writeByte(MESSAGE_HEADER_BYTE);
encodedByteBuffer.writeByte(MESSAGE_HEADER_BYTE);
// 2 bytes
encodedByteBuffer.writeUint16(this.type);
// 4 bytes (so 8 in total)
encodedByteBuffer.writeUint32(bytes.length);
// then put in the actual message
encodedByteBuffer.append(bytes);
// and convert to uint8 array
// (it can still be too long to send though)
const encoded: Uint8Array = new Uint8Array(encodedByteBuffer.buffer);
return encoded;
}
// encodes itself and splits into "nice" chunks
encode(): Array<ArrayBuffer> {
const bytes: Uint8Array = this._encodeLong();
const result: Array<ArrayBuffer> = [];
const size: number = BUFFER_SIZE;
// How many pieces will there actually be
const count: number = Math.floor((bytes.length - 1) / size) + 1;
// slice and dice
for (let i = 0; i < count; i++) {
const slice: Uint8Array = bytes.subarray(i * size, (i + 1) * size);
const newArray: Uint8Array = new Uint8Array(size);
newArray.set(slice);
result.push(newArray.buffer);
}
return result;
}
}
// Removes $$hashkey from angular and remove nulls
function cleanupInput(message: Object): void {
delete message.$$hashKey;
for (const key in message) {
const value = message[key];
if (value == null) {
delete message[key];
} else {
if (Array.isArray(value)) {
value.forEach((i) => {
if (typeof i === `object`) {
cleanupInput(i);
}
});
}
if (typeof value === `object`) {
cleanupInput(value);
}
}
}
}
// Builds buffers to send.
// messages: Builders, generated by reading config
// name: Name of the message
// data: Data to serialize, exactly as given by trezor.js
// Returning buffers that will be sent to Trezor
function buildBuffers(messages: Messages, name: string, data: Object): Array<ArrayBuffer> {
const message: BuiltMessage = new BuiltMessage(messages, name, data);
const encoded: Array<ArrayBuffer> = message.encode();
return encoded;
}
// Sends message to device.
// Resolves iff everything gets sent
export function buildAndSend(
messages: Messages,
sender: (data: ArrayBuffer) => Promise<void>,
name: string,
data: Object
): Promise<void> {
const buffers: Array<ArrayBuffer> = buildBuffers(messages, name, data);
return sendBuffers(sender, buffers);
}

View File

@@ -0,0 +1,52 @@
/* @flow */
"use strict";
// Module for verifying ECDSA signature of configuration.
import {ECPair, ECSignature, crypto} from "bitcoinjs-lib";
import BigInteger from "bigi";
/* eslint-disable quotes */
const SATOSHI_KEYS: Array<string> = [
'\x04\xd5\x71\xb7\xf1\x48\xc5\xe4\x23\x2c\x38\x14\xf7\x77\xd8\xfa\xea\xf1\xa8\x42\x16\xc7\x8d\x56\x9b\x71\x04\x1f\xfc\x76\x8a\x5b\x2d\x81\x0f\xc3\xbb\x13\x4d\xd0\x26\xb5\x7e\x65\x00\x52\x75\xae\xde\xf4\x3e\x15\x5f\x48\xfc\x11\xa3\x2e\xc7\x90\xa9\x33\x12\xbd\x58',
'\x04\x63\x27\x9c\x0c\x08\x66\xe5\x0c\x05\xc7\x99\xd3\x2b\xd6\xba\xb0\x18\x8b\x6d\xe0\x65\x36\xd1\x10\x9d\x2e\xd9\xce\x76\xcb\x33\x5c\x49\x0e\x55\xae\xe1\x0c\xc9\x01\x21\x51\x32\xe8\x53\x09\x7d\x54\x32\xed\xa0\x6b\x79\x20\x73\xbd\x77\x40\xc9\x4c\xe4\x51\x6c\xb1',
'\x04\x43\xae\xdb\xb6\xf7\xe7\x1c\x56\x3f\x8e\xd2\xef\x64\xec\x99\x81\x48\x25\x19\xe7\xef\x4f\x4a\xa9\x8b\x27\x85\x4e\x8c\x49\x12\x6d\x49\x56\xd3\x00\xab\x45\xfd\xc3\x4c\xd2\x6b\xc8\x71\x0d\xe0\xa3\x1d\xbd\xf6\xde\x74\x35\xfd\x0b\x49\x2b\xe7\x0a\xc7\x5f\xde\x58',
'\x04\x87\x7c\x39\xfd\x7c\x62\x23\x7e\x03\x82\x35\xe9\xc0\x75\xda\xb2\x61\x63\x0f\x78\xee\xb8\xed\xb9\x24\x87\x15\x9f\xff\xed\xfd\xf6\x04\x6c\x6f\x8b\x88\x1f\xa4\x07\xc4\xa4\xce\x6c\x28\xde\x0b\x19\xc1\xf4\xe2\x9f\x1f\xcb\xc5\xa5\x8f\xfd\x14\x32\xa3\xe0\x93\x8a',
'\x04\x73\x84\xc5\x1a\xe8\x1a\xdd\x0a\x52\x3a\xdb\xb1\x86\xc9\x1b\x90\x6f\xfb\x64\xc2\xc7\x65\x80\x2b\xf2\x6d\xbd\x13\xbd\xf1\x2c\x31\x9e\x80\xc2\x21\x3a\x13\x6c\x8e\xe0\x3d\x78\x74\xfd\x22\xb7\x0d\x68\xe7\xde\xe4\x69\xde\xcf\xbb\xb5\x10\xee\x9a\x46\x0c\xda\x45',
];
/* eslint-enable */
const keys: Array<Buffer> = SATOSHI_KEYS.map(key => new Buffer(key, `binary`));
// Verifies ECDSA signature
// pubkeys - Public keys
// signature - ECDSA signature (concatenated R and S, both 32 bytes)
// data - Data that are signed
// returns True, iff the signature is correct with any of the pubkeys
function verify(pubkeys: Array<Buffer>, bsignature: Buffer, data: Buffer): boolean {
const r = BigInteger.fromBuffer(bsignature.slice(0, 32));
const s = BigInteger.fromBuffer(bsignature.slice(32));
const signature = new ECSignature(r, s);
const hash = crypto.sha256(data);
return pubkeys.some(pubkey => {
const pair = ECPair.fromPublicKeyBuffer(pubkey);
return pair.verify(hash, signature);
});
}
// Verifies if a given data is a correctly signed config
// Returns the data, if correctly signed, else reject
export function verifyHexBin(data: string): Promise<Buffer> {
const signature = new Buffer(data.slice(0, 64 * 2), `hex`);
const dataB = new Buffer(data.slice(64 * 2), `hex`);
const verified = verify(keys, signature, dataB);
if (!verified) {
return Promise.reject(`Not correctly signed.`);
} else {
return Promise.resolve(dataB);
}
}

6
packages/transport/dist/index.js.flow vendored Normal file
View File

@@ -0,0 +1,6 @@
/* @flow */
import {Handler} from './handler';
// not sure how to do this in ES6 syntax, so I won't
module.exports = Handler;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,102 @@
/* @flow */
"use strict";
// Helper module for converting Trezor's raw input to
// ProtoBuf's message and from there to regular JSON to trezor.js
import * as ProtoBuf from "protobufjs";
import {ByteBuffer, Long} from "protobufjs";
import {Messages} from "./messages.js";
export class MessageDecoder {
// Builders, generated by reading config
messages: Messages;
// message type number
type: number;
// raw data to push to Trezor
data: ArrayBuffer;
constructor(messages: Messages, type: number, data: ArrayBuffer) {
this.type = type;
this.data = data;
this.messages = messages;
}
// Returns an info about this message,
// which includes the constructor object and a name
_messageInfo() : MessageInfo {
const r = this.messages.messagesByType[this.type];
if (r == null) {
throw new Error(`Method type not found`, this.type);
}
return new MessageInfo(r.constructor, r.name);
}
// Returns the name of the message
messageName() : string {
return this._messageInfo().name;
}
// Returns the actual decoded message, as a ProtoBuf.js object
_decodedMessage() : ProtoBuf.Builder.Message {
const constructor = this._messageInfo().messageConstructor;
return constructor.decode(this.data);
}
// Returns the message decoded to JSON, that could be handed back
// to trezor.js
decodedJSON() : Object {
const decoded = this._decodedMessage();
const converted = messageToJSON(decoded);
return JSON.parse(JSON.stringify(converted));
}
}
class MessageInfo {
messageConstructor: ProtoBuf.Builder.Message;
name: string;
constructor(messageConstructor: ProtoBuf.Builder.Message, name: string) {
this.messageConstructor = messageConstructor;
this.name = name;
}
}
// Converts any ProtoBuf message to JSON in Trezor.js-friendly format
function messageToJSON(message: ProtoBuf.Builder.Message) : Object {
const res = {};
const meta = message.$type;
for (const key in message) {
const value = message[key];
if (typeof value === `function`) {
// ignoring
} else if (value instanceof ByteBuffer) {
const hex = value.toHex();
res[key] = hex;
} else if (value instanceof Long) {
const num = value.toNumber();
res[key] = num;
} else if (Array.isArray(value)) {
const decodedArr = value.map((i) => {
if (typeof i === `object`) {
return messageToJSON(i);
} else {
return i;
}
});
res[key] = decodedArr;
} else if (value instanceof ProtoBuf.Builder.Message) {
res[key] = messageToJSON(value);
} else if (meta._fieldsByName[key].type.name === `enum`) {
const enumValues = meta._fieldsByName[key].resolvedType.getChildren();
res[key] = enumValues.find(e => e.id === value).name;
} else {
res[key] = value;
}
}
return res;
}

View File

@@ -0,0 +1,34 @@
/* @flow */
"use strict";
// This is a simple class that represents information about messages,
// as they are loaded from the protobuf definition,
// so they are understood by both sending and recieving code.
import * as ProtoBuf from "protobufjs";
type MessageArray<KeyType> = { [key: KeyType]: ProtoBuf.Bulder.Message };
export class Messages {
messagesByName: MessageArray<string>;
messagesByType: MessageArray<number>;
messageTypes: { [key: string]: number };
constructor(messages: MessageArray<string>) {
this.messagesByName = messages;
const messagesByType: MessageArray<number> = {};
Object.keys(messages.MessageType).forEach(longName => {
const typeId = messages.MessageType[longName];
const shortName = longName.split(`_`)[1];
messagesByType[typeId] = {
name: shortName,
constructor: messages[shortName],
};
});
this.messagesByType = messagesByType;
this.messageTypes = messages.MessageType;
}
}

View File

@@ -0,0 +1,30 @@
/* @flow */
"use strict";
// Module for loading the protobuf description from serialized description
import * as ProtoBuf from "protobufjs";
import {Messages} from "./messages.js";
import {protocolToJSON} from "./to_json.js";
import * as compiledConfigProto from "./config_proto_compiled.js";
// Parse configure data (it has to be already verified)
export function parseConfigure(data: Buffer): Messages {
const configBuilder = compiledConfigProto[`Configuration`];
const loadedConfig = configBuilder.decode(data);
const validUntil = loadedConfig.valid_until;
const timeNow = Math.floor(Date.now() / 1000);
if (timeNow >= validUntil) {
throw new Error(`Config too old; ` + timeNow + ` >= ` + validUntil);
}
const wireProtocol = loadedConfig.wire_protocol;
const protocolJSON = protocolToJSON(wireProtocol.toRaw());
const protobufMessages = ProtoBuf.newBuilder({})[`import`](protocolJSON).build();
return new Messages(protobufMessages);
}

View File

@@ -0,0 +1,121 @@
/* @flow */
"use strict";
// Helper module that does conversion from already parsed protobuf's
// FileDescriptorSet to JSON, that can be used to initialize ProtoBuf.js
//
// Theoretically this should not be necessary, since FileDescriptorSet is protobuf "native" description,
// but ProtoBuf.js does NOT know how to make Builder from FileDescriptorSet, but it can build it from JSON.
// See https://github.com/dcodeIO/ProtoBuf.js/issues/250
//
// This conversion is probably not very stable and does not "scale" that well, since it's
// intended just for our relatively small usecase.
// But it works here.
import {shim} from 'object.values';
if (!Object.values) {
shim();
}
export function protocolToJSON(p: any): Object {
// TODO: what if there are more files?
const res = fileToJSON(p.file[2]);
res.imports = [fileToJSON(p.file[1])];
return res;
}
function fileToJSON(f: any): Object {
const res = {};
res.package = f.package;
res.options = f.options;
res.services = [];
const messagesSimple = Object.values(f.message_type).map(messageToJSON);
const messagesRef = extensionToJSON(f.extension);
res.messages = messagesRef.concat(messagesSimple);
res.enums = Object.values(f.enum_type).map(enumToJSON);
return res;
}
function enumToJSON(enumm: any): Object {
const res = {};
res.name = enumm.name;
res.values = Object.values(enumm.value).map(enum_valueToJSON);
res.options = {};
return res;
}
function extensionToJSON(extensions: {[key: string]: any}): Array<any> {
const res = {};
Object.values(extensions).forEach(function (extension: any) {
const extendee = extension.extendee.slice(1);
if (res[extendee] == null) {
res[extendee] = {};
res[extendee].ref = extendee;
res[extendee].fields = [];
}
res[extendee].fields.push(fieldToJSON(extension));
});
return Object.values(res);
}
function enum_valueToJSON(val: any): Object {
const res = {};
res.name = val.name;
res.id = val.number;
return res;
}
function messageToJSON(message: any): Object {
const res = {};
res.enums = [];
res.name = message.name;
res.options = message.options || {};
res.messages = [];
res.fields = Object.values(message.field).map(fieldToJSON);
res.oneofs = {};
return res;
}
const type_map = {
"1": `double`,
"2": `float`,
"3": `int64`,
"4": `uint64`,
"5": `int32`,
"6": `fixed64`,
"7": `fixed32`,
"8": `bool`,
"9": `string`,
"10": `group`,
"11": `message`,
"12": `bytes`,
"13": `uint32`,
"14": `enum`,
"15": `sfixed32`,
"16": `sfixed64`,
"17": `sint32`,
"18": `sint64`,
};
function fieldToJSON(field: any): Object {
const res = {};
if (field.label === 1) {
res.rule = `optional`;
}
if (field.label === 2) {
res.rule = `required`;
}
if (field.label === 3) {
res.rule = `repeated`;
}
res.type = type_map[field.type];
if (field.type_name) {
res.type = field.type_name.slice(1);
}
res.name = field.name;
res.options = field.options || {};
res.id = field.number;
return res;
}

View File

@@ -0,0 +1,17 @@
/* @flow */
"use strict";
// does not have session
export type TrezorDeviceInfo = {
path: string;
}
export type Transport = {
enumerate: () => Promise<Array<TrezorDeviceInfo>>;
send: (path: string, session: string, data: ArrayBuffer) => Promise<void>;
receive: (path: string, session: string) => Promise<ArrayBuffer>;
connect: (path: string) => Promise<string>;
disconnect: (path: string, session: string) => Promise<void>;
}