Skip to content

Commit

Permalink
Merge branch 'master' into aaronmgdr/build4
Browse files Browse the repository at this point in the history
* master:
  Fix celotool expected cluster check (#1932)
  Build Page for Baklava Launch Part 1  (#1866)
  cli: Fix for #1875 (#1920)
  cli: Fixes for #1880 and #1874 (#1918)
  Minor edits to full-node and other docs (#1917)
  Check that we have provisioned phone numbers (#1927)
  Sort invite contact list (#1936)
  Generate the pop for the attestation service with geth (#1931)
  Have validators use ephemeral node keys by default (#1922)
  Enhanced Validator Setup Docs (#1926)
  Indicate account registration (#1928)
  Bugfix: actually whitelist oracle account (#1865)
  Display current account locked gold requirement in lockedgold:show (#1923)
  • Loading branch information
aaronmgdr committed Nov 27, 2019
2 parents e8c8ee1 + 3e40cca commit 453ba54
Show file tree
Hide file tree
Showing 40 changed files with 532 additions and 323 deletions.
4 changes: 2 additions & 2 deletions packages/attestation-service/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ You can use the following environment variables to configure the attestation ser

- `DATABASE_URL` - The URL under which your database is accessible, currently supported are `postgres://`, `mysql://` and `sqlite://`
- `CELO_PROVIDER` - The URL under which a celo blockchain node is reachable. This node should also have the `ATTESTATION_SIGNER_ADDRESS` unlocked for signing of the attestations themselves.
- `ACCOUNT_ADDRESS` - The address of the account on the `Accounts` smart contract
- `ATTESTATION_SIGNER_ADDRESS` - The address of the key with which attestations should be signed. You could use your account for attestations, but really you should authorize a dedicated attestation key
- `CELO_VALIDATOR_ADDRESS` - The address of the validator on the `Accounts` smart contract
- `ATTESTATION_SIGNER_ADDRESS` - The address of the key with which attestations should be signed.
- `APP_SIGNATURE` - The hash with which clients can auto-read SMS messages on android
- `SMS_PROVIDERS` - A comma-separated list of providers you want to configure, we currently support:

Expand Down
5 changes: 2 additions & 3 deletions packages/attestation-service/config/.env.development
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
DATABASE_URL=sqlite://db/dev.db
CELO_PROVIDER=https://integration-forno.celo-testnet.org
ACCOUNT_ADDRESS=0xE6e53b5fc2e18F51781f14a3ce5E7FD468247a15
ATTESTATION_KEY=x
CELO_PROVIDER=http://localhost:8545
CELO_VALIDATOR_ADDRESS=0xE6e53b5fc2e18F51781f14a3ce5E7FD468247a15
APP_SIGNATURE=x
SMS_PROVIDERS=twilio,nexmo
NEXMO_KEY=x
Expand Down
11 changes: 7 additions & 4 deletions packages/attestation-service/src/requestHandlers/attestation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,15 @@ export function getAttestationSignerAddress() {
}

export function getAccountAddress() {
if (process.env.ACCOUNT_ADDRESS === undefined || !isValidAddress(process.env.ACCOUNT_ADDRESS)) {
console.error('Did not specify valid ACCOUNT_ADDRESS')
throw new Error('Did not specify valid ACCOUNT_ADDRESS')
if (
process.env.CELO_VALIDATOR_ADDRESS === undefined ||
!isValidAddress(process.env.CELO_VALIDATOR_ADDRESS)
) {
console.error('Did not specify valid CELO_VALIDATOR_ADDRESS')
throw new Error('Did not specify valid CELO_VALIDATOR_ADDRESS')
}

return toChecksumAddress(process.env.ACCOUNT_ADDRESS)
return toChecksumAddress(process.env.CELO_VALIDATOR_ADDRESS)
}

function toBase64(str: string) {
Expand Down
6 changes: 6 additions & 0 deletions packages/attestation-service/src/sms/nexmo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ export class NexmoSmsProvider extends SmsProvider {

initialize = async () => {
const availableNumbers = await this.getAvailableNumbers()

if (!availableNumbers) {
throw new Error(
'You have no phone numbers in your Nexmo account. Please buy at least one number at https://dashboard.nexmo.com/buy-numbers'
)
}
this.nexmoNumbers = availableNumbers.map((number: any) => ({
phoneNumber: number.msisdn,
code: phoneUtil.getRegionCodeForNumber(phoneUtil.parse('+' + number.msisdn)),
Expand Down
7 changes: 3 additions & 4 deletions packages/celotool/src/e2e-tests/governance_tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import Web3 from 'web3'
import {
assertAlmostEqual,
getContext,
getEnode,
GethInstanceConfig,
importGenesis,
initAndStartGeth,
Expand Down Expand Up @@ -238,7 +237,7 @@ describe('governance tests', () => {
wsport: 8555,
rpcport: 8557,
privateKey: groupPrivateKey.slice(2),
peers: [await getEnode(8545)],
peers: [8545],
},
]
await Promise.all(
Expand All @@ -257,7 +256,7 @@ describe('governance tests', () => {
port: 30315,
wsport: 8559,
privateKey: rotation0PrivateKey.slice(2),
peers: [await getEnode(8557)],
peers: [8557],
},
{
name: 'validator2KeyRotation1',
Expand All @@ -267,7 +266,7 @@ describe('governance tests', () => {
port: 30317,
wsport: 8561,
privateKey: rotation1PrivateKey.slice(2),
peers: [await getEnode(8557)],
peers: [8557],
},
]
await Promise.all(
Expand Down
7 changes: 3 additions & 4 deletions packages/celotool/src/e2e-tests/sync_tests.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { assert } from 'chai'
import Web3 from 'web3'
import {
getEnode,
GethInstanceConfig,
getHooks,
initAndStartGeth,
Expand Down Expand Up @@ -36,7 +35,7 @@ describe('sync tests', function(this: any) {
lightserv: true,
port: 30311,
rpcport: 8553,
peers: [await getEnode(8545)],
peers: [8545],
}
await initAndStartGeth(hooks.gethBinaryPath, fullInstance)
const web3 = new Web3('http://localhost:8553')
Expand All @@ -57,7 +56,7 @@ describe('sync tests', function(this: any) {
port: 30313,
rpcport: 8555,
lightserv: syncmode !== 'light' && syncmode !== 'ultralight',
peers: [await getEnode(8553)],
peers: [8553],
}
await initAndStartGeth(hooks.gethBinaryPath, syncInstance)
})
Expand Down Expand Up @@ -94,7 +93,7 @@ describe('sync tests', function(this: any) {
this.timeout(0)
const instance: GethInstanceConfig = gethConfig.instances[0]
await killInstance(instance)
await initAndStartGeth(hooks.gethBinaryPath, instance)
await initAndStartGeth(hooks.gethBinaryPath, { ...instance, peers: [8547] })
await sleep(120) // wait for round change / resync
const address = (await web3.eth.getAccounts())[0]
const currentBlock = await web3.eth.getBlock('latest')
Expand Down
5 changes: 2 additions & 3 deletions packages/celotool/src/e2e-tests/transfer_tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { assert } from 'chai'
import Web3 from 'web3'
import { TransactionReceipt } from 'web3/types'
import {
getEnode,
GethInstanceConfig,
getHooks,
GethTestConfig,
Expand Down Expand Up @@ -196,7 +195,7 @@ describe('Transfer tests', function(this: any) {
// We need to set an etherbase here so that the full node will accept transactions from
// light clients.
etherbase: FeeRecipientAddress,
peers: [await getEnode(8545)],
peers: [8545],
}
await initAndStartGeth(hooks.gethBinaryPath, fullInstance)

Expand Down Expand Up @@ -227,7 +226,7 @@ describe('Transfer tests', function(this: any) {
port: 30307,
rpcport: 8549,
privateKey: DEF_FROM_PK,
peers: [await getEnode(8547)],
peers: [8547],
})

// Reset contracts to send RPCs through transferring node.
Expand Down
42 changes: 29 additions & 13 deletions packages/celotool/src/e2e-tests/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,8 @@ import {
generateGenesis,
getPrivateKeysFor,
getValidators,
privateKeyToPublicKey,
Validator,
} from '../lib/generate_utils'
import { getEnodeAddress } from '../lib/geth'
import { ensure0x } from '../lib/utils'

export interface GethInstanceConfig {
Expand All @@ -26,7 +24,7 @@ export interface GethInstanceConfig {
lightserv?: boolean
privateKey?: string
etherbase?: string
peers?: string[]
peers?: number[]
pid?: number
}

Expand Down Expand Up @@ -244,7 +242,8 @@ export async function killInstance(instance: GethInstanceConfig) {
}
}

export async function addStaticPeers(datadir: string, enodes: string[]) {
export async function addStaticPeers(datadir: string, ports: number[]) {
const enodes = await Promise.all(ports.map((port) => getEnode(port)))
fs.writeFileSync(`${datadir}/static-nodes.json`, JSON.stringify(enodes))
}

Expand Down Expand Up @@ -326,7 +325,7 @@ export async function startGeth(gethBinaryPath: string, instance: GethInstanceCo
}

if (validating) {
gethArgs.push('--mine', '--minerthreads=10', `--nodekeyhex=${privateKey}`)
gethArgs.push('--mine')
}

if (privateKey) {
Expand Down Expand Up @@ -438,6 +437,29 @@ export async function initAndStartGeth(gethBinaryPath: string, instance: GethIns
return startGeth(gethBinaryPath, instance)
}

// Add validator 0 as a peer of each other validator.
async function connectValidatorPeers(gethConfig: GethTestConfig) {
const admins = gethConfig.instances
.filter(({ wsport, rpcport, validating }) => validating && (wsport || rpcport))
.map(
({ wsport, rpcport }) =>
new Admin(`${wsport ? 'ws' : 'http'}://localhost:${wsport || rpcport}`)
)
const enodes = await Promise.all(admins.map(async (admin) => (await admin.getNodeInfo()).enode))
await Promise.all(
admins.map(async (admin, i) => {
await Promise.all(
enodes.map(async (enode, j) => {
if (i === j) {
return
}
await admin.addPeer(enode)
})
)
})
)
}

export function getContext(gethConfig: GethTestConfig) {
const mnemonic =
'jazz ripple brown cloth door bridge pen danger deer thumb cable prepare negative library vast'
Expand All @@ -446,9 +468,6 @@ export function getContext(gethConfig: GethTestConfig) {
const validatorPrivateKeys = getPrivateKeysFor(AccountType.VALIDATOR, mnemonic, numValidators)
const attestationKeys = getPrivateKeysFor(AccountType.ATTESTATION, mnemonic, numValidators)
const validators = getValidators(mnemonic, numValidators)
const validatorEnodes = validatorPrivateKeys.map((x: any, i: number) =>
getEnodeAddress(privateKeyToPublicKey(x), '127.0.0.1', validatorInstances[i].port)
)
const argv = require('minimist')(process.argv.slice(2))
const branch = argv.branch || 'master'
const gethRepoPath = argv.localgeth || '/tmp/geth'
Expand All @@ -464,16 +483,12 @@ export function getContext(gethConfig: GethTestConfig) {
let validatorIndex = 0
for (const instance of gethConfig.instances) {
if (instance.validating) {
// Automatically connect validator nodes to eachother.
const otherValidators = validatorEnodes.filter(
(__: string, i: number) => i !== validatorIndex
)
instance.peers = (instance.peers || []).concat(otherValidators)
instance.privateKey = instance.privateKey || validatorPrivateKeys[validatorIndex]
validatorIndex++
}
await initAndStartGeth(gethBinaryPath, instance)
}
await connectValidatorPeers(gethConfig)
if (gethConfig.migrate || gethConfig.migrateTo) {
await migrateContracts(
validatorPrivateKeys,
Expand Down Expand Up @@ -511,6 +526,7 @@ export function getContext(gethConfig: GethTestConfig) {
return startGeth(gethBinaryPath, instance)
})
)
await connectValidatorPeers(gethConfig)
}

const after = () => killGeth()
Expand Down
2 changes: 1 addition & 1 deletion packages/celotool/src/lib/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export async function switchToClusterFromEnv(checkOrPromptIfStagingOrProduction
const kubernetesClusterName = fetchEnv(envVar.KUBERNETES_CLUSTER_NAME)
const kubernetesClusterZone = fetchEnv(envVar.KUBERNETES_CLUSTER_ZONE)

const expectedCluster = `gke_${projectName}_${kubernetesClusterName}_${kubernetesClusterName}`
const expectedCluster = `gke_${projectName}_${kubernetesClusterZone}_${kubernetesClusterName}`

if (currentCluster === null || currentCluster.trim() !== expectedCluster) {
await execCmdWithExitOnFailure(
Expand Down
2 changes: 1 addition & 1 deletion packages/celotool/src/lib/migration-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ export function migrationOverrides() {
stableToken: {
initialAccounts: getAddressesFor(AccountType.FAUCET, mnemonic, 2),
values: getAddressesFor(AccountType.FAUCET, mnemonic, 2).map(() => '60000000000000000000000'), // 60k Celo Dollars
oracles: getAddressesFor(AccountType.PRICE_ORACLE, mnemonic, 1),
},
oracles: getAddressesFor(AccountType.PRICE_ORACLE, mnemonic, 1),
}
}

Expand Down
32 changes: 32 additions & 0 deletions packages/cli/src/commands/account/set-name.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { testWithGanache } from '@celo/dev-utils/lib/ganache-test'
import Web3 from 'web3'
import Register from '../account/register'
import SetName from './set-name'

process.env.NO_SYNCCHECK = 'true'

testWithGanache('account:set-name cmd', (web3: Web3) => {
test('can set the name of an account', async () => {
const accounts = await web3.eth.getAccounts()
await Register.run(['--from', accounts[0]])
await SetName.run(['--account', accounts[0], '--name', 'TestName'])
})

test('fails if account is not registered', async () => {
const accounts = await web3.eth.getAccounts()

await expect(SetName.run(['--account', accounts[0], '--name', 'TestName'])).rejects.toThrow(
"Some checks didn't pass!"
)
})

test('fails if account is not provided', async () => {
await expect(SetName.run(['--name', 'TestName'])).rejects.toThrow('Missing required flag')
})

test('fails if name is not provided', async () => {
const accounts = await web3.eth.getAccounts()

await expect(SetName.run(['--account', accounts[0]])).rejects.toThrow('Missing required flag')
})
})
33 changes: 33 additions & 0 deletions packages/cli/src/commands/account/set-name.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { flags } from '@oclif/command'
import { BaseCommand } from '../../base'
import { newCheckBuilder } from '../../utils/checks'
import { displaySendTx } from '../../utils/cli'
import { Flags } from '../../utils/command'

export default class SetName extends BaseCommand {
static description =
"Sets the name of a registered account on-chain. An account's name is an optional human readable identifier"

static flags = {
...BaseCommand.flags,
account: Flags.address({ required: true }),
name: flags.string({ required: true }),
}

static args = []

static examples = [
'register --account 0x5409ed021d9299bf6814279a6a1411a7e866a631 --name test-account',
]

async run() {
const res = this.parse(SetName)
this.kit.defaultAccount = res.flags.account
const accounts = await this.kit.contracts.getAccounts()

await newCheckBuilder(this)
.isAccount(res.flags.account)
.runChecks()
await displaySendTx('setName', accounts.setName(res.flags.name))
}
}
2 changes: 1 addition & 1 deletion packages/cli/src/commands/election/current.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export default class ElectionCurrent extends BaseCommand {
address: {},
name: {},
affiliation: {},
score: {},
score: { get: (v) => v.score.toFixed() },
ecdsaPublicKey: {},
blsPublicKey: {},
})
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/lockedgold/show.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Args } from '../../utils/command'

export default class Show extends BaseCommand {
static description =
'Show Locked Gold information for a given account. This includes the total amount of locked gold, the amount being used for voting in Validator Elections, and any pending withdrawals that have been initiated via "lockedgold:unlock".'
'Show Locked Gold information for a given account. This includes the total amount of locked gold, the amount being used for voting in Validator Elections, the Locked Gold balance this account is required to maintain due to a registered Validator or Validator Group, and any pending withdrawals that have been initiated via "lockedgold:unlock".'

static flags = {
...BaseCommand.flags,
Expand Down
Loading

0 comments on commit 453ba54

Please sign in to comment.