Skip to content

Commit

Permalink
Merge pull request #7 from CudoVentures/add-whitelist
Browse files Browse the repository at this point in the history
Add whitelist functionality
  • Loading branch information
kstoykov authored Mar 17, 2022
2 parents 4ef1512 + 19948d2 commit 99aa0dc
Show file tree
Hide file tree
Showing 17 changed files with 871 additions and 139 deletions.
4 changes: 2 additions & 2 deletions module/x/gravity/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func TestHandleMsgSendToEth(t *testing.T) {

// send transaction not meeting the minimum transaction requirement
sendingCoin.Amount = sdk.NewInt(4)
expectedErrMsg := "amount does not meet minimum sending amount requirement: 5"
expectedErrMsg := "amount does not meet minimum sending amount requirement: 5acudos: invalid"
msg3 := &types.MsgSendToEth{
Sender: userCosmosAddr.String(),
EthDest: ethDestination,
Expand All @@ -91,7 +91,7 @@ func TestHandleMsgSendToEth(t *testing.T) {
// send transaction not meeting the minimum transaction FEE requirement
sendingCoin.Amount = sdk.NewInt(40)
feeCoin.Amount = sdk.NewInt(4)
expectedErrMsg = "fee does not meet minimum fee requirement: 5"
expectedErrMsg = "fee does not meet minimum fee requirement: 5acudos: invalid"
msg4 := &types.MsgSendToEth{
Sender: userCosmosAddr.String(),
EthDest: ethDestination,
Expand Down
9 changes: 4 additions & 5 deletions solidity/contract-deployer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { TestERC20A } from "./typechain/TestERC20A";
import { TestERC20B } from "./typechain/TestERC20B";
import { TestERC20C } from "./typechain/TestERC20C";
import { TestUniswapLiquidity } from "./typechain/TestUniswapLiquidity";
import { BridgeAccessControl } from "./typechain/BridgeAccessControl";
import { ethers } from "ethers";
import fs from "fs";
import commandLineArgs from "command-line-args";
Expand Down Expand Up @@ -185,12 +184,12 @@ async function deploy() {
const gravityId = ethers.utils.formatBytes32String(gravityIdString);


let bridgeAccessControl:any
const AcArts = getContractArtifacts("artifacts/contracts/BridgeAccessControl.sol/BridgeAccessControl.json");
let cudosAccessControl:any
const AcArts = getContractArtifacts("artifacts/contracts/CudosAccessControls.sol/CudosAccessControls.json");
const AcFactory = new ethers.ContractFactory(AcArts.abi, AcArts.bytecode, wallet);

console.log("Deploying AccessControl contract...")
bridgeAccessControl = (await AcFactory.deploy());
cudosAccessControl = (await AcFactory.deploy());

console.log("Starting Gravity contract deploy");
const { abi, bytecode } = getContractArtifacts(args["contract"]);
Expand Down Expand Up @@ -232,7 +231,7 @@ async function deploy() {
vote_power,
eth_addresses,
powers,
bridgeAccessControl.address
cudosAccessControl.address
)) as Gravity;

await gravity.deployed();
Expand Down
53 changes: 0 additions & 53 deletions solidity/contracts/BridgeAccessControl.sol

This file was deleted.

84 changes: 84 additions & 0 deletions solidity/contracts/CudosAccessControls.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.6.12;

import "@openzeppelin/contracts/access/AccessControl.sol";

contract CudosAccessControls is AccessControl {
// Role definitions
bytes32 public constant WHITELISTED_ROLE = keccak256("WHITELISTED_ROLE");
bytes32 public constant SMART_CONTRACT_ROLE = keccak256("SMART_CONTRACT_ROLE");
// Events
event AdminRoleGranted(
address indexed beneficiary,
address indexed caller
);
event AdminRoleRemoved(
address indexed beneficiary,
address indexed caller
);
event WhitelistRoleGranted(
address indexed beneficiary,
address indexed caller
);
event WhitelistRoleRemoved(
address indexed beneficiary,
address indexed caller
);
event SmartContractRoleGranted(
address indexed beneficiary,
address indexed caller
);
event SmartContractRoleRemoved(
address indexed beneficiary,
address indexed caller
);
modifier onlyAdminRole() {
require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "CudosAccessControls: sender must be an admin");
_;
}

constructor() public {
_setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
}

/////////////
// Lookups //
/////////////
function hasAdminRole(address _address) external view returns (bool) {
return hasRole(DEFAULT_ADMIN_ROLE, _address);
}
function hasWhitelistRole(address _address) external view returns (bool) {
return hasRole(WHITELISTED_ROLE, _address);
}
function hasSmartContractRole(address _address) external view returns (bool) {
return hasRole(SMART_CONTRACT_ROLE, _address);
}
///////////////
// Modifiers //
///////////////
function addAdminRole(address _address) external onlyAdminRole {
_setupRole(DEFAULT_ADMIN_ROLE, _address);
emit AdminRoleGranted(_address, _msgSender());
}
function removeAdminRole(address _address) external onlyAdminRole {
revokeRole(DEFAULT_ADMIN_ROLE, _address);
emit AdminRoleRemoved(_address, _msgSender());
}
function addWhitelistRole(address _address) external onlyAdminRole {
_setupRole(WHITELISTED_ROLE, _address);
emit WhitelistRoleGranted(_address, _msgSender());
}
function removeWhitelistRole(address _address) external onlyAdminRole {
revokeRole(WHITELISTED_ROLE, _address);
emit WhitelistRoleRemoved(_address, _msgSender());
}
function addSmartContractRole(address _address) external onlyAdminRole {
_setupRole(SMART_CONTRACT_ROLE, _address);
emit SmartContractRoleGranted(_address, _msgSender());
}
function removeSmartContractRole(address _address) external onlyAdminRole {
revokeRole(SMART_CONTRACT_ROLE, _address);
emit SmartContractRoleRemoved(_address, _msgSender());
}
}
53 changes: 42 additions & 11 deletions solidity/contracts/Gravity.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "./CosmosToken.sol";
import "./BridgeAccessControl.sol";
import "./CudosAccessControls.sol";

pragma experimental ABIEncoderV2;

Expand Down Expand Up @@ -60,7 +60,9 @@ contract Gravity is ReentrancyGuard {
bytes32 public state_gravityId;
uint256 public state_powerThreshold;

BridgeAccessControl public bridgeAccessControl;
CudosAccessControls public cudosAccessControls;

mapping(address => bool) public whitelisted;

// TransactionBatchExecutedEvent and SendToCosmosEvent both include the field _eventNonce.
// This is incremented every time one of these events is emitted. It is checked by the
Expand Down Expand Up @@ -104,6 +106,35 @@ contract Gravity is ReentrancyGuard {
uint256 _eventNonce
);

event WhitelistedStatusModified(
address _sender,
address[] _users,
bool _isWhitelisted
);


modifier onlyWhitelisted() {
require(
whitelisted[msg.sender] || cudosAccessControls.hasAdminRole(msg.sender) ,
"The caller is not whitelisted for this operation"
);
_;
}

function manageWhitelist(
address[] memory _users,
bool _isWhitelisted
) public onlyWhitelisted {
for (uint256 i = 0; i < _users.length; i++) {
require(
_users[i] != address(0),
"User is the zero address"
);
whitelisted[_users[i]] = _isWhitelisted;
}
emit WhitelistedStatusModified(msg.sender, _users, _isWhitelisted);
}

// TEST FIXTURES
// These are here to make it easier to measure gas usage. They should be removed before production
function testMakeCheckpoint(ValsetArgs memory _valsetArgs, bytes32 _gravityId) public pure {
Expand Down Expand Up @@ -241,7 +272,7 @@ contract Gravity is ReentrancyGuard {
uint8[] memory _v,
bytes32[] memory _r,
bytes32[] memory _s
) public nonReentrant {
) public nonReentrant onlyWhitelisted {
// CHECKS

// Check that the valset nonce is greater than the old one
Expand Down Expand Up @@ -315,7 +346,7 @@ contract Gravity is ReentrancyGuard {
// to the destination addresses. It is approved by the current Cosmos validator set.
// Anyone can call this function, but they must supply valid signatures of state_powerThreshold of the current valset over
// the batch.
function submitBatch(
function submitBatch (
// The validators that approve the batch
ValsetArgs memory _currentValset,
// These are arrays of the parts of the validators signatures
Expand All @@ -331,7 +362,7 @@ contract Gravity is ReentrancyGuard {
// a block height beyond which this batch is not valid
// used to provide a fee-free timeout
uint256 _batchTimeout
) public nonReentrant {
) public nonReentrant onlyWhitelisted{
// CHECKS scoped to reduce stack depth
{
// Check that the batch nonce is higher than the last nonce for this token
Expand Down Expand Up @@ -433,7 +464,7 @@ contract Gravity is ReentrancyGuard {
bytes32[] memory _r,
bytes32[] memory _s,
LogicCallArgs memory _args
) public nonReentrant {
) public nonReentrant onlyWhitelisted {
// CHECKS scoped to reduce stack depth
{
// Check that the call has not timed out
Expand Down Expand Up @@ -541,7 +572,7 @@ contract Gravity is ReentrancyGuard {
address _tokenContract,
bytes32 _destination,
uint256 _amount
) public nonReentrant {
) public nonReentrant {
IERC20(_tokenContract).safeTransferFrom(msg.sender, address(this), _amount);
state_lastEventNonce = state_lastEventNonce.add(1);
emit SendToCosmosEvent(
Expand Down Expand Up @@ -577,7 +608,7 @@ contract Gravity is ReentrancyGuard {
function withdrawERC20(
address _tokenAddress)
external {
require(bridgeAccessControl.hasAdminRole(msg.sender), "Recipient is not an admin");
require(cudosAccessControls.hasAdminRole(msg.sender), "Recipient is not an admin");
uint256 totalBalance = IERC20(_tokenAddress).balanceOf(address(this));
IERC20(_tokenAddress).safeTransfer(msg.sender , totalBalance);
}
Expand All @@ -591,13 +622,13 @@ contract Gravity is ReentrancyGuard {
// arguments would never be used in this case
address[] memory _validators,
uint256[] memory _powers,
BridgeAccessControl _bridgeAccessControl
CudosAccessControls _cudosAccessControls
) public {
// CHECKS

// Check that validators, powers, and signatures (v,r,s) set is well-formed
require(_validators.length == _powers.length, "Malformed current validator set");
require(address(_bridgeAccessControl) != address(0), "Access control contract address is incorrect");
require(address(_cudosAccessControls) != address(0), "Access control contract address is incorrect");

// Check cumulative power to ensure the contract has sufficient power to actually
// pass a vote
Expand All @@ -624,7 +655,7 @@ contract Gravity is ReentrancyGuard {
state_powerThreshold = _powerThreshold;
state_lastValsetCheckpoint = newCheckpoint;

bridgeAccessControl = _bridgeAccessControl;
cudosAccessControls = _cudosAccessControls;

// LOGS

Expand Down
15 changes: 14 additions & 1 deletion solidity/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,25 @@ task("accounts", "Prints the list of accounts", async (args, hre) => {
module.exports = {
// This is a sample solc configuration that specifies which version of solc to use
solidity: {
compilers: [
{
version: "0.6.6",
settings: {
optimizer: {
enabled: true
}
} },
}
},
{
version: "0.6.12",
settings: {
optimizer: {
enabled: true
}
}
}
]
},
networks: {
hardhat: {
timeout: 2000000,
Expand Down
6 changes: 3 additions & 3 deletions solidity/test-utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Gravity } from "../typechain/Gravity";
import { TestERC20A } from "../typechain/TestERC20A";
import { BridgeAccessControl } from "../typechain/BridgeAccessControl";
import { CudosAccessControls } from "../typechain/CudosAccessControls";
import { ethers } from "hardhat";
import { makeCheckpoint, signHash, getSignerAddresses, ZeroAddress } from "./pure";
import { Signer } from "ethers";
Expand All @@ -14,7 +14,7 @@ export async function deployContracts(
powerThreshold: number,
validators: Signer[],
powers: number[],
bridgeAccessControl: String,
cudosAccessControl: String,
opts?: DeployContractsOptions
) {
const TestERC20 = await ethers.getContractFactory("TestERC20A");
Expand All @@ -31,7 +31,7 @@ export async function deployContracts(
powerThreshold,
await getSignerAddresses(validators),
powers,
bridgeAccessControl
cudosAccessControl
)) as Gravity;

await gravity.deployed();
Expand Down
Loading

0 comments on commit 99aa0dc

Please sign in to comment.