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

Scripts and unit tests for contract upgrade and ownership transfer #89

Merged
merged 15 commits into from
Jan 16, 2025
100 changes: 100 additions & 0 deletions test/ERC1967Proxy.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

import {Test} from "forge-std/Test.sol";
import {PDPVerifier} from "../src/PDPVerifier.sol";
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import {MyERC1967Proxy} from "../src/ERC1967Proxy.sol";
contract ERC1967ProxyTest is Test {
PDPVerifier public implementation;
PDPVerifier public proxy;
address owner = address(0x123);

function setUp() public {
// Set owner for testing
vm.startPrank(owner);
// Deploy implementation contract
implementation = new PDPVerifier();

// Deploy proxy pointing to implementation
bytes memory initData = abi.encodeWithSelector(
PDPVerifier.initialize.selector,
uint256(150) // challengeFinality
);

ERC1967Proxy proxyContract = new MyERC1967Proxy(
address(implementation),
initData
);

// Get PDPVerifier interface on proxy address
proxy = PDPVerifier(address(proxyContract));
}

function testInitialSetup() public view {
assertEq(proxy.getChallengeFinality(), 150);
assertEq(proxy.owner(), owner);
}

function assertImplementationEquals(address checkImpl) public view {
bytes32 implementationSlot = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
assertEq(address(uint160(uint256(vm.load(address(proxy), implementationSlot)))), address(checkImpl));
}

function testUpgradeImplementation() public {
assertImplementationEquals(address(implementation));

// Deploy new implementation
PDPVerifier newImplementation = new PDPVerifier();

// Upgrade proxy to new implementation
proxy.upgradeToAndCall(address(newImplementation), "");

// Verify upgrade was successful
assertImplementationEquals(address(newImplementation));
assertEq(proxy.getChallengeFinality(), 150); // State is preserved
assertEq(proxy.owner(), owner); // Owner is preserved
}

function testFailUpgradeFromNonOwner() public {
PDPVerifier newImplementation = new PDPVerifier();

vm.stopPrank();
vm.startPrank(address(0xdead));

vm.expectRevert("Ownable: caller is not the owner");
proxy.upgradeToAndCall(address(newImplementation), "");
assertEq(proxy.getChallengeFinality(), 150); // State is preserved
assertEq(proxy.owner(), owner); // Owner is preserved
}

function testOwnershipTransfer() public {
vm.stopPrank();
vm.startPrank(owner);
// Verify initial owner
assertEq(proxy.owner(), owner);

address newOwner = address(0x123);

// Transfer ownership
proxy.transferOwnership(newOwner);

// Verify ownership changed
assertEq(proxy.owner(), newOwner);
}

function testFailTransferFromNonOwner() public {
// Switch to non-owner account
vm.stopPrank();
vm.startPrank(address(0xdead));

address newOwner = address(0x123);

// Attempt transfer should fail
vm.expectRevert("Ownable: caller is not the owner");
proxy.transferOwnership(newOwner);

// Verify owner unchanged
assertEq(proxy.owner(), owner);
}
}
154 changes: 154 additions & 0 deletions tools/deploy-transfer-ownership-calibnet.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
#!/usr/bin/env bash
set -euo pipefail

#####################################
# Environment variables & defaults #
#####################################

: "${FIL_CALIBNET_RPC_URL:?FIL_CALIBNET_RPC_URL not set. Please export it and rerun.}"
: "${FIL_CALIBNET_PRIVATE_KEY:?FIL_CALIBNET_PRIVATE_KEY not set. Please export it and rerun.}"
Copy link
Contributor

@ZenGround0 ZenGround0 Jan 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Purely out of curiosity how do you get your private keys? In the past I've ended up needing to write my own script to extract from eth keystore since that design works hard to avoid extraction.

: "${NEW_OWNER:?NEW_OWNER not set. Please export it and rerun.}"


CHAIN_ID="${CHAIN_ID:-314159}"
COMPILER_VERSION="${COMPILER_VERSION:-0.8.22}"

#####################################
# 1. Create INIT_DATA #
#####################################
echo "Generating calldata for initialize(uint256) with argument 150 ..."
INIT_DATA=$(cast calldata "initialize(uint256)" 150)
echo "INIT_DATA = $INIT_DATA"
echo

#####################################
# 1. Get deployer address #
#####################################
echo "Deriving deployer address from private key ..."
DEPLOYER_ADDRESS=$(cast wallet address "$FIL_CALIBNET_PRIVATE_KEY")
echo "Deployer address: $DEPLOYER_ADDRESS"
echo

#####################################
# 2. Deploy PDPVerifier contract #
#####################################
echo "Deploying PDPVerifier contract ..."
DEPLOY_OUTPUT_VERIFIER=$(
forge create \
--rpc-url "$FIL_CALIBNET_RPC_URL" \
--private-key "$FIL_CALIBNET_PRIVATE_KEY" \
--chain-id "$CHAIN_ID" \
--compiler-version "$COMPILER_VERSION" \
--json \
src/PDPVerifier.sol:PDPVerifier
)

# Extract the deployed address from JSON output
PDP_VERIFIER_ADDRESS=$(echo "$DEPLOY_OUTPUT_VERIFIER" | jq -r '.deployedTo')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm having trouble parsing this on both OSX and linux. There is no deployedTo field in my output json. Have you gotten it working since making this change?

echo "PDPVerifier deployed at: $PDP_VERIFIER_ADDRESS"
echo

#####################################
# 3. Deploy Proxy contract #
#####################################
echo "Deploying Proxy contract (MyERC1967Proxy) ..."
DEPLOY_OUTPUT_PROXY=$(
forge create \
--rpc-url "$FIL_CALIBNET_RPC_URL" \
--private-key "$FIL_CALIBNET_PRIVATE_KEY" \
--chain-id "$CHAIN_ID" \
--compiler-version "$COMPILER_VERSION" \
--constructor-args "$PDP_VERIFIER_ADDRESS" "$INIT_DATA" \
--json \
src/ERC1967Proxy.sol:MyERC1967Proxy
)

# Extract the deployed proxy address
PROXY_ADDRESS=$(echo "$DEPLOY_OUTPUT_PROXY" | jq -r '.deployedTo')
echo "Proxy deployed at: $PROXY_ADDRESS"
echo

#####################################
# 4. Check owner of proxy #
#####################################
echo "Querying the proxy's owner ..."
OWNER_ADDRESS=$(
cast call \
--rpc-url "$FIL_CALIBNET_RPC_URL" \
"$PROXY_ADDRESS" \
"owner()(address)"
)
echo "Proxy owner: $OWNER_ADDRESS"

# Add validation check
if [ "${OWNER_ADDRESS,,}" != "${DEPLOYER_ADDRESS,,}" ]; then
echo "failed to validate owner address"
echo "Expected owner to be: ${DEPLOYER_ADDRESS}"
echo "Got: ${OWNER_ADDRESS}"
exit 1
fi
echo "✓ Owner address validated successfully"
echo

#####################################
# 5. Check implementation address #
#####################################
# The storage slot for ERC1967 implementation:
IMPLEMENTATION_SLOT="0x360894A13BA1A3210667C828492DB98DCA3E2076CC3735A920A3CA505D382BBC"

echo "Checking proxy's implementation address from storage slot $IMPLEMENTATION_SLOT ..."
IMPLEMENTATION_ADDRESS=$(
cast storage \
--rpc-url "$FIL_CALIBNET_RPC_URL" \
"$PROXY_ADDRESS" \
"$IMPLEMENTATION_SLOT"
)
echo "Implementation address in Proxy: $IMPLEMENTATION_ADDRESS"
echo


#####################################
# Summary #
#####################################
echo "========== DEPLOYMENT SUMMARY =========="
echo "PDPVerifier Address: $PDP_VERIFIER_ADDRESS"
echo "Proxy Address: $PROXY_ADDRESS"
echo "Proxy Owner (should match deployer): $OWNER_ADDRESS"
echo "PDPVerifier Implementation (via Proxy): $IMPLEMENTATION_ADDRESS"
echo "========================================"


#####################################
# 6. Transfer ownership #
#####################################
echo
echo "Transferring ownership to new owner..."

cast send \
--rpc-url "$FIL_CALIBNET_RPC_URL" \
--private-key "$FIL_CALIBNET_PRIVATE_KEY" \
--chain-id "$CHAIN_ID" \
"$PROXY_ADDRESS" \
"transferOwnership(address)" \
"$NEW_OWNER"

echo "✓ Ownership transfer transaction submitted"

# Verify the ownership transfer
echo "Verifying new owner..."
NEW_OWNER_ADDRESS=$(
cast call \
--rpc-url "$FIL_CALIBNET_RPC_URL" \
"$PROXY_ADDRESS" \
"owner()(address)"
)

if [ "${NEW_OWNER_ADDRESS,,}" != "${NEW_OWNER,,}" ]; then
echo "failed to transfer ownership"
echo "Expected new owner to be: ${NEW_OWNER}"
echo "Got: ${NEW_OWNER_ADDRESS}"
exit 1
fi

echo "✓ Ownership transferred successfully to ${NEW_OWNER}"
echo
Loading
Loading