Skip to content

Commit 749d2d7

Browse files
authored
*: v1.7.1 (#4049)
Charon v1.7.1 #4024 #4029 #4041 category: feature ticket: none
1 parent 3ac6bc9 commit 749d2d7

File tree

6 files changed

+160
-7
lines changed

6 files changed

+160
-7
lines changed

app/eth2wrap/eth2wrap.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ func incRequest(endpoint string) {
281281
func wrapError(ctx context.Context, err error, label string, fields ...z.Field) error {
282282
// Decompose go-eth2-client http errors
283283
if apiErr := new(eth2api.Error); errors.As(err, &apiErr) {
284-
err = errors.New("nok http response",
284+
err = errors.Wrap(err, "nok http response",
285285
z.Int("status_code", apiErr.StatusCode),
286286
z.Str("endpoint", apiErr.Endpoint),
287287
z.Str("method", apiErr.Method),

app/eth2wrap/synthproposer.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323

2424
"github.com/obolnetwork/charon/app/errors"
2525
"github.com/obolnetwork/charon/app/log"
26-
"github.com/obolnetwork/charon/app/z"
2726
)
2827

2928
const (
@@ -124,7 +123,9 @@ func (h *synthWrapper) syntheticProposal(ctx context.Context, slot eth2p0.Slot,
124123

125124
signed, err := h.SignedBeaconBlock(ctx, opts)
126125
if err != nil {
127-
if z.ContainsField(err, z.Int("status_code", http.StatusNotFound)) {
126+
// Check if error is a 404 (block not found)
127+
var apiErr *eth2api.Error
128+
if errors.As(err, &apiErr) && apiErr.StatusCode == http.StatusNotFound {
128129
continue
129130
}
130131

app/sse/listener.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ func (p *listener) handleBlockGossipEvent(ctx context.Context, event *event, add
214214
z.Str("delay", delay.String()),
215215
z.Str("block", blockGossip.Block))
216216

217-
sseBlockGossipHistogram.WithLabelValues(addr).Observe(float64(delay))
217+
sseBlockGossipHistogram.WithLabelValues(addr).Observe(delay.Seconds())
218218

219219
return nil
220220
}
@@ -245,7 +245,7 @@ func (p *listener) handleBlockEvent(ctx context.Context, event *event, addr stri
245245
z.Str("delay", delay.String()),
246246
z.Str("block", block.Block))
247247

248-
sseBlockHistogram.WithLabelValues(addr).Observe(float64(delay))
248+
sseBlockHistogram.WithLabelValues(addr).Observe(delay.Seconds())
249249

250250
return nil
251251
}

cmd/createdkg.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ func generateLaunchpadLink(configHash []byte, network string) string {
335335
}
336336

337337
func generateAPILink(configHash []byte) string {
338-
return "https://api.obol.tech/dv/" + fmt.Sprintf("%#x", configHash)
338+
return "https://api.obol.tech/v1/definition/" + fmt.Sprintf("%#x", configHash)
339339
}
340340

341341
func publishPartialDefinition(ctx context.Context, conf createDKGConfig, privKey *k1.PrivateKey, def cluster.Definition) error {
@@ -368,7 +368,7 @@ func publishPartialDefinition(ctx context.Context, conf createDKGConfig, privKey
368368
} else {
369369
log.Info(ctx, "Distributed Key Generation configuration created. Run one of the following commands from the directories where the associated .charon/charon-enr-private-key(s) that match these ENRs are: "+
370370
"(Without docker): `charon dkg --definition-file="+generateAPILink(def.ConfigHash)+"` "+
371-
"(With docker): `docker run --rm --it -v \"$(pwd):/opt/charon/.charon\" obolnetwork/charon:latest dkg --definition-file="+generateAPILink(def.ConfigHash)+"`")
371+
"(With docker): `docker run --rm -v \"$(pwd)/.charon:/opt/charon/.charon\" obolnetwork/charon:latest dkg --definition-file="+generateAPILink(def.ConfigHash)+"`")
372372
}
373373

374374
return nil

core/tracker/inclusion.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"context"
77
"fmt"
88
"maps"
9+
"net/http"
910
"slices"
1011
"strconv"
1112
"sync"
@@ -95,6 +96,13 @@ func inclSupported() map[core.DutyType]bool {
9596
return inclSupported
9697
}
9798

99+
// is404Error returns true if the error is a 404 (Not Found) error from the beacon node.
100+
// It checks if the error chain contains an eth2api.Error with a 404 status code.
101+
func is404Error(err error) bool {
102+
var apiErr *eth2api.Error
103+
return errors.As(err, &apiErr) && apiErr.StatusCode == http.StatusNotFound
104+
}
105+
98106
// Submitted is called when a duty is submitted to the beacon node.
99107
// It adds the duty to the list of submitted duties.
100108
func (i *inclusionCore) Submitted(duty core.Duty, pubkey core.PubKey, data core.SignedData, delay time.Duration) (err error) {
@@ -661,6 +669,12 @@ func (a *InclusionChecker) checkBlock(ctx context.Context, slot uint64, attDutie
661669

662670
eth2Resp, err := a.eth2Cl.SignedBeaconBlock(ctx, &eth2api.SignedBeaconBlockOpts{Block: strconv.FormatUint(slot, 10)})
663671
if err != nil {
672+
// 404 means no block was proposed for this slot, which is not an error condition
673+
if is404Error(err) {
674+
a.checkBlockFunc(ctx, slot, false)
675+
return nil
676+
}
677+
664678
return err
665679
}
666680

@@ -681,6 +695,11 @@ func (a *InclusionChecker) checkBlock(ctx context.Context, slot uint64, attDutie
681695
func (a *InclusionChecker) checkBlockAndAtts(ctx context.Context, slot uint64, attDuties []*eth2v1.AttesterDuty) error {
682696
attsResp, err := a.eth2Cl.BeaconBlockAttestations(ctx, &eth2api.BeaconBlockAttestationsOpts{Block: strconv.FormatUint(slot, 10)})
683697
if err != nil {
698+
// 404 means no block was proposed for this slot, which is not an error condition
699+
if is404Error(err) {
700+
return nil
701+
}
702+
684703
return err
685704
} else if len(attsResp.Data) == 0 {
686705
return nil // No block for this slot

core/tracker/inclusion_internal_test.go

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,3 +442,136 @@ func TestBlockInclusion(t *testing.T) {
442442
require.Empty(t, missed)
443443
})
444444
}
445+
446+
func TestInclusion404Handling(t *testing.T) {
447+
ctx := context.Background()
448+
449+
t.Run("checkBlock handles 404 error gracefully", func(t *testing.T) {
450+
bmock, err := beaconmock.New(ctx)
451+
require.NoError(t, err)
452+
453+
// Mock SignedBeaconBlock to return a 404 error
454+
bmock.SignedBeaconBlockFunc = func(ctx context.Context, blockID string) (*eth2spec.VersionedSignedBeaconBlock, error) {
455+
return nil, &eth2api.Error{
456+
StatusCode: 404,
457+
Method: "GET",
458+
Endpoint: "/eth/v2/beacon/blocks/" + blockID,
459+
Data: []byte(`{"code":404,"message":"NOT_FOUND: beacon block not found"}`),
460+
}
461+
}
462+
463+
// Wrap beaconmock with eth2wrap to get proper error handling
464+
eth2Cl, err := eth2wrap.Instrument([]eth2wrap.Client{bmock}, nil)
465+
require.NoError(t, err)
466+
467+
var (
468+
checkBlockCalled bool
469+
foundBlock bool
470+
)
471+
472+
noopTrackerInclFunc := func(duty core.Duty, key core.PubKey, data core.SignedData, err error) {}
473+
474+
incl, err := NewInclusion(ctx, eth2Cl, noopTrackerInclFunc)
475+
require.NoError(t, err)
476+
477+
// Override checkBlockFunc to capture the result
478+
incl.checkBlockFunc = func(ctx context.Context, slot uint64, found bool) {
479+
checkBlockCalled = true
480+
foundBlock = found
481+
}
482+
483+
// Call checkBlock with a slot that will trigger the 404
484+
err = incl.checkBlock(ctx, 12345, nil)
485+
require.NoError(t, err, "checkBlock should not return an error for 404")
486+
require.True(t, checkBlockCalled, "checkBlockFunc should have been called")
487+
require.False(t, foundBlock, "block should be marked as not found")
488+
})
489+
490+
t.Run("checkBlockAndAtts handles 404 error gracefully", func(t *testing.T) {
491+
featureset.EnableForT(t, featureset.AttestationInclusion)
492+
493+
bmock, err := beaconmock.New(ctx)
494+
require.NoError(t, err)
495+
496+
// Mock BeaconBlockAttestations to return a 404 error
497+
bmock.BeaconBlockAttestationsFunc = func(ctx context.Context, opts *eth2api.BeaconBlockAttestationsOpts) ([]*eth2spec.VersionedAttestation, error) {
498+
return nil, &eth2api.Error{
499+
StatusCode: 404,
500+
Method: "GET",
501+
Endpoint: "/eth/v1/beacon/blocks/" + opts.Block + "/attestations",
502+
Data: []byte(`{"code":404,"message":"NOT_FOUND: beacon block not found"}`),
503+
}
504+
}
505+
506+
// Wrap beaconmock with eth2wrap to get proper error handling
507+
eth2Cl, err := eth2wrap.Instrument([]eth2wrap.Client{bmock}, nil)
508+
require.NoError(t, err)
509+
510+
noopTrackerInclFunc := func(duty core.Duty, key core.PubKey, data core.SignedData, err error) {}
511+
512+
incl, err := NewInclusion(ctx, eth2Cl, noopTrackerInclFunc)
513+
require.NoError(t, err)
514+
515+
// Call checkBlockAndAtts with a slot that will trigger the 404
516+
err = incl.checkBlockAndAtts(ctx, 12345, nil)
517+
require.NoError(t, err, "checkBlockAndAtts should not return an error for 404")
518+
})
519+
520+
t.Run("checkBlock returns error for non-404 errors", func(t *testing.T) {
521+
bmock, err := beaconmock.New(ctx)
522+
require.NoError(t, err)
523+
524+
// Mock SignedBeaconBlock to return a 500 error
525+
bmock.SignedBeaconBlockFunc = func(ctx context.Context, blockID string) (*eth2spec.VersionedSignedBeaconBlock, error) {
526+
return nil, &eth2api.Error{
527+
StatusCode: 500,
528+
Method: "GET",
529+
Endpoint: "/eth/v2/beacon/blocks/" + blockID,
530+
Data: []byte(`{"code":500,"message":"Internal server error"}`),
531+
}
532+
}
533+
534+
// Wrap beaconmock with eth2wrap to get proper error handling
535+
eth2Cl, err := eth2wrap.Instrument([]eth2wrap.Client{bmock}, nil)
536+
require.NoError(t, err)
537+
538+
noopTrackerInclFunc := func(duty core.Duty, key core.PubKey, data core.SignedData, err error) {}
539+
540+
incl, err := NewInclusion(ctx, eth2Cl, noopTrackerInclFunc)
541+
require.NoError(t, err)
542+
543+
// Call checkBlock with a slot that will trigger the 500 error
544+
err = incl.checkBlock(ctx, 12345, nil)
545+
require.Error(t, err, "checkBlock should return an error for non-404 errors")
546+
})
547+
548+
t.Run("checkBlockAndAtts returns error for non-404 errors", func(t *testing.T) {
549+
featureset.EnableForT(t, featureset.AttestationInclusion)
550+
551+
bmock, err := beaconmock.New(ctx)
552+
require.NoError(t, err)
553+
554+
// Mock BeaconBlockAttestations to return a 500 error
555+
bmock.BeaconBlockAttestationsFunc = func(ctx context.Context, opts *eth2api.BeaconBlockAttestationsOpts) ([]*eth2spec.VersionedAttestation, error) {
556+
return nil, &eth2api.Error{
557+
StatusCode: 500,
558+
Method: "GET",
559+
Endpoint: "/eth/v1/beacon/blocks/" + opts.Block + "/attestations",
560+
Data: []byte(`{"code":500,"message":"Internal server error"}`),
561+
}
562+
}
563+
564+
// Wrap beaconmock with eth2wrap to get proper error handling
565+
eth2Cl, err := eth2wrap.Instrument([]eth2wrap.Client{bmock}, nil)
566+
require.NoError(t, err)
567+
568+
noopTrackerInclFunc := func(duty core.Duty, key core.PubKey, data core.SignedData, err error) {}
569+
570+
incl, err := NewInclusion(ctx, eth2Cl, noopTrackerInclFunc)
571+
require.NoError(t, err)
572+
573+
// Call checkBlockAndAtts with a slot that will trigger the 500 error
574+
err = incl.checkBlockAndAtts(ctx, 12345, nil)
575+
require.Error(t, err, "checkBlockAndAtts should return an error for non-404 errors")
576+
})
577+
}

0 commit comments

Comments
 (0)