Skip to content

Commit

Permalink
pluginization
Browse files Browse the repository at this point in the history
  • Loading branch information
nsjames committed Apr 21, 2018
1 parent b87cd91 commit 32b11ce
Show file tree
Hide file tree
Showing 20 changed files with 282 additions and 57 deletions.
9 changes: 3 additions & 6 deletions src/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import TimingHelpers from './util/TimingHelpers';
import Error from './models/errors/Error'
import ContractHelpers from './util/ContractHelpers'
const ecc = require('eosjs-ecc');
import {apis} from './util/BrowserApis';

// Gets bound when a user logs into scatter
// and unbound when they log out
Expand Down Expand Up @@ -55,7 +56,6 @@ export default class Background {
* @param message - The message to be dispensed
*/
dispenseMessage(sendResponse, message){
console.log(message);
Background.checkAutoLock();
switch(message.type){
case InternalMessageTypes.SET_SEED: Background.setSeed(sendResponse, message.payload); break;
Expand Down Expand Up @@ -183,7 +183,7 @@ export default class Background {
this.lockGuard(sendResponse, () => {
console.log("Destroying");
seed = '';
chrome.storage.local.clear();
apis.storage.local.clear();
sendResponse(true);
})
}
Expand Down Expand Up @@ -233,9 +233,6 @@ export default class Background {
return false;
}
const identity = permission.identity(scatter.keychain);
console.log('identity', identity);
console.log('payload', payload);
console.log('permission', permission);
sendResponse(identity.asOnlyRequiredFields(permission.fields, permission.network));
});
}
Expand Down Expand Up @@ -352,7 +349,7 @@ export default class Background {
* @param sendResponse
*/
static requestGetVersion(sendResponse){
sendResponse(chrome.app.getDetails().version)
sendResponse(apis.app.getDetails().version)
}

/***
Expand Down
6 changes: 2 additions & 4 deletions src/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ import * as NetworkMessageTypes from './messages/NetworkMessageTypes'
import InternalMessage from './messages/InternalMessage';
import * as InternalMessageTypes from './messages/InternalMessageTypes'
import Error from './models/errors/Error'

import Identity from './models/Identity'
import Scatterdapp from './scatterdapp'
import {apis} from './util/BrowserApis';

// The stream that connects between the content script
// and the website
Expand Down Expand Up @@ -70,7 +68,7 @@ class Content {
*/
injectInteractionScript(){
let script = document.createElement('script');
script.src = chrome.extension.getURL(INJECTION_SCRIPT_FILENAME);
script.src = apis.extension.getURL(INJECTION_SCRIPT_FILENAME);
(document.head||document.documentElement).appendChild(script);
script.onload = () => script.remove();
}
Expand Down
4 changes: 0 additions & 4 deletions src/models/Account.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,4 @@ export default class Account {
static nameIsValid(name){
return /(^[a-z1-5.]{1,11}[a-z1-5]$)|(^[a-z1-5.]{12}[a-j1-5]$)/g.test(name)
}

formatEOS(){
return `${this.name}@${this.authority}`
}
}
4 changes: 2 additions & 2 deletions src/models/Blockchains.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

export const Blockchains = {
EOS:'EOS',
ETH:'ETH'
EOS:'eos',
ETH:'eth'
};
5 changes: 5 additions & 0 deletions src/models/Keychain.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ export default class Keychain {
}

findIdentity(publicKey){ return this.identities.find(id => id.publicKey === publicKey); }
findIdentityFromDomain(domain){
const idFromPermissions = this.permissions.find(permission => permission.isIdentityOnly() && permission.domain === domain);
if(idFromPermissions) return this.findIdentity(idFromPermissions.publicKey);
else return null;
}
updateOrPushIdentity(identity){
this.identities.find(id => id.publicKey === identity.publicKey)
? this.identities = this.identities.map(id => id.publicKey === identity.publicKey ? identity : id)
Expand Down
4 changes: 3 additions & 1 deletion src/models/Meta.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import {apis} from '../util/BrowserApis';

export default class Meta {

constructor(){
const extension = (chrome && chrome.app) ? chrome.app.getDetails() : {};
const extension = (apis && apis.app && typeof apis.app.getDetails === 'function') ? apis.app.getDetails() : {};
this.version = extension.version || '';
this.extensionId = extension.id || '';
}
Expand Down
10 changes: 9 additions & 1 deletion src/models/errors/Error.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,15 @@ export default class Error {
}

static signatureAccountMissing(){
return this.signatureError("account_missing", "Missing required accounts");
return this.signatureError("account_missing", "Missing required accounts, repull the identity");
}

static usedKeyProvider(){
return new Error(
ErrorTypes.MALICIOUS,
"Do not use a `keyProvider` with a Scatter. Use a `signProvider` and return only signatures to this object. A malicious person could retrieve your keys otherwise.",
ErrorCodes.NO_SIGNATURE
)
}

}
9 changes: 9 additions & 0 deletions src/plugins/Plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

export default class Plugin {

constructor(_name = '', _type = ''){
this.name = _name;
this.type = _type;
}

}
34 changes: 34 additions & 0 deletions src/plugins/PluginRepository.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as PluginTypes from './PluginTypes';
import EOS from './defaults/eos';

/***
* Setting up for plugin based generators,
* this will add more blockchain compatibility in the future.
*/

class PluginRepositorySingleton {

constructor(){
this.plugins = [];

this.loadPlugins();
}

loadPlugins(){
this.plugins.push(new EOS());

// TODO: Get from store repo
}

signatureProviders(){
return this.plugins.filter(plugin => plugin.type === PluginTypes.BLOCKCHAIN_SUPPORT);
}

findPlugin(name){
return this.plugins.find(plugin => plugin.name === name);
}

}

const PluginRepository = new PluginRepositorySingleton();
export default PluginRepository;
1 change: 1 addition & 0 deletions src/plugins/PluginTypes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const BLOCKCHAIN_SUPPORT = 'blockchain_support';
139 changes: 139 additions & 0 deletions src/plugins/defaults/eos.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import Plugin from '../Plugin';
import * as PluginTypes from '../PluginTypes';
import {Blockchains} from '../../models/Blockchains'
import * as NetworkMessageTypes from '../../messages/NetworkMessageTypes'
const ecc = require('eosjs-ecc');

let networkBinder = new WeakMap();
let internalMessageSender = new WeakMap();
let throwIfNoIdentity = new WeakMap();

const proxy = (dummy, handler) => new Proxy(dummy, handler);

export default class EOS extends Plugin {

constructor(){
super(Blockchains.EOS, PluginTypes.BLOCKCHAIN_SUPPORT)
}

accountFormatter(account){
return `${account.name}@${account.authority}`
}

signer(bgContext, payload, publicKey, callback){
const buf = Buffer.from(payload.buf.data, 'utf8');
bgContext.publicToPrivate(privateKey => {
callback(privateKey ? ecc.sign(buf, privateKey) : null);
}, publicKey);
}

signatureProvider(...args){

networkBinder = args[0];
internalMessageSender = args[1];
throwIfNoIdentity = args[2];

return (_eos, network, _options = {}) => {

networkBinder(network);
const httpEndpoint = `http://${network.host}:${network.port}`;

// The proxy stands between the eosjs object and scatter.
// This is used to add special functionality like adding `requiredFields` arrays to transactions
return proxy(_eos(), {
get(eosInstance, method) {

let returnedFields = null;

return (...args) => {

if(args.find(arg => arg.hasOwnProperty('keyProvider'))) throw Error.usedKeyProvider();

let requiredFields = args.find(arg => arg.hasOwnProperty('requiredFields'));
requiredFields = requiredFields ? requiredFields.requiredFields : [];

// The signature provider which gets elevated into the user's Scatter
const signProvider = async signargs => {
throwIfNoIdentity();

// Friendly formatting
signargs.messages = await messagesBuilder(_eos, signargs, httpEndpoint, args[0]);

const payload = Object.assign(signargs, { domain:location.host, network, requiredFields });
const result = await internalMessageSender(NetworkMessageTypes.REQUEST_SIGNATURE, payload);

// No signature
if(!result) return null;

if(result.hasOwnProperty('signatures')){
// Holding onto the returned fields for the final result
returnedFields = result.returnedFields;

// Grabbing buf signatures from local multi sig sign provider
let multiSigKeyProvider = args.find(arg => arg.hasOwnProperty('signProvider'));
if(multiSigKeyProvider){
result.signatures.push(multiSigKeyProvider.signProvider(signargs.buf, signargs.sign));
}

// Returning only the signatures to eosjs
return result.signatures;
}

return result;
};

// TODO: We need to check about the implications of multiple eosjs instances
return new Promise((resolve, reject) => {
_eos(Object.assign(JSON.stringify(_options), {httpEndpoint, signProvider}))[method](...args)
.then(result => {

// Standard method ( ie. not contract )
if(!result.hasOwnProperty('fc')){
result = Object.assign(result, {returnedFields});
resolve(result);
return;
}

// Catching chained promise methods ( contract .then action )
const contractProxy = proxy(result, {
get(instance,method){
if(method === 'then') return instance[method];
return (...args) => {
return new Promise(async (res, rej) => {
instance[method](...args).then(actionResult => {
res(Object.assign(actionResult, {returnedFields}));
}).catch(rej);
})

}
}
});

resolve(contractProxy);
}).catch(error => reject(error))
})
}
}
}); // Proxy
}
}
}


const messagesBuilder = async (_eos, signargs, httpEndpoint, contractName) => await Promise.all(signargs.transaction.actions.map(async action => {
let data = null;

const eos = _eos({httpEndpoint});
if(action.account === 'eosio') data = eos.fc.fromBuffer(action.name, action.data);
else {
const abi = await eos.contract(contractName);
data = abi.fc.fromBuffer(action.name, action.data);
}

return {
data,
code:action.account,
type:action.name,
authorization:action.authorization
};
}));
12 changes: 9 additions & 3 deletions src/prompts/RequestSignaturePrompt.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

<section class="floating-header">
<figure class="identity-name">{{identity().name}}</figure>
<figure class="account-authority" v-if="network !== null">{{identity().networkedAccount(network).formatEOS()}}</figure>
<figure class="account-authority" v-if="network !== null">{{formattedAccount()}}</figure>
<figure class="switches">
<figure class="switch"
v-for="displayType in displayTypes"
Expand Down Expand Up @@ -106,6 +106,7 @@
import Identity from '../models/Identity'
import {LocationFields, PersonalFields} from '../models/Identity'
import ObjectHelpers from '../util/ObjectHelpers'
import PluginRepository from '../plugins/PluginRepository'
const displayTypes = {
JSON:'json',
Expand Down Expand Up @@ -157,6 +158,12 @@
},
methods: {
setDisplayType(type){ this.selectedDisplayType = type; },
formattedAccount(){
const account = this.identity().networkedAccount(this.network);
// TODO: EOS Hardcode
return PluginRepository.findPlugin('eos').accountFormatter(account)
},
bind(changed, dotNotation) {
let props = dotNotation.split(".");
Expand Down Expand Up @@ -189,10 +196,9 @@
}, {})
},
identity(){
return this.scatter.keychain.findIdentity(this.prompt.data.publicKey);
return this.scatter.keychain.findIdentityFromDomain(this.prompt.data.domain);
},
accepted(){
const returnedFields = Identity.asReturnedFields(this.requiredFields, this.returnedFields, this.selectedLocation);
this.prompt.responder({accepted:true, whitelisted:this.whitelisted, returnedFields, mutableFields:this.mutableFields});
Expand Down
13 changes: 10 additions & 3 deletions src/scatterdapp.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as NetworkMessageTypes from './messages/NetworkMessageTypes'
import * as PairingTags from './messages/PairingTags'
import Error from './models/errors/Error'
import IdGenerator from './util/IdGenerator';
import BlockchainPlugins from './services/BlockchainPlugins';
import PluginRepository from './plugins/PluginRepository';
const ecc = require('eosjs-ecc');


Expand Down Expand Up @@ -123,6 +123,12 @@ const _send = (_type, _payload, bypassNetwork = false) => {
});
};

const setupSigProviders = context => {
PluginRepository.signatureProviders().map(sigProvider => {
context[sigProvider.name] = sigProvider.signatureProvider(_bindNetwork, _send, throwIfNoIdentity);
})
};

/***
* Scatterdapp is the object injected into the web application that
* allows it to interact with Scatter. Without using this the web application
Expand All @@ -138,8 +144,9 @@ export default class Scatterdapp {
network = null;
resolvers = [];

const proxies = new BlockchainPlugins(_bindNetwork, _send, throwIfNoIdentity, publicKey, network)
this['eos'] = proxies.eos;
setupSigProviders(this);
// const proxies = new BlockchainPlugins(_bindNetwork, _send, throwIfNoIdentity, publicKey, network)
// this['eos'] = proxies.eos;

_subscribe();

Expand Down

0 comments on commit 32b11ce

Please sign in to comment.