Skip to content

Commit

Permalink
Merge pull request #11 from symbioticfi/worker
Browse files Browse the repository at this point in the history
feat: worker for selfregistersqrttaskmiddleware
  • Loading branch information
alrxy authored Dec 21, 2024
2 parents 4619bd5 + f402025 commit a880d2f
Show file tree
Hide file tree
Showing 5 changed files with 373 additions and 0 deletions.
35 changes: 35 additions & 0 deletions src/examples/sqrt-task-network/worker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
FROM python:3.11-slim

WORKDIR /app

# Install system dependencies
RUN apt-get update && \
apt-get install -y gcc python3-dev && \
rm -rf /var/lib/apt/lists/*

# Copy requirements first to leverage Docker cache
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy worker code and ABI
COPY worker.py .
COPY abi.json .

# Environment variables will be provided at runtime
ENV WEB3_URL=https://ethereum-holesky-rpc.publicnode.com
ENV VALIDATOR_PRIVATE_KEY=""

# Add restart script
RUN echo '#!/bin/sh\n' \
'while true; do\n' \
' python worker.py start \\\n' \
' --validator-private-key "$VALIDATOR_PRIVATE_KEY" \\\n' \
' --web3-url "$WEB3_URL"\n' \
' status=$?\n' \
' echo "Process exited with status $status, restarting in 5 seconds..."\n' \
' sleep 5\n' \
'done\n' > /app/start.sh

RUN chmod +x /app/start.sh

CMD ["/app/start.sh"]
77 changes: 77 additions & 0 deletions src/examples/sqrt-task-network/worker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Sqrt Task Network Worker

This is a worker implementation for the Sqrt Task Network example. The worker listens for new tasks, computes square roots, and submits the results back to the blockchain.

## Prerequisites

- Docker
- Python 3.11+
- Access to a Holesky Ethereum node
- Private key for validator (used to sign task results and operator registration)
- Private key for operator (used to submit transactions to the network and in core)
- Have at least 0.05 Holesky ETH in the operator and validator account each for gas fees
- Registration in `OperatorRegistry` and opt-in in `NetworkOptInService` to the network ([see example](https://docs.symbiotic.fi/handbooks/operators-handbook#actions-in-symbiotic-core))
- Network: [0x18586B8cb86b59EF3F44BC915Ef92C83B6BAfd75](https://holesky.etherscan.io/address/0x18586B8cb86b59EF3F44BC915Ef92C83B6BAfd75)

## Documentation

For detailed information about operating on SelfRegisterSqrtTaskNetwork, please refer to the [Operators Handbook](https://docs.symbiotic.fi/handbooks/operators-handbook).

## Usage

### Registering as an Operator

1. Install dependencies:

```
pip install -r requirements.txt
```

2. Register as an operator (one-time setup):
```bash
python worker.py register \
--validator-private-key YOUR_VALIDATOR_PRIVATE_KEY \
--operator-private-key YOUR_OPERATOR_PRIVATE_KEY \
--vault-address YOUR_VAULT_ADDRESS
```

3. (Optional) Submit an incorrect answer for a specific task:
```bash
python worker.py submit-incorrect-answer \
--validator-private-key YOUR_VALIDATOR_PRIVATE_KEY \
--task-id TASK_ID
```

### Running the Worker

1. Build the Docker image:
```bash
docker build -t sqrt-worker .
```

2. Run the container:
```bash
docker run -e VALIDATOR_PRIVATE_KEY=your_private_key -e WEB3_URL=your_web3_url sqrt-worker
```

## Configuration Options

- `--validator-private-key`: Private key for signing task results
- `--operator-private-key`: Private key for transaction submission (only needed for registration)
- `--web3-url`: RPC endpoint URL (default: Holesky public node)
- `--vault-address`: Operator-specific vault address (default: 0x0)

## How It Works

1. The worker monitors the blockchain for new sqrt calculation tasks
2. When a task is found, it computes the square root using Python's math.isqrt
3. The result is signed using EIP-712 and submitted back to the contract
4. The worker maintains a block cache to resume from the last processed block

## Error Handling

The worker includes automatic retry logic and will:
- Reconnect on network issues
- Skip already completed tasks
- Log all operations for debugging
- Restart automatically when run in Docker
1 change: 1 addition & 0 deletions src/examples/sqrt-task-network/worker/abi.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"type":"constructor","inputs":[{"name":"networkRegistry","type":"address","internalType":"address"},{"name":"slashingWindow","type":"uint48","internalType":"uint48"},{"name":"operatorRegistry","type":"address","internalType":"address"},{"name":"vaultRegistry","type":"address","internalType":"address"},{"name":"operatorNetOptin","type":"address","internalType":"address"},{"name":"reader","type":"address","internalType":"address"},{"name":"owner","type":"address","internalType":"address"}],"stateMutability":"nonpayable"},{"type":"fallback","stateMutability":"nonpayable"},{"type":"function","name":"BaseMiddleware_VERSION","inputs":[],"outputs":[{"name":"","type":"uint64","internalType":"uint64"}],"stateMutability":"view"},{"type":"function","name":"ECDSASig_VERSION","inputs":[],"outputs":[{"name":"","type":"uint64","internalType":"uint64"}],"stateMutability":"view"},{"type":"function","name":"KeyManager256_VERSION","inputs":[],"outputs":[{"name":"","type":"uint64","internalType":"uint64"}],"stateMutability":"view"},{"type":"function","name":"MAX_OPERATORS","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"MAX_OPERATOR_VAULTS","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"MAX_SHARED_VAULTS","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"OwnableAccessManager_VERSION","inputs":[],"outputs":[{"name":"","type":"uint64","internalType":"uint64"}],"stateMutability":"view"},{"type":"function","name":"SelfRegisterOperators_VERSION","inputs":[],"outputs":[{"name":"","type":"uint64","internalType":"uint64"}],"stateMutability":"view"},{"type":"function","name":"SharedVaults_VERSION","inputs":[],"outputs":[{"name":"","type":"uint64","internalType":"uint64"}],"stateMutability":"view"},{"type":"function","name":"TimestampCapture_VERSION","inputs":[],"outputs":[{"name":"","type":"uint64","internalType":"uint64"}],"stateMutability":"view"},{"type":"function","name":"completeTask","inputs":[{"name":"taskIndex","type":"uint256","internalType":"uint256"},{"name":"answer","type":"uint256","internalType":"uint256"},{"name":"signature","type":"bytes","internalType":"bytes"},{"name":"stakeHints","type":"bytes[]","internalType":"bytes[]"},{"name":"slashHints","type":"bytes[]","internalType":"bytes[]"}],"outputs":[{"name":"isValidAnswer","type":"bool","internalType":"bool"}],"stateMutability":"nonpayable"},{"type":"function","name":"createTask","inputs":[{"name":"value","type":"uint256","internalType":"uint256"},{"name":"operator","type":"address","internalType":"address"}],"outputs":[{"name":"taskIndex","type":"uint256","internalType":"uint256"}],"stateMutability":"nonpayable"},{"type":"function","name":"eip712Domain","inputs":[],"outputs":[{"name":"fields","type":"bytes1","internalType":"bytes1"},{"name":"name","type":"string","internalType":"string"},{"name":"version","type":"string","internalType":"string"},{"name":"chainId","type":"uint256","internalType":"uint256"},{"name":"verifyingContract","type":"address","internalType":"address"},{"name":"salt","type":"bytes32","internalType":"bytes32"},{"name":"extensions","type":"uint256[]","internalType":"uint256[]"}],"stateMutability":"view"},{"type":"function","name":"executeSlash","inputs":[{"name":"epochStart","type":"uint48","internalType":"uint48"},{"name":"vault","type":"address","internalType":"address"},{"name":"subnetwork","type":"bytes32","internalType":"bytes32"},{"name":"operator","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"},{"name":"hints","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"getCaptureTimestamp","inputs":[],"outputs":[{"name":"timestamp","type":"uint48","internalType":"uint48"}],"stateMutability":"view"},{"type":"function","name":"keyWasActiveAt","inputs":[{"name":"timestamp","type":"uint48","internalType":"uint48"},{"name":"key_","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"nonces","inputs":[{"name":"operator","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"operatorByKey","inputs":[{"name":"key","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"operatorKey","inputs":[{"name":"operator","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"bytes","internalType":"bytes"}],"stateMutability":"view"},{"type":"function","name":"owner","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"pauseOperator","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"pauseOperator","inputs":[{"name":"operator","type":"address","internalType":"address"},{"name":"signature","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"pauseOperatorVault","inputs":[{"name":"vault","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"pauseOperatorVault","inputs":[{"name":"operator","type":"address","internalType":"address"},{"name":"vault","type":"address","internalType":"address"},{"name":"signature","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"pauseSharedVault","inputs":[{"name":"sharedVault","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"recover","inputs":[{"name":"hash","type":"bytes32","internalType":"bytes32"},{"name":"signature","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"pure"},{"type":"function","name":"registerOperator","inputs":[{"name":"operator","type":"address","internalType":"address"},{"name":"key","type":"bytes","internalType":"bytes"},{"name":"vault","type":"address","internalType":"address"},{"name":"signature","type":"bytes","internalType":"bytes"},{"name":"keySignature","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"registerOperator","inputs":[{"name":"key","type":"bytes","internalType":"bytes"},{"name":"vault","type":"address","internalType":"address"},{"name":"signature","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"registerOperatorVault","inputs":[{"name":"vault","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"registerOperatorVault","inputs":[{"name":"operator","type":"address","internalType":"address"},{"name":"vault","type":"address","internalType":"address"},{"name":"signature","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"registerSharedVault","inputs":[{"name":"sharedVault","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"renounceOwnership","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"setOwner","inputs":[{"name":"owner_","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"stakeToPower","inputs":[{"name":"vault","type":"address","internalType":"address"},{"name":"stake","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"power","type":"uint256","internalType":"uint256"}],"stateMutability":"pure"},{"type":"function","name":"tasks","inputs":[{"name":"","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"captureTimestamp","type":"uint48","internalType":"uint48"},{"name":"value","type":"uint256","internalType":"uint256"},{"name":"operator","type":"address","internalType":"address"},{"name":"completed","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"unpauseOperator","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"unpauseOperator","inputs":[{"name":"operator","type":"address","internalType":"address"},{"name":"signature","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"unpauseOperatorVault","inputs":[{"name":"vault","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"unpauseOperatorVault","inputs":[{"name":"operator","type":"address","internalType":"address"},{"name":"vault","type":"address","internalType":"address"},{"name":"signature","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"unpauseSharedVault","inputs":[{"name":"sharedVault","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"unregisterOperator","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"unregisterOperator","inputs":[{"name":"operator","type":"address","internalType":"address"},{"name":"signature","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"unregisterOperatorVault","inputs":[{"name":"operator","type":"address","internalType":"address"},{"name":"vault","type":"address","internalType":"address"},{"name":"signature","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"unregisterOperatorVault","inputs":[{"name":"vault","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"unregisterSharedVault","inputs":[{"name":"sharedVault","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"updateOperatorKey","inputs":[{"name":"operator","type":"address","internalType":"address"},{"name":"key","type":"bytes","internalType":"bytes"},{"name":"signature","type":"bytes","internalType":"bytes"},{"name":"keySignature","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"updateOperatorKey","inputs":[{"name":"key","type":"bytes","internalType":"bytes"},{"name":"signature","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"event","name":"CompleteTask","inputs":[{"name":"taskIndex","type":"uint256","indexed":true,"internalType":"uint256"},{"name":"isValidAnswer","type":"bool","indexed":false,"internalType":"bool"}],"anonymous":false},{"type":"event","name":"CreateTask","inputs":[{"name":"taskIndex","type":"uint256","indexed":true,"internalType":"uint256"},{"name":"operator","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"EIP712DomainChanged","inputs":[],"anonymous":false},{"type":"event","name":"Initialized","inputs":[{"name":"version","type":"uint64","indexed":false,"internalType":"uint64"}],"anonymous":false},{"type":"event","name":"InstantSlash","inputs":[{"name":"vault","type":"address","indexed":false,"internalType":"address"},{"name":"subnetwork","type":"bytes32","indexed":false,"internalType":"bytes32"},{"name":"amount","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"VetoSlash","inputs":[{"name":"vault","type":"address","indexed":false,"internalType":"address"},{"name":"subnetwork","type":"bytes32","indexed":false,"internalType":"bytes32"},{"name":"index","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"error","name":"AlreadyEnabled","inputs":[]},{"type":"error","name":"AlreadyRegistered","inputs":[]},{"type":"error","name":"DuplicateKey","inputs":[]},{"type":"error","name":"ECDSAInvalidSignature","inputs":[]},{"type":"error","name":"ECDSAInvalidSignatureLength","inputs":[{"name":"length","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"ECDSAInvalidSignatureS","inputs":[{"name":"s","type":"bytes32","internalType":"bytes32"}]},{"type":"error","name":"Enabled","inputs":[]},{"type":"error","name":"ImmutablePeriodNotPassed","inputs":[]},{"type":"error","name":"InactiveVaultSlash","inputs":[]},{"type":"error","name":"InvalidHints","inputs":[]},{"type":"error","name":"InvalidInitialization","inputs":[]},{"type":"error","name":"InvalidOwner","inputs":[{"name":"owner","type":"address","internalType":"address"}]},{"type":"error","name":"InvalidSignature","inputs":[]},{"type":"error","name":"MaxDisabledKeysReached","inputs":[]},{"type":"error","name":"NoSlasher","inputs":[]},{"type":"error","name":"NonVetoSlasher","inputs":[]},{"type":"error","name":"NotEnabled","inputs":[]},{"type":"error","name":"NotInitializing","inputs":[]},{"type":"error","name":"NotOperator","inputs":[]},{"type":"error","name":"NotOperatorSpecificVault","inputs":[]},{"type":"error","name":"NotOperatorVault","inputs":[]},{"type":"error","name":"NotRegistered","inputs":[]},{"type":"error","name":"NotVault","inputs":[]},{"type":"error","name":"OnlyOwnerCanCall","inputs":[{"name":"sender","type":"address","internalType":"address"}]},{"type":"error","name":"OperatorNotOptedIn","inputs":[]},{"type":"error","name":"OperatorNotRegistered","inputs":[]},{"type":"error","name":"SafeCastOverflowedUintDowncast","inputs":[{"name":"bits","type":"uint8","internalType":"uint8"},{"name":"value","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"TaskCompleted","inputs":[]},{"type":"error","name":"TooManyOperatorVaults","inputs":[]},{"type":"error","name":"TooManyOperators","inputs":[]},{"type":"error","name":"TooManySharedVaults","inputs":[]},{"type":"error","name":"TooOldTimestampSlash","inputs":[]},{"type":"error","name":"UnknownSlasherType","inputs":[]},{"type":"error","name":"VaultAlreadyRegistered","inputs":[]},{"type":"error","name":"VaultEpochTooShort","inputs":[]},{"type":"error","name":"VaultNotInitialized","inputs":[]}]
3 changes: 3 additions & 0 deletions src/examples/sqrt-task-network/worker/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
web3==7.6.0
eth-account==0.13.4
click==8.1.7
Loading

0 comments on commit a880d2f

Please sign in to comment.