mirror of
https://github.com/serai-dex/serai.git
synced 2024-12-25 21:19:35 +00:00
Don't have the Deployer store the deployment block
Also updates how re-entrancy is handled to a more efficient and portable mechanism.
This commit is contained in:
parent
7feb7aed22
commit
ee0efe7cde
2 changed files with 19 additions and 43 deletions
|
@ -34,41 +34,12 @@ pragma solidity ^0.8.26;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
contract Deployer {
|
contract Deployer {
|
||||||
struct Deployment {
|
mapping(bytes32 => address) public deployments;
|
||||||
uint64 block_number;
|
|
||||||
address created_contract;
|
|
||||||
}
|
|
||||||
|
|
||||||
mapping(bytes32 => Deployment) public deployments;
|
|
||||||
|
|
||||||
error Reentrancy();
|
|
||||||
error PriorDeployed();
|
error PriorDeployed();
|
||||||
error DeploymentFailed();
|
error DeploymentFailed();
|
||||||
|
|
||||||
function deploy(bytes memory init_code) external {
|
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
|
// Deploy the contract
|
||||||
address created_contract;
|
address created_contract;
|
||||||
assembly {
|
assembly {
|
||||||
|
@ -78,9 +49,15 @@ contract Deployer {
|
||||||
revert DeploymentFailed();
|
revert DeploymentFailed();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the dpeloyment to storage
|
bytes32 init_code_hash = keccak256(init_code);
|
||||||
deployment.block_number = uint64(block.number);
|
|
||||||
deployment.created_contract = created_contract;
|
// Check this wasn't prior deployed
|
||||||
deployments[init_code_hash] = deployment;
|
// 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ mod abi {
|
||||||
/// compatible chain. It then supports retrieving the Router contract's address (which isn't
|
/// compatible chain. It then supports retrieving the Router contract's address (which isn't
|
||||||
/// deterministic) using a single call.
|
/// deterministic) using a single call.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Deployer;
|
pub struct Deployer(Arc<RootProvider<SimpleRequest>>);
|
||||||
impl Deployer {
|
impl Deployer {
|
||||||
/// Obtain the transaction to deploy this contract, already signed.
|
/// 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
|
/// 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.
|
/// so ETH sent can be neither misappropriated nor returned.
|
||||||
pub fn deployment_tx() -> Signed<TxLegacy> {
|
pub fn deployment_tx() -> Signed<TxLegacy> {
|
||||||
pub const BYTECODE: &str =
|
pub const BYTECODE: &[u8] =
|
||||||
include_str!(concat!(env!("OUT_DIR"), "/serai-processor-ethereum-deployer/Deployer.bin"));
|
include_bytes!(concat!(env!("OUT_DIR"), "/serai-processor-ethereum-deployer/Deployer.bin"));
|
||||||
let bytecode =
|
let bytecode =
|
||||||
Bytes::from_hex(BYTECODE).expect("compiled-in Deployer bytecode wasn't valid hex");
|
Bytes::from_hex(BYTECODE).expect("compiled-in Deployer bytecode wasn't valid hex");
|
||||||
|
|
||||||
|
@ -75,28 +75,27 @@ impl Deployer {
|
||||||
if code.is_empty() {
|
if code.is_empty() {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
Ok(Some(Self))
|
Ok(Some(Self(provider)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find the deployment of a contract.
|
/// Find the deployment of a contract.
|
||||||
pub async fn find_deployment(
|
pub async fn find_deployment(
|
||||||
&self,
|
&self,
|
||||||
provider: Arc<RootProvider<SimpleRequest>>,
|
|
||||||
init_code_hash: [u8; 32],
|
init_code_hash: [u8; 32],
|
||||||
) -> Result<Option<abi::Deployer::Deployment>, RpcError<TransportErrorKind>> {
|
) -> Result<Option<Address>, RpcError<TransportErrorKind>> {
|
||||||
let call = TransactionRequest::default().to(Self::address()).input(TransactionInput::new(
|
let call = TransactionRequest::default().to(Self::address()).input(TransactionInput::new(
|
||||||
abi::Deployer::deploymentsCall::new((init_code_hash.into(),)).abi_encode().into(),
|
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)
|
let deployment = abi::Deployer::deploymentsCall::abi_decode_returns(&bytes, true)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
TransportErrorKind::Custom(
|
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;
|
._0;
|
||||||
|
|
||||||
if deployment.created_contract == [0; 20] {
|
if **deployment == [0; 20] {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
Ok(Some(deployment))
|
Ok(Some(deployment))
|
||||||
|
|
Loading…
Reference in a new issue