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

test: add integration tests for Anvil migrations #11002

Merged
merged 41 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
cd987f3
test(protocol/migrations_sol): adds existing unit tests
arthurgousset May 21, 2024
601ad7c
Merge branch 'master' into arthurgousset/chore/anvil-integration-tests
arthurgousset May 24, 2024
3ae51c4
Merge branch 'master' into arthurgousset/chore/anvil-integration-tests
arthurgousset May 28, 2024
ee24877
test(test-sol/integration): adds e2e test for GoldToken
arthurgousset May 28, 2024
714bc75
test(test-sol/integration): adds loop to assert contract is in Registry
arthurgousset May 29, 2024
8651f31
chore(test-sol/integration): adds TODO comment about config file
arthurgousset May 29, 2024
eec3863
test(test-sol/integration): upgrades to solidity 0.8
arthurgousset May 30, 2024
710059f
Merge branch 'master' into arthurgousset/chore/anvil-integration-tests
arthurgousset May 30, 2024
c4f55d5
nit(test-sol/integration): updates constructor visibility
arthurgousset May 30, 2024
de6b143
nit(test-sol/integration): updates function state mutability
arthurgousset May 30, 2024
d99a746
test(test-sol/integration): adds MVP assertion that bytecode matches
arthurgousset May 30, 2024
4df1e7c
fix(test-sol/integration): use implementation address and deployedByt…
arthurgousset May 30, 2024
dc6a6c5
refactor(test-sol): adds registry array in `constants.sol`
arthurgousset May 31, 2024
d6389c8
refactor(protocol/migration_sol): uses registry array from `constants…
arthurgousset May 31, 2024
ab7bab9
fix(test-sol/integration): adds back `Test.sol` import
arthurgousset May 31, 2024
e372b8a
refactor(migrations_sol): simplifies script that runs integration tests
arthurgousset May 31, 2024
130523e
chore(test-sol/integration): commits WIP on failing bytecode test
arthurgousset May 31, 2024
1e61942
test(test-sol): adds helper to remove metadata from bytecode
arthurgousset May 31, 2024
e55c973
chore(test-sol/integration): excludes failing contracts from bytecode…
arthurgousset May 31, 2024
14bd4cf
chor(test-sol/integration): adds code comment for readability
arthurgousset May 31, 2024
075697e
chore(test-sol/integration): removing unused code comments
arthurgousset May 31, 2024
41bff1b
fix(workflows/protocol_tests): excludes integration tests
arthurgousset May 31, 2024
c7511a8
fix(workflows/protocol_tests): excludes integration tests more explic…
arthurgousset Jun 3, 2024
e983e9a
Merge branch 'master' into arthurgousset/chore/anvil-integration-tests
arthurgousset Jun 3, 2024
bb386de
nit(test-sol/constants): improves code comment for context
arthurgousset Jun 3, 2024
a4d4d8b
Merge branch 'arthurgousset/chore/anvil-integration-tests' of github.…
arthurgousset Jun 3, 2024
1f7ab42
fix(workflows/protocol_tests): downgrades foundry version
arthurgousset Jun 3, 2024
2f9f837
fix(workflows/protocol_tests): excludes integration tests correctly
arthurgousset Jun 4, 2024
30de987
chore(test-sol/utils): reverts `deployCodeTo` edit in `ECDSAHelper`
arthurgousset Jun 4, 2024
dcb09e1
style(protocol): linting
arthurgousset Jun 4, 2024
c27a53e
docs(workflows/protocol_tests): adds better code comment
arthurgousset Jun 4, 2024
b9ce129
refactor(test-sol/integration): rename variables for better readability
arthurgousset Jun 4, 2024
7a740cb
refactor(test-sol/integration): take constants out of `for`-loop
arthurgousset Jun 4, 2024
7201463
style(protocol): linting
arthurgousset Jun 4, 2024
19faa9b
fix(test-sol/integration): add contract name back into `for`-loop
arthurgousset Jun 4, 2024
d31c336
nit(test-sol/integration): rename variable for better readability
arthurgousset Jun 4, 2024
a7f4905
refactor(migrations_sol/constants): creates new `constants.sol`
arthurgousset Jun 4, 2024
75a4306
refactor(migrations_sol&test-sol): removes `Utils` import
arthurgousset Jun 4, 2024
1e6b230
test(test-sol/integration): bumps solidity version to `>=0.8.7 <0.8.20`
arthurgousset Jun 4, 2024
9c913ca
refactor(test-sol/integration): use `.code` property instead of helpe…
arthurgousset Jun 5, 2024
255f467
refactor(foundry): simplifies configs to exclude tests
arthurgousset Jun 5, 2024
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: 9 additions & 2 deletions .github/workflows/protocol_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ jobs:

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: "nightly-f625d0fa7c51e65b4bf1e8f7931cd1c6e2e285e9"

- name: Install forge dependencies
run: forge install
Expand Down Expand Up @@ -94,8 +96,13 @@ jobs:

- name: Run Everything just in case something was missed
# can't use gas limit because some setUp function use more than the limit
run: forge test -vvv #TODO this should ignore integration tests

# Excludes integration tests (`RegistryIntegrationTest`), and other tests that are
# excluded globally in `foundry.toml` like `RandomTest` and `BLS12_381Passthrough`.
# Unfortunately, when you specify a `--no-match-contract` flag, it overrides any global
# exclusions specified in `foundry.toml`. Therefore, somewhat annoyingly, the exclusions here
# have to cover all cases, namely the integration tests and the global exclusions.
run: forge test -vvv --no-match-contract "RegistryIntegrationTest|RandomTest|BLS12_381Passthrough"
arthurgousset marked this conversation as resolved.
Show resolved Hide resolved

- name: Generate migrations
if: success() || failure()
run: ./migrations_sol/run_integration_tests_in_anvil.sh
3 changes: 2 additions & 1 deletion packages/protocol/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ remappings = [
'forge-std-8/=lib/celo-foundry-8/lib/forge-std/src/',
'@celo-contracts-8=contracts-0.8/',
'@openzeppelin/contracts8/=lib/openzeppelin-contracts8/contracts/',
'@celo-contracts/=contracts/'
'@celo-contracts/=contracts/',
'@celo-migrations/=migrations_sol/'
]

no_match_contract = "RandomTest"
Expand Down
40 changes: 7 additions & 33 deletions packages/protocol/migrations_sol/Migration.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity >=0.8.7 <0.8.20;
// Note: This script should not include any cheatcode so that it can run in production

import { Script } from "forge-std-8/Script.sol";

import "forge-std/console.sol";
import "forge-std/StdJson.sol";

Expand Down Expand Up @@ -46,6 +47,8 @@ import "@openzeppelin/contracts8/utils/math/Math.sol";

import "@celo-contracts-8/common/UsingRegistry.sol";

import { Constants } from "@celo-migrations/constants.sol";

contract ForceTx {
// event to trigger so a tx can be processed
event VanillaEvent(string);
Expand All @@ -57,7 +60,7 @@ contract ForceTx {
}
}

contract Migration is Script, UsingRegistry {
contract Migration is Script, UsingRegistry, Constants {
using stdJson for string;

/**
Expand Down Expand Up @@ -941,38 +944,9 @@ contract Migration is Script, UsingRegistry {
(bool)
);
if (!skipTransferOwnership) {
// TODO move this list somewhere else

string[23] memory fixedStringArray = [
"Accounts",
// 'Attestations',
// BlockchainParameters ownership transitioned to governance in a follow-up script.?
"BlockchainParameters",
"DoubleSigningSlasher",
"DowntimeSlasher",
"Election",
"EpochRewards",
"Escrow",
"FederatedAttestations",
"FeeCurrencyWhitelist",
"FeeCurrencyDirectory",
"Freezer",
"FeeHandler",
"GoldToken",
"Governance",
"GovernanceSlasher",
"LockedGold",
"OdisPayments",
"Random",
"Registry",
"SortedOracles",
"UniswapFeeHandlerSeller",
"MentoFeeHandlerSeller",
"Validators"
];

for (uint256 i = 0; i < fixedStringArray.length; i++) {
string memory contractToTransfer = fixedStringArray[i];
// BlockchainParameters ownership transitioned to governance in a follow-up script.?
for (uint256 i = 0; i < contractsInRegistry.length; i++) {
string memory contractToTransfer = contractsInRegistry[i];
console.log("Transfering ownership of: ", contractToTransfer);
IProxy proxy = IProxy(registry.getAddressForStringOrDie(contractToTransfer));
proxy._transferOwnership(governanceAddress);
Expand Down
30 changes: 30 additions & 0 deletions packages/protocol/migrations_sol/constants.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
pragma solidity >=0.8.7 <0.8.20;

contract Constants {
// List of contracts that are expected to be in Registry.sol
string[23] contractsInRegistry = [
"Accounts",
"BlockchainParameters",
"DoubleSigningSlasher",
"DowntimeSlasher",
"Election",
"EpochRewards",
"Escrow",
"FederatedAttestations",
"FeeCurrencyWhitelist",
"FeeCurrencyDirectory",
"Freezer",
"FeeHandler",
"GoldToken",
"Governance",
"GovernanceSlasher",
"LockedGold",
"OdisPayments",
"Random",
"Registry",
"SortedOracles",
"UniswapFeeHandlerSeller",
"MentoFeeHandlerSeller",
"Validators"
];
}
4 changes: 0 additions & 4 deletions packages/protocol/migrations_sol/integration_tests.sh

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
#!/usr/bin/env bash
set -euo pipefail


# generate devchain
# Generate and run devchain
echo "Generating and running devchain before running integration tests..."
source $PWD/migrations_sol/create_and_migrate_anvil_devchain.sh

# Run integration tests
source $PWD/migrations_sol/integration_tests.sh

echo "Running integration tests..."
forge test \
--match-path test-sol/integration/Integration.t.sol \
-vvv \
--fork-url http://127.0.0.1:$ANVIL_PORT
arthurgousset marked this conversation as resolved.
Show resolved Hide resolved

# helper kill anvil
# kill $(lsof -i tcp:$ANVIL_PORT | tail -n 1 | awk '{print $2}')
Expand Down
117 changes: 109 additions & 8 deletions packages/protocol/test-sol/integration/Integration.t.sol
Original file line number Diff line number Diff line change
@@ -1,19 +1,120 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.5.13;
pragma solidity >=0.8.7 <0.8.20;

import { Test } from "forge-std-8/Test.sol";
import "forge-std-8/console2.sol";

import { Constants } from "@celo-migrations/constants.sol";

import "celo-foundry/Test.sol";
import "@celo-contracts/common/GoldToken.sol";
// import "@celo-contracts/common/test/MockGoldToken.sol";
import "forge-std/console.sol";
import "@celo-contracts/common/interfaces/IRegistry.sol";
import "@celo-contracts/common/interfaces/IProxy.sol";

contract IntegrationTest is Test {
address constant registryAddress = address(0x000000000000000000000000000000000000ce10);
address account1 = actor("account1");
address account2 = actor("account2");
IRegistry registry = IRegistry(registryAddress);

function setUp() public {}

function test_dummy() public {}
/**
* @notice Removes CBOR encoded metadata from the tail of the deployedBytecode.
* @param data Bytecode including the CBOR encoded tail.
* @return Bytecode without the CBOR encoded metadata.
*/
function removeMetadataFromBytecode(bytes memory data) public pure returns (bytes memory) {
// Ensure the data length is at least enough to contain the length specifier
require(data.length >= 2, "Data too short to contain a valid CBOR length specifier");

// Calculate the length of the CBOR encoded section from the last two bytes
uint16 cborLength = uint16(uint8(data[data.length - 2])) *
256 +
uint16(uint8(data[data.length - 1]));

// Ensure the length is valid (not greater than the data array length minus 2 bytes for the length field)
require(cborLength <= data.length - 2, "Invalid CBOR length");

// Calculate the new length of the data without the CBOR section
uint newLength = data.length - 2 - cborLength;

// Create a new byte array for the result
bytes memory result = new bytes(newLength);

// Copy data from the original byte array to the new one, excluding the CBOR section and its length field
for (uint i = 0; i < newLength; i++) {
result[i] = data[i];
}

return result;
}
}

contract RegistryIntegrationTest is IntegrationTest, Constants {
IProxy proxy;

function test_shouldHaveAddressInRegistry() public view {
for (uint256 i = 0; i < contractsInRegistry.length; i++) {
string memory contractName = contractsInRegistry[i];
address contractAddress = registry.getAddressFor(keccak256(abi.encodePacked(contractName)));
console2.log(contractName, "address in Registry is: ", contractAddress);
assert(contractAddress != address(0));
}
}

function test_shouldHaveCorrectBytecode() public {
// Converting contract names to hashes for comparison
bytes32 hashAccount = keccak256(abi.encodePacked("Accounts"));
bytes32 hashElection = keccak256(abi.encodePacked("Election"));
bytes32 hashEscrow = keccak256(abi.encodePacked("Escrow"));
bytes32 hashFederatedAttestations = keccak256(abi.encodePacked("FederatedAttestations"));
bytes32 hashGovernance = keccak256(abi.encodePacked("Governance"));
bytes32 hashSortedOracles = keccak256(abi.encodePacked("SortedOracles"));
bytes32 hashValidators = keccak256(abi.encodePacked("Validators"));

for (uint256 i = 0; i < contractsInRegistry.length; i++) {
// Read name from list of core contracts
string memory contractName = contractsInRegistry[i];
console2.log("Checking bytecode of:", contractName);

// Skipping test for contracts that depend on linked libraries
// This is a known limitation in Foundry at the moment:
// Source: https://github.com/foundry-rs/foundry/issues/6120
arthurgousset marked this conversation as resolved.
Show resolved Hide resolved
bytes32 hashContractName = keccak256(abi.encodePacked(contractName));
if (
hashContractName != hashAccount &&
hashContractName != hashElection &&
hashContractName != hashEscrow &&
hashContractName != hashFederatedAttestations &&
hashContractName != hashGovernance &&
hashContractName != hashSortedOracles &&
hashContractName != hashValidators
) {
// Get proxy address registered in the Registry
address proxyAddress = registry.getAddressForStringOrDie(contractName);
proxy = IProxy(address(uint160(proxyAddress)));

// Get implementation address
address implementationAddress = proxy._getImplementation();

// Get bytecode from deployed contract
bytes memory actualBytecodeWithMetadataOnDevchain = implementationAddress.code;
bytes memory actualBytecodeOnDevchain = removeMetadataFromBytecode(
actualBytecodeWithMetadataOnDevchain
);

// Get bytecode from build artifacts
bytes memory expectedBytecodeWithMetadataFromArtifacts = vm.getDeployedCode(
string(abi.encodePacked(contractName, ".sol"))
);
bytes memory expectedBytecodeFromArtifacts = removeMetadataFromBytecode(
expectedBytecodeWithMetadataFromArtifacts
);

// Compare the bytecodes
assertEq(
actualBytecodeOnDevchain,
expectedBytecodeFromArtifacts,
"Bytecode does not match"
);
}
}
}
}
2 changes: 1 addition & 1 deletion packages/protocol/test-sol/utils/ECDSAHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ contract ECDSAHelper is Test {
bytes32 _s
) public returns (bytes memory) {
address SECP256K1Address = actor("SECP256K1Address");
deployCodeTo("SECP256K1.sol:SECP256K1", SECP256K1Address);
deployCodeTo("out/SECP256K1.sol/SECP256K1.0.5.17.json", SECP256K1Address);
arthurgousset marked this conversation as resolved.
Show resolved Hide resolved
sECP256K1 = ISECP256K1(SECP256K1Address);

string memory header = "\x19Ethereum Signed Message:\n32";
Expand Down
Loading