From cc70435a1cd604741b1518bfc5b7215b2491f5f2 Mon Sep 17 00:00:00 2001 From: Asa Oines Date: Sun, 24 Nov 2019 17:09:29 -0800 Subject: [PATCH] Fix elect validators migration, deploy integration (#1847) --- .env | 6 +- .env.integration | 23 +++--- .../src/e2e-tests/governance_tests.ts | 14 +--- packages/celotool/src/e2e-tests/utils.ts | 3 - packages/celotool/src/lib/ethstats.ts | 4 +- packages/celotool/src/lib/generate_utils.ts | 8 +- .../contracts/governance/Election.sol | 18 +---- packages/protocol/migrations/19_governance.ts | 2 + .../migrations/20_elect_validators.ts | 73 +++++++++++-------- packages/protocol/migrationsConfig.js | 4 +- packages/walletkit/package.json | 2 +- 11 files changed, 74 insertions(+), 83 deletions(-) diff --git a/.env b/.env index 97d3ecca66a..24243c5006f 100644 --- a/.env +++ b/.env @@ -12,14 +12,14 @@ CLUSTER_DOMAIN_NAME="celo-networks-dev" TESTNET_PROJECT_NAME="celo-testnet" BLOCKSCOUT_DOCKER_IMAGE_REPOSITORY="gcr.io/celo-testnet/blockscout" -BLOCKSCOUT_DOCKER_IMAGE_TAG="8be64adeccbb8f67dea3b6653600021577e544c5" +BLOCKSCOUT_DOCKER_IMAGE_TAG="909682b7435fc3e05849211d96fb1dfbf76306f2" BLOCKSCOUT_WEB_REPLICAS=3 BLOCKSCOUT_DB_SUFFIX= ETHSTATS_DOCKER_IMAGE_REPOSITORY="gcr.io/celo-testnet/ethstats" ETHSTATS_DOCKER_IMAGE_TAG="cd037ea1e18848466452ba9890c1f1bcd3f61009" -ETHSTATS_TRUSTED_ADDRESSES="0x480b0e0A641AE45521377d4984d085a003934561,0xF523976B9FB2160e9a43D8Aee016b98ea8f57837" -ETHSTATS_BANNED_ADDRESSES="0xFd24A0699288E141A855bC8c0dd0400C5E89D5e5" +ETHSTATS_TRUSTED_ADDRESSES="" +ETHSTATS_BANNED_ADDRESSES="" FAUCET_GENESIS_ACCOUNTS=2 diff --git a/.env.integration b/.env.integration index 31f7b4abca4..86243ff42ca 100644 --- a/.env.integration +++ b/.env.integration @@ -11,31 +11,36 @@ CLUSTER_DOMAIN_NAME="celo-testnet" TESTNET_PROJECT_NAME="celo-testnet" BLOCKSCOUT_DOCKER_IMAGE_REPOSITORY="gcr.io/celo-testnet/blockscout" -BLOCKSCOUT_DOCKER_IMAGE_TAG="ad86714d629c01272e0651dec1fb6a968c3cec71" +BLOCKSCOUT_DOCKER_IMAGE_TAG="909682b7435fc3e05849211d96fb1dfbf76306f2" BLOCKSCOUT_WEB_REPLICAS=3 -BLOCKSCOUT_DB_SUFFIX="25" +BLOCKSCOUT_DB_SUFFIX="29" BLOCKSCOUT_SUBNETWORK_NAME="Integration" +ETHSTATS_DOCKER_IMAGE_REPOSITORY="gcr.io/celo-testnet/ethstats" +ETHSTATS_DOCKER_IMAGE_TAG="cd037ea1e18848466452ba9890c1f1bcd3f61009" +ETHSTATS_TRUSTED_ADDRESSES="" +ETHSTATS_BANNED_ADDRESSES="" + FAUCET_GENESIS_ACCOUNTS=2 GETH_NODE_DOCKER_IMAGE_REPOSITORY="us.gcr.io/celo-testnet/geth" # When upgrading change this to latest commit hash from the master of the geth repo # `geth $ git show | head -n 1` -GETH_NODE_DOCKER_IMAGE_TAG="c1ae452c707f8bee91a9a0bf49193e78e9c8512e" +GETH_NODE_DOCKER_IMAGE_TAG="09a217ff58a95214cbc5189c933359707f4fdaf2" GETH_BOOTNODE_DOCKER_IMAGE_REPOSITORY="gcr.io/celo-testnet/geth-all" # When upgrading change this to latest commit hash from the master of the geth repo # `geth $ git show | head -n 1` -GETH_BOOTNODE_DOCKER_IMAGE_TAG="c1ae452c707f8bee91a9a0bf49193e78e9c8512e" +GETH_BOOTNODE_DOCKER_IMAGE_TAG="09a217ff58a95214cbc5189c933359707f4fdaf2" CELOTOOL_DOCKER_IMAGE_REPOSITORY="gcr.io/celo-testnet/celo-monorepo" -CELOTOOL_DOCKER_IMAGE_TAG="celotool-2cb725c36b69e7ae608875610af080f4f3fa79bd" +CELOTOOL_DOCKER_IMAGE_TAG="celotool-5bea6d30cbe6aa4272b32a4d2cfed5567f422ea9" TRANSACTION_METRICS_EXPORTER_DOCKER_IMAGE_REPOSITORY="gcr.io/celo-testnet/celo-monorepo" -TRANSACTION_METRICS_EXPORTER_DOCKER_IMAGE_TAG="transaction-metrics-exporter-2cb725c36b69e7ae608875610af080f4f3fa79bd" +TRANSACTION_METRICS_EXPORTER_DOCKER_IMAGE_TAG="transaction-metrics-exporter-5bea6d30cbe6aa4272b32a4d2cfed5567f422ea9" -ATTESTATION_SERVICE_DOCKER_IMAGE_REPOSITORY="gcr.io/celo-testnet/celo-monorepo" -ATTESTATION_SERVICE_DOCKER_IMAGE_TAG="attestation-service-2cb725c36b69e7ae608875610af080f4f3fa79bd" +ATTESTATION_SERVICE_DOCKER_IMAGE_REPOSITORY="us.gcr.io/celo-testnet/celo-monorepo" +ATTESTATION_SERVICE_DOCKER_IMAGE_TAG="attestation-service-c8e3392aa2ca44ff83b4035700ece5fd12ed2b84" GETH_EXPORTER_DOCKER_IMAGE_REPOSITORY="gcr.io/celo-testnet-production/geth-exporter" GETH_EXPORTER_DOCKER_IMAGE_TAG="ed7d21bd50592709173368cd697ef73c1774a261" @@ -47,7 +52,6 @@ BLOCK_TIME=3 EPOCH=1000 ISTANBUL_REQUEST_TIMEOUT_MS=3000 -# "og" -> our original 4 tx nodes, "${n}" -> for deriving n tx nodes from the MNEMONIC # NOTE: we only create static IPs when TX_NODES is set to "og" VALIDATORS=20 TX_NODES=2 @@ -61,7 +65,6 @@ GETH_NODES_BACKUP_CRONJOB_ENABLED=true CONTRACT_CRONJOBS_ENABLED=true CLUSTER_CREATION_FLAGS="--enable-autoscaling --min-nodes 3 --max-nodes 8 --machine-type=n1-standard-4" - GETH_NODE_CPU_REQUEST=400m GETH_NODE_MEMORY_REQUEST=2.5G diff --git a/packages/celotool/src/e2e-tests/governance_tests.ts b/packages/celotool/src/e2e-tests/governance_tests.ts index bbb13416cb5..6173d66a13e 100644 --- a/packages/celotool/src/e2e-tests/governance_tests.ts +++ b/packages/celotool/src/e2e-tests/governance_tests.ts @@ -196,16 +196,6 @@ describe('governance tests', () => { return decryptedKeystore.privateKey } - const activate = async (account: string, txOptions: any = {}) => { - const [group] = await validators.methods.getRegisteredValidatorGroups().call() - const tx = election.methods.activate(group) - let gas = txOptions.gas - if (!gas) { - gas = (await tx.estimateGas({ ...txOptions })) * 2 - } - return tx.send({ from: account, ...txOptions, gas }) - } - const isLastBlockOfEpoch = (blockNumber: number, epochSize: number) => { return blockNumber % epochSize === 0 } @@ -302,12 +292,14 @@ describe('governance tests', () => { blockNumber = await web3.eth.getBlockNumber() await sleep(0.1) } while (blockNumber % epoch !== 1) - await activate(validatorAccounts[0]) // Prepare for member swapping. const groupWeb3 = new Web3('ws://localhost:8555') await waitToFinishSyncing(groupWeb3) const groupKit = newKitFromWeb3(groupWeb3) + const group: string = (await groupWeb3.eth.getAccounts())[0] + await (await groupKit.contracts.getElection()).activate(group) + validators = await groupKit._web3Contracts.getValidators() const membersToSwap = [validatorAccounts[0], validatorAccounts[1]] const memberSwapper = await newMemberSwapper(groupKit, membersToSwap) diff --git a/packages/celotool/src/e2e-tests/utils.ts b/packages/celotool/src/e2e-tests/utils.ts index c1a9421b0d7..05efcd8e954 100644 --- a/packages/celotool/src/e2e-tests/utils.ts +++ b/packages/celotool/src/e2e-tests/utils.ts @@ -362,9 +362,6 @@ export async function migrateContracts( election: { minElectableValidators: '1', }, - reserve: { - goldBalance: 100000, - }, stableToken: { initialBalances: { addresses: validators.map(ensure0x), diff --git a/packages/celotool/src/lib/ethstats.ts b/packages/celotool/src/lib/ethstats.ts index c512501cf84..21fd416b298 100644 --- a/packages/celotool/src/lib/ethstats.ts +++ b/packages/celotool/src/lib/ethstats.ts @@ -1,6 +1,6 @@ import { installGenericHelmChart, removeGenericHelmChart } from 'src/lib/helm_deploy' import { execCmdWithExitOnFailure } from 'src/lib/utils' -import { envVar, fetchEnv } from './env-utils' +import { envVar, fetchEnv, fetchEnvOrFallback } from './env-utils' import { AccountType, getAddressesFor } from './generate_utils' const helmChartPath = '../helm-charts/ethstats' @@ -50,6 +50,6 @@ function generateAuthorizedAddresses() { publicKeys.push(getAddressesFor(AccountType.TX_NODE, mnemonic, txNodes)) publicKeys.push(getAddressesFor(AccountType.VALIDATOR, mnemonic, validatorNodes)) - publicKeys.push(fetchEnv(envVar.ETHSTATS_TRUSTED_ADDRESSES).split(',')) + publicKeys.push(fetchEnvOrFallback(envVar.ETHSTATS_TRUSTED_ADDRESSES, '').split(',')) return publicKeys.reduce((accumulator, value) => accumulator.concat(value), []) } diff --git a/packages/celotool/src/lib/generate_utils.ts b/packages/celotool/src/lib/generate_utils.ts index dbd7f1f93d4..2633757b842 100644 --- a/packages/celotool/src/lib/generate_utils.ts +++ b/packages/celotool/src/lib/generate_utils.ts @@ -91,8 +91,8 @@ export const privateKeyToStrippedAddress = (privateKey: string) => const validatorZeroBalance = fetchEnvOrFallback( envVar.VALIDATOR_ZERO_GENESIS_BALANCE, - '100010011000000000000000000' -) // 100,010,011 CG + '103010030000000000000000000' +) // 103,010,030 CG const validatorBalance = fetchEnvOrFallback( envVar.VALIDATOR_GENESIS_BALANCE, '10011000000000000000000' @@ -109,12 +109,12 @@ export const getStrippedAddressesFor = (accountType: AccountType, mnemonic: stri getAddressesFor(accountType, mnemonic, n).map(strip0x) export const getValidators = (mnemonic: string, n: number) => { - return getPrivateKeysFor(AccountType.VALIDATOR, mnemonic, n).map((key) => { + return getPrivateKeysFor(AccountType.VALIDATOR, mnemonic, n).map((key, i) => { const blsKeyBytes = blsPrivateKeyToProcessedPrivateKey(key) return { address: strip0x(privateKeyToAddress(key)), blsPublicKey: bls12377js.BLS.privateToPublicBytes(blsKeyBytes).toString('hex'), - balance: n === 0 ? validatorZeroBalance : validatorBalance, + balance: i === 0 ? validatorZeroBalance : validatorBalance, } }) } diff --git a/packages/protocol/contracts/governance/Election.sol b/packages/protocol/contracts/governance/Election.sol index c3d90b1fa8b..72403b0206e 100644 --- a/packages/protocol/contracts/governance/Election.sol +++ b/packages/protocol/contracts/governance/Election.sol @@ -752,8 +752,7 @@ contract Election is totalNumMembersElected = totalNumMembersElected.add(1); } } - // Shuffle the validator set using validator-supplied entropy - return shuffleArray(electedValidators); + return electedValidators; } /** @@ -789,21 +788,6 @@ contract Election is return (groupIndex, memberElected); } - /** - * @notice Randomly permutes an array of addresses. - * @param array The array to permute. - * @return The permuted array. - */ - function shuffleArray(address[] memory array) private view returns (address[] memory) { - bytes32 r = getRandom().getBlockRandomness(block.number); - for (uint256 i = array.length - 1; i > 0; i = i.sub(1)) { - uint256 j = uint256(r) % (i + 1); - (array[i], array[j]) = (array[j], array[i]); - r = keccak256(abi.encodePacked(r)); - } - return array; - } - /** * @notice Returns get current validator signers using the precompiles. * @return List of current validator signers. diff --git a/packages/protocol/migrations/19_governance.ts b/packages/protocol/migrations/19_governance.ts index 187a99e676c..d38fd786329 100644 --- a/packages/protocol/migrations/19_governance.ts +++ b/packages/protocol/migrations/19_governance.ts @@ -52,9 +52,11 @@ module.exports = deploymentForCoreContract( ) const proxyAndImplementationOwnedByGovernance = [ + 'Accounts', 'Attestations', 'BlockchainParameters', 'Election', + 'EpochRewards', 'Escrow', 'Exchange', 'FeeCurrencyWhitelist', diff --git a/packages/protocol/migrations/20_elect_validators.ts b/packages/protocol/migrations/20_elect_validators.ts index c4db15d6a0a..c1e72566fa0 100644 --- a/packages/protocol/migrations/20_elect_validators.ts +++ b/packages/protocol/migrations/20_elect_validators.ts @@ -64,19 +64,26 @@ async function registerValidatorGroup( numMembers ) + // Add a premium to cover tx fees + const v = lockedGoldValue.times(1.01).toFixed() + + console.info(` - send funds ${v} to group address ${account.address}`) await sendTransactionWithPrivateKey(web3, null, privateKey, { to: account.address, - value: lockedGoldValue.times(1.01).toFixed(), // Add a premium to cover tx fees + value: v, }) + console.info(` - lock gold`) await lockGold(accounts, lockedGold, lockedGoldValue, account.privateKey) + console.info(` - setName`) // @ts-ignore const setNameTx = accounts.contract.methods.setName(`${name} ${encodedKey}`) await sendTransactionWithPrivateKey(web3, setNameTx, account.privateKey, { to: accounts.address, }) + console.info(` - registerValidatorGroup`) // @ts-ignore const tx = validators.contract.methods.registerValidatorGroup( toFixed(config.validators.commission).toString() @@ -86,7 +93,7 @@ async function registerValidatorGroup( to: validators.address, }) - return account + return [account, lockedGoldValue] } async function registerValidator( @@ -99,6 +106,9 @@ async function registerValidator( index: number, networkName: string ) { + const valName = `CLabs Validator #${index} on ${networkName}` + + console.info(` - lockGold ${valName}`) await lockGold( accounts, lockedGold, @@ -106,12 +116,15 @@ async function registerValidator( validatorPrivateKey ) + console.info(` - setName ${valName}`) + // @ts-ignore - const setNameTx = accounts.contract.methods.setName(`CLabs Validator #${index} on ${networkName}`) + const setNameTx = accounts.contract.methods.setName(valName) await sendTransactionWithPrivateKey(web3, setNameTx, validatorPrivateKey, { to: accounts.address, }) + console.info(` - registerValidator ${valName}`) const publicKey = privateKeyToPublicKey(validatorPrivateKey) const blsPublicKey = getBlsPublicKey(validatorPrivateKey) const blsPoP = getBlsPoP(privateKeyToAddress(validatorPrivateKey), validatorPrivateKey) @@ -123,6 +136,8 @@ async function registerValidator( to: validators.address, }) + console.info(` - affiliate ${valName}`) + // @ts-ignore const affiliateTx = validators.contract.methods.affiliate(groupAddress) @@ -130,6 +145,8 @@ async function registerValidator( to: validators.address, }) + console.info(` - setAccountDataEncryptionKey ${valName}`) + // @ts-ignore const registerDataEncryptionKeyTx = accounts.contract.methods.setAccountDataEncryptionKey( privateKeyToPublicKey(validatorPrivateKey) @@ -141,6 +158,7 @@ async function registerValidator( // Authorize the attestation signer const attestationKeyAddress = privateKeyToAddress(attestationKey) + console.info(` - authorizeAttestationSigner ${valName}->${attestationKeyAddress}`) const message = web3.utils.soliditySha3({ type: 'address', value: privateKeyToAddress(validatorPrivateKey), @@ -159,6 +177,7 @@ async function registerValidator( to: accounts.address, }) + console.info(` - done ${valName}`) return } @@ -195,6 +214,11 @@ module.exports = async (_deployer: any, networkName: string) => { return } + // Assumptions about where funds are located: + // * Validator 0 holds funds for all groups' stakes + // * Validator 1-n holds funds needed for their own stake + const validator0Key = valKeys[0] + if (valKeys.length < config.validators.minElectableValidators) { console.warn( ` Warning: Have ${valKeys.length} Validator keys but require a minimum of ${ @@ -219,32 +243,32 @@ module.exports = async (_deployer: any, networkName: string) => { } console.info(` Registering validator group: ${groupName} ...`) - const account = await registerValidatorGroup( + const [groupAccount, groupLockedGoldValue] = await registerValidatorGroup( groupName, accounts, lockedGold, validators, - groupKeys[0], + validator0Key, groupKeys.length ) - console.info(` * Registering ${groupKeys.length} validators ...`) await Promise.all( - groupKeys.map((key, index) => - registerValidator( + groupKeys.map((key, i) => { + const index = idx * config.validators.maxGroupSize + i + return registerValidator( accounts, lockedGold, validators, key, attestationKeys[index], - account.address, + groupAccount.address, index, networkName ) - ) + }) ) - console.info(' * Adding Validators to Validator Group ...') + console.info(` * Adding Validators to ${groupName} ...`) for (const [i, key] of groupKeys.entries()) { const address = privateKeyToAddress(key) console.info(` - Adding ${address} ...`) @@ -255,41 +279,30 @@ module.exports = async (_deployer: any, networkName: string) => { NULL_ADDRESS, prevGroupAddress ) - await sendTransactionWithPrivateKey(web3, addTx, account.privateKey, { + await sendTransactionWithPrivateKey(web3, addTx, groupAccount.privateKey, { to: validators.address, }) } else { // @ts-ignore const addTx = validators.contract.methods.addMember(address) - await sendTransactionWithPrivateKey(web3, addTx, account.privateKey, { + await sendTransactionWithPrivateKey(web3, addTx, groupAccount.privateKey, { to: validators.address, }) } } - console.info(' * Voting for the group ...') - // Make another deposit so our vote has more weight. - const minLockedGoldVotePerValidator = 10000 - const value = new BigNumber(groupKeys.length) - .times(minLockedGoldVotePerValidator) - .times(web3.utils.toWei('1')) - // @ts-ignore - const bondTx = lockedGold.contract.methods.lock() - await sendTransactionWithPrivateKey(web3, bondTx, groupKeys[0], { - to: lockedGold.address, - value, - }) - + console.info(' * Group voting for itself ...') // @ts-ignore const voteTx = election.contract.methods.vote( - account.address, - '0x' + value.toString(16), + groupAccount.address, + '0x' + groupLockedGoldValue.toString(16), NULL_ADDRESS, prevGroupAddress ) - await sendTransactionWithPrivateKey(web3, voteTx, groupKeys[0], { + await sendTransactionWithPrivateKey(web3, voteTx, groupAccount.privateKey, { to: election.address, }) - prevGroupAddress = account.address + + prevGroupAddress = groupAccount.address } } diff --git a/packages/protocol/migrationsConfig.js b/packages/protocol/migrationsConfig.js index 7318345d041..0bf5c32e95a 100644 --- a/packages/protocol/migrationsConfig.js +++ b/packages/protocol/migrationsConfig.js @@ -98,11 +98,11 @@ const DefaultConfig = { }, validators: { groupLockedGoldRequirements: { - value: '10000000000000000000000', // 10k gold + value: '10000000000000000000000', // 10k gold per validator duration: 60 * 24 * 60 * 60, // 60 days }, validatorLockedGoldRequirements: { - value: '10000000000000000000000', // 1 gold + value: '10000000000000000000000', // 10k gold duration: 60 * 24 * 60 * 60, // 60 days }, validatorScoreParameters: { diff --git a/packages/walletkit/package.json b/packages/walletkit/package.json index 88bc280334c..4c5deaba9de 100644 --- a/packages/walletkit/package.json +++ b/packages/walletkit/package.json @@ -12,7 +12,7 @@ "clean": "rm -rf lib .artifacts contracts types lib", "lint": "tslint -c tslint.json --project tsconfig.json", "prebuild": "yarn clean", - "build": "yarn run build:for-env integration", + "build": "yarn run build:for-env pilotstaging", "prepublishOnly": "yarn run build", "test": "export TZ=UTC && jest --ci --silent --coverage --runInBand test/attestations.test.ts test/erc20-utils.test.ts test/google-storage-utils.test.ts test/start_geth.sh test/transaction-utils.test.ts", "test:verbose": "export TZ=UTC && jest --ci --verbose --runInBand test/attestations.test.ts test/erc20-utils.test.ts test/google-storage-utils.test.ts test/start_geth.sh test/transaction-utils.test.ts"