diff --git a/processor/ethereum/deployer/contracts/Deployer.sol b/processor/ethereum/deployer/contracts/Deployer.sol index ad217fdc..2d4904e4 100644 --- a/processor/ethereum/deployer/contracts/Deployer.sol +++ b/processor/ethereum/deployer/contracts/Deployer.sol @@ -34,41 +34,12 @@ pragma solidity ^0.8.26; */ contract Deployer { - struct Deployment { - uint64 block_number; - address created_contract; - } + mapping(bytes32 => address) public deployments; - mapping(bytes32 => Deployment) public deployments; - - error Reentrancy(); error PriorDeployed(); error DeploymentFailed(); function deploy(bytes memory init_code) external { - // Prevent re-entrancy - // If we did allow it, one could deploy the same contract multiple times (with one overwriting - // the other's set value in storage) - bool called; - // This contract doesn't have any other use of transient storage, nor is to be inherited, making - // this usage of the zero address safe - assembly { - called := tload(0) - } - if (called) { - revert Reentrancy(); - } - assembly { - tstore(0, 1) - } - - // Check this wasn't prior deployed - bytes32 init_code_hash = keccak256(init_code); - Deployment memory deployment = deployments[init_code_hash]; - if (deployment.created_contract == address(0)) { - revert PriorDeployed(); - } - // Deploy the contract address created_contract; assembly { @@ -78,9 +49,15 @@ contract Deployer { revert DeploymentFailed(); } - // Set the dpeloyment to storage - deployment.block_number = uint64(block.number); - deployment.created_contract = created_contract; - deployments[init_code_hash] = deployment; + bytes32 init_code_hash = keccak256(init_code); + + // Check this wasn't prior deployed + // We check this *after* deploymeing (in violation of CEI) to handle re-entrancy related bugs + if (deployments[init_code_hash] != address(0)) { + revert PriorDeployed(); + } + + // Write the deployment to storage + deployments[init_code_hash] = created_contract; } } diff --git a/processor/ethereum/deployer/src/lib.rs b/processor/ethereum/deployer/src/lib.rs index bf2d1a9c..6fa59ee3 100644 --- a/processor/ethereum/deployer/src/lib.rs +++ b/processor/ethereum/deployer/src/lib.rs @@ -30,7 +30,7 @@ mod abi { /// compatible chain. It then supports retrieving the Router contract's address (which isn't /// deterministic) using a single call. #[derive(Clone, Debug)] -pub struct Deployer; +pub struct Deployer(Arc>); impl Deployer { /// Obtain the transaction to deploy this contract, already signed. /// @@ -38,8 +38,8 @@ impl Deployer { /// funded for this transaction to be submitted. This account has no known private key to anyone /// so ETH sent can be neither misappropriated nor returned. pub fn deployment_tx() -> Signed { - pub const BYTECODE: &str = - include_str!(concat!(env!("OUT_DIR"), "/serai-processor-ethereum-deployer/Deployer.bin")); + pub const BYTECODE: &[u8] = + include_bytes!(concat!(env!("OUT_DIR"), "/serai-processor-ethereum-deployer/Deployer.bin")); let bytecode = Bytes::from_hex(BYTECODE).expect("compiled-in Deployer bytecode wasn't valid hex"); @@ -75,28 +75,27 @@ impl Deployer { if code.is_empty() { return Ok(None); } - Ok(Some(Self)) + Ok(Some(Self(provider))) } /// Find the deployment of a contract. pub async fn find_deployment( &self, - provider: Arc>, init_code_hash: [u8; 32], - ) -> Result, RpcError> { + ) -> Result, RpcError> { let call = TransactionRequest::default().to(Self::address()).input(TransactionInput::new( abi::Deployer::deploymentsCall::new((init_code_hash.into(),)).abi_encode().into(), )); - let bytes = provider.call(&call).await?; + let bytes = self.0.call(&call).await?; let deployment = abi::Deployer::deploymentsCall::abi_decode_returns(&bytes, true) .map_err(|e| { TransportErrorKind::Custom( - format!("node returned a non-Deployment for function returning Deployment: {e:?}").into(), + format!("node returned a non-address for function returning address: {e:?}").into(), ) })? ._0; - if deployment.created_contract == [0; 20] { + if **deployment == [0; 20] { return Ok(None); } Ok(Some(deployment))