Skip to content

Commit

Permalink
refactor: reorg and renaming
Browse files Browse the repository at this point in the history
  • Loading branch information
fbwoolf authored and kyranjamie committed May 27, 2021
1 parent c3f20af commit 482099a
Show file tree
Hide file tree
Showing 24 changed files with 208 additions and 188 deletions.
22 changes: 17 additions & 5 deletions src/extension/background/index.ts → src/background/background.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
import { popupCenter } from '@common/popup';
/*
The background script is the extension's event handler; it contains listeners for browser
events that are important to the extension. It lies dormant until an event is fired then
performs the instructed logic. An effective background script is only loaded when it is
needed and unloaded when it goes idle.
*/
import { popupCenter } from 'background/popup';
import { ScreenPaths } from '@store/types';
import { CONTENT_SCRIPT_PORT, ExternalMethods, MessageFromContentScript } from '../message-types';
import { storePayload, StorageKey } from '../storage';
import { vaultMessageHandler } from './vault-manager';
import { vaultMessageHandler } from '@background/vault';
import { IS_TEST_ENV } from '@common/constants';
import { CONTENT_SCRIPT_PORT } from '@content-scripts/content-script';
import { VaultActions } from '@background/vault-types';
import { ExternalMethods, MessageFromContentScript } from '@content-scripts/message-types';

// Listen for install event
chrome.runtime.onInstalled.addListener(details => {
if (details.reason === 'install' && !IS_TEST_ENV) {
chrome.tabs.create({ url: chrome.runtime.getURL(`full-page.html#${ScreenPaths.INSTALLED}`) });
}
});

// Listen for connection to the content-script - port for two-way communication
chrome.runtime.onConnect.addListener(port => {
// Listen for auth and transaction events
if (port.name === CONTENT_SCRIPT_PORT) {
port.onMessage.addListener((message: MessageFromContentScript, port) => {
const { payload } = message;
Expand Down Expand Up @@ -47,7 +58,8 @@ chrome.runtime.onConnect.addListener(port => {
}
});

chrome.runtime.onMessage.addListener((message: VaultMessageFromApp, sender, sendResponse) => {
// Listen for events triggered by the background memory vault
chrome.runtime.onMessage.addListener((message: VaultActions, sender, sendResponse) => {
// Only respond to internal messages from our UI, not content scripts in other applications
if (!sender.url?.startsWith(chrome.runtime.getURL(''))) return;
void vaultMessageHandler(message).then(sendResponse).catch(sendResponse);
Expand All @@ -56,7 +68,7 @@ chrome.runtime.onMessage.addListener((message: VaultMessageFromApp, sender, send
});

if (IS_TEST_ENV) {
// expose a helper function to open a new tab with the wallet from tests
// Expose a helper function to open a new tab with the wallet from tests
(window as any).openOptionsPage = function (page: string) {
const url = chrome.runtime.getURL(`full-page.html#${page}`);
return url;
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
30 changes: 30 additions & 0 deletions src/background/vault-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { ExtensionMethods, InternalMethods, Message } from '@content-scripts/message-types';

/*
* Vault <-> Background Script
*/
export type VaultMessage<M extends ExtensionMethods, P> = Omit<Message<M, P>, 'source'>;

export type GetWallet = VaultMessage<InternalMethods.getWallet, undefined>;
export type MakeWallet = VaultMessage<InternalMethods.makeWallet, undefined>;
export type StoreSeed = VaultMessage<
InternalMethods.storeSeed,
{ secretKey: string; password?: string }
>;
export type CreateNewAccount = VaultMessage<InternalMethods.createNewAccount, undefined>;
export type SignOut = VaultMessage<InternalMethods.signOut, undefined>;
export type SetPassword = VaultMessage<InternalMethods.setPassword, string>;
export type UnlockWallet = VaultMessage<InternalMethods.unlockWallet, string>;
export type LockWallet = VaultMessage<InternalMethods.lockWallet, undefined>;
export type SwitchAccount = VaultMessage<InternalMethods.switchAccount, number>;

export type VaultActions =
| GetWallet
| MakeWallet
| StoreSeed
| CreateNewAccount
| SignOut
| SetPassword
| UnlockWallet
| SwitchAccount
| LockWallet;
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,12 @@ import {
} from '@stacks/wallet-sdk';

import { gaiaUrl } from '@common/constants';
import { VaultActions, InternalMethods } from '@extension/message-types';
import { decryptMnemonic, encryptMnemonic } from '@extension/crypto/mnemonic-encryption';
import { VaultActions } from '@background/vault-types';
import { decryptMnemonic, encryptMnemonic } from 'background/crypto/mnemonic-encryption';
import { DEFAULT_PASSWORD } from '@store/types';
import { InternalMethods } from '@content-scripts/message-types';

/**
* Manage a wallet instance, stored in memory in the background script
*/
// In-memory (background) wallet instance
export interface InMemoryVault {
encryptedSecretKey?: string;
salt?: string;
Expand Down Expand Up @@ -89,16 +88,16 @@ async function storeSeed(secretKey: string, password?: string): Promise<InMemory
};
}

// Ensure that TS will flag unhandled messages,
// and will throw at runtime
// Ensure that TS will flag unhandled messages and will throw at runtime
function throwUnhandledMethod(message: never): never;
function throwUnhandledMethod(message: VaultActions) {
throw new Error(`Unhandled message: ${JSON.stringify(message, null, 2)}`);
}

// Reducer to manage the state of the vault
export const vaultReducer = async (message: VaultActions): Promise<InMemoryVault> => {
switch (message.method) {
case InternalMethods.walletRequest:
case InternalMethods.getWallet:
return {
...inMemoryVault,
};
Expand Down
2 changes: 1 addition & 1 deletion src/common/hooks/callbacks/use-decode-request-callback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useRecoilCallback } from 'recoil';
import { isUnauthorizedTransactionStore, requestTokenStore } from '@store/transaction';
import { useLocation } from 'react-router-dom';
import { walletStore } from '@store/wallet';
import { getRequestOrigin, StorageKey } from '@extension/storage';
import { getRequestOrigin, StorageKey } from 'storage';
import { verifyTxRequest } from '@common/transaction-utils';

export function useDecodeRequestCallback() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useLocation } from 'react-router-dom';
import { useCallback, useEffect } from 'react';
import { decodeToken } from 'jsontokens';
import { getRequestOrigin, StorageKey } from '@extension/storage';
import { getRequestOrigin, StorageKey } from 'storage';
import { DecodedAuthRequest } from '@common/dev/types';
import { useWallet } from '@common/hooks/use-wallet';
import { ScreenPaths } from '@store/types';
Expand Down
2 changes: 1 addition & 1 deletion src/common/hooks/use-origin.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getRequestOrigin, StorageKey } from '@extension/storage';
import { getRequestOrigin, StorageKey } from 'storage';
import { useRecoilValue } from 'recoil';
import { requestTokenStore } from '@store/transaction';

Expand Down
8 changes: 4 additions & 4 deletions src/common/hooks/use-vault-messenger.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import {
InternalMethods,
VaultActions,
SetPassword,
StoreSeed,
UnlockWallet,
SwitchAccount,
} from '@extension/message-types';
import type { InMemoryVault } from '@extension/background/vault-manager';
} from '@background/vault-types';
import { RecoilState, useRecoilCallback } from 'recoil';
import {
hasSetPasswordStore,
Expand All @@ -16,6 +14,8 @@ import {
encryptedSecretKeyStore,
hasRehydratedVaultStore,
} from '@store/wallet';
import { InMemoryVault } from '@background/vault';
import { InternalMethods } from '@content-scripts/message-types';

type Set = <T>(store: RecoilState<T>, value: T) => void;

Expand Down Expand Up @@ -83,7 +83,7 @@ export const useVaultMessenger = () => {
return innerMessageWrapper(message, set);
});

const getWallet = messageWrapper({ method: InternalMethods.walletRequest, payload: undefined });
const getWallet = messageWrapper({ method: InternalMethods.getWallet, payload: undefined });
const doMakeWallet = messageWrapper({ method: InternalMethods.makeWallet, payload: undefined });
const doCreateNewAccount = messageWrapper({
method: InternalMethods.createNewAccount,
Expand Down
2 changes: 1 addition & 1 deletion src/common/transaction-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ import BN from 'bn.js';
import { StacksMainnet, StacksTestnet } from '@stacks/network';
import { TokenVerifier, decodeToken } from 'jsontokens';
import { Wallet, getAppPrivateKey } from '@stacks/wallet-sdk';
import type { TxResult } from '@extension/message-types';
import type { TransactionPayloadWithAttachment } from '@store/transaction';
import { TxResult } from '@content-scripts/message-types';

const getPostConditions = (
postConditions?: (PostCondition | string)[]
Expand Down
10 changes: 5 additions & 5 deletions src/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ import React from 'react';
import { DecodedAuthRequest } from './dev/types';
import { wordlists } from 'bip39';
import { isValidUrl } from './validate-url';
import { getTab, deleteTabForRequest, StorageKey } from '@extension/storage';
import { getTab, deleteTabForRequest, StorageKey } from 'storage';
import { BufferReader, deserializePostCondition, PostCondition } from '@stacks/transactions';
import { KEBAB_REGEX } from '@common/constants';
import {
AuthenticationResponseMessage,
MESSAGE_SOURCE,
ExternalMethods,
MESSAGE_SOURCE,
TransactionResponseMessage,
TxResult,
} from '@extension/message-types';
import { BufferReader, deserializePostCondition, PostCondition } from '@stacks/transactions';
import { KEBAB_REGEX } from '@common/constants';
} from '@content-scripts/message-types';

function kebabCase(str: string) {
return str.replace(KEBAB_REGEX, match => '-' + match.toLowerCase());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
/*
Extensions that read or write to web pages utilize a content script. The content script
contains JavaScript that executes in the contexts of a page that has been loaded into
the browser. Content scripts read and modify the DOM of web pages the browser visits.
*/
import { ScreenPaths } from '@store/types';
import { getEventSourceWindow } from '../../common/utils';
import { getEventSourceWindow } from '@common/utils';
import {
MessageFromContentScript,
ExternalMethods,
MessageFromContentScript,
MessageToContentScript,
MESSAGE_SOURCE,
} from '@content-scripts/message-types';
import {
AuthenticationRequestEvent,
DomEventName,
TransactionRequestEvent,
MessageToContentScript,
MESSAGE_SOURCE,
CONTENT_SCRIPT_PORT,
} from '../message-types';

const backgroundPort = chrome.runtime.connect({ name: CONTENT_SCRIPT_PORT });

function sendMessageToBackground(message: MessageFromContentScript) {
backgroundPort.postMessage(message);
}
} from '@inpage/inpage-types';

/**
* Legacy messaging to work with older versions of connect
*/
// Legacy messaging to work with older versions of Connect
window.addEventListener('message', event => {
const { data } = event;
if (data.source === 'blockstack-app') {
Expand All @@ -40,12 +38,32 @@ window.addEventListener('message', event => {
}
});

export const CONTENT_SCRIPT_PORT = 'content-script' as const;

// Connection to background script - fires onConnect event in background script
// and establishes two-way communication
const backgroundPort = chrome.runtime.connect({ name: CONTENT_SCRIPT_PORT });

// Sends message to background script that an event has fired
function sendMessageToBackground(message: MessageFromContentScript) {
backgroundPort.postMessage(message);
}

// Receives message from background script to execute in browser
chrome.runtime.onMessage.addListener((message: MessageToContentScript) => {
if (message.source === MESSAGE_SOURCE) {
// Forward to web app (browser)
window.postMessage(message, window.location.origin);
}
});

interface ForwardDomEventToBackgroundArgs {
payload: string;
method: MessageFromContentScript['method'];
urlParam: string;
path: ScreenPaths;
}

function forwardDomEventToBackground({ payload, method }: ForwardDomEventToBackgroundArgs) {
sendMessageToBackground({
method,
Expand All @@ -54,7 +72,7 @@ function forwardDomEventToBackground({ payload, method }: ForwardDomEventToBackg
});
}

// Listen for `CustomEvent`s coming from website
// Listen for a CustomEvent (auth request) coming from the web app
document.addEventListener(DomEventName.authenticationRequest, ((
event: AuthenticationRequestEvent
) => {
Expand All @@ -66,6 +84,7 @@ document.addEventListener(DomEventName.authenticationRequest, ((
});
}) as EventListener);

// Listen for a CustomEvent (transaction request) coming from the web app
document.addEventListener(DomEventName.transactionRequest, ((event: TransactionRequestEvent) => {
forwardDomEventToBackground({
path: ScreenPaths.TRANSACTION_POPUP,
Expand All @@ -75,15 +94,7 @@ document.addEventListener(DomEventName.transactionRequest, ((event: TransactionR
});
}) as EventListener);

// Background script --> Content script
chrome.runtime.onMessage.addListener((message: MessageToContentScript) => {
if (message.source === MESSAGE_SOURCE) {
// Forward to webpage
window.postMessage(message, window.location.origin);
}
});

// Inject inpage script
// Inject inpage script (Stacks Provider)
const inpage = document.createElement('script');
inpage.src = chrome.runtime.getURL('inpage.js');
inpage.id = 'stacks-wallet-provider';
Expand Down
63 changes: 63 additions & 0 deletions src/content-scripts/message-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { FinishedTxPayload, SponsoredFinishedTxPayload } from '@stacks/connect';

export const MESSAGE_SOURCE = 'stacks-wallet' as const;

export enum ExternalMethods {
transactionRequest = 'transactionRequest',
transactionResponse = 'transactionResponse',
authenticationRequest = 'authenticationRequest',
authenticationResponse = 'authenticationResponse',
}

export enum InternalMethods {
getWallet = 'getWallet',
makeWallet = 'makeWallet',
storeSeed = 'storeSeed',
createNewAccount = 'createNewAccount',
signOut = 'signOut',
setPassword = 'setPassword',
switchAccountIndex = 'switchAccountIndex',
unlockWallet = 'unlockWallet',
lockWallet = 'lockWallet',
switchAccount = 'switchAccount',
}

export type ExtensionMethods = ExternalMethods | InternalMethods;

interface BaseMessage {
source: typeof MESSAGE_SOURCE;
method: ExtensionMethods;
}

/*
* Content Script <-> Background Script
*/
export interface Message<M extends ExtensionMethods, P> extends BaseMessage {
method: M;
payload: P;
}

export type AuthenticationRequestMessage = Message<ExternalMethods.authenticationRequest, string>;

export type AuthenticationResponseMessage = Message<
ExternalMethods.authenticationResponse,
{
authenticationRequest: string;
authenticationResponse: string;
}
>;

export type TransactionRequestMessage = Message<ExternalMethods.transactionRequest, string>;

export type TxResult = SponsoredFinishedTxPayload | FinishedTxPayload;

export type TransactionResponseMessage = Message<
ExternalMethods.transactionResponse,
{
transactionRequest: string;
transactionResponse: TxResult;
}
>;

export type MessageFromContentScript = AuthenticationRequestMessage | TransactionRequestMessage;
export type MessageToContentScript = AuthenticationResponseMessage | TransactionResponseMessage;
Loading

0 comments on commit 482099a

Please sign in to comment.