-
Notifications
You must be signed in to change notification settings - Fork 208
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement relay url provider #1328
Merged
+384
−258
Merged
Changes from 14 commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
6b551f8
Draft relay URL provider
litt3 c44fce7
Draft usage of relay url provider
litt3 83c2623
Make formatting fixes
litt3 4ed780c
Iterate on concurrency logic
litt3 ef66f75
Fix tests
litt3 b5b1303
Add test for KeyLock
litt3 4608ae5
Fix improper test struct usage
litt3 e172aed
Tweak formatting
litt3 63131f0
Merge branch 'master' into relay-url-provider
litt3 0c3d309
Fix assignment bug
litt3 1872c24
Add additional method doc
litt3 8415ecf
Tweak order of operations, to retry upon failure
litt3 ee9b2ba
Change naming
litt3 12d3ac1
Merge branch 'master' into relay-url-provider
litt3 31de2cd
Combine interface and default impl into one file
litt3 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package relay | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"github.com/Layr-Labs/eigenda/common" | ||
relayRegistryBindings "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDARelayRegistry" | ||
v2 "github.com/Layr-Labs/eigenda/core/v2" | ||
|
||
"github.com/ethereum/go-ethereum/accounts/abi/bind" | ||
gethcommon "github.com/ethereum/go-ethereum/common" | ||
) | ||
|
||
// relayUrlProvider provides relay URL strings, based on relay key. | ||
type relayUrlProvider struct { | ||
relayRegistryCaller *relayRegistryBindings.ContractEigenDARelayRegistryCaller | ||
} | ||
|
||
var _ RelayUrlProvider = &relayUrlProvider{} | ||
|
||
// NewRelayUrlProvider constructs a relayUrlProvider | ||
func NewRelayUrlProvider( | ||
ethClient common.EthClient, | ||
relayRegistryAddress gethcommon.Address, | ||
) (RelayUrlProvider, error) { | ||
relayRegistryContractCaller, err := relayRegistryBindings.NewContractEigenDARelayRegistryCaller( | ||
relayRegistryAddress, ethClient) | ||
if err != nil { | ||
return nil, fmt.Errorf("NewContractEigenDARelayRegistryCaller: %w", err) | ||
} | ||
|
||
return &relayUrlProvider{ | ||
relayRegistryCaller: relayRegistryContractCaller, | ||
}, nil | ||
} | ||
|
||
// GetRelayUrl gets the URL string for a given relayKey | ||
func (rup *relayUrlProvider) GetRelayUrl(ctx context.Context, relayKey v2.RelayKey) (string, error) { | ||
relayUrl, err := rup.relayRegistryCaller.RelayKeyToUrl(&bind.CallOpts{Context: ctx}, relayKey) | ||
if err != nil { | ||
return "", fmt.Errorf("fetch relay key (%d) URL from EigenDARelayRegistry contract: %w", relayKey, err) | ||
} | ||
|
||
return relayUrl, nil | ||
} | ||
|
||
// GetRelayCount gets the number of relays that exist in the registry | ||
func (rup *relayUrlProvider) GetRelayCount(ctx context.Context) (uint32, error) { | ||
// NextRelayKey initializes to 0, and is incremented each time a relay is added | ||
// current logic doesn't support removing relays, so NextRelayKey therefore corresponds directly to relay count | ||
relayCount, err := rup.relayRegistryCaller.NextRelayKey(&bind.CallOpts{Context: ctx}) | ||
if err != nil { | ||
return 0, fmt.Errorf("get next relay key from EigenDARelayRegistry contract: %w", err) | ||
} | ||
|
||
return relayCount, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package relay | ||
|
||
import ( | ||
"sync" | ||
) | ||
|
||
// KeyLock is a utility that provides a way to lock access to a given key of type T | ||
// | ||
// This utility is useful in situations where you want to synchronize operations for something that doesn't exist | ||
// in a concrete form. For example, perhaps you only want to create connections with a given peer on a single | ||
// thread of execution, but the new peer could appear simultaneously in concurrent operations. This utility allows | ||
// the first thread which encounters the new peer to perform necessary initialization tasks, and store generated | ||
// artifacts in a central location for subsequent callers to access. | ||
type KeyLock[T comparable] struct { | ||
// Map from key T to a mutex that corresponds to that key | ||
keyMutexMap map[T]*sync.Mutex | ||
anupsv marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// Used to lock access to the keyMutexMap, so that only a single mutex is created for each key | ||
globalMutex sync.Mutex | ||
} | ||
|
||
// NewKeyLock constructs a KeyLock utility | ||
func NewKeyLock[T comparable]() *KeyLock[T] { | ||
return &KeyLock[T]{ | ||
keyMutexMap: make(map[T]*sync.Mutex), | ||
} | ||
} | ||
|
||
// AcquireKeyLock acquires an exclusive lock on a conceptual key, and returns a function to release the lock | ||
// | ||
// The caller MUST eventually invoke the returned unlock function, or all future calls with the same key will block | ||
// indefinitely | ||
func (kl *KeyLock[T]) AcquireKeyLock(key T) func() { | ||
// we must globally synchronize access to the mutex map, so that only a single mutex will be created for a given key | ||
kl.globalMutex.Lock() | ||
keyMutex, valueAlreadyExists := kl.keyMutexMap[key] | ||
if !valueAlreadyExists { | ||
keyMutex = &sync.Mutex{} | ||
kl.keyMutexMap[key] = keyMutex | ||
} | ||
kl.globalMutex.Unlock() | ||
|
||
keyMutex.Lock() | ||
return keyMutex.Unlock | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package relay | ||
|
||
import ( | ||
"sync" | ||
"sync/atomic" | ||
"testing" | ||
|
||
"github.com/Layr-Labs/eigenda/common/testutils/random" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestKeyLock(t *testing.T) { | ||
// test in a field of 100 unique keys | ||
keyCount := 100 | ||
|
||
// keep an atomic count, and a non-atomic count for each key | ||
// the atomic count can be used at the end of the test, to make sure that the non-atomic count was handled correctly | ||
atomicKeyAccessCounts := make([]atomic.Uint32, keyCount) | ||
nonAtomicKeyAccessCounts := make([]uint32, keyCount) | ||
for i := 0; i < keyCount; i++ { | ||
atomicKeyAccessCounts = append(atomicKeyAccessCounts, atomic.Uint32{}) | ||
nonAtomicKeyAccessCounts = append(nonAtomicKeyAccessCounts, uint32(0)) | ||
} | ||
|
||
keyLock := NewKeyLock[uint32]() | ||
|
||
var waitGroup sync.WaitGroup | ||
|
||
targetValue := uint32(1000) | ||
worker := func() { | ||
workerRandom := random.NewTestRandom() | ||
|
||
for { | ||
// randomly select a key to access | ||
keyToAccess := uint32(workerRandom.Intn(keyCount)) | ||
newValue := atomicKeyAccessCounts[keyToAccess].Add(1) | ||
|
||
unlock := keyLock.AcquireKeyLock(keyToAccess) | ||
// increment the non-atomic count after acquiring access | ||
// if the access controls are working correctly, this is a safe operation | ||
nonAtomicKeyAccessCounts[keyToAccess] = nonAtomicKeyAccessCounts[keyToAccess] + 1 | ||
unlock() | ||
|
||
// each worker stops looping after it sees a counter that has increased to targetValue | ||
if newValue >= targetValue { | ||
break | ||
} | ||
} | ||
|
||
waitGroup.Done() | ||
} | ||
|
||
// start up 100 concurrent workers | ||
for i := 0; i < 100; i++ { | ||
waitGroup.Add(1) | ||
go worker() | ||
} | ||
waitGroup.Wait() | ||
|
||
for i := 0; i < keyCount; i++ { | ||
require.Equal(t, atomicKeyAccessCounts[i].Load(), nonAtomicKeyAccessCounts[i]) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package relay | ||
|
||
import ( | ||
"context" | ||
|
||
v2 "github.com/Layr-Labs/eigenda/core/v2" | ||
) | ||
|
||
// RelayUrlProvider provides relay URL strings, based on relay key | ||
type RelayUrlProvider interface { | ||
// GetRelayUrl gets the URL string for a given relayKey | ||
GetRelayUrl(ctx context.Context, relayKey v2.RelayKey) (string, error) | ||
// GetRelayCount returns the number of relays in the registry | ||
GetRelayCount(ctx context.Context) (uint32, error) | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: filename
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed