Skip to content

Commit

Permalink
read blob version params from chain
Browse files Browse the repository at this point in the history
  • Loading branch information
ian-shim committed Nov 21, 2024
1 parent cd97fb8 commit cb01646
Show file tree
Hide file tree
Showing 26 changed files with 340 additions and 141 deletions.
26 changes: 26 additions & 0 deletions common/read_only_map.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package common

type ReadOnlyMap[K comparable, V any] struct {
data map[K]V
}

func NewReadOnlyMap[K comparable, V any](data map[K]V) *ReadOnlyMap[K, V] {
return &ReadOnlyMap[K, V]{data: data}
}

func (m *ReadOnlyMap[K, V]) Get(key K) (V, bool) {
value, ok := m.data[key]
return value, ok
}

func (m *ReadOnlyMap[K, V]) Keys() []K {
keys := make([]K, 0, len(m.data))
for key := range m.data {
keys = append(keys, key)
}
return keys
}

func (m *ReadOnlyMap[K, V]) Len() int {
return len(m.data)
}
31 changes: 31 additions & 0 deletions common/read_only_map_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package common_test

import (
"testing"

"github.com/Layr-Labs/eigenda/common"
"github.com/stretchr/testify/require"
)

func TestReadOnlyMap(t *testing.T) {
data := map[uint8]string{
1: "one",
2: "two",
3: "three",
}
m := common.NewReadOnlyMap(data)
res, ok := m.Get(1)
require.True(t, ok)
require.Equal(t, "one", res)
res, ok = m.Get(2)
require.True(t, ok)
require.Equal(t, "two", res)
res, ok = m.Get(3)
require.True(t, ok)
require.Equal(t, "three", res)
res, ok = m.Get(4)
require.False(t, ok)
require.Equal(t, "", res)
require.Equal(t, 3, m.Len())
require.Equal(t, []uint8{1, 2, 3}, m.Keys())
}
6 changes: 6 additions & 0 deletions core/chainio.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ type Reader interface {
// GetRequiredQuorumNumbers returns set of required quorum numbers
GetRequiredQuorumNumbers(ctx context.Context, blockNumber uint32) ([]QuorumID, error)

// GetVersionedBlobParams returns the blob version parameters for the given block number and blob version.
GetVersionedBlobParams(ctx context.Context, blobVersion uint8) (*BlobVersionParameters, error)

// GetAllVersionedBlobParams returns the blob version parameters for all blob versions at the given block number.
GetAllVersionedBlobParams(ctx context.Context) (map[uint8]*BlobVersionParameters, error)

// GetActiveReservations returns active reservations (end timestamp > current timestamp)
GetActiveReservations(ctx context.Context, blockNumber uint32, accountIDs []string) (map[string]ActiveReservation, error)

Expand Down
6 changes: 6 additions & 0 deletions core/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -612,3 +612,9 @@ type ActiveReservation struct {
type OnDemandPayment struct {
CumulativePayment *big.Int // Total amount deposited by the user
}

type BlobVersionParameters struct {
CodingRate uint32
MaxNumOperators uint32
NumChunks uint32
}
36 changes: 36 additions & 0 deletions core/eth/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"crypto/ecdsa"
"math/big"
"strings"

"github.com/Layr-Labs/eigenda/common"
avsdir "github.com/Layr-Labs/eigenda/contracts/bindings/AVSDirectory"
Expand Down Expand Up @@ -580,6 +581,41 @@ func (t *Reader) GetRequiredQuorumNumbers(ctx context.Context, blockNumber uint3
return requiredQuorums, nil
}

func (t *Reader) GetVersionedBlobParams(ctx context.Context, blobVersion uint8) (*core.BlobVersionParameters, error) {
params, err := t.bindings.EigenDAServiceManager.GetBlobParams(&bind.CallOpts{
Context: ctx,
}, uint16(blobVersion))
if err != nil {
return nil, err
}
return &core.BlobVersionParameters{
CodingRate: uint32(params.CodingRate),
NumChunks: uint32(params.NumChunks),
MaxNumOperators: uint32(params.MaxNumOperators),
}, nil
}

func (t *Reader) GetAllVersionedBlobParams(ctx context.Context) (map[uint8]*core.BlobVersionParameters, error) {
res := make(map[uint8]*core.BlobVersionParameters)
version := uint8(0)
for {
params, err := t.GetVersionedBlobParams(ctx, version)
if err != nil && strings.Contains(err.Error(), "execution reverted") {
break
} else if err != nil {
return nil, err
}
res[version] = params
version++
}

if len(res) == 0 {
return nil, errors.New("no blob version parameters found")
}

return res, nil
}

func (t *Reader) GetActiveReservations(ctx context.Context, blockNumber uint32, accountIDs []string) (map[string]core.ActiveReservation, error) {
// contract is not implemented yet
return map[string]core.ActiveReservation{}, nil
Expand Down
2 changes: 1 addition & 1 deletion core/mock/v2/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func (v *MockShardValidator) ValidateBatchHeader(ctx context.Context, header *co
return args.Error(0)
}

func (v *MockShardValidator) ValidateBlobs(ctx context.Context, blobs []*corev2.BlobShard, pool common.WorkerPool, state *core.OperatorState) error {
func (v *MockShardValidator) ValidateBlobs(ctx context.Context, blobs []*corev2.BlobShard, blobVersionParams *corev2.BlobVersionParameterMap, pool common.WorkerPool, state *core.OperatorState) error {
args := v.Called()
return args.Error(0)
}
15 changes: 15 additions & 0 deletions core/mock/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,21 @@ func (t *MockWriter) GetRequiredQuorumNumbers(ctx context.Context, blockNumber u
return result.([]uint8), args.Error(1)
}

func (t *MockWriter) GetVersionedBlobParams(ctx context.Context, blobVersion uint8) (*core.BlobVersionParameters, error) {
args := t.Called()
if args.Get(0) == nil {
return nil, args.Error(1)
}
result := args.Get(0)
return result.(*core.BlobVersionParameters), args.Error(1)
}

func (t *MockWriter) GetAllVersionedBlobParams(ctx context.Context) (map[uint8]*core.BlobVersionParameters, error) {
args := t.Called()
result := args.Get(0)
return result.(map[uint8]*core.BlobVersionParameters), args.Error(1)
}

func (t *MockWriter) PubkeyHashToOperator(ctx context.Context, operatorId core.OperatorID) (gethcommon.Address, error) {
args := t.Called()
result := args.Get(0)
Expand Down
39 changes: 19 additions & 20 deletions core/v2/assignment.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,22 @@ import (
"github.com/Layr-Labs/eigenda/core"
)

func GetAssignments(state *core.OperatorState, blobVersion BlobVersion, quorum uint8) (map[core.OperatorID]Assignment, error) {

params, ok := ParametersMap[blobVersion]
if !ok {
return nil, fmt.Errorf("blob version %d not found", blobVersion)
func GetAssignments(state *core.OperatorState, blobParams *core.BlobVersionParameters, quorum uint8) (map[core.OperatorID]Assignment, error) {
if blobParams == nil {
return nil, fmt.Errorf("blob params cannot be nil")
}

ops, ok := state.Operators[quorum]
if !ok {
return nil, fmt.Errorf("no operators found for quorum %d", quorum)
}

if len(ops) > int(params.MaxNumOperators()) {
return nil, fmt.Errorf("too many operators for blob version %d", blobVersion)
if len(ops) > int(blobParams.MaxNumOperators) {
return nil, fmt.Errorf("too many operators (%d) to get assignments: max number of operators is %d", len(ops), blobParams.MaxNumOperators)
}

numOperators := big.NewInt(int64(len(ops)))
numChunks := big.NewInt(int64(params.NumChunks))
numChunks := big.NewInt(int64(blobParams.NumChunks))

type assignment struct {
id core.OperatorID
Expand Down Expand Up @@ -58,9 +56,9 @@ func GetAssignments(state *core.OperatorState, blobVersion BlobVersion, quorum u
mp += int(a.chunks)
}

delta := int(params.NumChunks) - mp
delta := int(blobParams.NumChunks) - mp
if delta < 0 {
return nil, fmt.Errorf("total chunks %d exceeds maximum %d", mp, params.NumChunks)
return nil, fmt.Errorf("total chunks %d exceeds maximum %d", mp, blobParams.NumChunks)
}

assignments := make(map[core.OperatorID]Assignment, len(chunkAssignments))
Expand All @@ -81,9 +79,11 @@ func GetAssignments(state *core.OperatorState, blobVersion BlobVersion, quorum u

}

func GetAssignment(state *core.OperatorState, blobVersion BlobVersion, quorum core.QuorumID, id core.OperatorID) (Assignment, error) {

assignments, err := GetAssignments(state, blobVersion, quorum)
func GetAssignment(state *core.OperatorState, blobParams *core.BlobVersionParameters, quorum core.QuorumID, id core.OperatorID) (Assignment, error) {
if blobParams == nil {
return Assignment{}, fmt.Errorf("blob params cannot be nil")
}
assignments, err := GetAssignments(state, blobParams, quorum)
if err != nil {
return Assignment{}, err
}
Expand All @@ -96,22 +96,21 @@ func GetAssignment(state *core.OperatorState, blobVersion BlobVersion, quorum co
return assignment, nil
}

func GetChunkLength(blobVersion BlobVersion, blobLength uint32) (uint32, error) {

func GetChunkLength(blobLength uint32, blobParams *core.BlobVersionParameters) (uint32, error) {
if blobLength == 0 {
return 0, fmt.Errorf("blob length must be greater than 0")
}

if blobParams == nil {
return 0, fmt.Errorf("blob params cannot be nil")
}

// Check that the blob length is a power of 2
if blobLength&(blobLength-1) != 0 {
return 0, fmt.Errorf("blob length %d is not a power of 2", blobLength)
}

if _, ok := ParametersMap[blobVersion]; !ok {
return 0, fmt.Errorf("blob version %d not found", blobVersion)
}

chunkLength := blobLength * ParametersMap[blobVersion].CodingRate / ParametersMap[blobVersion].NumChunks
chunkLength := blobLength * blobParams.CodingRate / blobParams.NumChunks
if chunkLength == 0 {
chunkLength = 1
}
Expand Down
56 changes: 13 additions & 43 deletions core/v2/assignment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,12 @@ import (
"github.com/stretchr/testify/assert"
)

const (
maxNumOperators = 3537
)

func TestOperatorAssignmentsV2(t *testing.T) {

state := dat.GetTotalOperatorState(context.Background(), 0)
operatorState := state.OperatorState

blobVersion := corev2.BlobVersion(0)

assignments, err := corev2.GetAssignments(operatorState, blobVersion, 0)
assignments, err := corev2.GetAssignments(operatorState, blobParams, 0)
assert.NoError(t, err)
expectedAssignments := map[core.OperatorID]corev2.Assignment{
mock.MakeOperatorId(0): {
Expand Down Expand Up @@ -55,7 +49,7 @@ func TestOperatorAssignmentsV2(t *testing.T) {

assert.Equal(t, assignment, expectedAssignments[operatorID])

assignment, err := corev2.GetAssignment(operatorState, blobVersion, 0, operatorID)
assignment, err := corev2.GetAssignment(operatorState, blobParams, 0, operatorID)
assert.NoError(t, err)

assert.Equal(t, assignment, expectedAssignments[operatorID])
Expand All @@ -64,20 +58,14 @@ func TestOperatorAssignmentsV2(t *testing.T) {

}

func TestMaxNumOperators(t *testing.T) {

assert.Equal(t, corev2.ParametersMap[0].MaxNumOperators(), uint32(maxNumOperators))

}

func TestAssignmentWithTooManyOperators(t *testing.T) {

numOperators := maxNumOperators + 1
numOperators := blobParams.MaxNumOperators + 1

stakes := map[core.QuorumID]map[core.OperatorID]int{
0: {},
}
for i := 0; i < numOperators; i++ {
for i := 0; i < int(numOperators); i++ {
stakes[0][mock.MakeOperatorId(i)] = rand.Intn(100) + 1
}

Expand All @@ -90,9 +78,7 @@ func TestAssignmentWithTooManyOperators(t *testing.T) {

assert.Equal(t, len(state.Operators[0]), numOperators)

blobVersion := corev2.BlobVersion(0)

_, err = corev2.GetAssignments(state.OperatorState, blobVersion, 0)
_, err = corev2.GetAssignments(state.OperatorState, blobParams, 0)
assert.Error(t, err)

}
Expand All @@ -110,7 +96,7 @@ func FuzzOperatorAssignmentsV2(f *testing.F) {
}

for i := 0; i < 5; i++ {
f.Add(maxNumOperators)
f.Add(int(blobParams.MaxNumOperators))
}

f.Fuzz(func(t *testing.T, numOperators int) {
Expand All @@ -131,39 +117,32 @@ func FuzzOperatorAssignmentsV2(f *testing.F) {

state := dat.GetTotalOperatorState(context.Background(), 0)

blobVersion := corev2.BlobVersion(0)

assignments, err := corev2.GetAssignments(state.OperatorState, blobVersion, 0)
assignments, err := corev2.GetAssignments(state.OperatorState, blobParams, 0)
assert.NoError(t, err)

// Check that the total number of chunks is correct
totalChunks := uint32(0)
for _, assignment := range assignments {
totalChunks += assignment.NumChunks
}
assert.Equal(t, totalChunks, corev2.ParametersMap[blobVersion].NumChunks)
assert.Equal(t, totalChunks, blobParams.NumChunks)

// Check that each operator's assignment satisfies the security requirement
for operatorID, assignment := range assignments {

totalStake := uint32(state.Totals[0].Stake.Uint64())
myStake := uint32(state.Operators[0][operatorID].Stake.Uint64())

LHS := assignment.NumChunks * totalStake * corev2.ParametersMap[blobVersion].CodingRate * uint32(corev2.ParametersMap[blobVersion].ReconstructionThreshold*100)
RHS := 100 * myStake * corev2.ParametersMap[blobVersion].NumChunks
reconstructionThreshold := 0.22
LHS := assignment.NumChunks * totalStake * blobParams.CodingRate * uint32(reconstructionThreshold*100)
RHS := 100 * myStake * blobParams.NumChunks

assert.GreaterOrEqual(t, LHS, RHS)

}

})

}

func TestChunkLength(t *testing.T) {

blobVersion := corev2.BlobVersion(0)

pairs := []struct {
blobLength uint32
chunkLength uint32
Expand All @@ -176,20 +155,13 @@ func TestChunkLength(t *testing.T) {
}

for _, pair := range pairs {

chunkLength, err := corev2.GetChunkLength(blobVersion, pair.blobLength)

chunkLength, err := corev2.GetChunkLength(pair.blobLength, blobParams)
assert.NoError(t, err)

assert.Equal(t, pair.chunkLength, chunkLength)
}

}

func TestInvalidChunkLength(t *testing.T) {

blobVersion := corev2.BlobVersion(0)

invalidLengths := []uint32{
0,
3,
Expand All @@ -212,9 +184,7 @@ func TestInvalidChunkLength(t *testing.T) {
}

for _, length := range invalidLengths {

_, err := corev2.GetChunkLength(blobVersion, length)
_, err := corev2.GetChunkLength(length, blobParams)
assert.Error(t, err)
}

}
Loading

0 comments on commit cb01646

Please sign in to comment.