Skip to content

Commit f7b9678

Browse files
authored
feat(rewards): log per-validator attestation failures with request_id (#852)
When a validator fails to return a reward claim attestation, we silently marked it bad and moved on, leaving no way to see which validator failed or why. Add a warn-level log in fetchAttestations and in the coin redeem inline validator loop with the validator endpoint, owner, claim context (handle, rewardId, specifier), and underlying error. Also add an ApiServer.requestLogger(c) helper that binds the requestid-middleware request_id into a per-request child of app.logger, and use it in v1ClaimRewards and v1CoinsPostRedeem so handler logs can be correlated with access logs.
1 parent 8f53b82 commit f7b9678

4 files changed

Lines changed: 47 additions & 4 deletions

File tree

api/server.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -841,6 +841,16 @@ type ApiServer struct {
841841
openAudioPool *OpenAudioPool
842842
}
843843

844+
// requestLogger returns app.logger annotated with the current request_id (set
845+
// by the requestid middleware) when available, so handler logs can be
846+
// correlated with access logs.
847+
func (app *ApiServer) requestLogger(c *fiber.Ctx) *zap.Logger {
848+
if requestId, ok := c.Locals("requestId").(string); ok && requestId != "" {
849+
return app.logger.With(zap.String("request_id", requestId))
850+
}
851+
return app.logger
852+
}
853+
844854
func (app *ApiServer) home(c *fiber.Ctx) error {
845855
return c.JSON(fiber.Map{
846856
"env": app.config.Env,

api/v1_claim_rewards.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ func getValidatorAttestation(args GetValidatorAttestationParams) (*SenderAttesta
212212
// handling retries and reselection
213213
func fetchAttestations(
214214
ctx context.Context,
215+
logger *zap.Logger,
215216
rewardClaim RewardClaim,
216217
allValidators []config.Node,
217218
excludedOperators []string,
@@ -321,6 +322,14 @@ func fetchAttestations(
321322

322323
for _, result := range results {
323324
if result.err != nil {
325+
logger.Warn("validator attestation failed",
326+
zap.String("validator", result.node.Endpoint),
327+
zap.String("validatorOwner", result.node.Owner),
328+
zap.String("handle", rewardClaim.Handle),
329+
zap.String("rewardId", rewardClaim.RewardID),
330+
zap.String("specifier", rewardClaim.Specifier),
331+
zap.Error(result.err),
332+
)
324333
badValidators[result.node.Endpoint] = true
325334
continue
326335
}
@@ -597,6 +606,7 @@ type RelayTransactionResponse struct {
597606
// Claims an individual reward.
598607
func claimReward(
599608
ctx context.Context,
609+
logger *zap.Logger,
600610
rewardClaim RewardClaim,
601611
rewardManagerClient *reward_manager.RewardManagerClient,
602612
rewardAttester *rewards.RewardAttester,
@@ -639,6 +649,7 @@ func claimReward(
639649
// Fetch AAO and validator attestations
640650
attestations, err := fetchAttestations(
641651
ctx,
652+
logger,
642653
rewardClaim,
643654
validators,
644655
existingValidatorOwners,
@@ -740,6 +751,7 @@ type ClaimRewardsBody struct {
740751

741752
// Claims all the filtered undisbursed rewards for a user.
742753
func (app *ApiServer) v1ClaimRewards(c *fiber.Ctx) error {
754+
logger := app.requestLogger(c)
743755

744756
body := ClaimRewardsBody{}
745757
err := c.BodyParser(&body)
@@ -831,6 +843,7 @@ func (app *ApiServer) v1ClaimRewards(c *fiber.Ctx) error {
831843
validators := app.config.ArtistCoinRewardsStaticSenders
832844
sigs, err := claimReward(
833845
ctx,
846+
logger,
834847
rewardClaim,
835848
app.rewardManagerClient,
836849
app.rewardAttester,
@@ -842,7 +855,7 @@ func (app *ApiServer) v1ClaimRewards(c *fiber.Ctx) error {
842855
if err != nil {
843856
var instrErr *spl.InstructionError
844857
if errors.As(err, &instrErr) {
845-
app.logger.Error("failed to claim challenge reward. transaction failed to send.",
858+
logger.Error("failed to claim challenge reward. transaction failed to send.",
846859
zap.String("handle", row.Handle.String),
847860
zap.String("rewardId", row.ChallengeID),
848861
zap.String("specifier", row.Specifier),
@@ -851,7 +864,7 @@ func (app *ApiServer) v1ClaimRewards(c *fiber.Ctx) error {
851864
zap.Error(err),
852865
)
853866
} else {
854-
app.logger.Error("failed to claim challenge reward.",
867+
logger.Error("failed to claim challenge reward.",
855868
zap.String("handle", row.Handle.String),
856869
zap.String("rewardId", row.ChallengeID),
857870
zap.String("specifier", row.Specifier),

api/v1_claim_rewards_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/OpenAudio/go-openaudio/pkg/rewards"
1212
"github.com/gagliardetto/solana-go"
1313
"github.com/stretchr/testify/assert"
14+
"go.uber.org/zap"
1415
)
1516

1617
func TestFetchAttestations(t *testing.T) {
@@ -106,6 +107,7 @@ func TestFetchAttestations(t *testing.T) {
106107
// Call fetchAttestations
107108
attestations, err := fetchAttestations(
108109
context.Background(),
110+
zap.NewNop(),
109111
rewardClaim,
110112
allValidators,
111113
[]string{}, // no excluded operators

api/v1_coins_post_redeem.go

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ type CoinRewardParams struct {
2929
}
3030

3131
func (app *ApiServer) v1CoinsPostRedeem(c *fiber.Ctx) error {
32+
logger := app.requestLogger(c)
3233
// #region Validate Params
3334
if app.config.LaunchpadDeterministicSecret == "" {
3435
return fiber.NewError(fiber.StatusInternalServerError, "Claim authority base is not configured")
@@ -332,6 +333,14 @@ func (app *ApiServer) v1CoinsPostRedeem(c *fiber.Ctx) error {
332333
})
333334

334335
if err != nil {
336+
logger.Warn("validator attestation failed",
337+
zap.String("validator", validator.Endpoint),
338+
zap.String("validatorOwner", validator.Owner),
339+
zap.String("handle", userHandle),
340+
zap.String("rewardId", redeemCode),
341+
zap.String("specifier", specifier),
342+
zap.Error(err),
343+
)
335344
continue
336345
}
337346

@@ -342,6 +351,15 @@ func (app *ApiServer) v1CoinsPostRedeem(c *fiber.Ctx) error {
342351
}
343352
signatureBytes, err := hex.DecodeString(strings.TrimPrefix(signature, "0x"))
344353
if err != nil {
354+
logger.Warn("validator attestation signature decode failed",
355+
zap.String("validator", validator.Endpoint),
356+
zap.String("validatorOwner", validator.Owner),
357+
zap.String("handle", userHandle),
358+
zap.String("rewardId", redeemCode),
359+
zap.String("specifier", specifier),
360+
zap.String("attestation", response.Attestation),
361+
zap.Error(err),
362+
)
345363
continue
346364
}
347365

@@ -375,7 +393,7 @@ func (app *ApiServer) v1CoinsPostRedeem(c *fiber.Ctx) error {
375393
if err != nil {
376394
var instrErr *spl.InstructionError
377395
if errors.As(err, &instrErr) {
378-
app.logger.Error("failed to claim challenge reward. transaction failed to send.",
396+
logger.Error("failed to claim challenge reward. transaction failed to send.",
379397
zap.String("handle", userHandle),
380398
zap.String("rewardId", "code"),
381399
zap.String("specifier", specifier),
@@ -384,7 +402,7 @@ func (app *ApiServer) v1CoinsPostRedeem(c *fiber.Ctx) error {
384402
zap.Error(err),
385403
)
386404
} else {
387-
app.logger.Error("failed to claim challenge reward.",
405+
logger.Error("failed to claim challenge reward.",
388406
zap.String("handle", userHandle),
389407
zap.String("rewardId", "code"),
390408
zap.String("specifier", specifier),

0 commit comments

Comments
 (0)