diff --git a/.github/workflows/check-anvil-state.yml b/.github/workflows/check-anvil-state.yml new file mode 100644 index 000000000..663946be1 --- /dev/null +++ b/.github/workflows/check-anvil-state.yml @@ -0,0 +1,58 @@ +name: Check anvil state +on: + push: + branches: + - dev + pull_request: + merge_group: + +jobs: + generate-anvil-state: + name: generate anvil state + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: stable + + - name: Generate anvil state + run: make deploy-contracts-to-anvil-and-save-state + + check-anvil-state: + name: Check anvil dump is up-to-date + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + # This step is needed to know if the contracts were changed. + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + contracts: + - 'contracts/lib/**' + - 'contracts/script/**' + - 'contracts/src/**' + anvil_state: + - 'contracts/anvil/contracts-deployed-anvil-state.json' + + # This step runs only if some contract changed. + # It checks whether the anvil state has changed. + # If there are no changes, then the anvil state is outdated + # and therefore this step will fail. + - name: Check the anvil state is up-to-date + if: steps.filter.outputs.contracts == 'true' + run: | + ANVIL_STATE_UPDATED=${{ steps.filter.outputs.anvil_state }} + if [[ "$ANVIL_STATE_UPDATED" == "false" ]]; then + echo "The anvil state is outdated"; + exit 1 + fi diff --git a/.github/workflows/contracts.yml b/.github/workflows/contracts.yml new file mode 100644 index 000000000..ba932e2f8 --- /dev/null +++ b/.github/workflows/contracts.yml @@ -0,0 +1,40 @@ +name: Contracts CI + +on: + push: + branches: [main] + pull_request: + branches: [ '**' ] + +env: + FOUNDRY_PROFILE: ci + +jobs: + check: + strategy: + fail-fast: true + + name: Foundry project + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./contracts + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: stable + + - name: Show Forge version + run: forge --version + + - name: Run Forge fmt + run: forge fmt --check + + - name: Run Forge build + run: forge build diff --git a/chainio/clients/avsregistry/reader.go b/chainio/clients/avsregistry/reader.go index 382cb27ba..c714364a3 100644 --- a/chainio/clients/avsregistry/reader.go +++ b/chainio/clients/avsregistry/reader.go @@ -30,6 +30,8 @@ type Config struct { OperatorStateRetrieverAddress common.Address } +// The ChainReader provides methods to call the +// AVS registry contract's view functions. type ChainReader struct { logger logging.Logger blsApkRegistryAddr common.Address @@ -40,6 +42,7 @@ type ChainReader struct { ethClient eth.HttpBackend } +// Creates a new instance of the ChainReader. func NewChainReader( registryCoordinatorAddr common.Address, blsApkRegistryAddr common.Address, @@ -84,6 +87,7 @@ func NewReaderFromConfig( ), nil } +// Returns the total quorum count read from the RegistryCoordinator func (r *ChainReader) GetQuorumCount(opts *bind.CallOpts) (uint8, error) { if r.registryCoordinator == nil { return 0, errors.New("RegistryCoordinator contract not provided") @@ -91,6 +95,8 @@ func (r *ChainReader) GetQuorumCount(opts *bind.CallOpts) (uint8, error) { return r.registryCoordinator.QuorumCount(opts) } +// Returns, for each quorum in `quorumNumbers`, a vector of the operators registered for +// that quorum at the current block, containing each operator's `operatorId` and `stake`. func (r *ChainReader) GetOperatorsStakeInQuorumsAtCurrentBlock( opts *bind.CallOpts, quorumNumbers types.QuorumNums, @@ -130,6 +136,8 @@ func (r *ChainReader) GetOperatorsStakeInQuorumsAtBlock( return operatorStakes, nil } +// Returns, for each quorum in `quorumNumbers`, a vector of the addresses of the +// operators registered for that quorum at the current block. func (r *ChainReader) GetOperatorAddrsInQuorumsAtCurrentBlock( opts *bind.CallOpts, quorumNumbers types.QuorumNums, @@ -137,7 +145,6 @@ func (r *ChainReader) GetOperatorAddrsInQuorumsAtCurrentBlock( if r.operatorStateRetriever == nil { return nil, errors.New("OperatorStateRetriever contract not provided") } - if opts.Context == nil { opts.Context = context.Background() } @@ -169,6 +176,10 @@ func (r *ChainReader) GetOperatorAddrsInQuorumsAtCurrentBlock( } +// Returns a tuple containing +// - An array with the quorum IDs in which the given operator is registered at the given block +// - An array that contains, for each quorum, an array with the address, id and stake +// of each operator registered in that quorum. func (r *ChainReader) GetOperatorsStakeInQuorumsOfOperatorAtBlock( opts *bind.CallOpts, operatorId types.OperatorId, @@ -261,6 +272,8 @@ func (r *ChainReader) GetOperatorStakeInQuorumsOfOperatorAtCurrentBlock( return quorumStakes, nil } +// Returns a struct containing the indices of the quorum members that signed, +// and the ones that didn't. func (r *ChainReader) GetCheckSignaturesIndices( opts *bind.CallOpts, referenceBlockNumber uint32, @@ -293,6 +306,7 @@ func (r *ChainReader) GetCheckSignaturesIndices( return checkSignatureIndices, nil } +// Given an operator address, returns its ID. func (r *ChainReader) GetOperatorId( opts *bind.CallOpts, operatorAddress common.Address, @@ -311,6 +325,7 @@ func (r *ChainReader) GetOperatorId( return operatorId, nil } +// Given an operator ID, returns its address. func (r *ChainReader) GetOperatorFromId( opts *bind.CallOpts, operatorId types.OperatorId, @@ -329,6 +344,8 @@ func (r *ChainReader) GetOperatorFromId( return operatorAddress, nil } +// Returns an array of booleans, where the boolean at index i represents +// whether the operator is registered for the quorum i. func (r *ChainReader) QueryRegistrationDetail( opts *bind.CallOpts, operatorAddress common.Address, @@ -358,6 +375,7 @@ func (r *ChainReader) QueryRegistrationDetail( return quorums, nil } +// Returns true if the operator is registered, false otherwise. func (r *ChainReader) IsOperatorRegistered( opts *bind.CallOpts, operatorAddress common.Address, @@ -376,6 +394,9 @@ func (r *ChainReader) IsOperatorRegistered( return registeredWithAvs, nil } +// Queries existing operators for a particular block range. +// Returns two arrays. The first one contains the addresses +// of the operators, and the second contains their corresponding public keys. func (r *ChainReader) QueryExistingRegisteredOperatorPubKeys( ctx context.Context, startBlock *big.Int, @@ -475,6 +496,9 @@ func (r *ChainReader) QueryExistingRegisteredOperatorPubKeys( return operatorAddresses, operatorPubkeys, nil } +// Queries existing operator sockets for a particular block range. +// Returns a mapping containing operator IDs as keys and their +// corresponding sockets as values. func (r *ChainReader) QueryExistingRegisteredOperatorSockets( ctx context.Context, startBlock *big.Int, diff --git a/chainio/clients/avsregistry/writer.go b/chainio/clients/avsregistry/writer.go index 99e5ec1ac..aa22d12d9 100644 --- a/chainio/clients/avsregistry/writer.go +++ b/chainio/clients/avsregistry/writer.go @@ -35,6 +35,8 @@ type eLReader interface { ) ([32]byte, error) } +// The ChainWriter provides methods to call the +// AVS registry contract's state-changing functions. type ChainWriter struct { serviceManagerAddr gethcommon.Address registryCoordinator *regcoord.ContractRegistryCoordinator @@ -47,6 +49,7 @@ type ChainWriter struct { txMgr txmgr.TxManager } +// Returns a new instance of ChainWriter. func NewChainWriter( serviceManagerAddr gethcommon.Address, registryCoordinator *regcoord.ContractRegistryCoordinator, @@ -380,6 +383,8 @@ func (w *ChainWriter) UpdateStakesOfEntireOperatorSetForQuorums( } +// Updates the stakes of a the given `operators` for all the quorums. +// On success, returns the receipt of the transaction. func (w *ChainWriter) UpdateStakesOfOperatorSubsetForAllQuorums( ctx context.Context, operators []gethcommon.Address, @@ -408,6 +413,8 @@ func (w *ChainWriter) UpdateStakesOfOperatorSubsetForAllQuorums( return receipt, nil } +// Deregisters the caller from the quorums given by `quorumNumbers`. +// On success, returns the receipt of the transaction. func (w *ChainWriter) DeregisterOperator( ctx context.Context, quorumNumbers types.QuorumNums, @@ -435,6 +442,8 @@ func (w *ChainWriter) DeregisterOperator( return receipt, nil } +// Deregisters an operator from the given operator sets. +// On success, returns the receipt of the transaction. func (w *ChainWriter) DeregisterOperatorOperatorSets( ctx context.Context, operatorSetIds types.OperatorSetIds, @@ -465,6 +474,8 @@ func (w *ChainWriter) DeregisterOperatorOperatorSets( return receipt, nil } +// Updates the socket of the sender (if it is a registered operator). +// On success, returns the receipt of the transaction. func (w *ChainWriter) UpdateSocket( ctx context.Context, socket types.Socket,