fix: non atomic aligned layer service manager contract deployment
[!NOTE] This PR was an initial exploration to the problem, and will not be merged We will keep this PR in draft to use as guide and to extract its value easier The new PR is the following: https://github.com/yetanotherco/aligned_layer/pull/828
Description
In AlignedLayerDeployer.s.sol, if an attacker calls init() after we do alignedLayerServiceManagerImplementation = new AlignedLayerServiceManager(…) then, when we upgrade the proxy to the implementation:
alignedLayerProxyAdmin.upgradeAndCall(
TransparentUpgradeableProxy(
payable(address(alignedLayerServiceManager))
),
address(alignedLayerServiceManagerImplementation),
abi.encodeWithSelector(
AlignedLayerServiceManager.initialize.selector,
deployer
)
);
It will fail because initialize has already been called and since initialize has the initializer modifier, it can only be called once. So this wouldn’t be a problem because it will revert the transaction:
AlignedLayerServiceManager::initialize(0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266)
│ └─ ← [Revert] revert: Initializable: contract is already initialized
└─ ← [Revert] revert: Initializable: contract is already initialized
An attacker could potentially make our transactions revert making us spend unnecessary ether but it wouldn't go further than that and they would be spending ether too.
Using a factory pattern solves this issue, but the ownership of the ProxyAdmin causes some problems. We deploy the ProxyAdmin with the AlignedLayerDeployer.s.sol and then we want to call upgradeAndCall from the ProxyAdmin in AlignedLayerServiceManagerFactory.sol and it isn’t possible since it’s not the owner of ProxyAdmin.