Skip to content
Merged
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
5 changes: 3 additions & 2 deletions Dockerfile-local
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ RUN apt-get update && apt-get -y upgrade && apt-get install -y --no-install-reco
&& update-ca-certificates
ARG userid=10001
ARG groupid=10001
RUN groupadd -g ${groupid} assertoor && useradd -m -u ${userid} -g assertoor assertoor
RUN (getent group ${groupid} || groupadd -g ${groupid} assertoor) && \
useradd -m -u ${userid} -g ${groupid} assertoor
RUN echo "assertoor ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/assertoor
WORKDIR /app
COPY --from=builder /src/bin/* /app/
RUN chown -R assertoor:assertoor /app
RUN chown -R assertoor:${groupid} /app
RUN mkdir /workspace
USER assertoor
WORKDIR /workspace
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ devnet:
.hack/devnet/run.sh

devnet-run: devnet
go run main.go --config .hack/devnet/generated-assertoor-config.yaml
go run main.go --config .hack/devnet/generated-assertoor-config.yaml --verbose

devnet-run-docker: devnet
docker build --file ./Dockerfile-local -t assertoor:devnet-run --build-arg userid=$(CURRENT_UID) --build-arg groupid=$(CURRENT_GID) .
Expand Down
27 changes: 27 additions & 0 deletions pkg/coordinator/clients/consensus/rpc/beaconapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -496,3 +496,30 @@ func (bc *BeaconClient) SubmitProposerSlashing(ctx context.Context, slashing *ph

return nil
}

type NodeIdentity struct {
PeerID string `json:"peer_id"`
ENR string `json:"enr"`
P2PAddresses []string `json:"p2p_addresses"`
DiscoveryAddresses []string `json:"discovery_addresses"`
Metadata struct {
SeqNumber uint64 `json:"seq_number,string"`
Attnets string `json:"attnets"`
Syncnets string `json:"syncnets"`
} `json:"metadata"`
}

type apiNodeIdentity struct {
Data *NodeIdentity `json:"data"`
}

func (bc *BeaconClient) GetNodeIdentity(ctx context.Context) (*NodeIdentity, error) {
var nodeIdentity apiNodeIdentity

err := bc.getJSON(ctx, fmt.Sprintf("%s/eth/v1/node/identity", bc.endpoint), &nodeIdentity)
if err != nil {
return nil, fmt.Errorf("error retrieving node identity: %v", err)
}

return nodeIdentity.Data, nil
}
117 changes: 117 additions & 0 deletions pkg/coordinator/tasks/check_consensus_identity/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# `check_consensus_identity` Task

This task checks consensus client node identity information by querying the `/eth/v1/node/identity` API endpoint. It can verify various aspects of the node identity including CGC (Custody Group Count) extracted from ENR (Ethereum Node Record).

## Configuration

### Required Parameters
- **`clientPattern`** *(string)*: Pattern to match client names (e.g., `"lodestar-*"`, `"*"` for all)

### Optional Parameters
- **`pollInterval`** *(duration)*: Interval between checks (default: `10s`)
- **`minClientCount`** *(int)*: Minimum number of clients that must pass checks (default: `1`)
- **`maxFailCount`** *(int)*: Maximum number of clients that can fail (-1 for no limit, default: `-1`)
- **`failOnCheckMiss`** *(bool)*: Whether to fail the task when checks don't pass (default: `false`)

### CGC (Custody Group Count) Checks
- **`expectCgc`** *(int)*: Expect exact CGC value
- **`minCgc`** *(int)*: Minimum CGC value required
- **`maxCgc`** *(int)*: Maximum CGC value allowed

### ENR Checks
- **`expectEnrField`** *(map[string]interface{})*: Expected ENR field values

### PeerID Checks
- **`expectPeerIdPattern`** *(string)*: Regex pattern that PeerID must match

### P2P Address Checks
- **`expectP2pAddressCount`** *(int)*: Expected number of P2P addresses
- **`expectP2pAddressMatch`** *(string)*: Regex pattern that at least one P2P address must match

### Metadata Checks
- **`expectSeqNumber`** *(uint64)*: Expected sequence number
- **`minSeqNumber`** *(uint64)*: Minimum sequence number required

## Outputs

The task exports the following data via `ctx.Outputs`:

- **`matchingClients`**: Array of clients that passed all checks
- **`failedClients`**: Array of clients that failed checks
- **`totalCount`**: Total number of clients checked
- **`matchingCount`**: Number of clients that passed checks
- **`failedCount`**: Number of clients that failed checks

Each client result includes:
- `clientName`: Name of the consensus client
- `peerId`: Peer ID from node identity
- `enr`: ENR string
- `p2pAddresses`: Array of P2P addresses
- `discoveryAddresses`: Array of discovery addresses
- `seqNumber`: Metadata sequence number
- `attnets`: Attestation subnets
- `syncnets`: Sync subnets
- `cgc`: Extracted Custody Group Count
- `enrFields`: Parsed ENR fields
- `checksPassed`: Whether all configured checks passed
- `failureReasons`: Array of reasons why checks failed (if any)

## Example Configurations

### Basic Identity Check
```yaml
- name: check_node_identity
task: check_consensus_identity
config:
clientPattern: "lodestar-*"
minClientCount: 1
```

### CGC Validation
```yaml
- name: validate_cgc
task: check_consensus_identity
config:
clientPattern: "*"
expectCgc: 8
failOnCheckMiss: true
```

### Comprehensive Identity Check
```yaml
- name: full_identity_check
task: check_consensus_identity
config:
clientPattern: "prysm-*"
minCgc: 4
maxCgc: 16
expectP2pAddressCount: 2
expectPeerIdPattern: "^16Uiu2HA.*"
minSeqNumber: 1
pollInterval: 30s
failOnCheckMiss: true
```

### Using Outputs in Subsequent Tasks
```yaml
- name: check_identity
task: check_consensus_identity
config:
clientPattern: "*"
expectCgc: 8

- name: verify_results
task: run_shell
config:
command: |
echo "Found ${check_identity.matchingCount} matching clients"
echo "Total CGC sum: $(echo '${check_identity.matchingClients}' | jq '[.[] | .cgc] | add')"
```

## Use Cases

1. **PeerDAS Validation**: Verify nodes have correct custody assignments
2. **Network Health**: Check node identity consistency across clients
3. **Configuration Validation**: Ensure nodes are properly configured for specific network requirements
4. **Testing**: Validate node behavior changes after deposits or configuration updates
5. **Monitoring**: Track node identity changes over time
59 changes: 59 additions & 0 deletions pkg/coordinator/tasks/check_consensus_identity/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package checkconsensusidentity

import (
"fmt"
"time"

"github.com/ethpandaops/assertoor/pkg/coordinator/helper"
)

type Config struct {
ClientPattern string `yaml:"clientPattern" json:"clientPattern"`
PollInterval helper.Duration `yaml:"pollInterval" json:"pollInterval"`
MinClientCount int `yaml:"minClientCount" json:"minClientCount"`
MaxFailCount int `yaml:"maxFailCount" json:"maxFailCount"`
FailOnCheckMiss bool `yaml:"failOnCheckMiss" json:"failOnCheckMiss"`

// CGC (Custody Group Count) checks
ExpectCGC *uint64 `yaml:"expectCgc" json:"expectCgc"`
MinCGC *uint64 `yaml:"minCgc" json:"minCgc"`
MaxCGC *uint64 `yaml:"maxCgc" json:"maxCgc"`

// ENR checks
ExpectENRField map[string]interface{} `yaml:"expectEnrField" json:"expectEnrField"`

// PeerID checks
ExpectPeerIDPattern string `yaml:"expectPeerIdPattern" json:"expectPeerIdPattern"`

// P2P address checks
ExpectP2PAddressCount *int `yaml:"expectP2pAddressCount" json:"expectP2pAddressCount"`
ExpectP2PAddressMatch string `yaml:"expectP2pAddressMatch" json:"expectP2pAddressMatch"`

// Metadata checks
ExpectSeqNumber *uint64 `yaml:"expectSeqNumber" json:"expectSeqNumber"`
MinSeqNumber *uint64 `yaml:"minSeqNumber" json:"minSeqNumber"`
}

func DefaultConfig() Config {
return Config{
PollInterval: helper.Duration{Duration: 10 * time.Second},
MaxFailCount: -1,
MinClientCount: 1,
}
}

func (c *Config) Validate() error {
if c.ClientPattern == "" {
return fmt.Errorf("clientPattern is required")
}

if c.MinCGC != nil && c.MaxCGC != nil && *c.MinCGC > *c.MaxCGC {
return fmt.Errorf("minCgc must be <= maxCgc")
}

if c.ExpectP2PAddressCount != nil && *c.ExpectP2PAddressCount < 0 {
return fmt.Errorf("expectP2pAddressCount must be >= 0")
}

return nil
}
Loading