Skip to content

Commit

Permalink
Merge pull request #19 from rsksmart/feature/fixMultipleVotes
Browse files Browse the repository at this point in the history
Refactored vote process
  • Loading branch information
gsoares85 authored Dec 29, 2023
2 parents e8475a1 + 32e118e commit 395c67b
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 56 deletions.
17 changes: 17 additions & 0 deletions src/entities/Votes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn} from 'typeorm';

@Entity()
export class Votes {
@PrimaryGeneratedColumn()
id: number
@Column()
transactionId: string
@Column({default: false})
voted: boolean;
@Column({nullable: true})
transactionData: string;
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
}
167 changes: 112 additions & 55 deletions src/lib/FederatorERC.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {ConfigData} from './config';
import web3 from 'web3';
import fs from 'fs';
import TransactionSender from './TransactionSender';
import {CustomError} from './CustomError';
import {BridgeFactory} from '../contracts/BridgeFactory';
Expand All @@ -21,6 +20,15 @@ import {
} from '../types/federator';
import {AppDataSource} from "../services/AppDataSource";
import {FailedTransactions} from "../entities/FailedTransactions";
import {Votes} from "../entities/Votes";


type ValidateAndVoteReturn = {
receipt: unknown,
wasVotedBefore: boolean,
wasProcessed: boolean,
voteSuccess: boolean
}

export default class FederatorERC extends Federator {
constructor(config: ConfigData, logger: LogWrapper) {
Expand Down Expand Up @@ -373,9 +381,8 @@ export default class FederatorERC extends Federator {
try {
voteTransactionParams.transactionId = voteTransactionParams.transactionId.toLowerCase();
this.logger.info(
`TransactionId ${voteTransactionParams.transactionId} Voting Transfer ${voteTransactionParams.amount}
of originalTokenAddress:${voteTransactionParams.tokenAddress} trough sidechain bridge
${voteTransactionParams.sideChainConfig.bridge} to receiver ${voteTransactionParams.receiver}`,
`Starting vote process for the TX ${voteTransactionParams.transactionHash} from
${voteTransactionParams.originChainId} with transactionId ${voteTransactionParams.transactionId}`,
);

const txDataAbi = await voteTransactionParams.sideFedContract.getVoteTransactionABI({
Expand All @@ -390,71 +397,121 @@ export default class FederatorERC extends Federator {
destinationChainId: voteTransactionParams.destinationChainId,
});

let revertedTx: FailedTransactions | null;

const revertedTxnsPath = this.getRevertedTxnsPath(
voteTransactionParams.sideChainId,
voteTransactionParams.mainChainId,
await this.verifyIfwasRevertedAndRetry(voteTransactionParams, txDataAbi);
} catch (err) {
throw new CustomError(
`Exception Voting tx:${voteTransactionParams.transactionHash} block: ${voteTransactionParams.blockHash} originalTokenAddress: ${voteTransactionParams.tokenAddress}`,
err,
);
if (fs.existsSync(revertedTxnsPath)) {
revertedTx = await AppDataSource.getRepository(FailedTransactions)
.findOne({
where: {
mainChain: voteTransactionParams.mainChainId,
sideChain: voteTransactionParams.sideChainId,
transactionId: voteTransactionParams.transactionId
}
});
}
}
}

if (revertedTx) {
this.logger.warn(
`Skipping Voting ${voteTransactionParams.amount} of originalTokenAddress:${voteTransactionParams.tokenAddress}
TransactionId ${voteTransactionParams.transactionId} since it's marked as reverted.`,
revertedTx.txData,
);
return false;
}
async verifyIfwasRevertedAndRetry(params, txAbi) {
const revertedTx = await AppDataSource.getRepository(FailedTransactions)
.findOne({
where: {
mainChain: params.mainChainId,
sideChain: params.sideChainId,
transactionId: params.transactionId
}
});

const result = await this.validateAndVote(params, txAbi);

if (revertedTx && (result.voteSuccess || result.wasVotedBefore || result.wasProcessed)) {
await AppDataSource.getRepository(FailedTransactions).delete({
mainChain: params.mainChainId,
sideChain: params.sideChainId,
transactionId: params.transactionId
});
}
}

const receipt = await voteTransactionParams.transactionSender.sendTransaction(
voteTransactionParams.sideFedContract.getAddress(),
txDataAbi,
0,
this.config.privateKey,
async validateAndVote(params, txDataAbi): Promise<ValidateAndVoteReturn> {
const validateAndVoteReturn: ValidateAndVoteReturn = {
receipt: null,
voteSuccess: false,
wasVotedBefore: false,
wasProcessed: false
};

const fedAddress = await params.transactionSender.getAddress(this.config.privateKey);

const hasVoted = await params.sideFedContract
.hasVoted(params.transactionId, fedAddress);

const hasVotedDb = await AppDataSource.getRepository(Votes).findOne({
where: {transactionId: params.transactionId}});

const wasProcessed = await params.sideFedContract
.transactionWasProcessed(params.transactionId);

if(hasVoted || wasProcessed || hasVotedDb) {
this.logger.warn(`Transaction ${params.transactionId} will not be voted by the
federator: ${fedAddress} hasVoted result ${hasVoted} - wasProcessed result ${wasProcessed} hasVotedDb
result ${hasVotedDb}`);

validateAndVoteReturn.wasVotedBefore = hasVoted || hasVotedDb;
validateAndVoteReturn.wasProcessed = wasProcessed;
validateAndVoteReturn.receipt = null;
validateAndVoteReturn.voteSuccess = false;

return validateAndVoteReturn;
}

const receipt = await params.transactionSender.sendTransaction(
params.sideFedContract.getAddress(),
txDataAbi,
0,
this.config.privateKey,
);

if(receipt.status) {
validateAndVoteReturn.receipt = receipt;
validateAndVoteReturn.voteSuccess = true;

await AppDataSource.getRepository(Votes).insert({
voted: true,
transactionId: params.transactionId,
transactionData: JSON.stringify(params)
});
} else {
validateAndVoteReturn.receipt = null;
validateAndVoteReturn.voteSuccess = false;

this.logger.error(
`Voting ${params.amount} of originalTokenAddress:${params.tokenAddress}
TransactionId ${params.transactionId} failed, check the receipt`,
receipt,
);

if (!receipt.status) {
this.logger.error(
`Voting ${voteTransactionParams.amount} of originalTokenAddress:${voteTransactionParams.tokenAddress}
TransactionId ${voteTransactionParams.transactionId} failed, check the receipt`,
receipt,
);
const hasFailedBefore = await AppDataSource.getRepository(FailedTransactions)
.findOne({
where: {
transactionId: params.transactionId
}
});

if(!hasFailedBefore) {
await AppDataSource.getRepository(FailedTransactions).insert({
mainChain: voteTransactionParams.mainChainId,
sideChain: voteTransactionParams.sideChainId,
transactionId: voteTransactionParams.transactionId,
mainChain: params.mainChainId,
sideChain: params.sideChainId,
transactionId: params.transactionId,
txData: JSON.stringify({
originalTokenAddress: voteTransactionParams.tokenAddress,
sender: voteTransactionParams.senderAddress,
receiver: voteTransactionParams.receiver,
amount: voteTransactionParams.amount,
blockHash: voteTransactionParams.blockHash,
transactionHash: voteTransactionParams.transactionHash,
logIndex: voteTransactionParams.logIndex,
originalTokenAddress: params.tokenAddress,
sender: params.senderAddress,
receiver: params.receiver,
amount: params.amount,
blockHash: params.blockHash,
transactionHash: params.transactionHash,
logIndex: params.logIndex,
error: receipt.error,
status: receipt.status,
receipt: {...receipt},
})
});
}

return true;
} catch (err) {
throw new CustomError(
`Exception Voting tx:${voteTransactionParams.transactionHash} block: ${voteTransactionParams.blockHash} originalTokenAddress: ${voteTransactionParams.tokenAddress}`,
err,
);
}
return validateAndVoteReturn;
}
}
3 changes: 2 additions & 1 deletion src/services/AppDataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import {DataSource} from "typeorm";
import {FederatorEntity} from "../entities/Federator.entity";
import {Log} from "../entities/Log";
import {FailedTransactions} from "../entities/FailedTransactions";
import {Votes} from "../entities/Votes";

export const AppDataSource = new DataSource({
type :"sqlite",
database: "db/federatorDB.sqlite",
entities: [FederatorEntity, Log, FailedTransactions],
entities: [FederatorEntity, Log, FailedTransactions, Votes],
synchronize: true
});

0 comments on commit 395c67b

Please sign in to comment.