add operator, compensations for mistakenly unregistered relayers and MANY tests
This commit is contained in:
parent
3677fe4cad
commit
e1ebde6748
@ -14,11 +14,21 @@ contract Proposal {
|
|||||||
newRelayerRegistry = _newRelayerRegistry;
|
newRelayerRegistry = _newRelayerRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getNullifiedTotal(address payable[15] memory relayers) public view returns (uint256) {
|
||||||
|
uint256 nullifiedTotal;
|
||||||
|
|
||||||
|
for (uint8 i = 0; i < relayers.length; i++) {
|
||||||
|
nullifiedTotal += IRelayerRegistry(relayerRegistryProxyAddr).getRelayerBalance(relayers[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullifiedTotal;
|
||||||
|
}
|
||||||
|
|
||||||
function executeProposal() public {
|
function executeProposal() public {
|
||||||
IRelayerRegistryProxy relayerRegistryProxy = IRelayerRegistryProxy(relayerRegistryProxyAddr);
|
IRelayerRegistryProxy relayerRegistryProxy = IRelayerRegistryProxy(relayerRegistryProxyAddr);
|
||||||
relayerRegistryProxy.upgradeTo(newRelayerRegistry);
|
relayerRegistryProxy.upgradeTo(newRelayerRegistry);
|
||||||
|
|
||||||
address payable[14] memory cheatingRelayers = [
|
address payable[15] memory cheatingRelayers = [
|
||||||
0x853281B7676DFB66B87e2f26c9cB9D10Ce883F37, // available-reliable-relayer.eth,
|
0x853281B7676DFB66B87e2f26c9cB9D10Ce883F37, // available-reliable-relayer.eth,
|
||||||
0x0000208a6cC0299dA631C08fE8c2EDe435Ea83B8, // 0xtornadocash.eth,
|
0x0000208a6cC0299dA631C08fE8c2EDe435Ea83B8, // 0xtornadocash.eth,
|
||||||
0xaaaaD0b504B4CD22348C4Db1071736646Aa314C6, // tornrelayers.eth
|
0xaaaaD0b504B4CD22348C4Db1071736646Aa314C6, // tornrelayers.eth
|
||||||
@ -32,11 +42,15 @@ contract Proposal {
|
|||||||
0x7853E027F37830790685622cdd8685fF0c8255A2, // tornado-secure.eth
|
0x7853E027F37830790685622cdd8685fF0c8255A2, // tornado-secure.eth
|
||||||
0xf0D9b969925116074eF43e7887Bcf035Ff1e7B19, // lowfee-relayer.eth
|
0xf0D9b969925116074eF43e7887Bcf035Ff1e7B19, // lowfee-relayer.eth
|
||||||
0xEFa22d23de9f293B11e0c4aC865d7b440647587a, // tornado-relayer.eth
|
0xEFa22d23de9f293B11e0c4aC865d7b440647587a, // tornado-relayer.eth
|
||||||
0x14812AE927e2BA5aA0c0f3C0eA016b3039574242 // pls-im-poor.eth
|
0x14812AE927e2BA5aA0c0f3C0eA016b3039574242, // pls-im-poor.eth
|
||||||
|
0x87BeDf6AD81A2907633Ab68D02c44f0415bc68C1 // tornrelayer.eth
|
||||||
];
|
];
|
||||||
|
|
||||||
IRelayerRegistry relayerRegistry = IRelayerRegistry(relayerRegistryProxyAddr);
|
IRelayerRegistry relayerRegistry = IRelayerRegistry(relayerRegistryProxyAddr);
|
||||||
|
|
||||||
|
uint256 nullifiedTotal = getNullifiedTotal(cheatingRelayers);
|
||||||
|
uint256 compensation = nullifiedTotal / 3;
|
||||||
|
|
||||||
for (uint i = 0; i < cheatingRelayers.length; i++) {
|
for (uint i = 0; i < cheatingRelayers.length; i++) {
|
||||||
relayerRegistry.unregisterRelayer(cheatingRelayers[i]);
|
relayerRegistry.unregisterRelayer(cheatingRelayers[i]);
|
||||||
}
|
}
|
||||||
@ -44,17 +58,19 @@ contract Proposal {
|
|||||||
relayerRegistry.registerRelayerAdmin(
|
relayerRegistry.registerRelayerAdmin(
|
||||||
0x4750BCfcC340AA4B31be7e71fa072716d28c29C5,
|
0x4750BCfcC340AA4B31be7e71fa072716d28c29C5,
|
||||||
"reltor.eth",
|
"reltor.eth",
|
||||||
19612626855788464787775
|
19612626855788464787775 + compensation
|
||||||
);
|
);
|
||||||
relayerRegistry.registerRelayerAdmin(
|
relayerRegistry.registerRelayerAdmin(
|
||||||
0xa0109274F53609f6Be97ec5f3052C659AB80f012,
|
0xa0109274F53609f6Be97ec5f3052C659AB80f012,
|
||||||
"relayer007.eth",
|
"relayer007.eth",
|
||||||
15242825423346070140850
|
15242825423346070140850 + compensation
|
||||||
);
|
);
|
||||||
relayerRegistry.registerRelayerAdmin(
|
relayerRegistry.registerRelayerAdmin(
|
||||||
0xC49415493eB3Ec64a0F13D8AA5056f1CfC4ce35c,
|
0xC49415493eB3Ec64a0F13D8AA5056f1CfC4ce35c,
|
||||||
"k-relayer.eth",
|
"k-relayer.eth",
|
||||||
11850064862377598277981
|
11850064862377598277981 + compensation
|
||||||
);
|
);
|
||||||
|
|
||||||
|
relayerRegistry.setOperator(address(0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,8 @@ contract RelayerRegistry is Initializable, EnsResolve {
|
|||||||
mapping(address => RelayerState) public relayers;
|
mapping(address => RelayerState) public relayers;
|
||||||
mapping(address => address) public workers;
|
mapping(address => address) public workers;
|
||||||
|
|
||||||
|
address public operator;
|
||||||
|
|
||||||
event RelayerBalanceNullified(address relayer);
|
event RelayerBalanceNullified(address relayer);
|
||||||
event WorkerRegistered(address relayer, address worker);
|
event WorkerRegistered(address relayer, address worker);
|
||||||
event WorkerUnregistered(address relayer, address worker);
|
event WorkerUnregistered(address relayer, address worker);
|
||||||
@ -76,6 +78,11 @@ contract RelayerRegistry is Initializable, EnsResolve {
|
|||||||
_;
|
_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
modifier onlyGovernanceOrOperator() {
|
||||||
|
require(msg.sender == governance || msg.sender == operator, "only governance or operator");
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(address _torn, address _governance, address _ens, address _staking, address _feeManager) public {
|
constructor(address _torn, address _governance, address _ens, address _staking, address _feeManager) public {
|
||||||
torn = TORN(_torn);
|
torn = TORN(_torn);
|
||||||
governance = _governance;
|
governance = _governance;
|
||||||
@ -305,14 +312,14 @@ contract RelayerRegistry is Initializable, EnsResolve {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @notice This function should allow governance to nullify a relayers balance
|
* @notice This function should allow governance or relayer registry operator to nullify a relayers balance
|
||||||
* @dev IMPORTANT FUNCTION:
|
* @dev IMPORTANT FUNCTION:
|
||||||
* - Should nullify the balance
|
* - Should nullify the balance
|
||||||
* - Adding nullified balance as rewards was refactored to allow for the flexibility of these funds (for gov to operate with them)
|
* - Adding nullified balance as rewards was refactored to allow for the flexibility of these funds (for gov to operate with them)
|
||||||
* @param relayer address of relayer who's balance is to nullify
|
* @param relayer address of relayer who's balance is to nullify
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
function nullifyBalance(address relayer) public onlyGovernance {
|
function nullifyBalance(address relayer) public onlyGovernanceOrOperator {
|
||||||
address masterAddress = workers[relayer];
|
address masterAddress = workers[relayer];
|
||||||
require(relayer == masterAddress, "must be master");
|
require(relayer == masterAddress, "must be master");
|
||||||
relayers[masterAddress].balance = 0;
|
relayers[masterAddress].balance = 0;
|
||||||
@ -359,4 +366,14 @@ contract RelayerRegistry is Initializable, EnsResolve {
|
|||||||
function getRelayerBalance(address relayer) external view returns (uint256) {
|
function getRelayerBalance(address relayer) external view returns (uint256) {
|
||||||
return relayers[workers[relayer]].balance;
|
return relayers[workers[relayer]].balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice This function should allow governance to set operator, who can nullify relayers balance at any time
|
||||||
|
* @dev to renounce operator, just call with address(0)
|
||||||
|
* @param newOperator new operator address
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function setOperator(address newOperator) external onlyGovernance {
|
||||||
|
operator = newOperator;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,4 +6,8 @@ interface IRelayerRegistry {
|
|||||||
function unregisterRelayer(address relayer) external;
|
function unregisterRelayer(address relayer) external;
|
||||||
|
|
||||||
function registerRelayerAdmin(address relayer, string calldata ensName, uint256 stake) external;
|
function registerRelayerAdmin(address relayer, string calldata ensName, uint256 stake) external;
|
||||||
|
|
||||||
|
function setOperator(address newOperator) external;
|
||||||
|
|
||||||
|
function getRelayerBalance(address relayer) external view returns (uint256);
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ const {
|
|||||||
getRelayerRegistryContract,
|
getRelayerRegistryContract,
|
||||||
getRelayerBalance,
|
getRelayerBalance,
|
||||||
governanceAddr,
|
governanceAddr,
|
||||||
|
cheatingRelayers
|
||||||
} = require("./utils");
|
} = require("./utils");
|
||||||
|
|
||||||
describe("Registry update", function () {
|
describe("Registry update", function () {
|
||||||
@ -117,23 +118,6 @@ describe("Registry update", function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("Cheating relayers should be unregistered", async function () {
|
it("Cheating relayers should be unregistered", async function () {
|
||||||
const cheatingRelayers = [
|
|
||||||
"0x853281B7676DFB66B87e2f26c9cB9D10Ce883F37", // available-reliable-relayer.eth,
|
|
||||||
"0x0000208a6cC0299dA631C08fE8c2EDe435Ea83B8", // 0xtornadocash.eth,
|
|
||||||
"0xaaaaD0b504B4CD22348C4Db1071736646Aa314C6", // tornrelayers.eth
|
|
||||||
"0x36DD7b862746fdD3eDd3577c8411f1B76FDC2Af5", // tornado-crypto-bot-exchange.eth
|
|
||||||
"0x5007565e69E5c23C278c2e976beff38eF4D27B3d", // official-tornado.eth
|
|
||||||
"0xa42303EE9B2eC1DB7E2a86Ed6C24AF7E49E9e8B9", // relayer-tornado.eth
|
|
||||||
"0x18F516dD6D5F46b2875Fd822B994081274be2a8b", // torn69.eth
|
|
||||||
"0x2ffAc4D796261ba8964d859867592B952b9FC158", // safe-tornado.eth
|
|
||||||
"0x12D92FeD171F16B3a05ACB1542B40648E7CEd384", // torn-relayers.eth
|
|
||||||
"0x996ad81FD83eD7A87FD3D03694115dff19db0B3b", // secure-tornado.eth
|
|
||||||
"0x7853E027F37830790685622cdd8685fF0c8255A2", // tornado-secure.eth
|
|
||||||
"0xf0D9b969925116074eF43e7887Bcf035Ff1e7B19", // lowfee-relayer.eth
|
|
||||||
"0xEFa22d23de9f293B11e0c4aC865d7b440647587a", // tornado-relayer.eth
|
|
||||||
"0x14812AE927e2BA5aA0c0f3C0eA016b3039574242" // pls-im-poor.eth
|
|
||||||
]
|
|
||||||
|
|
||||||
const relayerRegistryOldContract = await getOldRelayerRegistryContract();
|
const relayerRegistryOldContract = await getOldRelayerRegistryContract();
|
||||||
let areRegistered = await Promise.all(cheatingRelayers.map((r) => relayerRegistryOldContract.isRelayer(r)));
|
let areRegistered = await Promise.all(cheatingRelayers.map((r) => relayerRegistryOldContract.isRelayer(r)));
|
||||||
let balances = await Promise.all(cheatingRelayers.map((r) => relayerRegistryOldContract.getRelayerBalance(r)));
|
let balances = await Promise.all(cheatingRelayers.map((r) => relayerRegistryOldContract.getRelayerBalance(r)));
|
||||||
@ -232,14 +216,17 @@ describe("Registry update", function () {
|
|||||||
expect(areRelayers).to.deep.equal([false, false, false]);
|
expect(areRelayers).to.deep.equal([false, false, false]);
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Should return before-proposal stake to mistakenly unregistered relayers and register them again", async function() {
|
it("Should return before-proposal stake plus compensation to mistakenly unregistered relayers and register them again", async function() {
|
||||||
const relayerBalancesOld = await Promise.all(unregisteredAddrs.map(addr => getRelayerBalance(addr, blockBeforeProposal32Execution)));
|
const relayerBalancesOld = await Promise.all(unregisteredAddrs.map(addr => getRelayerBalance(addr, blockBeforeProposal32Execution)));
|
||||||
|
const cheatingRelayersBalances = await Promise.all(cheatingRelayers.map((r) => getRelayerBalance(r)));
|
||||||
|
const compensationForOneRelayer = cheatingRelayersBalances.reduce((a, b) => a + b, 0n) / 3n;
|
||||||
const { relayerRegistryContract } = await deployAndExecuteProposal();
|
const { relayerRegistryContract } = await deployAndExecuteProposal();
|
||||||
const relayerBalancesNew = await Promise.all(unregisteredAddrs.map(addr => getRelayerBalance(addr)));
|
const relayerBalancesNew = await Promise.all(unregisteredAddrs.map(addr => getRelayerBalance(addr)));
|
||||||
const areWorkers = await Promise.all(unregisteredAddrs.map(addr => relayerRegistryContract.isRelayer(addr)));
|
const areWorkers = await Promise.all(unregisteredAddrs.map(addr => relayerRegistryContract.isRelayer(addr)));
|
||||||
const areRelayers = await Promise.all(unregisteredAddrs.map(addr => relayerRegistryContract.isRelayerRegistered(addr, addr)));
|
const areRelayers = await Promise.all(unregisteredAddrs.map(addr => relayerRegistryContract.isRelayerRegistered(addr, addr)));
|
||||||
|
|
||||||
expect(relayerBalancesNew).to.deep.equal(relayerBalancesOld);
|
expect(relayerBalancesNew).to.deep.equal(relayerBalancesOld.map(b => b + compensationForOneRelayer));
|
||||||
|
expect(relayerBalancesNew).satisfy((b) => b.every((b) => b < 20000n * 10n ** 18n && b > 10000n * 10n ** 18n));
|
||||||
expect(areWorkers).to.deep.equal([true, true, true]);
|
expect(areWorkers).to.deep.equal([true, true, true]);
|
||||||
expect(areRelayers).to.deep.equal([true, true, true]);
|
expect(areRelayers).to.deep.equal([true, true, true]);
|
||||||
})
|
})
|
||||||
@ -298,4 +285,72 @@ describe("Registry update", function () {
|
|||||||
await expect(relayerRegistryContract.registerRelayerAdmin(me, "butterfly-attractor.eth", 1)).to.be.revertedWith("only governance");
|
await expect(relayerRegistryContract.registerRelayerAdmin(me, "butterfly-attractor.eth", 1)).to.be.revertedWith("only governance");
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("Operator", async function(){
|
||||||
|
it("Operator should be zero address after proposal", async function(){
|
||||||
|
const { relayerRegistryContract } = await deployAndExecuteProposal();
|
||||||
|
const operator = await relayerRegistryContract.operator();
|
||||||
|
|
||||||
|
expect(operator).to.be.equal("0x0000000000000000000000000000000000000000");
|
||||||
|
})
|
||||||
|
|
||||||
|
it("Operator should be set correctly by governance", async function(){
|
||||||
|
const { relayerRegistryContract } = await deployAndExecuteProposal();
|
||||||
|
const me = "0xeb3E49Af2aB5D5D0f83A9289cF5a34d9e1f6C5b4";
|
||||||
|
await relayerRegistryContract.setOperator(me);
|
||||||
|
|
||||||
|
const operator = await relayerRegistryContract.operator();
|
||||||
|
|
||||||
|
expect(operator).to.be.equal(me);
|
||||||
|
})
|
||||||
|
|
||||||
|
it("Operator should be able to nullify any relayer balance", async function(){
|
||||||
|
let { relayerRegistryContract } = await deployAndExecuteProposal();
|
||||||
|
const me = "0xeb3E49Af2aB5D5D0f83A9289cF5a34d9e1f6C5b4";
|
||||||
|
await relayerRegistryContract.setOperator(me);
|
||||||
|
|
||||||
|
const meAsSigner = await ethers.getImpersonatedSigner(me);
|
||||||
|
relayerRegistryContract = await getRelayerRegistryContract(meAsSigner);
|
||||||
|
|
||||||
|
const realRelayer = await resolveAddr("torrelayer.eth");
|
||||||
|
expect(await relayerRegistryContract.getRelayerBalance(realRelayer)).to.be.greaterThan(10n ** 18n);
|
||||||
|
expect(await relayerRegistryContract.isRelayerRegistered(realRelayer, realRelayer)).to.be.equal(true);
|
||||||
|
|
||||||
|
await relayerRegistryContract.nullifyBalance(realRelayer);
|
||||||
|
expect(await relayerRegistryContract.getRelayerBalance(realRelayer)).to.be.equal(0);
|
||||||
|
})
|
||||||
|
|
||||||
|
it("Governance also should be able to nullify any relayer balance", async function(){
|
||||||
|
const { relayerRegistryContract } = await deployAndExecuteProposal();
|
||||||
|
const realRelayer = await resolveAddr("torrelayer.eth");
|
||||||
|
expect(await relayerRegistryContract.getRelayerBalance(realRelayer)).to.be.greaterThan(10n ** 18n);
|
||||||
|
|
||||||
|
await relayerRegistryContract.nullifyBalance(realRelayer);
|
||||||
|
expect(await relayerRegistryContract.getRelayerBalance(realRelayer)).to.be.equal(0);
|
||||||
|
})
|
||||||
|
|
||||||
|
it("No one except governance and operator should be able to nullify relayers balance", async function(){
|
||||||
|
let { relayerRegistryContract } = await deployAndExecuteProposal();
|
||||||
|
const me = "0xeb3E49Af2aB5D5D0f83A9289cF5a34d9e1f6C5b4";
|
||||||
|
|
||||||
|
const realRelayer = await resolveAddr("torrelayer.eth");
|
||||||
|
const relayerBalance = await relayerRegistryContract.getRelayerBalance(realRelayer)
|
||||||
|
expect(relayerBalance).to.be.greaterThan(10n ** 18n);
|
||||||
|
|
||||||
|
const meAsSigner = await ethers.getImpersonatedSigner(me);
|
||||||
|
relayerRegistryContract = await getRelayerRegistryContract(meAsSigner);
|
||||||
|
await expect(relayerRegistryContract.nullifyBalance(realRelayer)).to.be.revertedWith("only governance or operator");
|
||||||
|
|
||||||
|
expect(await relayerRegistryContract.getRelayerBalance(realRelayer)).to.be.equal(relayerBalance)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("Only governance can set operator", async function(){
|
||||||
|
await deployAndExecuteProposal();
|
||||||
|
const me = "0xeb3E49Af2aB5D5D0f83A9289cF5a34d9e1f6C5b4";
|
||||||
|
const meAsSigner = await ethers.getImpersonatedSigner(me);
|
||||||
|
const relayerRegistryContract = await getRelayerRegistryContract(meAsSigner);
|
||||||
|
|
||||||
|
await expect(relayerRegistryContract.setOperator(me)).to.be.revertedWith("only governance")
|
||||||
|
})
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
@ -4,6 +4,24 @@ const ensAddr = "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e";
|
|||||||
const relayerRegistryProxyAddr = "0x58E8dCC13BE9780fC42E8723D8EaD4CF46943dF2";
|
const relayerRegistryProxyAddr = "0x58E8dCC13BE9780fC42E8723D8EaD4CF46943dF2";
|
||||||
const tornAddr = "0x77777FeDdddFfC19Ff86DB637967013e6C6A116C";
|
const tornAddr = "0x77777FeDdddFfC19Ff86DB637967013e6C6A116C";
|
||||||
const governanceAddr = "0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce";
|
const governanceAddr = "0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce";
|
||||||
|
const cheatingRelayers = [
|
||||||
|
"0x853281B7676DFB66B87e2f26c9cB9D10Ce883F37", // available-reliable-relayer.eth,
|
||||||
|
"0x0000208a6cC0299dA631C08fE8c2EDe435Ea83B8", // 0xtornadocash.eth,
|
||||||
|
"0xaaaaD0b504B4CD22348C4Db1071736646Aa314C6", // tornrelayers.eth
|
||||||
|
"0x36DD7b862746fdD3eDd3577c8411f1B76FDC2Af5", // tornado-crypto-bot-exchange.eth
|
||||||
|
"0x5007565e69E5c23C278c2e976beff38eF4D27B3d", // official-tornado.eth
|
||||||
|
"0xa42303EE9B2eC1DB7E2a86Ed6C24AF7E49E9e8B9", // relayer-tornado.eth
|
||||||
|
"0x18F516dD6D5F46b2875Fd822B994081274be2a8b", // torn69.eth
|
||||||
|
"0x2ffAc4D796261ba8964d859867592B952b9FC158", // safe-tornado.eth
|
||||||
|
"0x12D92FeD171F16B3a05ACB1542B40648E7CEd384", // torn-relayers.eth
|
||||||
|
"0x996ad81FD83eD7A87FD3D03694115dff19db0B3b", // secure-tornado.eth
|
||||||
|
"0x7853E027F37830790685622cdd8685fF0c8255A2", // tornado-secure.eth
|
||||||
|
"0xf0D9b969925116074eF43e7887Bcf035Ff1e7B19", // lowfee-relayer.eth
|
||||||
|
"0xEFa22d23de9f293B11e0c4aC865d7b440647587a", // tornado-relayer.eth
|
||||||
|
"0x14812AE927e2BA5aA0c0f3C0eA016b3039574242", // pls-im-poor.eth
|
||||||
|
"0x87BeDf6AD81A2907633Ab68D02c44f0415bc68C1" // tornrelayer.eth
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
async function getPermitSignature(signer, tokenContract, spender, value, deadline) {
|
async function getPermitSignature(signer, tokenContract, spender, value, deadline) {
|
||||||
const [nonce, name, version, chainId] = await Promise.all([
|
const [nonce, name, version, chainId] = await Promise.all([
|
||||||
@ -173,5 +191,6 @@ module.exports = {
|
|||||||
deployAndExecuteProposal,
|
deployAndExecuteProposal,
|
||||||
getRegisterRelayerParams,
|
getRegisterRelayerParams,
|
||||||
getRelayerBalance,
|
getRelayerBalance,
|
||||||
governanceAddr
|
governanceAddr,
|
||||||
|
cheatingRelayers
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user