Skip to content
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

feat: Integrate Aggregator in HelloWorldServiceManager #85

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
11 changes: 10 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
AGGREGATOR_RPC_LISTEN_SERVER=localhost:8080
AGGREGATOR_PRIVATE_KEY=59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d

DEPLOYER_PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

HOLESKY_PRIVATE_KEY=
HOLESKY_RPC_URL=


HELLOWORLD_OPERATORS_ECDSA_MNEMONIC="test test test test test test test test test test test junk"


4 changes: 3 additions & 1 deletion contracts/.env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
DEPLOYER_PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
AGGREGATOR_PRIVATE_KEY=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d

HOLESKY_PRIVATE_KEY=
HOLESKY_RPC_URL=
ETHERSCAN_API_KEY=
2 changes: 1 addition & 1 deletion contracts/script/DeployEigenLayerCore.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ contract DeployEigenlayerCore is Script {
CoreDeploymentLib.DeploymentConfigData internal configData;

function setUp() public virtual {
deployer = vm.rememberKey(vm.envUint("PRIVATE_KEY"));
deployer = vm.rememberKey(vm.envUint("DEPLOYER_PRIVATE_KEY"));
vm.label(deployer, "Deployer");
}

Expand Down
10 changes: 8 additions & 2 deletions contracts/script/HelloWorldDeployer.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@ contract HelloWorldDeployer is Script {

address private deployer;
address proxyAdmin;
address aggregator;
CoreDeploymentLib.DeploymentData coreDeployment;
HelloWorldDeploymentLib.DeploymentData helloWorldDeployment;
Quorum internal quorum;

function setUp() public virtual {
deployer = vm.rememberKey(vm.envUint("PRIVATE_KEY"));
deployer = vm.rememberKey(vm.envUint("DEPLOYER_PRIVATE_KEY"));
vm.label(deployer, "Deployer");

coreDeployment = CoreDeploymentLib.readDeploymentJson("deployments/core/", block.chainid);
Expand All @@ -38,8 +39,12 @@ contract HelloWorldDeployer is Script {
vm.startBroadcast(deployer);
proxyAdmin = UpgradeableProxyLib.deployProxyAdmin();

aggregator = vm.addr(vm.envUint("AGGREGATOR_PRIVATE_KEY"));

console2.log("aggregator", aggregator);

helloWorldDeployment =
HelloWorldDeploymentLib.deployContracts(proxyAdmin, coreDeployment, quorum);
HelloWorldDeploymentLib.deployContracts(proxyAdmin, aggregator, coreDeployment, quorum);

vm.stopBroadcast();

Expand All @@ -56,6 +61,7 @@ contract HelloWorldDeployer is Script {
"HelloWorldServiceManager address cannot be zero"
);
require(proxyAdmin != address(0), "ProxyAdmin address cannot be zero");
require(aggregator != address(0), "Aggregator address cannot be zero");
require(
coreDeployment.delegationManager != address(0),
"DelegationManager address cannot be zero"
Expand Down
4 changes: 1 addition & 3 deletions contracts/script/utils/CoreDeploymentLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -378,9 +378,7 @@ library CoreDeploymentLib {
}

/// TODO: Need to be able to read json from eigenlayer-contracts repo for holesky/mainnet and output the json here
function writeDeploymentJson(
DeploymentData memory data
) internal {
function writeDeploymentJson(DeploymentData memory data) internal {
writeDeploymentJson("deployments/core/", block.chainid, data);
}

Expand Down
34 changes: 23 additions & 11 deletions contracts/script/utils/HelloWorldDeploymentLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ library HelloWorldDeploymentLib {

function deployContracts(
address proxyAdmin,
address aggregator,
CoreDeploymentLib.DeploymentData memory core,
Quorum memory quorum
) internal returns (DeploymentData memory) {
Expand All @@ -43,7 +44,7 @@ library HelloWorldDeploymentLib {
address(new ECDSAStakeRegistry(IDelegationManager(core.delegationManager)));
address helloWorldServiceManagerImpl = address(
new HelloWorldServiceManager(
core.avsDirectory, result.stakeRegistry, core.delegationManager
core.avsDirectory, result.stakeRegistry, core.delegationManager, aggregator
)
);
// Upgrade contracts
Expand All @@ -53,12 +54,17 @@ library HelloWorldDeploymentLib {
UpgradeableProxyLib.upgradeAndCall(result.stakeRegistry, stakeRegistryImpl, upgradeCall);
UpgradeableProxyLib.upgrade(result.helloWorldServiceManager, helloWorldServiceManagerImpl);

// Initialize HelloWorldServiceManager with aggregator
bytes memory helloWorldInitCall =
abi.encodeCall(HelloWorldServiceManager.initialize, (aggregator));
UpgradeableProxyLib.upgradeAndCall(
result.helloWorldServiceManager, helloWorldServiceManagerImpl, helloWorldInitCall
);

return result;
}

function readDeploymentJson(
uint256 chainId
) internal returns (DeploymentData memory) {
function readDeploymentJson(uint256 chainId) internal returns (DeploymentData memory) {
return readDeploymentJson("deployments/", chainId);
}

Expand All @@ -81,9 +87,7 @@ library HelloWorldDeploymentLib {
}

/// write to default output path
function writeDeploymentJson(
DeploymentData memory data
) internal {
function writeDeploymentJson(DeploymentData memory data) internal {
writeDeploymentJson("deployments/hello-world/", block.chainid, data);
}

Expand All @@ -95,7 +99,11 @@ library HelloWorldDeploymentLib {
address proxyAdmin =
address(UpgradeableProxyLib.getProxyAdmin(data.helloWorldServiceManager));

string memory deploymentData = _generateDeploymentJson(data, proxyAdmin);
address aggregator = HelloWorldServiceManager(data.helloWorldServiceManager).aggregator();

console2.log("aggregator2", aggregator);

string memory deploymentData = _generateDeploymentJson(data, proxyAdmin, aggregator);

string memory fileName = string.concat(outputPath, vm.toString(chainId), ".json");
if (!vm.exists(outputPath)) {
Expand All @@ -108,26 +116,30 @@ library HelloWorldDeploymentLib {

function _generateDeploymentJson(
DeploymentData memory data,
address proxyAdmin
address proxyAdmin,
address aggregator
) private view returns (string memory) {
return string.concat(
'{"lastUpdate":{"timestamp":"',
vm.toString(block.timestamp),
'","block_number":"',
vm.toString(block.number),
'"},"addresses":',
_generateContractsJson(data, proxyAdmin),
_generateContractsJson(data, proxyAdmin, aggregator),
"}"
);
}

function _generateContractsJson(
DeploymentData memory data,
address proxyAdmin
address proxyAdmin,
address aggregator
) private view returns (string memory) {
return string.concat(
'{"proxyAdmin":"',
proxyAdmin.toHexString(),
'","aggregator":"',
aggregator.toHexString(),
'","helloWorldServiceManager":"',
data.helloWorldServiceManager.toHexString(),
'","helloWorldServiceManagerImpl":"',
Expand Down
12 changes: 3 additions & 9 deletions contracts/script/utils/UpgradeableProxyLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ library UpgradeableProxyLib {
return address(new ProxyAdmin());
}

function setUpEmptyProxy(
address admin
) internal returns (address) {
function setUpEmptyProxy(address admin) internal returns (address) {
address emptyContract = address(new EmptyContract());
return address(new TransparentUpgradeableProxy(emptyContract, admin, ""));
}
Expand All @@ -36,16 +34,12 @@ library UpgradeableProxyLib {
admin.upgradeAndCall(TransparentUpgradeableProxy(payable(proxy)), impl, initData);
}

function getImplementation(
address proxy
) internal view returns (address) {
function getImplementation(address proxy) internal view returns (address) {
bytes32 value = vm.load(proxy, IMPLEMENTATION_SLOT);
return address(uint160(uint256(value)));
}

function getProxyAdmin(
address proxy
) internal view returns (ProxyAdmin) {
function getProxyAdmin(address proxy) internal view returns (ProxyAdmin) {
bytes32 value = vm.load(proxy, ADMIN_SLOT);
return ProxyAdmin(address(uint160(uint256(value))));
}
Expand Down
78 changes: 56 additions & 22 deletions contracts/src/HelloWorldServiceManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {ECDSAStakeRegistry} from "@eigenlayer-middleware/src/unaudited/ECDSAStak
import {IServiceManager} from "@eigenlayer-middleware/src/interfaces/IServiceManager.sol";
import {ECDSAUpgradeable} from
"@openzeppelin-upgrades/contracts/utils/cryptography/ECDSAUpgradeable.sol";
import {IERC1271Upgradeable} from "@openzeppelin-upgrades/contracts/interfaces/IERC1271Upgradeable.sol";
import {IERC1271Upgradeable} from
"@openzeppelin-upgrades/contracts/interfaces/IERC1271Upgradeable.sol";
import {IHelloWorldServiceManager} from "./IHelloWorldServiceManager.sol";
import "@openzeppelin/contracts/utils/Strings.sol";

Expand All @@ -20,27 +21,29 @@ contract HelloWorldServiceManager is ECDSAServiceManagerBase, IHelloWorldService

uint32 public latestTaskNum;

address public aggregator;

// mapping of task indices to all tasks hashes
// when a task is created, task hash is stored here,
// and responses need to pass the actual task,
// which is hashed onchain and checked against this mapping
mapping(uint32 => bytes32) public allTaskHashes;

// mapping of task indices to hash of abi.encode(taskResponse, taskResponseMetadata)
mapping(address => mapping(uint32 => bytes)) public allTaskResponses;
mapping(uint32 => bytes) public allTaskResponses;

modifier onlyOperator() {
require(
ECDSAStakeRegistry(stakeRegistry).operatorRegistered(msg.sender),
"Operator must be the caller"
);
modifier onlyAggregator() {
if (msg.sender != aggregator) {
revert("Aggregator must be the caller");
}
_;
}

constructor(
address _avsDirectory,
address _stakeRegistry,
address _delegationManager
address _delegationManager,
address _aggregator
)
ECDSAServiceManagerBase(
_avsDirectory,
Expand All @@ -50,11 +53,13 @@ contract HelloWorldServiceManager is ECDSAServiceManagerBase, IHelloWorldService
)
{}

function initialize(address _aggregator) public initializer {
_updateAggregator(_aggregator);
}

/* FUNCTIONS */
// NOTE: this function creates new task, assigns it a taskId
function createNewTask(
string memory name
) external returns (Task memory) {
function createNewTask(string memory name) external returns (Task memory) {
// create a new task struct
Task memory newTask;
newTask.name = name;
Expand All @@ -71,30 +76,59 @@ contract HelloWorldServiceManager is ECDSAServiceManagerBase, IHelloWorldService
function respondToTask(
Task calldata task,
uint32 referenceTaskIndex,
bytes memory signature
) external {
bytes memory aggregateSignatureData
) external onlyAggregator {
// check that the task is valid, hasn't been responsed yet, and is being responded in time
require(
keccak256(abi.encode(task)) == allTaskHashes[referenceTaskIndex],
"supplied task does not match the one recorded in the contract"
);
require(
allTaskResponses[msg.sender][referenceTaskIndex].length == 0,
"Operator has already responded to the task"
);

// The message that was signed
bytes32 messageHash = keccak256(abi.encodePacked("Hello, ", task.name));
bytes32 ethSignedMessageHash = messageHash.toEthSignedMessageHash();

(address[] memory operators, bytes[] memory signatures, uint32 referenceBlock) =
abi.decode(aggregateSignatureData, (address[], bytes[], uint32));

// check if all signers are registered operators
// @note is this necessary? _checkSignatures requires that the total signer weight meets our threshold
// only registered operators can contribute to the weight.
for (uint256 i = 0; i < operators.length; i++) {
require(_isOperator(operators[i]), "Invalid signer: not registered as operator");
}

// check if signatures are valid
bytes4 magicValue = IERC1271Upgradeable.isValidSignature.selector;
if (!(magicValue == ECDSAStakeRegistry(stakeRegistry).isValidSignature(ethSignedMessageHash,signature))){
revert();
bytes32 ethSignedMessageHash = messageHash.toEthSignedMessageHash();
if (
!(
magicValue
== ECDSAStakeRegistry(stakeRegistry).isValidSignature(
ethSignedMessageHash, aggregateSignatureData
)
)
) {
revert("Invalid signature");
}

// updating the storage with task responses
allTaskResponses[msg.sender][referenceTaskIndex] = signature;
// updating the storage with task response
allTaskResponses[referenceTaskIndex] = aggregateSignatureData;

// emitting event
emit TaskResponded(referenceTaskIndex, task, msg.sender);
}

function updateAggregator(address _newAggregator) external onlyOwner {
_updateAggregator(_newAggregator);
}

function _isOperator(address _operator) internal view returns (bool) {
return ECDSAStakeRegistry(stakeRegistry).operatorRegistered(_operator);
}

function _updateAggregator(address _newAggregator) internal {
address oldAggregator = aggregator;
aggregator = _newAggregator;
emit AggregatorUpdated(oldAggregator, _newAggregator);
}
}
20 changes: 10 additions & 10 deletions contracts/src/IHelloWorldServiceManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,29 @@ interface IHelloWorldServiceManager {

event TaskResponded(uint32 indexed taskIndex, Task task, address operator);

event AggregatorUpdated(address oldAggregator, address newAggregator);

struct Task {
string name;
uint32 taskCreatedBlock;
}

function latestTaskNum() external view returns (uint32);

function allTaskHashes(
uint32 taskIndex
) external view returns (bytes32);
function aggregator() external view returns (address);

function allTaskHashes(uint32 taskIndex) external view returns (bytes32);

function allTaskResponses(
address operator,
uint32 taskIndex
) external view returns (bytes memory);
function allTaskResponses(uint32 taskIndex) external view returns (bytes memory);

function createNewTask(
string memory name
) external returns (Task memory);
function createNewTask(string memory name) external returns (Task memory);

function respondToTask(
Task calldata task,
uint32 referenceTaskIndex,
bytes calldata signature
) external;

function updateAggregator(address _newAggregator) external;

}
2 changes: 1 addition & 1 deletion contracts/test/ERC20Mock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract ERC20Mock is ERC20 {
constructor() ERC20("", "") {}

function mint(address account, uint256 amount) public {
_mint(account, amount);
}
Expand Down
Loading