Skip to content

Commit

Permalink
feat: add rpc for generating rewards claim proofs (#172)
Browse files Browse the repository at this point in the history
  • Loading branch information
seanmcgary authored Jan 14, 2025
2 parents c8c540d + dce7fb4 commit 5de5d94
Show file tree
Hide file tree
Showing 13 changed files with 393 additions and 26 deletions.
10 changes: 6 additions & 4 deletions cmd/debugger/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/Layr-Labs/sidecar/pkg/indexer"
"github.com/Layr-Labs/sidecar/pkg/pipeline"
"github.com/Layr-Labs/sidecar/pkg/postgres"
"github.com/Layr-Labs/sidecar/pkg/proofs"
"github.com/Layr-Labs/sidecar/pkg/rewards"
"github.com/Layr-Labs/sidecar/pkg/rewards/stakerOperators"
"github.com/Layr-Labs/sidecar/pkg/rewardsCalculatorQueue"
Expand Down Expand Up @@ -100,21 +101,22 @@ func main() {
rcq := rewardsCalculatorQueue.NewRewardsCalculatorQueue(rc, l)

p := pipeline.NewPipeline(fetchr, idxr, mds, sm, rc, rcq, cfg, sdc, eb, l)
rps := proofs.NewRewardsProofsStore(rc, l)

// Create new sidecar instance
// Create new sidecar instance
_ = sidecar.NewSidecar(&sidecar.SidecarConfig{
GenesisBlockNumber: cfg.GetGenesisBlockNumber(),
}, cfg, mds, p, sm, rc, rcq, l, client)
}, cfg, mds, p, sm, rc, rcq, rps, l, client)

rpcServer := rpcServer.NewRpcServer(&rpcServer.RpcServerConfig{
rpc := rpcServer.NewRpcServer(&rpcServer.RpcServerConfig{
GrpcPort: cfg.RpcConfig.GrpcPort,
HttpPort: cfg.RpcConfig.HttpPort,
}, mds, sm, rc, rcq, eb, l)
}, mds, sm, rc, rcq, eb, rps, l)

// RPC channel to notify the RPC server to shutdown gracefully
rpcChannel := make(chan bool)
if err := rpcServer.Start(ctx, rpcChannel); err != nil {
if err := rpc.Start(ctx, rpcChannel); err != nil {
l.Sugar().Fatalw("Failed to start RPC server", zap.Error(err))
}

Expand Down
11 changes: 7 additions & 4 deletions cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/Layr-Labs/sidecar/pkg/indexer"
"github.com/Layr-Labs/sidecar/pkg/pipeline"
"github.com/Layr-Labs/sidecar/pkg/postgres"
"github.com/Layr-Labs/sidecar/pkg/proofs"
"github.com/Layr-Labs/sidecar/pkg/rewards"
"github.com/Layr-Labs/sidecar/pkg/rewards/stakerOperators"
"github.com/Layr-Labs/sidecar/pkg/rewardsCalculatorQueue"
Expand Down Expand Up @@ -116,23 +117,25 @@ var runCmd = &cobra.Command{

rcq := rewardsCalculatorQueue.NewRewardsCalculatorQueue(rc, l)

rps := proofs.NewRewardsProofsStore(rc, l)

go rcq.Process()

p := pipeline.NewPipeline(fetchr, idxr, mds, sm, rc, rcq, cfg, sdc, eb, l)

// Create new sidecar instance
sidecar := sidecar.NewSidecar(&sidecar.SidecarConfig{
GenesisBlockNumber: cfg.GetGenesisBlockNumber(),
}, cfg, mds, p, sm, rc, rcq, l, client)
}, cfg, mds, p, sm, rc, rcq, rps, l, client)

rpcServer := rpcServer.NewRpcServer(&rpcServer.RpcServerConfig{
rpc := rpcServer.NewRpcServer(&rpcServer.RpcServerConfig{
GrpcPort: cfg.RpcConfig.GrpcPort,
HttpPort: cfg.RpcConfig.HttpPort,
}, mds, sm, rc, rcq, eb, l)
}, mds, sm, rc, rcq, eb, rps, l)

// RPC channel to notify the RPC server to shutdown gracefully
rpcChannel := make(chan bool)
if err := rpcServer.Start(ctx, rpcChannel); err != nil {
if err := rpc.Start(ctx, rpcChannel); err != nil {
l.Sugar().Fatalw("Failed to start RPC server", zap.Error(err))
}

Expand Down
49 changes: 49 additions & 0 deletions examples/rewardsProofs/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package main

import (
"context"
"crypto/tls"
"fmt"
v1 "github.com/Layr-Labs/protocol-apis/gen/protos/eigenlayer/sidecar/v1"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
"log"
"strings"
)

func NewSidecarClient(url string, insecureConn bool) (v1.RewardsClient, error) {
var creds grpc.DialOption
if strings.Contains(url, "localhost:") || strings.Contains(url, "127.0.0.1:") || insecureConn {
creds = grpc.WithTransportCredentials(insecure.NewCredentials())
} else {
creds = grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{InsecureSkipVerify: false}))
}

grpcClient, err := grpc.NewClient(url, creds)
if err != nil {
return nil, err
}

return v1.NewRewardsClient(grpcClient), nil
}

func main() {
earnerAddress := "0x111116fe4f8c2f83e3eb2318f090557b7cd0bf76"
tokens := []string{"0xdeeeeE2b48C121e6728ed95c860e296177849932"}

client, err := NewSidecarClient("localhost:7100", true)
if err != nil {
log.Fatal(err)
}

res, err := client.GenerateClaimProof(context.Background(), &v1.GenerateClaimProofRequest{
EarnerAddress: earnerAddress,
Tokens: tokens,
})
if err != nil {
log.Fatal(err)
}

fmt.Printf("Proof: %+v\n", res.Proof)
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/DataDog/datadog-go/v5 v5.5.0
github.com/Layr-Labs/eigenlayer-contracts v0.4.1-holesky-pepe.0.20240813143901-00fc4b95e9c1
github.com/Layr-Labs/eigenlayer-rewards-proofs v0.2.13
github.com/Layr-Labs/protocol-apis v1.1.0
github.com/Layr-Labs/protocol-apis v1.1.1-0.20250114181701-acb87ef4eeb5
github.com/ethereum/go-ethereum v1.14.9
github.com/gocarina/gocsv v0.0.0-20240520201108-78e41c74b4b1
github.com/google/uuid v1.6.0
Expand Down
12 changes: 8 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,16 @@ github.com/Layr-Labs/eigenlayer-contracts v0.4.1-holesky-pepe.0.20240813143901-0
github.com/Layr-Labs/eigenlayer-contracts v0.4.1-holesky-pepe.0.20240813143901-00fc4b95e9c1/go.mod h1:Ie8YE3EQkTHqG6/tnUS0He7/UPMkXPo/3OFXwSy0iRo=
github.com/Layr-Labs/eigenlayer-rewards-proofs v0.2.13 h1:Blb4AE+jC/vddV71w4/MQAPooM+8EVqv9w2bL4OytgY=
github.com/Layr-Labs/eigenlayer-rewards-proofs v0.2.13/go.mod h1:PD/HoyzZjxDw1tAcZw3yD0yGddo+yhmwQAi+lk298r4=
github.com/Layr-Labs/protocol-apis v1.0.0-rc.1.0.20250109222723-376a40434d4e h1:IArMOWI0V+2KFexZbJwZvIxVFTJdaDZG8PMcih/GV4A=
github.com/Layr-Labs/protocol-apis v1.0.0-rc.1.0.20250109222723-376a40434d4e/go.mod h1:prNA2/mLO5vpMZ2q78Nsn0m97wm28uiRnwO+/yOxigk=
github.com/Layr-Labs/protocol-apis v1.0.0-rc.1.0.20250109230911-e6fec5ffbd4c h1:uKOEYFWJ6OcCGTMZK8M4rZJHIxdWMuTI7j+LvNe8D84=
github.com/Layr-Labs/protocol-apis v1.0.0-rc.1.0.20250109230911-e6fec5ffbd4c/go.mod h1:prNA2/mLO5vpMZ2q78Nsn0m97wm28uiRnwO+/yOxigk=
github.com/Layr-Labs/protocol-apis v1.1.0 h1:PO6x+Y9ORiac2dkaWJayRFqhyzcvMbvRQkDIpLTNtVc=
github.com/Layr-Labs/protocol-apis v1.1.0/go.mod h1:prNA2/mLO5vpMZ2q78Nsn0m97wm28uiRnwO+/yOxigk=
github.com/Layr-Labs/protocol-apis v1.1.1-0.20250110201222-e8670ac00c32 h1:nRHAH0dn5qkQXUjdrPlGThtKLt154UKAHfzCEyMqfr0=
github.com/Layr-Labs/protocol-apis v1.1.1-0.20250110201222-e8670ac00c32/go.mod h1:prNA2/mLO5vpMZ2q78Nsn0m97wm28uiRnwO+/yOxigk=
github.com/Layr-Labs/protocol-apis v1.1.1-0.20250110201843-c2f2cf37e910 h1:X3t1mr1kAOGDJ3paPS/lzHJchK4y+oWSJ+D/7+MgOmY=
github.com/Layr-Labs/protocol-apis v1.1.1-0.20250110201843-c2f2cf37e910/go.mod h1:prNA2/mLO5vpMZ2q78Nsn0m97wm28uiRnwO+/yOxigk=
github.com/Layr-Labs/protocol-apis v1.1.1-0.20250114180833-6f2487a7e08c h1:kcTwHJVRDQAGqVacRJ4h6r6LKIZP6nBkxaBEJvZ9A3Q=
github.com/Layr-Labs/protocol-apis v1.1.1-0.20250114180833-6f2487a7e08c/go.mod h1:prNA2/mLO5vpMZ2q78Nsn0m97wm28uiRnwO+/yOxigk=
github.com/Layr-Labs/protocol-apis v1.1.1-0.20250114181701-acb87ef4eeb5 h1:0PLxb8fpwdpWpfk24yhdZzETFCxVMN2yJjRDyBBf6wM=
github.com/Layr-Labs/protocol-apis v1.1.1-0.20250114181701-acb87ef4eeb5/go.mod h1:prNA2/mLO5vpMZ2q78Nsn0m97wm28uiRnwO+/yOxigk=
github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
Expand Down
2 changes: 1 addition & 1 deletion pkg/pipeline/pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ func (p *Pipeline) RunForFetchedBlock(ctx context.Context, block *fetcher.Fetche
zap.String("cutoffDate", cutoffDate),
zap.Uint64("blockNumber", blockNumber),
)
accountTree, _, err := p.rewardsCalculator.MerkelizeRewardsForSnapshot(rewardsCalculationEnd)
accountTree, _, _, err := p.rewardsCalculator.MerkelizeRewardsForSnapshot(rewardsCalculationEnd)
if err != nil {
p.Logger.Sugar().Errorw("Failed to merkelize rewards for snapshot date",
zap.String("cutoffDate", cutoffDate), zap.Error(err),
Expand Down
121 changes: 121 additions & 0 deletions pkg/proofs/rewardsProofs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package proofs

import (
"fmt"
rewardsCoordinator "github.com/Layr-Labs/eigenlayer-contracts/pkg/bindings/IRewardsCoordinator"
"github.com/Layr-Labs/eigenlayer-rewards-proofs/pkg/claimgen"
"github.com/Layr-Labs/eigenlayer-rewards-proofs/pkg/distribution"
"github.com/Layr-Labs/sidecar/pkg/rewards"
"github.com/Layr-Labs/sidecar/pkg/utils"
gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/wealdtech/go-merkletree/v2"
"go.uber.org/zap"
)

type RewardsProofsStore struct {
rewardsCalculator *rewards.RewardsCalculator
logger *zap.Logger
rewardsData map[string]*ProofData
}

type ProofData struct {
SnapshotDate string
AccountTree *merkletree.MerkleTree
TokenTree map[gethcommon.Address]*merkletree.MerkleTree
Distribution *distribution.Distribution
}

func NewRewardsProofsStore(
rc *rewards.RewardsCalculator,
l *zap.Logger,
) *RewardsProofsStore {
return &RewardsProofsStore{
rewardsCalculator: rc,
logger: l,
rewardsData: make(map[string]*ProofData),
}
}

func (rps *RewardsProofsStore) getRewardsDataForSnapshot(snapshot string) (*ProofData, error) {
data, ok := rps.rewardsData[snapshot]
if !ok {
accountTree, tokenTree, distro, err := rps.rewardsCalculator.MerkelizeRewardsForSnapshot(snapshot)
if err != nil {
rps.logger.Sugar().Errorw("Failed to fetch rewards for snapshot",
zap.String("snapshot", snapshot),
zap.Error(err),
)
return nil, err
}

data = &ProofData{
SnapshotDate: snapshot,
AccountTree: accountTree,
TokenTree: tokenTree,
Distribution: distro,
}
rps.rewardsData[snapshot] = data
}
return data, nil
}

func (rps *RewardsProofsStore) GenerateRewardsClaimProof(earnerAddress string, tokenAddresses []string, rootIndex int64) (
[]byte,
*rewardsCoordinator.IRewardsCoordinatorRewardsMerkleClaim,
error,
) {
distributionRoot, err := rps.rewardsCalculator.FindClaimableDistributionRoot(rootIndex)
if err != nil {
rps.logger.Sugar().Errorf("Failed to find claimable distribution root for root_index",
zap.Int64("rootIndex", rootIndex),
zap.Error(err),
)
return nil, nil, err
}
if distributionRoot == nil {
return nil, nil, fmt.Errorf("No claimable distribution root found for root index %d", rootIndex)
}
snapshotDate := distributionRoot.GetSnapshotDate()

// Make sure rewards have been generated for this snapshot.
// Any snapshot that is >= the provided date is valid since we'll select only data up
// to the snapshot/cutoff date
generatedSnapshot, err := rps.rewardsCalculator.GetGeneratedRewardsForSnapshotDate(snapshotDate)
if err != nil {
rps.logger.Sugar().Errorf("Failed to get generated rewards for snapshot date", zap.Error(err))
return nil, nil, err
}
rps.logger.Sugar().Infow("Using snapshot for rewards proof",
zap.String("requestedSnapshot", snapshotDate),
zap.String("snapshot", generatedSnapshot.SnapshotDate),
)

proofData, err := rps.getRewardsDataForSnapshot(snapshotDate)
if err != nil {
rps.logger.Sugar().Error("Failed to get rewards data for snapshot",
zap.String("snapshot", snapshotDate),
zap.Error(err),
)
return nil, nil, err
}

tokens := utils.Map(tokenAddresses, func(addr string, i uint64) gethcommon.Address {
return gethcommon.HexToAddress(addr)
})
earner := gethcommon.HexToAddress(earnerAddress)

claim, err := claimgen.GetProofForEarner(
proofData.Distribution,
uint32(distributionRoot.RootIndex),
proofData.AccountTree,
proofData.TokenTree,
earner,
tokens,
)
if err != nil {
rps.logger.Sugar().Error("Failed to generate claim proof for earner", zap.Error(err))
return nil, nil, err
}

return proofData.AccountTree.Root(), claim, nil
}
Loading

0 comments on commit 5de5d94

Please sign in to comment.