Skip to content

Commit

Permalink
Mobile discovery (celo-org#4680)
Browse files Browse the repository at this point in the history
### Description

Should not be merged until valora-inc/react-native-geth#28 is merged (which itself relies upon celo-org/celo-blockchain#1132), and then the react-native-geth version in this PR will be updated.

This uses the enodes that are used for static nodes as bootnodes for discovery. The current bootnodes that are deployed do not support the type of discovery required by mobile clients (discovery v5), but the static nodes do. Having more nodes as bootnodes will also help with discovery speed & diversity of the nodes discovered.

When testing on baklava with both static nodes and discovery enabled, peering was nearly immediate. When only testing with discovery enabled and no static nodes, peering was very quick (< a few seconds). A few times I found when only relying upon discovery that no peers would be found, so I recommend keeping static nodes & discovery for the time being.

### Other changes

I added some instructions on how to attach to the geth instance on the app.

### Tested

Ran a lot on android & iOS devices

### Related issues

### Backwards compatibility

Requires updates from react-native-geth (valora-inc/react-native-geth#28) and celo-blockchain (celo-org/celo-blockchain#1132) to work as intended
  • Loading branch information
tkporter authored and ewilz committed Sep 29, 2020
1 parent 4dacd7a commit d342f17
Show file tree
Hide file tree
Showing 14 changed files with 139 additions and 37 deletions.
4 changes: 4 additions & 0 deletions packages/mobile/.env
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,7 @@ APP_STORE_ID=1482389446
APP_DISPLAY_NAME=Celo (dev)
IOS_GOOGLE_SERVICE_PLIST=GoogleService-Info.dev.plist
DYNAMIC_LINK_DOMAIN=http://l.celo.org
GETH_USE_FULL_NODE_DISCOVERY=true
GETH_USE_STATIC_NODES=true
# Only for development, use with caution
GETH_START_HTTP_RPC_SERVER=false
6 changes: 5 additions & 1 deletion packages/mobile/.env.alfajores
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
ENVIRONMENT=alfajores
DEFAULT_TESTNET=alfajores
SMS_RETRIEVER_APP_SIGNATURE=GH+4Okn6nOW
# If FORNO_ENABLED_INITIALLY, local geth will not run initially.
# If FORNO_ENABLED_INITIALLY, local geth will not run initially.
# If toggled on, it will use DEFAULT_SYNC_MODE. See src/geth/consts.ts for more info
FORNO_ENABLED_INITIALLY=false
DEFAULT_SYNC_MODE=5
Expand All @@ -15,3 +15,7 @@ APP_STORE_ID=1482389446
APP_DISPLAY_NAME=Celo
IOS_GOOGLE_SERVICE_PLIST=GoogleService-Info.alfajores.plist
DYNAMIC_LINK_DOMAIN=http://l.celo.org
GETH_USE_FULL_NODE_DISCOVERY=true
GETH_USE_STATIC_NODES=true
# Only for development, use with caution
GETH_START_HTTP_RPC_SERVER=false
6 changes: 5 additions & 1 deletion packages/mobile/.env.integration
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
ENVIRONMENT=integration
DEFAULT_TESTNET=alfajores
SMS_RETRIEVER_APP_SIGNATURE=l5k6LvdPDXS
# If FORNO_ENABLED_INITIALLY, local geth will not run initially.
# If FORNO_ENABLED_INITIALLY, local geth will not run initially.
# If toggled on, it will use DEFAULT_SYNC_MODE. See src/geth/consts.ts for more info
FORNO_ENABLED_INITIALLY=false
DEFAULT_SYNC_MODE=5
Expand All @@ -13,3 +13,7 @@ SHOW_GET_INVITE_LINK=false
APP_BUNDLE_ID=org.celo.mobile.integration
APP_DISPLAY_NAME=Celo (int)
IOS_GOOGLE_SERVICE_PLIST=GoogleService-Info.integration.plist
GETH_USE_FULL_NODE_DISCOVERY=true
GETH_USE_STATIC_NODES=true
# Only for development, use with caution
GETH_START_HTTP_RPC_SERVER=false
6 changes: 5 additions & 1 deletion packages/mobile/.env.mainnet
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
ENVIRONMENT=mainnet
DEFAULT_TESTNET=mainnet
SMS_RETRIEVER_APP_SIGNATURE=bU9E4ctGtIW
# If FORNO_ENABLED_INITIALLY, local geth will not run initially.
# If FORNO_ENABLED_INITIALLY, local geth will not run initially.
# If toggled on, it will use DEFAULT_SYNC_MODE. See src/geth/consts.ts for more info
FORNO_ENABLED_INITIALLY=false
DEFAULT_SYNC_MODE=5
Expand All @@ -15,3 +15,7 @@ APP_STORE_ID=1520414263
APP_DISPLAY_NAME=Valora
IOS_GOOGLE_SERVICE_PLIST=GoogleService-Info.mainnet.plist
DYNAMIC_LINK_DOMAIN=https://vlra.app
GETH_USE_FULL_NODE_DISCOVERY=true
GETH_USE_STATIC_NODES=true
# Only for development, use with caution
GETH_START_HTTP_RPC_SERVER=false
6 changes: 5 additions & 1 deletion packages/mobile/.env.pilot
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
ENVIRONMENT=pilot
DEFAULT_TESTNET=pilot
SMS_RETRIEVER_APP_SIGNATURE=1SlgTw9pFW5
# If FORNO_ENABLED_INITIALLY, local geth will not run initially.
# If FORNO_ENABLED_INITIALLY, local geth will not run initially.
# If toggled on, it will use DEFAULT_SYNC_MODE. See src/geth/consts.ts for more info
FORNO_ENABLED_INITIALLY=true
DEFAULT_SYNC_MODE=5
Expand All @@ -13,3 +13,7 @@ SHOW_GET_INVITE_LINK=false
APP_BUNDLE_ID=org.celo.mobile.pilot
APP_DISPLAY_NAME=Celo (pilot)
IOS_GOOGLE_SERVICE_PLIST=GoogleService-Info.pilot.plist
GETH_USE_FULL_NODE_DISCOVERY=true
GETH_USE_STATIC_NODES=true
# Only for development, use with caution
GETH_START_HTTP_RPC_SERVER=false
4 changes: 4 additions & 0 deletions packages/mobile/.env.test
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,7 @@ SHOW_GET_INVITE_LINK=true
APP_BUNDLE_ID=org.celo.mobile.test
APP_DISPLAY_NAME=Celo (test)
IOS_GOOGLE_SERVICE_PLIST=GoogleService-Info.dev.plist
GETH_USE_FULL_NODE_DISCOVERY=true
GETH_USE_STATIC_NODES=true
# Only for development, use with caution
GETH_START_HTTP_RPC_SERVER=false
20 changes: 20 additions & 0 deletions packages/mobile/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,26 @@ There are two major differences in Forno mode:

Websockets (`ws`) would have been a better choice but we cannot use unencrypted `ws` provider since it would be bad to send plain-text data from a privacy perspective. Geth does not support `wss` by [default](https://github.com/ethereum/go-ethereum/issues/16423). And Kubernetes does not support it either. This forced us to use https provider.

### Attaching to the geth instance

#### Android

1. Start geth's HTTP RPC server by setting the config variable `GETH_START_HTTP_RPC_SERVER` to true. This is meant for development purposes only and can be a serious vulnerability if used in production.
2. Forward traffic from your computer's port 8545 to the android device's: `adb forward tcp:8545 tcp:8545`
3. Using a geth binary on your computer, run `geth attach http://localhost:8545`

#### iOS

We need the IP address of the iOS device. If it is being run in a simulator, the IP address is `127.0.0.1`. If not running in a simulator:

1. Ensure the iOS device is on the same network as your computer.
2. Find the device's local IP address by going to the Settings app, Wi-Fi, and tapping the 'i' next to the network.

To attach:

1. Start geth's HTTP RPC server by setting the config variable `GETH_START_HTTP_RPC_SERVER` to true. This is meant for development purposes only and can be a serious vulnerability if used in production.
2. Using a geth binary on your computer, run `geth attach http://<DEVICE_IP_ADDRESS>:8545`

### Troubleshooting

#### `Activity class {org.celo.mobile.staging/org.celo.mobile.MainActivity} does not exist.`
Expand Down
4 changes: 2 additions & 2 deletions packages/mobile/ios/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PODS:
- Analytics (3.8.0)
- boost-for-react-native (1.63.0)
- CeloBlockchain (0.0.315)
- CeloBlockchain (0.0.316)
- DoubleConversion (1.1.6)
- FBLazyVector (0.62.2)
- FBReactNativeSpec (0.62.2):
Expand Down Expand Up @@ -743,7 +743,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
Analytics: fcf79ebc393a7d77befb8d43ce296353ba9ede3d
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
CeloBlockchain: e332160cd94e43856a198aba7aa0af567021b3bd
CeloBlockchain: ea6c85d1a1a74eacf28830d47dd01ad239d1779c
DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2
FBLazyVector: 4aab18c93cd9546e4bfed752b4084585eca8b245
FBReactNativeSpec: 5465d51ccfeecb7faa12f9ae0024f2044ce4044e
Expand Down
4 changes: 2 additions & 2 deletions packages/mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
]
},
"dependencies": {
"@celo/client": "0.0.315",
"@celo/client": "0.0.316",
"@celo/contractkit": "0.4.13-dev",
"@celo/react-components": "1.0.0",
"@celo/react-native-sms-retriever": "git+https://github.com/celo-org/react-native-sms-retriever#b88e502",
Expand Down Expand Up @@ -105,7 +105,7 @@
"react-native-flag-secure-android": "git://github.com/kristiansorens/react-native-flag-secure-android#e234251",
"react-native-fs": "^2.16.6",
"react-native-gesture-handler": "^1.6.1",
"react-native-geth": "https://github.com/celo-org/react-native-geth#6beb2b3",
"react-native-geth": "https://github.com/celo-org/react-native-geth#1ba8817",
"react-native-keep-awake": "^4.0.0",
"react-native-keyboard-aware-scroll-view": "^0.9.1",
"react-native-keychain": "6.0.0",
Expand Down
8 changes: 8 additions & 0 deletions packages/mobile/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ export const FORNO_ENABLED_INITIALLY = Config.FORNO_ENABLED_INITIALLY
export const DEFAULT_SYNC_MODE: GethSyncMode = Config.DEFAULT_SYNC_MODE
? new BigNumber(Config.DEFAULT_SYNC_MODE).toNumber()
: GethSyncMode.Lightest
export const GETH_USE_FULL_NODE_DISCOVERY = stringToBoolean(
Config.GETH_USE_FULL_NODE_DISCOVERY || 'true'
)
export const GETH_USE_STATIC_NODES = stringToBoolean(Config.GETH_USE_STATIC_NODES || 'true')
// NOTE: Development purposes only
export const GETH_START_HTTP_RPC_SERVER = stringToBoolean(
Config.GETH_START_HTTP_RPC_SERVER || 'false'
)

// SECRETS
export const SEGMENT_API_KEY = keyOrUndefined(secretsFile, Config.SECRETS_KEY, 'SEGMENT_API_KEY')
Expand Down
68 changes: 47 additions & 21 deletions packages/mobile/src/geth/geth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as RNFS from 'react-native-fs'
import GethBridge, { NodeConfig } from 'react-native-geth'
import { GethEvents } from 'src/analytics/Events'
import ValoraAnalytics from 'src/analytics/ValoraAnalytics'
import { DEFAULT_TESTNET } from 'src/config'
import { DEFAULT_TESTNET, GETH_START_HTTP_RPC_SERVER } from 'src/config'
import { SYNCING_MAX_PEERS } from 'src/geth/consts'
import networkConfig from 'src/geth/networkConfig'
import Logger from 'src/utils/Logger'
Expand Down Expand Up @@ -78,24 +78,41 @@ function getFolder(filePath: string) {
return filePath.substr(0, filePath.lastIndexOf('/'))
}

async function setupGeth(sync: boolean = true): Promise<boolean> {
async function setupGeth(sync: boolean = true, bootnodeEnodes: string[]): Promise<boolean> {
Logger.debug('Geth@newGeth', 'Configure and create new Geth')
const { nodeDir, syncMode } = networkConfig
const { nodeDir, useDiscovery, syncMode } = networkConfig
const genesis: string = await readGenesisBlockFile(nodeDir)
const networkID: number = GenesisBlockUtils.getChainIdFromGenesis(genesis)

Logger.debug('Geth@newGeth', `Network ID is ${networkID}, syncMode is ${syncMode}`)

const maxPeers = sync ? SYNCING_MAX_PEERS : 0

const gethOptions: NodeConfig = {
let gethOptions: NodeConfig = {
nodeDir,
networkID,
genesis,
syncMode,
maxPeers,
useLightweightKDF: true,
ipcPath: IPC_PATH,
noDiscovery: !useDiscovery,
}

if (useDiscovery) {
Logger.debug('Geth@newGeth', 'Using discovery, bootnodes = ' + bootnodeEnodes)
gethOptions.bootnodeEnodes = bootnodeEnodes
}

if (__DEV__ && GETH_START_HTTP_RPC_SERVER) {
Logger.debug('Geth@newGeth', 'Starting HTTP RPC server')
gethOptions = {
...gethOptions,
httpHost: '0.0.0.0',
httpPort: 8545,
httpVirtualHosts: '*',
httpModules: 'admin,debug,eth,istanbul,les,net,rpc,txpool,web3',
}
}

// Setup Logging
Expand All @@ -121,23 +138,31 @@ export async function initGeth(shouldStartNode: boolean = true): Promise<boolean
}
gethLock = true

const { useDiscovery, useStaticNodes } = networkConfig

try {
let staticNodes: string[] = []
await Promise.all([
ensureGenesisBlockWritten().then((ok) => {
if (!ok) {
throw FailedToFetchGenesisBlockError
}
}),
ensureStaticNodesInitialized().then((ok) => {
if (!ok) {
throw FailedToFetchStaticNodesError
(async () => {
if (shouldStartNode && (useDiscovery || useStaticNodes)) {
staticNodes = await getStaticNodes()
}
}),
Logger.info('Geth@init', `Got static nodes: ${staticNodes}`)
return initializeStaticNodesFile(useStaticNodes ? staticNodes : [])
})(),
])

ValoraAnalytics.track(GethEvents.create_geth_start)
try {
await setupGeth(shouldStartNode)
// Use staticNodes as bootnodes because they support v4 and v5 discovery,
// and there are many of them.
// The dedicated bootnode currently only supports v4.
await setupGeth(shouldStartNode, staticNodes)
} catch (error) {
ValoraAnalytics.track(GethEvents.create_geth_error, { error: error.message })
throw error
Expand Down Expand Up @@ -178,25 +203,25 @@ export function isProviderConnectionError(error: any) {
.includes(PROVIDER_CONNECTION_ERROR)
}

async function ensureStaticNodesInitialized(): Promise<boolean> {
const { nodeDir } = networkConfig
Logger.debug('Geth@ensureStaticNodesInitialized', 'initializing static nodes')
let enodes: string | null = null
async function getStaticNodes(): Promise<string[]> {
try {
enodes = await StaticNodeUtils.getStaticNodesAsync(DEFAULT_TESTNET)
const enodesStr = await StaticNodeUtils.getStaticNodesAsync(DEFAULT_TESTNET)
return JSON.parse(enodesStr)
} catch (error) {
Logger.error(
`Failed to get static nodes for network ${DEFAULT_TESTNET},` +
`the node will not be able to sync with the network till restart`,
error
)
return false
}
if (enodes != null) {
await writeStaticNodes(nodeDir, enodes)
return true
throw FailedToFetchStaticNodesError
}
return false
}

// Writes static nodes to the correct location
async function initializeStaticNodesFile(staticNodes: string[]): Promise<void> {
const { nodeDir } = networkConfig
Logger.debug('Geth@initializeStaticNodesFile', 'initializing static nodes')
return writeStaticNodes(nodeDir, JSON.stringify(staticNodes))
}

export async function stopGethIfInitialized() {
Expand Down Expand Up @@ -268,8 +293,9 @@ function getStaticNodesFile(nodeDir: string) {
}

async function writeStaticNodes(nodeDir: string, enodes: string) {
console.info(`writeStaticNodes enodes are "${enodes}"`)
Logger.info('Geth@writeStaticNodes', `enodes are "${enodes}"`)
const staticNodesFile = getStaticNodesFile(nodeDir)
Logger.info('Geth@writeStaticNodes', `static nodes file is ${staticNodesFile}"`)
await RNFS.mkdir(getFolder(staticNodesFile))
await deleteFileIfExists(staticNodesFile)
await RNFS.writeFile(staticNodesFile, enodes, 'utf8')
Expand Down
26 changes: 25 additions & 1 deletion packages/mobile/src/geth/networkConfig.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { DEFAULT_SYNC_MODE, DEFAULT_TESTNET, FORNO_ENABLED_INITIALLY } from 'src/config'
import {
DEFAULT_SYNC_MODE,
DEFAULT_TESTNET,
FORNO_ENABLED_INITIALLY,
GETH_USE_FULL_NODE_DISCOVERY,
GETH_USE_STATIC_NODES,
} from 'src/config'
import { GethSyncMode } from 'src/geth/consts'
import Logger from 'src/utils/Logger'

Expand All @@ -21,6 +27,8 @@ interface NetworkConfig {
odisUrl: string // Phone Number Privacy service url
odisPubKey: string
signMoonpayUrl: string
useDiscovery: boolean
useStaticNodes: boolean
}

const odisUrlStaging = 'https://us-central1-celo-phone-number-privacy-stg.cloudfunctions.net'
Expand All @@ -40,6 +48,8 @@ const networkConfigs: { [testnet: string]: NetworkConfig } = {
odisUrl: odisUrlStaging,
odisPubKey: odisPubKeyStaging,
signMoonpayUrl: signMoonpayUrlStaging,
useDiscovery: GETH_USE_FULL_NODE_DISCOVERY,
useStaticNodes: GETH_USE_STATIC_NODES,
},
[Testnets.alfajoresstaging]: {
nodeDir: `.${Testnets.alfajoresstaging}`,
Expand All @@ -49,6 +59,8 @@ const networkConfigs: { [testnet: string]: NetworkConfig } = {
odisUrl: odisUrlStaging,
odisPubKey: odisPubKeyStaging,
signMoonpayUrl: signMoonpayUrlStaging,
useDiscovery: GETH_USE_FULL_NODE_DISCOVERY,
useStaticNodes: GETH_USE_STATIC_NODES,
},
[Testnets.alfajores]: {
nodeDir: `.${Testnets.alfajores}`,
Expand All @@ -59,6 +71,8 @@ const networkConfigs: { [testnet: string]: NetworkConfig } = {
odisPubKey:
'kPoRxWdEdZ/Nd3uQnp3FJFs54zuiS+ksqvOm9x8vY6KHPG8jrfqysvIRU0wtqYsBKA7SoAsICMBv8C/Fb2ZpDOqhSqvr/sZbZoHmQfvbqrzbtDIPvUIrHgRS0ydJCMsA',
signMoonpayUrl: signMoonpayUrlStaging,
useDiscovery: GETH_USE_FULL_NODE_DISCOVERY,
useStaticNodes: GETH_USE_STATIC_NODES,
},
[Testnets.pilot]: {
nodeDir: `.${Testnets.pilot}`,
Expand All @@ -68,6 +82,8 @@ const networkConfigs: { [testnet: string]: NetworkConfig } = {
odisUrl: odisUrlStaging,
odisPubKey: odisPubKeyStaging,
signMoonpayUrl: signMoonpayUrlStaging,
useDiscovery: GETH_USE_FULL_NODE_DISCOVERY,
useStaticNodes: GETH_USE_STATIC_NODES,
},
[Testnets.pilotstaging]: {
nodeDir: `.${Testnets.pilotstaging}`,
Expand All @@ -77,6 +93,8 @@ const networkConfigs: { [testnet: string]: NetworkConfig } = {
odisUrl: odisUrlStaging,
odisPubKey: odisPubKeyStaging,
signMoonpayUrl: signMoonpayUrlStaging,
useDiscovery: GETH_USE_FULL_NODE_DISCOVERY,
useStaticNodes: GETH_USE_STATIC_NODES,
},
[Testnets.baklavastaging]: {
nodeDir: `.${Testnets.baklavastaging}`,
Expand All @@ -86,6 +104,8 @@ const networkConfigs: { [testnet: string]: NetworkConfig } = {
odisUrl: odisUrlStaging,
odisPubKey: odisPubKeyStaging,
signMoonpayUrl: signMoonpayUrlStaging,
useDiscovery: GETH_USE_FULL_NODE_DISCOVERY,
useStaticNodes: GETH_USE_STATIC_NODES,
},
[Testnets.baklava]: {
nodeDir: `.${Testnets.baklava}`,
Expand All @@ -95,6 +115,8 @@ const networkConfigs: { [testnet: string]: NetworkConfig } = {
odisUrl: odisUrlStaging,
odisPubKey: odisPubKeyStaging,
signMoonpayUrl: signMoonpayUrlStaging,
useDiscovery: GETH_USE_FULL_NODE_DISCOVERY,
useStaticNodes: GETH_USE_STATIC_NODES,
},
[Testnets.mainnet]: {
nodeDir: `.${Testnets.mainnet}`,
Expand All @@ -105,6 +127,8 @@ const networkConfigs: { [testnet: string]: NetworkConfig } = {
odisPubKey:
'FvreHfLmhBjwxHxsxeyrcOLtSonC9j7K3WrS4QapYsQH6LdaDTaNGmnlQMfFY04Bp/K4wAvqQwO9/bqPVCKf8Ze8OZo8Frmog4JY4xAiwrsqOXxug11+htjEe1pj4uMA',
signMoonpayUrl: signMoonpayUrlProd,
useDiscovery: GETH_USE_FULL_NODE_DISCOVERY,
useStaticNodes: GETH_USE_STATIC_NODES,
},
}

Expand Down
Loading

0 comments on commit d342f17

Please sign in to comment.