-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #11 from symbioticfi/worker
feat: worker for selfregistersqrttaskmiddleware
- Loading branch information
Showing
5 changed files
with
373 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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":[]}] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.