Skip to content

Commit

Permalink
Merge pull request #290 from airgap-it/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
AndreasGassmann authored Nov 4, 2021
2 parents d207bd4 + c18db5f commit 01ea422
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 14 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@airgap/beacon-sdk",
"version": "2.3.5",
"version": "2.3.6",
"description": "The beacon-sdk allows you to easily connect DApps with Wallets through P2P communication or a chrome extension.",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
Expand Down
2 changes: 1 addition & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export const SDK_VERSION: string = '2.3.5'
export const SDK_VERSION: string = '2.3.6'
export const BEACON_VERSION: string = '2'
30 changes: 20 additions & 10 deletions src/transports/clients/P2PCommunicationClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,29 @@ import { ExposedPromise } from '../../utils/exposed-promise'

const logger = new Logger('P2PCommunicationClient')

const KNOWN_RELAY_SERVERS = [
export const KNOWN_RELAY_SERVERS = [
'beacon-node-1.sky.papers.tech',
'beacon-node-0.papers.tech:8448',
'beacon-node-2.sky.papers.tech'
]

const publicKeyToNumber = (arr: Uint8Array, mod: number) => {
export const publicKeyToNumber = (arr: Uint8Array, mod: number) => {
let sum = 0
for (let i = 0; i < arr.length; i++) {
sum += arr[i] + i
}
return Math.floor(sum % mod)
}

export const deterministicShuffle = (arr: string[], keypair: sodium.KeyPair) => {
const arrCopy: string[] = JSON.parse(JSON.stringify(arr))
const newArr: string[] = []
while (arrCopy.length > 0) {
const position = publicKeyToNumber(keypair.publicKey, arrCopy.length)
newArr.push(...arrCopy.splice(position, 1))
}
return newArr
}

/**
* @internalapi
*
Expand All @@ -56,7 +65,7 @@ export class P2PCommunicationClient extends CommunicationClient {
| ((event: MatrixClientEvent<MatrixClientEventType.MESSAGE>) => void)
| undefined

private readonly KNOWN_RELAY_SERVERS: string[]
private readonly ENABLED_RELAY_SERVERS: string[]
public relayServer: ExposedPromise<string> | undefined

private readonly activeListeners: Map<string, (event: MatrixClientEvent<any>) => void> = new Map()
Expand All @@ -76,7 +85,8 @@ export class P2PCommunicationClient extends CommunicationClient {
super(keyPair)

logger.log('constructor', 'P2PCommunicationClient created')
this.KNOWN_RELAY_SERVERS = matrixNodes.length > 0 ? matrixNodes : KNOWN_RELAY_SERVERS
const nodes = matrixNodes.length > 0 ? matrixNodes : KNOWN_RELAY_SERVERS
this.ENABLED_RELAY_SERVERS = deterministicShuffle(nodes, keyPair)
}

public async getPairingRequestInfo(): Promise<P2PPairingRequest> {
Expand Down Expand Up @@ -132,12 +142,12 @@ export class P2PCommunicationClient extends CommunicationClient {
return node
}

const startIndex = publicKeyToNumber(this.keyPair.publicKey, this.KNOWN_RELAY_SERVERS.length)
const startIndex = publicKeyToNumber(this.keyPair.publicKey, this.ENABLED_RELAY_SERVERS.length)
let offset = 0

while (offset < this.KNOWN_RELAY_SERVERS.length) {
const serverIndex = (startIndex + offset) % this.KNOWN_RELAY_SERVERS.length
const server = this.KNOWN_RELAY_SERVERS[serverIndex]
while (offset < this.ENABLED_RELAY_SERVERS.length) {
const serverIndex = (startIndex + offset) % this.ENABLED_RELAY_SERVERS.length
const server = this.ENABLED_RELAY_SERVERS[serverIndex]

try {
await axios.get(`https://${server}/_matrix/client/versions`)
Expand Down Expand Up @@ -234,7 +244,7 @@ export class P2PCommunicationClient extends CommunicationClient {
console.log('ERROR, RETRYING')
await this.reset() // If we can't log in, let's reset
console.log('TRYING AGAIN')
if (this.loginCounter <= this.KNOWN_RELAY_SERVERS.length) {
if (this.loginCounter <= this.ENABLED_RELAY_SERVERS.length) {
this.loginCounter++
this.start()
return
Expand Down
137 changes: 137 additions & 0 deletions test/transports/p2p-client.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import * as chai from 'chai'
import * as chaiAsPromised from 'chai-as-promised'
import 'mocha'
import * as sinon from 'sinon'

import { LocalStorage, P2PCommunicationClient } from '../../src'
import { BeaconEventHandler } from '../../src/events'
import {
deterministicShuffle,
publicKeyToNumber
} from '../../src/transports/clients/P2PCommunicationClient'
import { getKeypairFromSeed } from '../../src/utils/crypto'
import { generateGUID } from '../../src/utils/generate-uuid'

// use chai-as-promised plugin
chai.use(chaiAsPromised)
const expect = chai.expect

const SEED = 'test'

describe.only(`P2PCommunicationClient`, () => {
let client: P2PCommunicationClient

beforeEach(async () => {
sinon.restore()

const keypair = await getKeypairFromSeed(SEED)
const localStorage = new LocalStorage()
const eventHandler = new BeaconEventHandler()
sinon.stub(eventHandler, 'emit').resolves()

client = new P2PCommunicationClient('Test', keypair, 2, localStorage, [])
})

it(`should have more than 1 node available`, async () => {
expect((client as any).ENABLED_RELAY_SERVERS.length > 1).to.be.true
})

it(`should return a random number from public key`, async () => {
const numberOfIterations = 1000
for (let maxNumber of [1, 2, 3, 4, 5, 10, 20]) {
const results: Record<number, number> = {}
for (let x = 0; x < numberOfIterations; x++) {
const seed = await getKeypairFromSeed(await generateGUID())
const result = publicKeyToNumber(seed.publicKey, maxNumber)
const temp = results[result] ?? 0
results[result] = temp + 1
}

const margin = 0.04 * maxNumber
const expectedAmount = Math.floor(numberOfIterations / maxNumber)

const upperLimit = expectedAmount * (1 + margin)
const lowerLimit = expectedAmount * (1 - margin)

for (let el of Object.values(results)) {
expect(el).to.be.lessThan(upperLimit)
expect(el).to.be.greaterThan(lowerLimit)
}

// TODO: Why does this fail?
// Object.values(results).forEach((el: number) => {
// isValid = isValid && el < upperLimit
// isValid = isValid && el > lowerLimit
// expect(el).to.be.greaterThan(2)
// expect(el).to.be.lessThan(2)
// })
}
})

it(`should deterministicalls shuffle an array`, async () => {
const keypair1 = await getKeypairFromSeed('test')
const keypair2 = await getKeypairFromSeed('test1')
const arr1 = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']

const shuffled1A = deterministicShuffle(arr1, keypair1)
// console.log('shuffled1A', shuffled1A)
expect(shuffled1A).to.deep.equal(['7', '2', '6', '3', '9', '4', '1', '8', '5', '10'])
const shuffled1B = deterministicShuffle(arr1, keypair2)
// console.log('shuffled1B', shuffled1B)
expect(shuffled1B).to.deep.equal(['9', '6', '3', '5', '4', '8', '7', '10', '1', '2'])

const arr2 = ['1', '2', '3', '4', '5', '6', '7', '8', '9']
const shuffled2A = deterministicShuffle(arr2, keypair1)
// console.log('shuffled2A', shuffled2A)
expect(shuffled2A).to.deep.equal(['2', '6', '3', '8', '4', '1', '7', '5', '9'])
const shuffled2B = deterministicShuffle(arr2, keypair2)
// console.log('shuffled2B', shuffled2B)
expect(shuffled2B).to.deep.equal(['6', '3', '5', '4', '8', '7', '9', '1', '2'])
})

it(`should randomize node array`, async () => {
const testCases = [
{
seed: 'test',
order: [
'beacon-node-0.papers.tech:8448',
'beacon-node-1.sky.papers.tech',
'beacon-node-2.sky.papers.tech'
]
},
{
seed: 'test1',
order: [
'beacon-node-2.sky.papers.tech',
'beacon-node-1.sky.papers.tech',
'beacon-node-0.papers.tech:8448'
]
},
{
seed: 'test2',
order: [
'beacon-node-1.sky.papers.tech',
'beacon-node-2.sky.papers.tech',
'beacon-node-0.papers.tech:8448'
]
},
{
seed: 'test3',
order: [
'beacon-node-1.sky.papers.tech',
'beacon-node-2.sky.papers.tech',
'beacon-node-0.papers.tech:8448'
]
}
]
for (let testCase of testCases) {
const keypair = await getKeypairFromSeed(testCase.seed)
const localStorage = new LocalStorage()

const c = new P2PCommunicationClient('Test', keypair, 2, localStorage, [])
expect((c as any).ENABLED_RELAY_SERVERS, `seed: ${testCase.seed}`).to.deep.equal(
testCase.order
)
}
})
})

0 comments on commit 01ea422

Please sign in to comment.