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

AVS-507: Integration test scaffold: add anvil chain with deployed contracts to be able to test sdk features like bindings, txmgr, etc #176

Merged
merged 12 commits into from
Jun 14, 2024
10 changes: 9 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,12 @@ format-lines: ## formats all go files with golines

lint: ## runs all linters
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
golangci-lint run ./...
golangci-lint run ./...

___CONTRACTS___: ##

deploy-contracts-to-anvil-and-save-state: ##
./contracts/tests/anvil/deploy-contracts-save-anvil-state.sh

start-anvil-with-contracts-deployed: ##
./contracts/tests/anvil/start-anvil-chain-with-el-and-avs-deployed.sh
2 changes: 1 addition & 1 deletion chainio/clients/elcontracts/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ func (r *ELChainReader) GetOperatorDetails(opts *bind.CallOpts, operator types.O

return types.Operator{
Address: operator.Address,
EarningsReceiverAddress: operatorDetails.EarningsReceiver.Hex(),
EarningsReceiverAddress: operatorDetails.DeprecatedEarningsReceiver.Hex(),
StakerOptOutWindowBlocks: operatorDetails.StakerOptOutWindowBlocks,
DelegationApproverAddress: operatorDetails.DelegationApprover.Hex(),
}, nil
Expand Down
12 changes: 6 additions & 6 deletions chainio/clients/elcontracts/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,9 @@ func BuildELChainWriter(
func (w *ELChainWriter) RegisterAsOperator(ctx context.Context, operator types.Operator) (*gethtypes.Receipt, error) {
w.logger.Infof("registering operator %s to EigenLayer", operator.Address)
opDetails := delegationmanager.IDelegationManagerOperatorDetails{
EarningsReceiver: gethcommon.HexToAddress(operator.EarningsReceiverAddress),
StakerOptOutWindowBlocks: operator.StakerOptOutWindowBlocks,
DelegationApprover: gethcommon.HexToAddress(operator.DelegationApproverAddress),
DeprecatedEarningsReceiver: gethcommon.HexToAddress(operator.EarningsReceiverAddress),
StakerOptOutWindowBlocks: operator.StakerOptOutWindowBlocks,
DelegationApprover: gethcommon.HexToAddress(operator.DelegationApproverAddress),
}

noSendTxOpts, err := w.txMgr.GetNoSendTxOpts()
Expand All @@ -143,9 +143,9 @@ func (w *ELChainWriter) UpdateOperatorDetails(

w.logger.Infof("updating operator details of operator %s to EigenLayer", operator.Address)
opDetails := delegationmanager.IDelegationManagerOperatorDetails{
EarningsReceiver: gethcommon.HexToAddress(operator.EarningsReceiverAddress),
DelegationApprover: gethcommon.HexToAddress(operator.DelegationApproverAddress),
StakerOptOutWindowBlocks: operator.StakerOptOutWindowBlocks,
DeprecatedEarningsReceiver: gethcommon.HexToAddress(operator.EarningsReceiverAddress),
DelegationApprover: gethcommon.HexToAddress(operator.DelegationApproverAddress),
StakerOptOutWindowBlocks: operator.StakerOptOutWindowBlocks,
}

noSendTxOpts, err := w.txMgr.GetNoSendTxOpts()
Expand Down
2 changes: 1 addition & 1 deletion contracts/bindings/AVSDirectory/binding.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion contracts/bindings/BLSApkRegistry/binding.go

Large diffs are not rendered by default.

221 changes: 5 additions & 216 deletions contracts/bindings/DelegationManager/binding.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion contracts/bindings/EigenPod/binding.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion contracts/bindings/EigenPodManager/binding.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion contracts/bindings/IndexRegistry/binding.go

Large diffs are not rendered by default.

70 changes: 68 additions & 2 deletions contracts/bindings/OperatorStateRetriever/binding.go

Large diffs are not rendered by default.

93 changes: 91 additions & 2 deletions contracts/bindings/RegistryCoordinator/binding.go

Large diffs are not rendered by default.

235 changes: 234 additions & 1 deletion contracts/bindings/ServiceManagerBase/binding.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion contracts/bindings/StakeRegistry/binding.go

Large diffs are not rendered by default.

109 changes: 2 additions & 107 deletions contracts/bindings/StrategyManager/binding.go

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions contracts/generate-bindings.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#!/bin/bash

set -o errexit -o nounset -o pipefail

# cd to the directory of this script so that this can be run from anywhere
script_path=$(
cd "$(dirname "${BASH_SOURCE[0]}")"
Expand Down
2 changes: 1 addition & 1 deletion contracts/lib/eigenlayer-middleware
Submodule eigenlayer-middleware updated 45 files
+0 −3 .gitmodules
+29 −3 LICENSE
+24 −10 README.md
+ audits/M2 Mainnet - Dedaub - Feb 2024.pdf
+2 −2 docs/README.md
+1 −1 docs/ServiceManagerBase.md
+1 −1 docs/experimental/AVS-Guide.md
+0 −1 lib/ds-test
+1 −1 lib/eigenlayer-contracts
+1 −1 lib/forge-std
+116 −62 src/BLSSignatureChecker.sol
+175 −0 src/EjectionManager.sol
+33 −0 src/OperatorStateRetriever.sol
+30 −4 src/RegistryCoordinator.sol
+6 −1 src/RegistryCoordinatorStorage.sol
+89 −27 src/ServiceManagerBase.sol
+51 −0 src/ServiceManagerBaseStorage.sol
+3 −3 src/ServiceManagerRouter.sol
+24 −3 src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol
+55 −0 src/interfaces/IEjectionManager.sol
+15 −41 src/interfaces/IServiceManager.sol
+62 −0 src/interfaces/IServiceManagerUI.sol
+240 −0 src/unaudited/ECDSAServiceManagerBase.sol
+204 −75 src/unaudited/ECDSAStakeRegistry.sol
+10 −3 src/unaudited/ECDSAStakeRegistryStorage.sol
+14 −10 src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol
+69 −0 test/events/IServiceManagerBaseEvents.sol
+1 −1 test/ffi/BLSPubKeyCompendiumFFI.t.sol
+6 −1 test/integration/CoreRegistration.t.sol
+117 −61 test/integration/IntegrationDeployer.t.sol
+1 −1 test/integration/TimeMachine.t.sol
+4 −2 test/integration/User.t.sol
+3 −3 test/mocks/DelegationMock.sol
+72 −0 test/mocks/RewardsCoordinatorMock.sol
+9 −3 test/mocks/ServiceManagerMock.sol
+368 −146 test/unit/BLSApkRegistryUnit.t.sol
+1 −1 test/unit/BitmapUtils.t.sol
+60 −14 test/unit/ECDSAStakeRegistryEqualWeightUnit.t.sol
+48 −16 test/unit/ECDSAStakeRegistryPermissionedUnit.t.sol
+472 −105 test/unit/ECDSAStakeRegistryUnit.t.sol
+387 −0 test/unit/EjectionManagerUnit.t.sol
+56 −0 test/unit/RegistryCoordinatorUnit.t.sol
+527 −0 test/unit/ServiceManagerBase.t.sol
+1 −0 test/unit/ServiceManagerRouter.t.sol
+122 −91 test/utils/MockAVSDeployer.sol
31 changes: 31 additions & 0 deletions contracts/script/output/31337/eigenlayer_deployment_output.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"addresses": {
"avsDirectory": "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707",
"avsDirectoryImplementation": "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82",
"baseStrategyImplementation": "0x322813Fd9A801c5507c9de605d63CEA4f2CE6c44",
"delayedWithdrawalRouter": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6",
"delayedWithdrawalRouterImplementation": "0x959922bE3CAee4b8Cd9a407cc3ac1C251C2007B1",
"delegation": "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9",
"delegationImplementation": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e",
"eigenLayerPauserReg": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512",
"eigenLayerProxyAdmin": "0x5FbDB2315678afecb367f032d93F642f64180aa3",
"eigenPodBeacon": "0x610178dA211FEF7D417bC0e6FeD39F05609AD788",
"eigenPodImplementation": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318",
"eigenPodManager": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853",
"eigenPodManagerImplementation": "0x0B306BF915C4d645ff596e518fAf3F9669b97016",
"emptyContract": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0",
"slasher": "0x0165878A594ca255338adfa4d48449f69242Eb8F",
"slasherImplementation": "0x9A676e781A523b5d0C0e43731313A708CB607508",
"strategies": "",
"strategyManager": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9",
"strategyManagerImplementation": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0"
},
"chainInfo": {
"chainId": 31337,
"deploymentBlock": 0
},
"parameters": {
"executorMultisig": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
"operationsMultisig": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
}
}
21 changes: 21 additions & 0 deletions contracts/tests/anvil/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Integration Tests

We store an anvil state files in this directory, so that we can start an anvil chain with the correct state for integration tests.
```
anvil --load-state STATE_FILE.json
```

## Eigenlayer deployment state file
`eigenlayer-deployed-anvil-state.json` contains the eigenlayer deployment.

It was created by running this [deploy script](https://github.com/Layr-Labs/eigenlayer-contracts/blob/2cb9ed107c6c918b9dfbac94cd71b4ab7c94e8c2/script/testing/M2_Deploy_From_Scratch.s.sol). If you ever need to redeploy a new version of eigenlayer contracts, first start an anvil chain that dumps its state after exiting
```
anvil --dump-state eigenlayer-deployed-anvil-state.json
```
Then run the deploy script
```
forge script script/testing/M2_Deploy_From_Scratch.s.sol --rpc-url http://localhost:8545 --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --broadcast --sig "run(string memory configFile)" -- M2_deploy_from_scratch.anvil.config.json
```
and finally kill the anvil chain with `Ctrl-C`. Make sure to copy the deployment [output file](https://github.com/Layr-Labs/eigenlayer-contracts/blob/master/script/output/M2_from_scratch_deployment_data.json) to [eigenlayer_deployment_output.json](../../contracts/script/output/31337/eigenlayer_deployment_output.json) so that the tests can find the deployed contracts.

See the main [README](../../README.md#dependencies) to understand why we deploy from the `experimental-reduce-strategy-manager-bytecode-size` branch of eigenlayer-contracts.
1 change: 1 addition & 0 deletions contracts/tests/anvil/contracts-deployed-anvil-state.json

Large diffs are not rendered by default.

54 changes: 54 additions & 0 deletions contracts/tests/anvil/deploy-contracts-save-anvil-state.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/bin/bash

# Enable the script to exit immediately if a command exits with a non-zero status
set -o errexit -o nounset -o pipefail

# Define your cleanup function
clean_up() {
echo "Executing cleanup function..."
set +e
pkill -f anvil

# Check if the exit status is non-zero
exit_status=$?
if [ $exit_status -ne 0 ]; then
echo "Script exited due to set -e on line $1 with command '$2'. Exit status: $exit_status"
fi
}
# Use trap to call the clean_up function when the script exits
trap 'clean_up $LINENO "$BASH_COMMAND"' EXIT

# cd to the directory of this script so that this can be run from anywhere
parent_path=$(
cd "$(dirname "${BASH_SOURCE[0]}")"
pwd -P
)
root_dir=$parent_path/../../..

set -a
source $parent_path/utils.sh
# we overwrite some variables here because should always deploy to anvil (localhost)
ETH_HTTP_URL=http://localhost:8545
set +a

# start an empty anvil chain in the background and dump its state to a json file upon exit
start_anvil_docker "" $parent_path/contracts-deployed-anvil-state.json
sleep 1

CHAIN_ID=$(cast chain-id)

# DEPLOY CONTRACT REGISTRY
# cd $parent_path/../../contracts
# forge create src/ContractsRegistry.sol:ContractsRegistry --rpc-url ${ETH_HTTP_URL:?} --private-key ${DEPLOYER_PRIVATE_KEY:?}

# DEPLOY EIGENLAYER
samlaf marked this conversation as resolved.
Show resolved Hide resolved
EIGEN_CONTRACTS_DIR=$parent_path/../../lib/eigenlayer-middleware/lib/eigenlayer-contracts
DEVNET_OUTPUT_DIR=$EIGEN_CONTRACTS_DIR/script/output/devnet
# deployment overwrites this file, so we save it as backup, because we want that output in our local files, and not in the eigenlayer-contracts submodule files
mv $DEVNET_OUTPUT_DIR/M2_from_scratch_deployment_data.json $DEVNET_OUTPUT_DIR/M2_from_scratch_deployment_data.json.bak
cd $EIGEN_CONTRACTS_DIR
forge script script/deploy/devnet/M2_Deploy_From_Scratch.s.sol --rpc-url http://localhost:8545 \
--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --broadcast \
--sig "run(string memory configFileName)" -- M2_deploy_from_scratch.anvil.config.json
mv $DEVNET_OUTPUT_DIR/M2_from_scratch_deployment_data.json $root_dir/contracts/script/output/${CHAIN_ID:?}/eigenlayer_deployment_output.json
mv $DEVNET_OUTPUT_DIR/M2_from_scratch_deployment_data.json.bak $DEVNET_OUTPUT_DIR/M2_from_scratch_deployment_data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/bin/bash

set -o errexit -o nounset -o pipefail

# cd to the directory of this script so that this can be run from anywhere
parent_path=$(
cd "$(dirname "${BASH_SOURCE[0]}")"
pwd -P
)
root_dir=$parent_path/../../..

set -a
source $parent_path/utils.sh
set +a

# start an anvil instance in the background that has eigenlayer contracts deployed
# we start anvil in the background so that we can run the below script
start_anvil_docker $parent_path/contracts-deployed-anvil-state.json ""

cd $root_dir/contracts
# we need to restart the anvil chain at the correct block, otherwise the indexRegistry has a quorumUpdate at the block number
# at which it was deployed (aka quorum was created/updated), but when we start anvil by loading state file it starts at block number 0
# so calling getOperatorListAtBlockNumber reverts because it thinks there are no quorums registered at block 0
# advancing chain manually like this is a current hack until https://github.com/foundry-rs/foundry/issues/6679 is merged
cast rpc anvil_mine 200 --rpc-url http://localhost:8545 > /dev/null
echo "Anvil is ready. Advanced chain to block-number:" $(cast block-number)


docker attach anvil
41 changes: 41 additions & 0 deletions contracts/tests/anvil/utils.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/bin/bash

set -e -o nounset

parent_path=$(
cd "$(dirname "${BASH_SOURCE[0]}")"
pwd -P
)

FOUNDRY_IMAGE=ghcr.io/foundry-rs/foundry:nightly-5b7e4cb3c882b28f3c32ba580de27ce7381f415a

clean_up() {
# Check if the exit status is non-zero
exit_status=$?
if [ $exit_status -ne 0 ]; then
echo "Script exited due to set -e on line $1 with command '$2'. Exit status: $exit_status"
fi
}
# Use trap to call the clean_up function when the script exits
trap 'clean_up $LINENO "$BASH_COMMAND"' ERR

# start_anvil_docker $LOAD_STATE_FILE $DUMP_STATE_FILE
start_anvil_docker() {
LOAD_STATE_FILE=$1
DUMP_STATE_FILE=$2
LOAD_STATE_VOLUME_DOCKER_ARG=$([[ -z $LOAD_STATE_FILE ]] && echo "" || echo "-v $LOAD_STATE_FILE:/load-state.json")
DUMP_STATE_VOLUME_DOCKER_ARG=$([[ -z $DUMP_STATE_FILE ]] && echo "" || echo "-v $DUMP_STATE_FILE:/dump-state.json")
LOAD_STATE_ANVIL_ARG=$([[ -z $LOAD_STATE_FILE ]] && echo "" || echo "--load-state /load-state.json")
DUMP_STATE_ANVIL_ARG=$([[ -z $DUMP_STATE_FILE ]] && echo "" || echo "--dump-state /dump-state.json")

echo $LOAD_STATE_VOLUME_DOCKER_ARG $DUMP_STATE_VOLUME_DOCKER_ARG
echo $LOAD_STATE_ANVIL_ARG $DUMP_STATE_ANVIL_ARG
trap 'docker stop anvil' EXIT
set -o xtrace
docker run --rm -d --name anvil -p 8545:8545 $LOAD_STATE_VOLUME_DOCKER_ARG $DUMP_STATE_VOLUME_DOCKER_ARG \
--entrypoint anvil \
$FOUNDRY_IMAGE \
$LOAD_STATE_ANVIL_ARG $DUMP_STATE_ANVIL_ARG --host 0.0.0.0
set +o xtrace
sleep 2
}
2 changes: 1 addition & 1 deletion signerv2/kms_signer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func TestSendTransaction(t *testing.T) {
keyAddr := crypto.PubkeyToAddress(*pk)
t.Logf("Public key address: %s", keyAddr.String())
assert.NotEqual(t, keyAddr, common.Address{0})
err = rpcClient.CallContext(context.Background(), nil, "anvil_setBalance", keyAddr, "2_000_000_000_000_000_000")
err = rpcClient.CallContext(context.Background(), nil, "anvil_setBalance", keyAddr, 2_000_000_000_000_000_000)
assert.Nil(t, err)

logger := &logging.NoopLogger{}
Expand Down
3 changes: 2 additions & 1 deletion testutils/anvil.go
samlaf marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ func StartAnvilContainer(anvilStateFileName string) (testcontainers.Container, e

ctx := context.Background()
req := testcontainers.ContainerRequest{
Image: "ghcr.io/foundry-rs/foundry:latest",
// pinning at old foundry commit because of https://github.com/foundry-rs/foundry/issues/7502
Image: "ghcr.io/foundry-rs/foundry:nightly-5b7e4cb3c882b28f3c32ba580de27ce7381f415a",
Entrypoint: []string{"anvil"},
Cmd: []string{"--host", "0.0.0.0", "--base-fee", "0", "--gas-price", "0"},
ExposedPorts: []string{"8545/tcp"},
Expand Down
Loading