-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
305 additions
and
20 deletions.
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
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
92 changes: 92 additions & 0 deletions
92
core/services/ocr2/plugins/ccip/internal/cache/tokenpool.go
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,92 @@ | ||
package cache | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"sync" | ||
|
||
"github.com/ethereum/go-ethereum/accounts/abi/bind" | ||
"github.com/ethereum/go-ethereum/common" | ||
"golang.org/x/sync/errgroup" | ||
|
||
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" | ||
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_offramp" | ||
"github.com/smartcontractkit/chainlink/v2/core/logger" | ||
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers" | ||
) | ||
|
||
func NewTokenPools( | ||
lggr logger.Logger, | ||
lp logpoller.LogPoller, | ||
offRamp evm_2_evm_offramp.EVM2EVMOffRampInterface, | ||
optimisticConfirmations int64, | ||
numWorkers int, | ||
) *CachedChain[map[common.Address]common.Address] { | ||
return &CachedChain[map[common.Address]common.Address]{ | ||
observedEvents: []common.Hash{ | ||
abihelpers.EventSignatures.PoolAdded, | ||
abihelpers.EventSignatures.PoolRemoved, | ||
}, | ||
logPoller: lp, | ||
address: []common.Address{offRamp.Address()}, | ||
optimisticConfirmations: optimisticConfirmations, | ||
lock: &sync.RWMutex{}, | ||
value: make(map[common.Address]common.Address), | ||
lastChangeBlock: 0, | ||
origin: newTokenPoolsOrigin(lggr, offRamp, numWorkers), | ||
} | ||
} | ||
|
||
func newTokenPoolsOrigin( | ||
lggr logger.Logger, | ||
offRamp evm_2_evm_offramp.EVM2EVMOffRampInterface, | ||
numWorkers int) *tokenPools { | ||
return &tokenPools{ | ||
lggr: lggr, | ||
offRamp: offRamp, | ||
numWorkers: numWorkers, | ||
} | ||
} | ||
|
||
type tokenPools struct { | ||
lggr logger.Logger | ||
offRamp evm_2_evm_offramp.EVM2EVMOffRampInterface | ||
numWorkers int | ||
} | ||
|
||
func (t *tokenPools) Copy(value map[common.Address]common.Address) map[common.Address]common.Address { | ||
return copyMap(value) | ||
} | ||
|
||
func (t *tokenPools) CallOrigin(ctx context.Context) (map[common.Address]common.Address, error) { | ||
destTokens, err := t.offRamp.GetDestinationTokens(&bind.CallOpts{Context: ctx}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
eg := new(errgroup.Group) | ||
eg.SetLimit(t.numWorkers) | ||
var mu sync.Mutex | ||
|
||
mapping := make(map[common.Address]common.Address, len(destTokens)) | ||
for _, token := range destTokens { | ||
token := token | ||
eg.Go(func() error { | ||
poolAddress, err := t.offRamp.GetPoolByDestToken(&bind.CallOpts{Context: ctx}, token) | ||
if err != nil { | ||
return fmt.Errorf("get token pool for token '%s': %w", token, err) | ||
} | ||
|
||
mu.Lock() | ||
mapping[token] = poolAddress | ||
mu.Unlock() | ||
return nil | ||
}) | ||
} | ||
|
||
if err := eg.Wait(); err != nil { | ||
return nil, err | ||
} | ||
|
||
return mapping, nil | ||
} |
127 changes: 127 additions & 0 deletions
127
core/services/ocr2/plugins/ccip/internal/cache/tokenpool_test.go
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,127 @@ | ||
package cache | ||
|
||
import ( | ||
"math/rand" | ||
"testing" | ||
|
||
"github.com/ethereum/go-ethereum/common" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/mock" | ||
|
||
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" | ||
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils" | ||
"github.com/smartcontractkit/chainlink/v2/core/logger" | ||
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/testhelpers" | ||
"github.com/smartcontractkit/chainlink/v2/core/utils" | ||
) | ||
|
||
func TestNewTokenPools(t *testing.T) { | ||
ctx := testutils.Context(t) | ||
|
||
tk1src := utils.RandomAddress() | ||
tk1dst := utils.RandomAddress() | ||
tk1pool := utils.RandomAddress() | ||
|
||
tk2src := utils.RandomAddress() | ||
tk2dst := utils.RandomAddress() | ||
tk2pool := utils.RandomAddress() | ||
|
||
testCases := []struct { | ||
name string | ||
sourceToDestTokens map[common.Address]common.Address // offramp | ||
feeTokens []common.Address // price registry | ||
tokenToPool map[common.Address]common.Address // offramp | ||
expRes map[common.Address]common.Address | ||
expErr bool | ||
}{ | ||
{ | ||
name: "no tokens", | ||
sourceToDestTokens: map[common.Address]common.Address{}, | ||
feeTokens: []common.Address{}, | ||
tokenToPool: map[common.Address]common.Address{}, | ||
expRes: map[common.Address]common.Address{}, | ||
expErr: false, | ||
}, | ||
{ | ||
name: "happy flow", | ||
sourceToDestTokens: map[common.Address]common.Address{ | ||
tk1src: tk1dst, | ||
tk2src: tk2dst, | ||
}, | ||
feeTokens: []common.Address{tk1dst, tk2dst}, | ||
tokenToPool: map[common.Address]common.Address{ | ||
tk1dst: tk1pool, | ||
tk2dst: tk2pool, | ||
}, | ||
expRes: map[common.Address]common.Address{ | ||
tk1dst: tk1pool, | ||
tk2dst: tk2pool, | ||
}, | ||
expErr: false, | ||
}, | ||
{ | ||
name: "token pool not found", | ||
sourceToDestTokens: map[common.Address]common.Address{ | ||
tk1src: tk1dst, | ||
}, | ||
feeTokens: []common.Address{tk1dst}, | ||
tokenToPool: map[common.Address]common.Address{}, | ||
expErr: true, | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
mockLp := mocks.NewLogPoller(t) | ||
mockLp.On("LatestBlock", mock.Anything).Return(int64(100), nil) | ||
|
||
offRamp, _ := testhelpers.NewFakeOffRamp(t) | ||
offRamp.SetSourceToDestTokens(tc.sourceToDestTokens) | ||
offRamp.SetTokenPools(tc.tokenToPool) | ||
|
||
priceReg, _ := testhelpers.NewFakePriceRegistry(t) | ||
priceReg.SetFeeTokens(tc.feeTokens) | ||
|
||
c := NewTokenPools(logger.TestLogger(t), mockLp, offRamp, 0, 5) | ||
|
||
res, err := c.Get(ctx) | ||
if tc.expErr { | ||
assert.Error(t, err) | ||
return | ||
} | ||
assert.NoError(t, err) | ||
assert.Equal(t, len(tc.expRes), len(res)) | ||
for k, v := range tc.expRes { | ||
assert.Equal(t, v, res[k]) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func Test_tokenPools_CallOrigin_concurrency(t *testing.T) { | ||
numDestTokens := rand.Intn(500) | ||
numWorkers := rand.Intn(500) | ||
|
||
sourceToDestTokens := make(map[common.Address]common.Address, numDestTokens) | ||
tokenToPool := make(map[common.Address]common.Address) | ||
for i := 0; i < numDestTokens; i++ { | ||
sourceToken := utils.RandomAddress() | ||
destToken := utils.RandomAddress() | ||
destPool := utils.RandomAddress() | ||
sourceToDestTokens[sourceToken] = destToken | ||
tokenToPool[destToken] = destPool | ||
} | ||
|
||
offRamp, _ := testhelpers.NewFakeOffRamp(t) | ||
offRamp.SetSourceToDestTokens(sourceToDestTokens) | ||
offRamp.SetTokenPools(tokenToPool) | ||
|
||
origin := newTokenPoolsOrigin(logger.TestLogger(t), offRamp, numWorkers) | ||
res, err := origin.CallOrigin(testutils.Context(t)) | ||
assert.NoError(t, err) | ||
|
||
assert.Equal(t, len(tokenToPool), len(res)) | ||
for k, v := range tokenToPool { | ||
assert.Equal(t, v, res[k]) | ||
} | ||
} |
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
Oops, something went wrong.