Skip to content

AVS based on incredible squaring by eigenlayer to explore what a Sertn AVS will need

License

Notifications You must be signed in to change notification settings

inference-labs-inc/sertn-avs

Repository files navigation

sertn AVS

Do not use it in Production, testnet only.

Repository for AVS middleware with full EigenLayer integration.

Based on the work github.com/Layr-Labs/incredible-squaring-avs

Dependencies

You will need foundry and zap-pretty and docker to run the examples below.

curl -L https://foundry.paradigm.xyz | bash
foundryup
go install github.com/maoueh/zap-pretty@latest

You will also need to install docker, and build the contracts:

make build-contracts && make deploy-contracts

Running via make

This simple session illustrates the basic flow of the AVS. The makefile commands are hardcoded for a single operator, but it's however easy to create new operator config files, and start more operators manually (see the actual commands that the makefile calls).

Start anvil in a separate terminal:

make start-chain

The above command starts a local fork of the Holesky testnet.

make deploy-contracts

The above command deploys all of the AVS contracts to the Holesky fork.

Start the aggregator:

make start-aggregator

Register the operator with eigenlayer and sertn, and then start the process:

make start-operator

By default, the start-operator command will also setup the operator (see register_operator_on_startup flag in config-files/operator.anvil.yaml). To disable this, set register_operator_on_startup to false, and run make cli-setup-operator before running start-operator.

make start-interface

The above command will start a web interface to interact with the AVS system in an intuitive manner.

Important

Make sure to update common/constants.ts and operator.anvil.yaml to the > contracts deployed under sertn_avs_deployment_output.json.

export const taskManagerAddress = "0x8ac5eE52F70AE01dB914bE459D8B3d50126fd6aE";

matches

{
  "addresses": {
    "erc20Mock": "0x0000000000000000000000000000000000000000",
    ...
    "sertnTaskManager": "0x8ac5eE52F70AE01dB914bE459D8B3d50126fd6aE",
    ...
  }
}

and

avs_registry_coordinator_address: 0x325c8Df4CFb5B068675AFF8f62aA668D1dEc3C4B
operator_state_retriever_address: 0xa62835D1A6bf5f521C4e2746E1F51c923b8f3483

match

{
  "addresses": {
    "erc20Mock": "0x0000000000000000000000000000000000000000",
    ...
    "operatorStateRetriever": "0xa62835D1A6bf5f521C4e2746E1F51c923b8f3483",
    "registryCoordinator": "0x325c8Df4CFb5B068675AFF8f62aA668D1dEc3C4B",
    ...
  }
}

Avs Task Description

The architecture of the AVS contains:

  • Eigenlayer core contracts
  • AVS contracts
  • Task Generator
    • in a real world scenario, this could be a separate entity, but for this simple demo, the aggregator also acts as the task generator
  • Aggregator
    • aggregates BLS signatures from operators and posts the aggregated response to the task manager
    • For this simple demo, the aggregator is not an operator, and thus does not need to register with eigenlayer or the AVS contract. It's IP address is simply hardcoded into the operators' config.
  • Operators
    • Computer the inference given input sent to the task manager by the task generator, sign it, and send it to the aggregator

  1. A task generator (in our case, same as the aggregator) publishes tasks once every regular interval (say 10 blocks, you are free to set your own interval) to the SertnTaskManager contract's createNewTask function. Each task specifies an array of 5 uint256s inputs for which it wants the currently opted-in operators to determine its inference. createNewTask also takes quorumNumbers and quorumThresholdPercentage which requests that each listed quorum (we only use quorumNumber 0 in sertn) needs to reach at least thresholdPercentage of operator signatures.

  2. A registry contract is deployed that allows any eigenlayer operator with at least 1 delegated mockerc20 token to opt-in to this AVS and also de-register from this AVS.

  3. [Operator] The operators who are currently opted-in with the AVS need to read the inputs from the Task contract, compute its inference, sign on that computed result (over the BN254 curve) and send their taskResponse and signature to the aggregator.

  4. [Aggregator] The aggregator collects the signatures from the operators and aggregates them using BLS aggregation. If any response passes the quorumThresholdPercentage set by the task generator when posting the task, the aggregator posts the aggregated response to the Task contract.

  5. If a response was sent within the response window, we enter the [Dispute resolution] period.

    • [Off-chain] A challenge window is launched during which anyone can raise a dispute in a DisputeResolution contract (in our case, this is the same as the TaskManager contract)
    • [On-chain] The DisputeResolution contract resolves that a particular operator’s response is not the correct response (that is, not the inference of the integers specified in the task) or the opted-in operator didn’t respond during the response window. If the dispute is resolved, the operator will be frozen in the Registration contract and the veto committee will decide whether to veto the freezing request or not.

Avs node spec compliance

Every AVS node implementation is required to abide by the Eigenlayer AVS Node Specification. We suggest reading through the whole spec, including the keys management section, but the hard requirements are currently only to:

If you are using golang, you can use our metrics and nodeapi implementation in the eigensdk, just like this repo does. Otherwise, you will have to implement it on your own.

StakeUpdates Cronjob

AVS Registry contracts have a stale view of operator shares in the delegation manager contract. In order to update their stake table, they need to periodically call the StakeRegistry.updateStakes() function. We are currently writing a cronjob binary to do this for you, will be open sourced soon!

Integration Tests

See the integration tests README for more details.

Troubleshooting

Received error from aggregator

When running on anvil, a typical log for the operator is

[2024-04-09 18:25:08.647 PDT] INFO (logging/zap_logger.go:49) rpc client is nil. Dialing aggregator rpc client
[2024-04-09 18:25:08.650 PDT] INFO (logging/zap_logger.go:49) Sending signed task response header to aggregator {"signedTaskResponse":"\u0026aggregator.SignedTaskResponse{TaskResponse:contractSertnTaskManager.ITaskStructTaskResponse{ReferenceTaskIndex:0x2, NumberSquared:4}, BlsSignature:bls.Signature{G1Point:(*bls.G1Point)(0x14000282068)}, OperatorId:[32]uint8{0xc4, 0xc2, 0x10, 0x30, 0xe, 0x28, 0xab, 0x4b, 0xa7, 0xb, 0x7f, 0xbb, 0xe, 0xfa, 0x55, 0x7d, 0x2a, 0x2a, 0x5f, 0x1f, 0xbf, 0xa6, 0xf8, 0x56, 0xe4, 0xcf, 0x3e, 0x9d, 0x76, 0x6a, 0x21, 0xdc}}"}
[2024-04-09 18:25:08.651 PDT] INFO (logging/zap_logger.go:49) Received error from aggregator {"err":"task 2 not initialized or already completed"}
[2024-04-09 18:25:08.651 PDT] INFO (logging/zap_logger.go:69) Retrying in 2 seconds
[2024-04-09 18:25:10.679 PDT] INFO (logging/zap_logger.go:49) Signed task response header accepted by aggregator. {"reply":false}

The error task 2 not initialized or already completed is expected behavior. This is because the aggregator needs to setup its data structures before it can accept responses. But on a local anvil setup, the operator had time to receive the websocket event for the new task, run the inference, sign the response, and send it to the aggregator process before the aggregator has finalized its setup. Hence, the operator retries sending the response 2 seconds later and it is accepted.

About

AVS based on incredible squaring by eigenlayer to explore what a Sertn AVS will need

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published