Skip to content

REVIEW ONLY: Blacklist test #32

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

Open
wants to merge 5 commits into
base: master
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
2 changes: 1 addition & 1 deletion docker-compose-prod.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
version: "3"
services:
federation:
image: sovryn/fed-tokenbridge:10.19
image: sovryn/fed-tokenbridge:10.19.1
ports:
- 4444:30303
volumes:
Expand Down
2 changes: 1 addition & 1 deletion docker-compose-test.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
version: "3"
services:
federation:
image: sovryn/fed-tokenbridge:10.19
image: sovryn/fed-tokenbridge:10.19.1
ports:
- 30303:30303
volumes:
Expand Down
2 changes: 1 addition & 1 deletion federator-env/mainnet-BSC-RSK/bmainnet.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
"allowTokens": "0xc4b5178Cc086E764568AdfB2dacCBB0d973e8132",
"erc777Converter": "0x9D46B33171eA7124aEE472bFe61B5B7084B55069",
"host": "https://bsc.sovryn.app/mainnet",
"fromBlock": 21118895
"fromBlock": 21892594
}
2 changes: 1 addition & 1 deletion federator-env/mainnet-BSC-RSK/rskmainnet.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
"allowTokens": "0x200FD7a1cCEa4651F15008cC99bF82d7461EFd3F",
"erc777Converter": "0xc8149b1F15794D135Dfe2924955cb90709Ac7070",
"host": "https://mainnet.sovryn.app/rpc",
"fromBlock": 4614985
"fromBlock": 4690509
}
2 changes: 1 addition & 1 deletion federator-env/mainnet-ETH-RSK/mainnet.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
"allowTokens": "0xf9A59a649859A27d664C8bDb51fA53bCb268545C",
"erc777Converter": "0xC0b2A9E31f69e4F0bC24584C678C582714a4fA1b",
"host": "https://mainnet.infura.io/v3/f02caaf003d14ad7bee83f33eeda2f5a",
"fromBlock": 15483239
"fromBlock": 15674601
}
2 changes: 1 addition & 1 deletion federator-env/mainnet-ETH-RSK/rskmainnet.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
"allowTokens": "0x7DC1D73C620cF8eB167eDD32942DA0d01B70adC0",
"erc777Converter": "0xb86623c103843ccf75c6f0073d84bcfc0e34536c",
"host": "https://mainnet.sovryn.app/rpc",
"fromBlock": 4615325
"fromBlock": 4690549
}
74 changes: 71 additions & 3 deletions sovryn-token-bridge/federator/src/lib/Federator.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ module.exports = class Federator {
this.confirmationTable = config.confirmationTable;

this.chatBot = chatBot || new NullBot(this.logger);

this.numBlacklisted = 0;
this.numValid = 0;
}

async populateMemberAddresses() {
Expand Down Expand Up @@ -150,6 +153,13 @@ module.exports = class Federator {
let newLastBlockNumber;
let allLogsConfirmed = true;
for (let log of logs) {
if (await this._isBlackListedLog(log)) {
this.numBlacklisted++;
this.logger.warn(`BLACKLISTED_TRANSFER in tx: ${log.transactionHash}, num_bl: ${this.numBlacklisted}, num_valid: ${this.numValid}`);
this.logger.warn('Skipping transfer to/from a blacklisted address, skipped log:');
continue;
}

this.logger.info('Processing event log:', log);

const { _amount: amount, _symbol: symbol } = log.returnValues;
Expand All @@ -161,9 +171,11 @@ module.exports = class Federator {
`Block: ${log.blockHash} Tx: ${log.transactionHash} token: ${symbol} is already processed (on Bridge) -- no need to get signatures`
);
} else {
const signatures = await this._requestSignatureFromFederators(log);
this.logger.info('Collected enough signatures');
await this._processLog(log, signatures);
this.numValid++;
this.logger.info(`VALID_TRANSFER in tx: ${log.transactionHash}, num_bl: ${this.numBlacklisted}, num_valid: ${this.numValid}`);
// const signatures = await this._requestSignatureFromFederators(log);
// this.logger.info('Collected enough signatures');
// await this._processLog(log, signatures);
}
} else if (allLogsConfirmed) {
newLastBlockNumber = log.blockNumber - 1;
Expand All @@ -178,6 +190,7 @@ module.exports = class Federator {
}

async _requestSignatureFromFederators(log) {
return [];
return new Promise((resolve, reject) => {
this.logger.info('Requesting other federators to sign event');

Expand Down Expand Up @@ -289,6 +302,12 @@ module.exports = class Federator {
}

async _isAlreadyProcessed(log) {
const isBlacklisted = await this._isBlackListedLog(log); // extra safety
if (isBlacklisted) {
this.logger.warn('Treating already processed log as blacklisted. Log:');
this.logger.warn(log);
return true;
}
const {
_to: receiver,
_amount: amount,
Expand Down Expand Up @@ -426,6 +445,8 @@ module.exports = class Federator {
transactionIdU,
signatures
) {
this.logger.error("EXECUTION DISABLED");
return;
signatures = signatures && this._removeNotNeededSignatures(signatures);

try {
Expand Down Expand Up @@ -597,6 +618,13 @@ module.exports = class Federator {
throw new CustomError('Invalid return when searching for event');
}

if (await this._isBlackListedLog(logs[0])) {
throw new CustomError(`Refusing to sign blacklisted log ${logs[0].id}, tx ${logs[0].transactionHash}`);
}

this.logger.info("Would sign valid tx but that is currently disabled");
return;

const { _tokenAddress, _amount, _to, _symbol, _decimals, _granularity, _userData } =
logs[0].returnValues;
const { blockHash, transactionHash, logIndex } = logs[0];
Expand Down Expand Up @@ -636,4 +664,44 @@ module.exports = class Federator {

return { signatureData: { signature, deadline }, logId: id };
}

_isBlackListedAddress(address) {
if (typeof address !== 'string' || !address.startsWith('0x') || address.length !== 42) {
throw new CustomError(`Invalid address: ${address}`);
}
return (
address.toLowerCase() ===
'0xc92ebecda030234c10e149beead6bba61197531a'.toLowerCase()
);
}

async _isBlackListedLog(log) {
const txHash = log.transactionHash;
const receiver = log.returnValues._to;
const userData = log.returnValues._userData;
if (this._isBlackListedAddress(receiver)) {
this.logger.warn(`Transaction ${txHash} is blacklisted (receiver ${receiver})`);
return true;
}
if (userData) {
if (userData.length === 66) {
const userDataAddress = userData.replace(/^0x000000000000000000000000/, '0x')
if (userDataAddress.length === 42 && this._isBlackListedAddress(userDataAddress)) {
this.logger.warn(`Transaction ${txHash} is blacklisted (userData ${userData})`);
return true;
}
} else if (userData.length > 66) {
this.logger.warn(`Transaction ${txHash} is a direct fastbtc transfer which is currently blocked`);
return true;
}
}

const transaction = await this.mainWeb3.eth.getTransaction(txHash);
if (this._isBlackListedAddress(transaction.from)) {
this.logger.warn(`Transaction ${txHash} is blacklisted (sender ${transaction.from})`);
return true;
}

return false;
}
};
2 changes: 2 additions & 0 deletions sovryn-token-bridge/federator/src/lib/GasServices.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ module.exports = class GasServices {
this.logger.error('Unhandled Error on gasPriceAvg start()', err);
});

console.log("Blacklist test neabled, not waiting for average gas.");
return;
this.logger.info(
`Process is waiting to calculate average gas of ${avgGasPeriodInterval} ms.`
);
Expand Down
9 changes: 5 additions & 4 deletions sovryn-token-bridge/federator/src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,10 +202,11 @@ async function run() {
} catch (e) {
// just ignore now, this is only for debugging
}
if (numOtherPeers < config.minimumPeerAmount) {
logger.info(`Waiting for enough peers (now ${numOtherPeers}. node ${nodeId}, leader ${leaderId})`);
return;
}
console.log("Blacklist test neabled, not waiting for peers.")
// if (numOtherPeers < config.minimumPeerAmount) {
// logger.info(`Waiting for enough peers (now ${numOtherPeers}. node ${nodeId}, leader ${leaderId})`);
// return;
// }

if (p2pNode.isLeader()) {
console.log(`This node is a leader -- handling iteration. (${numOtherPeers} other peers, node ${nodeId}, leader ${leaderId})`);
Expand Down
178 changes: 178 additions & 0 deletions sovryn-token-bridge/federator/test/Federator.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1108,4 +1108,182 @@ describe('Federator module tests', () => {
expect(sendTransactionSpy).toHaveBeenCalledTimes(2);
});
});

describe('blacklist', () => {
const exampleBlacklistedLog = {
address: '0xdfc7127593c8Af1a17146893F10e08528F4C2AA7',
blockNumber: 21892325,
transactionHash: '0xecb840d31f006d39b5fd5ff3cee63b8dcf9cbe4c6c42b38ad110eb4c61de865a',
transactionIndex: 102,
blockHash: '0xdca05d2e51690a481001ec0b2494c973a5293eb4ae480af8fb4103af0a080e0c',
logIndex: 287,
removed: false,
id: 'log_5db89b85',
returnValues: {
'0': '0xa233108b33dc77F1Eee9d183eE1DC9725E76d475',
'1': '0xc92EBeCDa030234C10e149bEEAD6bba61197531a',
'2': '99900000000000000',
'3': 'bRBTC',
'4': '0x00',
'5': '18',
'6': '1',
_tokenAddress: '0xa233108b33dc77F1Eee9d183eE1DC9725E76d475',
_to: '0xc92EBeCDa030234C10e149bEEAD6bba61197531a',
_amount: '99900000000000000',
_symbol: 'bRBTC',
_userData: '0x00',
_decimals: '18',
_granularity: '1'
},
event: 'Cross',
signature: '0x33409cca56f705a7bbed38b7db57cf3a63317f3c1b9a747bbfb3d3ecffa84f6f',
raw: {
data: '0x0000000000000000000000000000000000000000000000000162ea854d0fc00000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000005625242544300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000',
topics: [
'0x33409cca56f705a7bbed38b7db57cf3a63317f3c1b9a747bbfb3d3ecffa84f6f',
'0x000000000000000000000000a233108b33dc77f1eee9d183ee1dc9725e76d475',
'0x000000000000000000000000c92ebecda030234c10e149beead6bba61197531a'
]
}
};
const exampleBlacklistedTx = {
"blockHash": "0xdca05d2e51690a481001ec0b2494c973a5293eb4ae480af8fb4103af0a080e0c",
"blockNumber": 21892325,
"from": "0xc92EBeCDa030234C10e149bEEAD6bba61197531a",
"gas": 255550,
"gasPrice": "5000000000",
"hash": "0xecb840d31f006d39b5fd5ff3cee63b8dcf9cbe4c6c42b38ad110eb4c61de865a",
"input": "0x8d8773ae00000000000000000000000068e75416a99f61a8ef3186b3bee41dbf2a3fd4e8000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000c92ebecda030234c10e149beead6bba61197531a000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000",
"nonce": 98,
"to": "0x1dA3D286a3aBeaDb2b7677c99730D725aF58e39D",
"transactionIndex": 102,
"value": "0",
"type": 0,
"v": "0x94",
"r": "0x34b8c865bc317e94911f52331cea759e3e8e04d8b1ac5bde83c72cb1d06bf5ba",
"s": "0x65ce4779aca0f6f78ad6e5bf7659b93270c4ddc990f881bc6faf7140940f95b1"
}
const blacklistedAddress = exampleBlacklistedLog.returnValues._to;
const nonBlacklistedAddress = '0x1234567890123456789012345678901234567890'
const currentBlock = 30000000;
const chainId = 1;

let federator;
beforeEach(() => {
federator = new Federator(MAIN_FEDERATOR, testConfig, logger, {}, web3Mock);
})

function copyBlacklistedLogAndTx() {
return {
log: JSON.parse(JSON.stringify(exampleBlacklistedLog)),
tx: JSON.parse(JSON.stringify(exampleBlacklistedTx))
}
}

function mockMethods(log, tx) {
federator.mainBridgeContract.getPastEvents = (event, { fromBlock, toBlock}) => {
if(event !== 'Cross') {
throw new Error('Invalid event');
}
if(fromBlock !== log.blockNumber || toBlock !== log.blockNumber) {
throw new Error('Invalid block range');
}
return [log];
}
mockFederatorMethods(federator, {
async getTransaction(txid) {
if (txid !== log.transactionHash) {
throw new Error("unknown tx")
}
return tx;
},
async getBlockNumber() {
return currentBlock
},
net: {
getId: () => Promise.resolve(chainId),
}
});
}

it('_isBlacklistedLog/_isBlacklistedAddress/_alreadyProcessed', async () => {
const { log, tx } = copyBlacklistedLogAndTx();
mockMethods(log, tx);

expect(federator._isBlackListedAddress(blacklistedAddress)).toBeTruthy();
expect(federator._isBlackListedAddress(nonBlacklistedAddress)).toBeFalsy();

expect(await federator._isBlackListedLog(log)).toBeTruthy();
expect(await federator._isAlreadyProcessed(log)).toBeTruthy();

log.returnValues._to = nonBlacklistedAddress;
expect(await federator._isBlackListedLog(log)).toBeTruthy();
expect(await federator._isAlreadyProcessed(log)).toBeTruthy();

tx.from = nonBlacklistedAddress;
expect(await federator._isBlackListedLog(log)).toBeFalsy();
expect(await federator._isAlreadyProcessed(log)).toBeFalsy();

log.returnValues._userData = '0x000000000000000000000000c92ebecda030234c10e149beead6bba61197531a'
expect(await federator._isBlackListedLog(log)).toBeTruthy();
expect(await federator._isAlreadyProcessed(log)).toBeTruthy();

log.returnValues._userData = "0x000000000000000000000000c92ebecda030234c10e149beead6bba61197531a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
expect(await federator._isBlackListedLog(log)).toBeTruthy();
expect(await federator._isAlreadyProcessed(log)).toBeTruthy();

log.returnValues._to = blacklistedAddress;
log.returnValues._userData = '0x00';
tx.from = nonBlacklistedAddress;
expect(await federator._isBlackListedLog(log)).toBeTruthy();
expect(await federator._isAlreadyProcessed(log)).toBeTruthy();

log.returnValues._to = nonBlacklistedAddress;
expect(await federator._isBlackListedLog(log)).toBeFalsy();
expect(await federator._isAlreadyProcessed(log)).toBeFalsy();
});

it('signTransaction', async () => {
const { log, tx } = copyBlacklistedLogAndTx();
mockMethods(log, tx);
const expectedMsg = `Refusing to sign blacklisted log ${log.id}, tx ${log.transactionHash}`
const signData = {
blockNumber: log.blockNumber,
id: log.id,
}
let signed = false;
try {
await federator.signTransaction(signData)
signed = true;
} catch (e) {
expect(e.message).toEqual(expectedMsg)
}
expect(signed).toBeFalsy();

log.returnValues._to = nonBlacklistedAddress;
tx.from = nonBlacklistedAddress;
const ret = await federator.signTransaction(signData)
expect(ret).toBeTruthy();
});

it('_processLogs', async () => {
const { log, tx } = copyBlacklistedLogAndTx();
mockMethods(log, tx);
const signatures = ['signature1', 'signature2']
federator._requestSignatureFromFederators = () => signatures;
const processLogSpy = jest.spyOn(federator, '_processLog');
const executeTransactionSpy = jest.spyOn(federator, '_executeTransaction');

const ctr = new ConfirmationTableReader(1, federator.confirmationTable);
await federator._processLogs(ctr, [log]);
expect(processLogSpy).toHaveBeenCalledTimes(0);

log.returnValues._to = nonBlacklistedAddress;
tx.from = nonBlacklistedAddress;
await federator._processLogs(ctr, [log]);
expect(processLogSpy).toHaveBeenCalledTimes(1);
expect(processLogSpy).toHaveBeenCalledWith(log, signatures);
expect(executeTransactionSpy).toHaveBeenCalledTimes(1);
});
})
});
1 change: 1 addition & 0 deletions sovryn-token-bridge/federator/test/web3Mock/eth.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ eth.getTransactionCount = () => defaults.data.ethTransactionCount;
eth.getGasPrice = () => defaults.data.gasPrice;
eth.estimateGas = () => defaults.data.estimatedGas;
eth.getNonce = () => defaults.data.ethTransactionCount;
eth.getTransaction = () => defaults.data.receipt; // not the same return type but close

let promiseSend = function () {
var promiEvent = Web3PromiEvent();
Expand Down