Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/migrate delegation to onchain #505

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion services/delegation/.env.dist
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ MAINNET_EXPLORER_URL=https://explorer.mainnet.taraxa.io
TESTNET_EXPLORER_URL=https://explorer.testnet.taraxa.io
INDEXER_STAKING_URL=http://localhost:8000/subgraphs/name/taraxa_project/staking
PORT=3001
NR_BLOCKS_UNDELEGATION_DELAY=5
NR_BLOCKS_UNDELEGATION_DELAY=5
DPOS_PROXY_ADDRESS=0x0000000000000000000000000000000000000000
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ data:
MAINNET_INDEXER_URL: {{ .Values.app.mainnetIndexerUrl | quote }}
TESTNET_INDEXER_URL: {{ .Values.app.testnetIndexerUrl | quote }}
INDEXER_STAKING_URL: {{ .Values.app.indexerStakingUrl | quote }}
TESTNET_OWN_NODES: {{ .Values.app.testnetOwnNodes | toJson | quote }}
DPOS_PROXY_ADDRESS: {{ .Values.app.dposProxyAddress | quote }}
NODE_ENV: {{ .Values.app.environment | quote }}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ app:
dbCommunityName: taraxa-community-default
queueHost: default-queue.address
indexerStakingUrl: http://default-taraxa-indexer:8000/subgraphs/name/taraxa_project/staking
dposProxyAddress: ''

serverPort: 3000
dbPort: 5432
Expand Down
17 changes: 1 addition & 16 deletions services/delegation/src/config/delegation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,6 @@ import { ethers } from 'ethers';
import { registerAs } from '@nestjs/config';

export default registerAs('delegation', () => {
let ownNodes = [];

if (process.env.TESTNET_OWN_NODES !== '') {
try {
ownNodes = JSON.parse(process.env.TESTNET_OWN_NODES);
} catch (e) {
ownNodes = [];
console.error(
`Could not parse own nodes JSON`,
process.env.TESTNET_OWN_NODES,
);
}
}

return {
yield: 20,
commissionChangeThreshold: 5,
Expand All @@ -27,8 +13,7 @@ export default registerAs('delegation', () => {
testnetDelegation: ethers.BigNumber.from(1000000).mul(
ethers.BigNumber.from(10).pow(18),
),
mainnetDelegation: ethers.BigNumber.from(10).pow(18),
testnetOwnNodes: ownNodes,
mainnetDelegation: ethers.BigNumber.from(10).pow(18), // not sure why this is here
undelegationConfirmationDelay: Number(
process.env.NR_BLOCKS_UNDELEGATION_DELAY || 5,
),
Expand Down
1 change: 1 addition & 0 deletions services/delegation/src/config/ethereum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export default registerAs('ethereum', () => ({
testnetWallet: process.env.TESTNET_WALLET,
mainnetIndexerUrl: process.env.MAINNET_INDEXER_URL,
testnetIndexerUrl: process.env.TESTNET_INDEXER_URL,
dposProxyAddress: process.env.DPOS_PROXY_ADDRESS,
}));
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const getProviders = () => [
ethereum.testnetEndpoint,
ethereum.testnetWallet,
delegation.testnetDelegation,
delegation.testnetOwnNodes,
ethereum.dposProxyAddress,
);
},
inject: [delegationConfig.KEY, ethereumConfig.KEY],
Expand All @@ -36,7 +36,7 @@ const getProviders = () => [
ethereum.mainnetEndpoint,
ethereum.mainnetWallet,
delegation.minDelegation,
[],
ethereum.dposProxyAddress,
);
},
inject: [delegationConfig.KEY, ethereumConfig.KEY],
Expand Down
129 changes: 7 additions & 122 deletions services/delegation/src/modules/blockchain/blockchain.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export class BlockchainService {
endpoint: string,
walletKey: string,
public defaultDelegationAmount: ethers.BigNumber,
private ownNodes: string[],
dposProxyAddress: string,
) {
this.provider = new ethers.providers.JsonRpcProvider({
url: endpoint,
Expand All @@ -21,7 +21,7 @@ export class BlockchainService {
this.wallet = new ethers.Wallet(walletKey, this.provider);

this.contract = new ethers.Contract(
'0x00000000000000000000000000000000000000fe',
dposProxyAddress,
[
'function delegate(address validator) payable',
'function undelegate(address validator, uint256 amount) external',
Expand All @@ -39,13 +39,13 @@ export class BlockchainService {
endpoint: string,
walletKey: string,
defaultDelegationAmount: ethers.BigNumber,
ownNodes: string[],
dposProxyAddress: string,
) {
return new BlockchainService(
endpoint,
walletKey,
defaultDelegationAmount,
ownNodes,
dposProxyAddress,
);
}

Expand Down Expand Up @@ -75,9 +75,10 @@ export class BlockchainService {
addressProof: string,
vrfKey: string,
) {
await this.rebalanceOwnNodes(true);

try {
const validatorData = await this.contract.getValidator(address);
if (validatorData && validatorData.owner) return true;

const tx = await this.contract.registerValidator(
address,
addressProof,
Expand All @@ -95,7 +96,6 @@ export class BlockchainService {
} catch (e) {
console.error(`Could not create validator ${address}`, e);
}

return false;
}

Expand Down Expand Up @@ -132,119 +132,4 @@ export class BlockchainService {
const receipt = await tx.wait();
return receipt.blockNumber;
}

async rebalanceOwnNodes(addOneNode = false) {
if (this.ownNodes.length === 0) {
console.error(`Can't delegate to own nodes - No own nodes.`);
return false;
}

// Getting all validators from contract (including own nodes)
const allValidators = await this.getAllValidators();
if (allValidators.length === 0) {
console.error(`Can't delegate to own nodes - No validators in contract.`);
return false;
}

// Getting all registered own nodes and total stake for each
const ownNodes = allValidators
.filter((validator) =>
this.ownNodes
.map((node) => node.toLowerCase())
.includes(validator.account.toLowerCase()),
)
.map((validator) => ({
address: validator.account,
stake: validator.info.total_stake,
}));
if (ownNodes.length === 0) {
console.error(`Can't delegate to own nodes - No own nodes in contract.`);
return false;
}
const totalStakeOwnNodes = ownNodes.reduce((prev, curr) => {
return prev.add(curr.stake);
}, ethers.BigNumber.from(0));

let numberOfCommunityNodes = allValidators.length - ownNodes.length;
if (addOneNode) {
numberOfCommunityNodes++;
}

const totalStakeCommunityNodes = this.defaultDelegationAmount.mul(
numberOfCommunityNodes,
);
const majorityStake = totalStakeCommunityNodes
.mul(2)
.add(this.defaultDelegationAmount);

// If our node have 2 x stake of community nodes already, we exit
if (totalStakeOwnNodes.gte(majorityStake)) {
return;
}

// Sorting own nodes in ascending order
ownNodes.sort((a, b) => {
if (a.stake.eq(b.stake)) {
return 0;
}
if (a.stake.gt(b.stake)) {
return 1;
}
return -1;
});

const avgStakeOwnNode = majorityStake.div(ownNodes.length);
let left = majorityStake.sub(totalStakeOwnNodes);
for (const ownNode of ownNodes) {
if (left.isZero()) {
break;
}

if (ownNode.stake.gte(avgStakeOwnNode)) {
continue;
}

const d = avgStakeOwnNode.sub(ownNode.stake);
let toDelegate = ethers.BigNumber.from(0);
if (d.gt(left)) {
toDelegate = ethers.BigNumber.from(left);
} else {
toDelegate = ethers.BigNumber.from(d);
}

console.log(`Delegating ${toDelegate} to ${ownNode.address}`);

try {
const tx = await this.contract.delegate(ownNode.address, {
gasPrice: this.provider.getGasPrice(),
value: toDelegate,
});
await tx.wait();
} catch (e) {
console.error(
`Can't delegate to own nodes - delegation call failed for node ${ownNode.address}`,
);
break;
}

left = left.sub(toDelegate);
}
}

private async getAllValidators() {
let validators = [];
let page = 0;
let hasNextPage = true;
while (hasNextPage) {
try {
const allValidators = await this.contract.getValidators(page);
validators = [...validators, ...allValidators.validators];
hasNextPage = !allValidators.end;
page++;
} catch (e) {
hasNextPage = false;
}
}
return validators;
}
}
58 changes: 0 additions & 58 deletions services/delegation/src/modules/delegation/delegation.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,6 @@ export class DelegationService {
// add call to undelegation, create undelegation, save in db
// await this.blockchainService.unregisterValidator(address);
} else {
let toDelegate = totalNodeDelegation.sub(currentDelegation);
if (!isNodeRegistered) {
let isCreatedOnchain: boolean;
if (type === NodeType.TESTNET) {
Expand All @@ -352,19 +351,6 @@ export class DelegationService {
node.vrfKey,
);
}
if (type === NodeType.MAINNET) {
isCreatedOnchain =
await this.mainnetBlockchainService.registerValidator(
address,
node.addressProof,
node.vrfKey,
);
if (isCreatedOnchain) {
toDelegate = totalNodeDelegation.sub(
this.mainnetBlockchainService.defaultDelegationAmount,
);
}
}
if (node) {
node.isCreatedOnchain = isCreatedOnchain;
await this.nodeRepository.save(node);
Expand Down Expand Up @@ -444,50 +430,6 @@ export class DelegationService {
.outbound_deposits;
}

async rebalanceMainnet() {
const ownNodes = await this.getOwnNodes('mainnet');

for (const node of ownNodes) {
const nodeEntity = await this.nodeRepository.findOne({ where: { node } });
await this.ensureDelegation(nodeEntity?.id, NodeType.MAINNET, node);
}
}

private async getOwnNodes(type: 'mainnet' | 'testnet'): Promise<string[]> {
let endpoint: string;
if (type === 'mainnet') {
endpoint = this.config.get<string>('ethereum.mainnetEndpoint');
} else {
endpoint = this.config.get<string>('ethereum.testnetEndpoint');
}

const state = await this.httpService
.post(
endpoint,
{
jsonrpc: '2.0',
method: 'taraxa_getConfig',
params: [],
id: 1,
},
{
headers: {
'Content-Type': 'application/json',
},
},
)
.toPromise();

if (state.status !== 200) {
throw new Error('Failed to get DPOS stake');
}

const genesisState = state.data.result.final_chain.state.dpos.genesis_state;
const genesisStateKey = Object.keys(genesisState)[0];

return Object.keys(genesisState[genesisStateKey]);
}

async getDelegators() {
const d = this.delegationRepository
.createQueryBuilder('d')
Expand Down
19 changes: 0 additions & 19 deletions services/delegation/src/modules/node/node.consumer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,17 +66,6 @@ export class NodeConsumer implements OnModuleInit {
}
}

if (node.isMainnet()) {
try {
await this.mainnetBlockchainService.getValidator(node.address);
isCreatedOnchain = true;
} catch (e) {
this.logger.debug(
`${ENSURE_NODE_ONCHAIN_JOB} worker (job ${job.id}): Validator ${node.address} doesn't exist in contract`,
);
}
}

if (isCreatedOnchain) {
node.isCreatedOnchain = isCreatedOnchain;
await this.nodeRepository.save(node);
Expand All @@ -97,14 +86,6 @@ export class NodeConsumer implements OnModuleInit {
);
}

if (node.isMainnet()) {
node.isCreatedOnchain =
await this.mainnetBlockchainService.registerValidator(
node.address,
node.addressProof,
node.vrfKey,
);
}
await this.nodeRepository.save(node);
} catch (e) {
this.logger.error(
Expand Down