Skip to content
This repository has been archived by the owner on Jun 29, 2020. It is now read-only.

Commit

Permalink
Merge 313fee1 into 2729456
Browse files Browse the repository at this point in the history
  • Loading branch information
garatortiz committed Jun 6, 2018
2 parents 2729456 + 313fee1 commit 3450f7c
Show file tree
Hide file tree
Showing 9 changed files with 148 additions and 44 deletions.
60 changes: 52 additions & 8 deletions blockchain/contracts/PoBA.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,75 @@ contract PoBA {
address public owner;
address public signer;

mapping (address => string[]) public accounts;

constructor() {
constructor() public {
owner = msg.sender;
signer = owner;
}

struct BankAccount {
string accountNumber;
string bankName;
uint256 attestationDate;
bool attestationFact;

uint256 creationBlock;
}

struct User {
uint256 creationBlock;
BankAccount[] bankAccounts;
}

mapping (address => User) public users;

function signerIsValid(bytes32 data, uint8 v, bytes32 r, bytes32 s)
public constant returns (bool)
{
bytes memory prefix = "\x19Ethereum Signed Message:\n32";
bytes32 prefixed = keccak256(prefix, data);
bytes32 prefixed = keccak256(abi.encodePacked(prefix, data));
return (ecrecover(prefixed, v, r, s) == signer);
}

function register(string account, uint8 v, bytes32 r, bytes32 s) {
function userExists(address wallet)
public view returns (bool)
{
return (users[wallet].creationBlock > 0);
}

function register(
string account,
string institution,
uint8 v, bytes32 r, bytes32 s) public {

require(bytes(account).length > 0);
require(bytes(institution).length > 0);
require(users[msg.sender].bankAccounts.length < 2**256-1);

if (!userExists(msg.sender)) {
// new user
users[msg.sender].creationBlock = block.number;
}

BankAccount memory ba;

ba.accountNumber = account;
ba.bankName = institution;
ba.attestationDate = now;
ba.attestationFact = true;
ba.creationBlock = block.number;

bytes32 hash = keccak256(msg.sender, account);
bytes32 hash = keccak256(
abi.encodePacked(
msg.sender,
ba.accountNumber,
ba.bankName
));
require(signerIsValid(hash, v, r, s));

accounts[msg.sender].push(account);
users[msg.sender].bankAccounts.push(ba);
}

function accountsLength(address _address) public constant returns (uint256) {
return accounts[_address].length;
return users[_address].bankAccounts.length;
}
}
17 changes: 12 additions & 5 deletions frontend/src/PoBA.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ const PobaContract = contract(pobaArtifact)

const getBankAccounts = async token => {
const result = await axios.get(`/api/accounts/bank-accounts/${token}`)

return result.data.accounts.numbers
const { ach, eft } = result.data.accounts.numbers
return [...ach, ...eft]
}

const getSignedBankAccount = async (accountId, ethAccount, token) => {
Expand Down Expand Up @@ -80,9 +80,16 @@ class PoBA extends Component {
this.setState({ loading: true })
return getSignedBankAccount(accountId, ethAccount, token)
.then(txData => {
return this.pobaContract.register(txData.account, txData.v, txData.r, txData.s, {
from: ethAccount
})
return this.pobaContract.register(
txData.bankAccount.account,
txData.bankAccount.institution,
txData.v,
txData.r,
txData.s,
{
from: ethAccount
}
)
})
.then(
() => successAlert(),
Expand Down
6 changes: 2 additions & 4 deletions frontend/src/ui/BankAccountList.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,9 @@ const Button = glamorous.button(buttonStyles, {
export default ({ bankAccounts, onClick }) => (
<BankAccountList>
{bankAccounts.map((bankAccount, index) => (
<Div textAlign="center">
<Div key={index} textAlign="center">
Bank account number: <strong>{bankAccount.account} </strong>
<Button key={index} onClick={() => onClick(bankAccount)}>
Verify
</Button>
<Button onClick={() => onClick(bankAccount)}>Verify</Button>
</Div>
))}
</BankAccountList>
Expand Down
21 changes: 18 additions & 3 deletions server/controllers/accounts.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,26 @@ const getBankAccounts = async accessToken => {
}
}

const getInstitutionById = async institutionId => {
try {
return await plaidClient.getInstitutionById(institutionId)
} catch (error) {
logger.error({ status: error.status_code }, 'Error getting institution')
throw Error(`[getInstitutionById] ${error.error_code}: ${error.error_message}`)
}
}

const getBankAccount = async (accessToken, accountId) => {
const bankAccounts = await getBankAccounts(accessToken)
const numberBankAccount = bankAccounts.numbers.filter(account => account.account_id === accountId)
logger.info({ numberBankAccount }, 'Got bank account')
return numberBankAccount[0]
const { numbers, item } = bankAccounts
const ach = numbers.ach.filter(account => account.account_id === accountId)
const eft = numbers.eft.filter(account => account.account_id === accountId)
const number = [...ach, ...eft][0]
const institutionId = item.institution_id
const { institution } = await getInstitutionById(institutionId)
const bankAccount = { account: number.account, institution: institution.name }
logger.info(bankAccount, 'Got bank account')
return bankAccount
}

module.exports = {
Expand Down
3 changes: 2 additions & 1 deletion server/etc/plaid.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ const plaidClient = new plaid.Client(
PLAID_CLIENT_ID,
PLAID_SECRET,
PLAID_PUBLIC_KEY,
plaid.environments[PLAID_ENV]
plaid.environments[PLAID_ENV],
{ version: '2018-05-22' }
)

module.exports = plaidClient
6 changes: 3 additions & 3 deletions server/routes/accounts.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ const signBankAccount = (req, res) => {

const accessToken = await accountsController.getAccessToken(token)

const { account } = await accountsController.getBankAccount(accessToken, accountId)
const hash = web3.utils.sha3(ethAccount + Buffer.from(account).toString('hex'))
const bankAccount = await accountsController.getBankAccount(accessToken, accountId)
const hash = web3.utils.sha3(ethAccount + Buffer.from(bankAccount.account).toString('hex'))
const { v, r, s } = web3.eth.accounts.sign(hash, PRIVATE_KEY)
return res.send({ account, v, r, s })
return res.send({ bankAccount, v, r, s })
} catch (e) {
logger.error(e.message, 'There was an error getting the transaction data')
return res.status(400).send({ error: e.message })
Expand Down
12 changes: 9 additions & 3 deletions server/tests/accounts.controller.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const mockExchangePublicToken = mocks.exchangePublicToken
const mockExchangePublicTokenError = mocks.exchangePublicTokenError
const mockGetAuth = mocks.getAuth
const mockGetAuthError = mocks.getAuthError
const mockGetInstitutionById = mocks.getInstitutionById

jest.mock('../etc/plaid', () => ({
exchangePublicToken: jest.fn(publicToken => {
Expand All @@ -16,7 +17,8 @@ jest.mock('../etc/plaid', () => ({
getAuth: jest.fn(accessToken => {
if (accessToken === mockAccessToken) return mockGetAuth
return mockGetAuthError
})
}),
getInstitutionById: jest.fn(() => mockGetInstitutionById)
}))

describe('[controllers] accounts', () => {
Expand All @@ -39,8 +41,12 @@ describe('[controllers] accounts', () => {
})
})
it('should return only one bank account', async () => {
const accountId = mockGetAuth.numbers[0].account_id
const accountId = mockGetAuth.numbers.ach[0].account_id
const account = await accountsController.getBankAccount(mockAccessToken, accountId)
expect(account).toEqual(mockGetAuth.numbers[0])
const expected = {
account: mockGetAuth.numbers.ach[0].account,
institution: mockGetInstitutionById.institution.name
}
expect(account).toEqual(expected)
})
})
26 changes: 17 additions & 9 deletions server/tests/accounts.route.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ const mockEthAccount = '0x92970dbD5C0Ee6b439422bFd7cD71e1DDA921A03'
const mockToken = 'public-token-de3ce8ef-33f8-452c-a685-8671031fc0f6'
const mockAccessToken = mocks.exchangePublicToken.access_token
const mockBankAccounts = mocks.getAuth
const mockBankAccount = {
bankAccount: {
account: '9900009606',
account_id: 'vzeNDwK7KQIm4yEog683uElbp9GRLEFXGK98D',
routing: '011401533',
wire_routing: '021000021'
}
}
const mockSign = {
v: '0x1b',
r: '0x9a4acff8fcc5fc48278482669d3db5a728a226c8f82bce2895208c59ca5637b9',
Expand All @@ -22,8 +30,8 @@ jest.mock('../controllers/accounts', () => ({
throw new Error('[getBankAccounts] INVALID_TOKEN: Error getting bank accounts')
}),
getBankAccount: jest.fn((accessToken, accountId) => {
if (accessToken === mockAccessToken && accountId === mockBankAccounts.numbers[0].account_id)
return mockBankAccounts.numbers[0]
if (accessToken === mockAccessToken && accountId === mockBankAccounts.numbers.ach[0].account_id)
return mockBankAccounts.numbers.ach[0]
throw new Error('There was an error getting the transaction data')
})
}))
Expand All @@ -47,7 +55,8 @@ describe('[routes] accounts', () => {
.then(res => {
const { accounts } = res.body
expect(res.status).toEqual(200)
expect(accounts.numbers.length).toBeGreaterThanOrEqual(0)
expect(accounts.numbers.ach.length).toBeGreaterThanOrEqual(0)
expect(accounts.numbers.eft.length).toBeGreaterThanOrEqual(0)
}))
it('should return error', () =>
request(app)
Expand All @@ -60,7 +69,7 @@ describe('[routes] accounts', () => {
.post('/api/accounts/sign-account')
.send({
token: mockToken,
accountId: mockBankAccounts.numbers[0].account_id
accountId: mockBankAccounts.numbers.ach[0].account_id
})
.then(res => {
expect(res.status).toEqual(404)
Expand All @@ -70,7 +79,7 @@ describe('[routes] accounts', () => {
.post('/api/accounts/sign-account')
.send({
ethAccount: mockEthAccount,
accountId: mockBankAccounts.numbers[0].account_id
accountId: mockBankAccounts.numbers.ach[0].account_id
})
.then(res => {
expect(res.status).toEqual(404)
Expand All @@ -91,7 +100,7 @@ describe('[routes] accounts', () => {
.send({
ethAccount: mockEthAccount,
token: `${mockToken}-fail`,
accountId: mockBankAccounts.numbers[0].account_id
accountId: mockBankAccounts.numbers.ach[0].account_id
})
.then(res => {
expect(res.status).toEqual(400)
Expand All @@ -102,11 +111,10 @@ describe('[routes] accounts', () => {
.send({
ethAccount: mockEthAccount,
token: mockToken,
accountId: mockBankAccounts.numbers[0].account_id
accountId: mockBankAccounts.numbers.ach[0].account_id
})
.then(res => {
const { account } = mockBankAccounts.numbers[0]
expect(res.body).toEqual({ account, ...mockSign })
expect(res.body).toEqual({ ...mockBankAccount, ...mockSign })
expect(res.status).toEqual(200)
}))
})
41 changes: 33 additions & 8 deletions server/tests/utils/mocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,17 @@ const data = {
type: 'depository'
}
],
numbers: [
{
account: '9900009606',
account_id: 'vzeNDwK7KQIm4yEog683uElbp9GRLEFXGK98D',
routing: '011401533',
wire_routing: '021000021'
}
],
numbers: {
ach: [
{
account: '9900009606',
account_id: 'vzeNDwK7KQIm4yEog683uElbp9GRLEFXGK98D',
routing: '011401533',
wire_routing: '021000021'
}
],
eft: []
},
item: { Object },
request_id: '45QSn',
status_code: 200
Expand All @@ -48,6 +51,28 @@ const data = {
error_type: 'INVALID_REQUEST',
request_id: 'qcaHk',
http_code: 404
},
getInstitutionById: {
institution: {
credentials: [
{
label: 'User ID',
name: 'username',
type: 'text'
},
{
label: 'Password',
name: 'password',
type: 'password'
}
],
has_mfa: true,
institution_id: 'ins_109512',
mfa: ['code', 'list', 'questions', 'selections'],
name: 'Houndstooth Bank',
products: ['auth', 'balance', 'identity', 'transactions']
},
request_id: '9VpnU'
}
}

Expand Down

0 comments on commit 3450f7c

Please sign in to comment.