Skip to content

Instantly share code, notes, and snippets.

@srinjoychakravarty
Last active November 8, 2022 01:52
Show Gist options
  • Save srinjoychakravarty/4399157896dad5ea100d8a7acf17090a to your computer and use it in GitHub Desktop.
Save srinjoychakravarty/4399157896dad5ea100d8a7acf17090a to your computer and use it in GitHub Desktop.
intro to localhost cows comms
// pragma experimental ABIEncoderV2;
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.16;
import {VerifySignatureUtil as SigUtil} from "./VerifySignatureUtil.sol";
import {VerifyUtil} from "./VerifyUtil.sol";
import {KeyManagement} from "./KeyManagement.sol";
import {GlobalState} from "./GlobalState.sol";
import {CustodyWallet} from "./CustodyWallet.sol";
import "forge-std/console.sol";
contract COWSManagement {
using SigUtil for SigUtil.VerifiedSignature;
//*******************//
//****** ENUMS ******//
//*******************//
enum ActionType {
TerminateCOWS,
PauseCOWS,
UnpauseCOWS
}
enum Status {
Processing,
Approved,
Completed,
ForceClosed
}
enum AccountType {
Empty,
Omnibus,
Airdrop,
CustomerInvestigation,
Garbage
}
//*********************//
//****** STRUCTS ******//
//*********************//
struct ContractInfo {
int32 contractId;
address contractAddr;
}
struct COWSInfo {
string chain;
AccountType accountType;
mapping(string => ContractInfo) contracts;
}
struct RecognizeRecord {
SigUtil.VerifiedSignature adminSignature;
}
struct RequestUnit {
ActionType action;
bytes32 cowId;
}
struct RequestData {
bytes32 requestId;
address requester;
RequestUnit[] units;
SigUtil.VerifiedSignature[] signatures;
SigUtil.VerifiedSignature callerSignature;
uint256 expireAtBlock;
bool isPassed;
bool isExecuted;
Status status;
}
struct InitUnit {
bytes32 cowId;
string chain;
AccountType accountType;
address[] castlKeys;
}
//***********************//
//****** VARIABLES ******//
//***********************//
mapping(bytes32 => COWSInfo) public COWS;
mapping(bytes32 => RequestData) public cowsChangeRequests;
mapping(bytes32 => RecognizeRecord) public recognizeRecords;
//********************//
//****** EVENTS ******//
//********************//
event DeployNewCOWS(string msg, bool isSuccess);
event Recognize(string msg, bool isSuccess);
event RequestChanges(string msg, bool isSuccess);
event ApproveChanges(string msg, bytes32 failedRequestId, bool isSuccess);
event ExecuteChanges(string msg, bytes32 failedCowId, bool isSuccess);
event ForceClose(string msg, bool isSuccess);
event SpillContractAddrs(
address globalStateContractLocation,
address keyManagementContractLocation,
address custodyWalletContractLocation
);
//******************//
//****** VIEW ******//
//******************//
function returnContractAddress(bytes32 cowId, string memory _contract)
public
view
returns (address)
{
ContractInfo memory _info = COWS[cowId].contracts[_contract];
return _info.contractAddr;
}
function getAccountType(bytes32 cowId) public view returns (AccountType accountType) {
return COWS[cowId].accountType;
}
function getRecognizeRecord(bytes32 cowId) public view returns (RecognizeRecord memory record) {
return recognizeRecords[cowId];
}
//*****************************//
//****** DEPLOY NEW COWS ******//
//*****************************//
function deployNewCOWS(InitUnit[] calldata cowsUnits) public returns (bool) {
for (uint256 i; i < cowsUnits.length; ) {
bytes32 _cowId = cowsUnits[i].cowId;
// Check if number of castl_keys > 1; if not, emit event DeployNewCOWS("Need at least 1 CASTL key when add a new COWS!", false)
if (cowsUnits[i].castlKeys.length < 1) {
emit DeployNewCOWS("Need at least 1 CASTL key when add a new COWS!", false);
revert("Need 1+ CASTLkeys for new COWS!");
}
/* Deploy contracts, set init global state as Deployed and update info & activeStatus as Preparing for each COWS */
// Deploy GlobalState contract
address _globalStateAddress;
try new GlobalState(GlobalState.COWSState.Deployed, address(this)) returns (
GlobalState _address
) {
_globalStateAddress = address(_address);
} catch Error(string memory reason) {
emit DeployNewCOWS("Failed to deploy COWS!", false);
revert(string(abi.encodePacked("GLOBALSTATE: ", reason)));
}
COWSInfo storage _cowInfo = COWS[_cowId];
_cowInfo.chain = cowsUnits[i].chain;
_cowInfo.accountType = cowsUnits[i].accountType;
{
string memory _globalState = string("GlobalState");
ContractInfo storage _stateContractInfo = _cowInfo.contracts[_globalState];
_stateContractInfo.contractId = 0;
_stateContractInfo.contractAddr = _globalStateAddress; // deployed contract address
}
// Deploy KeyManagement contract
address _keyManagementAddress;
try
new KeyManagement(cowsUnits[i].castlKeys, _cowId, address(this), _globalStateAddress)
returns (KeyManagement _address) {
_keyManagementAddress = address(_address);
} catch Error(string memory reason) {
emit DeployNewCOWS("Failed to deploy COWS!", false);
revert(string(abi.encodePacked("KEYMANAGEMENT: ", reason)));
}
{
string memory _keyContract = string("KeyManagement");
ContractInfo storage _keyContractInfo = _cowInfo.contracts[_keyContract];
_keyContractInfo.contractId = 1;
_keyContractInfo.contractAddr = _keyManagementAddress; // deployed contract address
}
// Deploy CustodyWallet contract
address _custodyWalletAddress;
try new CustodyWallet(_cowId, address(this), _globalStateAddress) returns (
CustodyWallet _address
) {
_custodyWalletAddress = address(_address);
} catch Error(string memory reason) {
emit DeployNewCOWS("Failed to deploy COWS!", false);
revert(string(abi.encodePacked("CUSTODYWALLET: ", reason)));
}
{
string memory _custodyWallet = string("CustodyWallet");
ContractInfo storage _custodyContractInfo = _cowInfo.contracts[_custodyWallet];
_custodyContractInfo.contractId = 2;
_custodyContractInfo.contractAddr = _custodyWalletAddress; // deployed contract address
}
// TODO: deploy all other contracts when they're made
unchecked {
++i;
}
emit SpillContractAddrs(_globalStateAddress, _keyManagementAddress, _custodyWalletAddress);
}
emit DeployNewCOWS("All deployed, preparing!", true);
return true;
}
//***********************//
//****** RECOGNIZE ******//
//***********************//
// This function is for confirming CASTL keys already recognized the contract deployment and intend to init it
function recognize(SigUtil.BaseSignature calldata adminSignature) public {
// Extract params from message in adminSignature
bytes32 cowsId;
address signerAddr;
{
string[] memory _extractedMessage = SigUtil.extractMessage(adminSignature.message);
SigUtil.ExtractedSigningMessage memory message = SigUtil.convertToStruct(_extractedMessage);
cowsId = message.cowId;
signerAddr = message.signerPublicAddress;
}
// Check if the cowId is already existed in recognizeRecords
if (recognizeRecords[cowsId].adminSignature.recoveredSigner != address(0)) {
emit Recognize("COW is already recognized!", false);
revert("cow id existed");
}
KeyManagement keyManagement;
{
address _keyAddr = returnContractAddress(cowsId, string("KeyManagement"));
keyManagement = KeyManagement(_keyAddr);
}
// Check if caller has valid CASTL key;
// if not, emit event Recognize("Invalid CASTL key from caller!", false)
KeyManagement.CASTLState castlState = keyManagement.castlKeys(signerAddr);
if (castlState != KeyManagement.CASTLState.Enabled) {
emit Recognize("Invalid CASTL key from caller!", false);
revert("castl key not enabled");
}
// Call verifySignature() to check if the recovered signer of caller signature match;
// if not, emit event Recognize("Invalid CASTL signature from caller!", false)
(bool isVerified, ) = VerifyUtil.verifySingleSignature(signerAddr, adminSignature);
if (!isVerified) {
emit Recognize("Invalid CASTL signature from caller!", false);
revert("invalid castl signature");
}
// Update adminSignature to recognizeRecords
RecognizeRecord storage _recognizeRecords = recognizeRecords[cowsId];
_recognizeRecords.adminSignature = SigUtil.VerifiedSignature({
signature: adminSignature,
recoveredSigner: signerAddr,
isVerified: true
});
// Update global state to Recognized
{
address _keyAddr = returnContractAddress(cowsId, string("GlobalState"));
GlobalState globalState = GlobalState(_keyAddr);
globalState.setGlobalState(GlobalState.COWSState.Recognized);
}
// Emit event Recognize("All passed!", true)
emit Recognize("All passed!", true);
}
}
import hre from "hardhat";
import { ContractFactory, Contract } from "ethers";
async function deployRegistry() {
const registryContract: ContractFactory = await hre.ethers.getContractFactory("Registry");
const registryContractInstance: Contract = await registryContract.deploy();
return registryContractInstance;
}
async function deployVerifySigUtilLib() {
const verifySigUtilLibrary: ContractFactory = await hre.ethers.getContractFactory(
"VerifySignatureUtil",
);
const verifySigUtilLibInstance: Contract = await verifySigUtilLibrary.deploy();
return verifySigUtilLibInstance;
}
async function deployVerifyUtilLib(verifySigUtilLibAddr: string) {
const verifyUtilLibrary: ContractFactory = await hre.ethers.getContractFactory("VerifyUtil", {
libraries: {
VerifySignatureUtil: verifySigUtilLibAddr,
},
});
const verifyUtilLibInstance: Contract = await verifyUtilLibrary.deploy();
return verifyUtilLibInstance;
}
async function deployCowsMgmt(verifySigUtilLibAddr: string, verifyUtilLibAddr: string) {
const cowsMgmtContract: ContractFactory = await hre.ethers.getContractFactory("COWSManagement", {
libraries: {
VerifySignatureUtil: verifySigUtilLibAddr,
VerifyUtil: verifyUtilLibAddr,
},
});
const cowsMgmtContractInstance: Contract = await cowsMgmtContract.deploy();
return cowsMgmtContractInstance;
}
async function main() {
const verifySigUtilLibInstance: Contract = await deployVerifySigUtilLib();
const verifySigUtilLibAddr: string = verifySigUtilLibInstance.address;
console.log(`VerifySignatureUtil library deployed at: ${verifySigUtilLibAddr}`); //VerifySignatureUtil library deployed at: 0x5FbDB2315678afecb367f032d93F642f64180aa3
const verifyUtilLibInstance: Contract = await deployVerifyUtilLib(verifySigUtilLibAddr);
const verifyUtilLibAddr: string = verifyUtilLibInstance.address;
console.log(`VerifyUtil library deployed at: ${verifyUtilLibAddr}`); //VerifyUtil library deployed at: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512
const cowsMgmtContractInstance: Contract = await deployCowsMgmt(
verifySigUtilLibAddr,
verifyUtilLibAddr,
);
const cowsMgmtContractAddr: string = cowsMgmtContractInstance.address;
const registryContractInstance: Contract = await deployRegistry();
const registryContractAddr: string = registryContractInstance.address;
console.log(`Registry contract deployed at: ${registryContractAddr}`); //Registry contract deployed to: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 / 0xE451980132E65465d0a498c53f0b5227326Dd73F
console.log(`COWSManagement contract deployed at: ${cowsMgmtContractAddr}`); //COWSManagement contract deployed at: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0
const cowId1 = "CowsCluster1";
const cowId1Proto = "Ethereum";
let knownCastlAddrs: string[] = ["0xFfB92A0e14BC8966358aEbe4C41026b2b6bd9fE7"];
enum AccountType {
Empty = 0,
Omnibus = 1,
Airdrop = 2,
CustomerInvestigation = 3,
Garbage = 4,
}
type InitUnit = {
cowId: string;
chain: string;
accountType: AccountType;
castlKeys: string[];
};
const initUnits: [InitUnit] = [
{
cowId: hre.ethers.utils.formatBytes32String(cowId1),
chain: cowId1Proto,
accountType: AccountType.Omnibus,
castlKeys: knownCastlAddrs,
},
];
const deployNewCowsTxn = await cowsMgmtContractInstance.deployNewCOWS(initUnits);
const deployNewCowsTxnReceipt = await deployNewCowsTxn.wait();
console.log(deployNewCowsTxnReceipt);
const etherDecimals = 18;
const deployNewCowsTxnGasUsed = hre.ethers.utils.formatUnits(
deployNewCowsTxnReceipt.gasUsed,
etherDecimals,
);
console.log(`deployNewCOWS Gas Usage: ${deployNewCowsTxnGasUsed}`);
const spillAddrsEvent = deployNewCowsTxnReceipt.events.find(
(event: any) => event.event === "SpillContractAddrs",
);
const spillAddrsEventArgs = spillAddrsEvent.args;
const globalStateContractAddr = spillAddrsEventArgs.globalStateContractLocation;
console.log(`GlobalState contract deployed at: ${globalStateContractAddr}`); //GlobalState contract deployed at: 0x75537828f2ce51be7289709686A69CbFDbB714F1
const keyMgmtContractAddr = spillAddrsEventArgs.keyManagementContractLocation;
console.log(`KeyManagement contract deployed at: ${keyMgmtContractAddr}`); //KeyManagement contract deployed at: 0xE451980132E65465d0a498c53f0b5227326Dd73F
const CustodyWalletContractAddr = spillAddrsEventArgs.custodyWalletContractLocation; //CustodyWallet contract deployed at: 0x5392A33F7F677f59e833FEBF4016cDDD88fF9E67
console.log(`CustodyWallet contract deployed at: ${CustodyWalletContractAddr}`);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
import { task } from "hardhat/config";
import "@nomiclabs/hardhat-waffle";
import "hardhat-preprocessor";
import fs from "fs";
import dotenv from "dotenv";
dotenv.config({ path: __dirname + "/.env" });
const {
INFURA_PROJECT_ID,
METAMASK_PRIVATE_KEY,
ETHERNAL_EMAIL,
ETHERNAL_PASSWORD,
ETHERNAL_WORKSPACE,
} = process.env;
task("accounts", "Prints the list of accounts", async (taskArgs, hre) => {
const accounts = await hre.ethers.getSigners();
for (const account of accounts) {
console.log(account.address);
}
});
function getRemappings() {
return fs
.readFileSync("remappings.txt", "utf8")
.split("\n")
.filter(Boolean) // remove empty lines
.map((line) => line.trim().split("="));
}
export default {
ethernal: {
email: ETHERNAL_EMAIL,
password: ETHERNAL_PASSWORD,
workspace: ETHERNAL_WORKSPACE,
uploadAst: true,
resetOnStart: ETHERNAL_WORKSPACE,
serverSync: true,
},
solidity: {
compilers: [
{
version: "0.8.16",
},
],
},
preprocess: {
eachLine: (hre: any) => ({
transform: (line: any) => {
if (line.match(/^\s*import /i)) {
getRemappings().forEach(([find, replace]) => {
if (line.match(find)) {
line = line.replace(find, replace);
}
});
}
return line;
},
}),
},
paths: {
sources: "./src",
tests: "./test_hardhat",
cache: "./cache_hardhat",
},
networks: {
ethernal: {
url: "http://prometh-cows.click:8545",
accounts: [`0x${METAMASK_PRIVATE_KEY}`],
},
hardhat: {
chainId: 1337,
mining: {
auto: false,
interval: 1000, // mines 1 block every 1 millisecond
},
allowUnlimitedContractSize: true,
},
ropsten: {
url: `https://ropsten.infura.io/v3/${INFURA_PROJECT_ID}`,
accounts: [`0x${METAMASK_PRIVATE_KEY}`],
},
rinkeby: {
url: `https://rinkeby.infura.io/v3/${INFURA_PROJECT_ID}`,
accounts: [`0x${METAMASK_PRIVATE_KEY}`],
},
kovan: {
url: `https://kovan.infura.io/v3/${INFURA_PROJECT_ID}`,
accounts: [`0x${METAMASK_PRIVATE_KEY}`],
},
},
mocha: {
timeout: 100000000,
},
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment