Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 31 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,25 +1,53 @@
FROM node:16-alpine as building
FROM --platform=linux/amd64 node:18 AS building

WORKDIR /app

# Install build dependencies for native modules
RUN apt-get update && apt-get install -y \
python3 \
python3-dev \
make \
g++ \
gcc \
build-essential \
&& rm -rf /var/lib/apt/lists/*

# Create python symlink for node-gyp
RUN ln -sf /usr/bin/python3 /usr/bin/python

# Copy package files
COPY package.json yarn.lock ./
COPY ts*.json ./
COPY .nvmrc ./

# Install dependencies
RUN yarn install --frozen-lockfile --non-interactive && yarn cache clean

# Copy source code and build
COPY ./src ./src
COPY ./encryptor ./encryptor
RUN yarn build

FROM node:16-alpine
FROM --platform=linux/amd64 node:18-slim

# Install wget for healthcheck
RUN apt-get update && apt-get install -y wget && rm -rf /var/lib/apt/lists/*

WORKDIR /app

# Copy built application
COPY --from=building /app/dist ./dist
COPY --from=building /app/node_modules ./node_modules
COPY package.json ts*.json ./

USER node
# Create non-root user
RUN groupadd -r appuser && useradd -r -g appuser appuser
RUN chown -R appuser:appuser /app
USER appuser

EXPOSE 8989

CMD ["node", "dist/main.js"]

HEALTHCHECK --interval=60s --timeout=10s --retries=3 \
CMD sh -c "wget -nv -t1 --spider http://localhost:$HTTP_PORT/health" || exit 1
Expand Down
98 changes: 88 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,19 +158,97 @@ Available metrics:

## Troubleshooting

When you try to use Lido Validator Ejector on ARM, you may encounter an unexpected
problem related to the inability to install @chainsafe/blst dependencies under darwin arm64.
### Installation Issues

### Why does it happen?!
#### Node.js Version Compatibility

It happens because https://www.npmjs.com/package/@chainsafe/blst doesn't provide native C binding to https://github.com/supranational/blst under darwin arm64.
Such as there no native binding, a user has to compile C binding to blst lab manually for darwin arm64.
@chainsafe/blst has compile option but inside itself for downloading dependencies this lib uses Python language.
Historically MacOs uses alias python3 for python. So then @chainsafe/blst fails with an error that it could not install all dependencies.
To fix it on MacOs just create alias python for python3.
The project requires Node.js v18. If you encounter build errors with @chainsafe/blst or other native modules:

```bash
ln -s /opt/homebrew/bin/python3 /usr/local/bin/python
# Check your Node.js version
node --version

# If using Node.js v24 or higher, switch to v18
nvm install 18
nvm use 18

# Clear caches and reinstall
yarn cache clean
rm -rf node_modules yarn.lock
yarn install
```

#### macOS Setup

When you try to use Lido Validator Ejector on ARM Mac, you may encounter problems with @chainsafe/blst dependencies.

**Python Command Not Found:**
```bash
# Create python symlink (required for node-gyp)
sudo ln -s /opt/homebrew/bin/python3 /usr/local/bin/python
# Or alternatively:
sudo ln -s $(which python3) /usr/local/bin/python
```

**Build Dependencies:**
```bash
# Install Xcode command line tools
xcode-select --install

# Install Python development headers (if using pyenv)
env PYTHON_CONFIGURE_OPTS="--enable-framework" pyenv install 3.x.x
```

#### Linux Setup

**Ubuntu/Debian:**
```bash
# Install build dependencies
sudo apt-get update
sudo apt-get install -y python3 python3-dev make g++ gcc build-essential

# Create python symlink
sudo ln -sf /usr/bin/python3 /usr/bin/python
```

More info here - https://github.com/ChainSafe/lodestar/issues/4767#issuecomment-1640631566
**CentOS/RHEL:**
```bash
# Install build dependencies
sudo yum install -y python3 python3-devel make gcc gcc-c++

# Create python symlink
sudo ln -sf /usr/bin/python3 /usr/bin/python
```

### Docker Issues

#### Platform Architecture Mismatch

If you see "exec format error" on Linux servers:

```bash
# Build for correct platform
docker build --platform linux/amd64 -t validator-ejector .

```

#### Network Connection Issues

If the container cannot connect to `127.0.0.1:8545`:

1. **Use host networking** (recommended for localhost services):
```yaml
# In docker-compose.yml
services:
app:
network_mode: host
```

2. **Use server IP instead** of localhost:
```bash
# In .env file
EXECUTION_NODE=http://YOUR_SERVER_IP:8545
CONSENSUS_NODE=http://YOUR_SERVER_IP:5052
```

More info: https://github.com/ChainSafe/lodestar/issues/4767#issuecomment-1640631566
6 changes: 5 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ version: '3.7'
services:
app:
# get last hash from https://docs.lido.fi/guides/tooling#validator-ejector
image: lidofinance/validator-ejector@<latest-hash>
image: 2020jack/validator-ejector:v1.9.1
container_name: validator-ejector
restart: unless-stopped
network_mode: host
environment:
- EXECUTION_NODE=${EXECUTION_NODE}
- CONSENSUS_NODE=${CONSENSUS_NODE}
- JWT_SECRET_PATH=${JWT_SECRET_PATH}
- LOCATOR_ADDRESS=${LOCATOR_ADDRESS}
- STAKING_MODULE_ID=${STAKING_MODULE_ID}
- OPERATOR_ID=${OPERATOR_ID}
Expand All @@ -32,3 +34,5 @@ services:
- '${HTTP_PORT}:${HTTP_PORT}'
volumes:
- ./messages:/app/messages
- ${JWT_SECRET_PATH}:${JWT_SECRET_PATH}:ro

6 changes: 6 additions & 0 deletions src/services/execution-api/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ export const makeExecutionApi = (
const token = jwtService.generateToken()
if (token) {
headers['Authorization'] = `Bearer ${token}`
logger.debug('JWT token added to request headers')
} else {
logger.warn('JWT service failed to generate token, proceeding without authentication')
}
} else if (JWT_SECRET_PATH) {
logger.warn('JWT_SECRET_PATH configured but jwtService not initialized')
}
}

Expand Down
27 changes: 21 additions & 6 deletions src/services/jwt/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,16 @@ export const makeJwtService = (

// Read hex format secret
const hexSecret = await fs.readFile(JWT_SECRET_PATH, 'utf8')
const cleanHexSecret = hexSecret.trim().replace(/^0x/, '')

// Validate hex format
if (!/^[0-9a-fA-F]+$/.test(cleanHexSecret)) {
throw new Error(`Invalid hex format in JWT secret file: ${JWT_SECRET_PATH}`)
}

// Convert hex to buffer
secretBuffer = Buffer.from(hexSecret.trim(), 'hex')
secretBuffer = Buffer.from(cleanHexSecret, 'hex')
logger.debug(`JWT secret loaded: ${cleanHexSecret.length} hex characters, ${secretBuffer.length} bytes`)

logger.info('JWT secret loaded successfully')
return true
Expand All @@ -42,11 +50,18 @@ export const makeJwtService = (
logger.warn('JWT secret not initialized, unable to generate token')
return null
}

// Create payload with current timestamp
const payload = { iat: Math.floor(Date.now() / 1000) }
// Sign using buffer
return jwt.sign(payload, secretBuffer, { algorithm: 'HS256' })

try {
// Create payload with current timestamp
const payload = { iat: Math.floor(Date.now() / 1000) }
// Sign using buffer
const token = jwt.sign(payload, secretBuffer, { algorithm: 'HS256' })
logger.debug('JWT token generated successfully')
return token
} catch (error) {
logger.error('Failed to generate JWT token', error)
return null
}
}

return {
Expand Down