Skip to content

Commit

Permalink
Add a changeset for deploying LLO contracts.
Browse files Browse the repository at this point in the history
  • Loading branch information
ro-tex committed Oct 11, 2024
1 parent a41ce0a commit a41e0f8
Show file tree
Hide file tree
Showing 10 changed files with 287 additions and 1 deletion.
4 changes: 4 additions & 0 deletions GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ testscripts: chainlink-test ## Install and run testscript against testdata/scrip
testscripts-update: ## Update testdata/scripts/* files via testscript.
make testscripts TS_FLAGS="-u"

.PHONY: start-testdb
start-testdb:
docker run --name test-db-core -p 5432:5432 -e POSTGRES_PASSWORD=postgres -d postgres

.PHONY: setup-testdb
setup-testdb: ## Setup the test database.
./core/scripts/setup_testdb.sh
Expand Down
13 changes: 12 additions & 1 deletion integration-tests/deployment/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,15 @@ func TestDoSomething(t *testing.T)
// Send traffic, run assertions etc.
}
```
- Changesets are exposed and applied via a different repo.
- Changesets are exposed and applied via a different repo.

/deployment/llo
- package name `llodeployment`
- Similar to /deploymet/ccip, these are product-specific deployment/configuration workflows
- Tests can use deployment/memory for fast integration testing

/deployment/llo/changeset
- package name `changeset` imported as `llochangesets`
- Similar to deployment/ccip/changesets
- These function like scripts describing state transitions
you wish to apply to _persistent_ environments like testnet/mainnet
5 changes: 5 additions & 0 deletions integration-tests/deployment/llo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
### LLO Deployments and Configurations

This module contains workflows for deploying and configuring Data Streams LLO contracts.

The contracts in question can be found under contracts/src/v0.8/llo-feeds.
1 change: 1 addition & 0 deletions integration-tests/deployment/llo/changeset/configure.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package changeset
18 changes: 18 additions & 0 deletions integration-tests/deployment/llo/changeset/deploy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package changeset

import (
"github.com/smartcontractkit/chainlink/integration-tests/deployment"
llodeployment "github.com/smartcontractkit/chainlink/integration-tests/deployment/llo"
)

func DeployChannelConfigStoreChangeSet(env deployment.Environment, c llodeployment.DeployLLOContractConfig) (deployment.ChangesetOutput, error) {
ab := deployment.NewMemoryAddressBook()
err := llodeployment.DeployChannelConfigStore(env, ab, c)
if err != nil {
env.Logger.Errorw("Failed to deploy ChannelConfigStore", "err", err, "addresses", ab)
return deployment.ChangesetOutput{AddressBook: ab}, deployment.MaybeDataErr(err)
}
return deployment.ChangesetOutput{
AddressBook: ab,
}, nil
}
33 changes: 33 additions & 0 deletions integration-tests/deployment/llo/changeset/deploy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package changeset

import (
"testing"

"github.com/stretchr/testify/require"

"github.com/smartcontractkit/chainlink/integration-tests/deployment/llo"
"github.com/smartcontractkit/chainlink/v2/core/logger"
)

func TestDeployChannelConfigStoreChangeSet(t *testing.T) {
lggr := logger.TestLogger(t)
tenv := llo.NewMemoryEnvironment(t, lggr)
e := tenv.Env

c := llo.DeployLLOContractConfig{
ChainsToDeploy: []uint64{llo.TestChain.Selector},
}
out, err := DeployChannelConfigStoreChangeSet(e, c)
require.NoError(t, err)

ab, err := out.AddressBook.Addresses()
require.NoError(t, err)
require.Len(t, ab, 1)

for sel, addrMap := range ab {
require.Equal(t, llo.TestChain.Selector, sel)
for _, tv := range addrMap {
require.Equal(t, tv.Type, llo.ChannelConfigStore)
}
}
}
1 change: 1 addition & 0 deletions integration-tests/deployment/llo/configure.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package llo
126 changes: 126 additions & 0 deletions integration-tests/deployment/llo/deploy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package llo

import (
"fmt"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/pkg/errors"

"github.com/smartcontractkit/chainlink-common/pkg/logger"

"github.com/smartcontractkit/chainlink/integration-tests/deployment"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/channel_config_store"
)

type DeployLLOContractConfig struct {
ChainsToDeploy []uint64 // Chain Selectors
}

// LLOContract covers contracts such as channel_config_store.ChannelConfigStore and fee_manager.FeeManager.
type LLOContract interface {
// Caller:
Owner(opts *bind.CallOpts) (common.Address, error)
SupportsInterface(opts *bind.CallOpts, interfaceId [4]byte) (bool, error)
TypeAndVersion(opts *bind.CallOpts) (string, error)
// Transactor:
AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error)
TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error)
}

type ContractDeploy[C LLOContract] struct {
Address common.Address
Contract C
Tx *types.Transaction
Tv deployment.TypeAndVersion
Err error
}

var (
ChannelConfigStore deployment.ContractType = "ChannelConfigStore"
)

func DeployChannelConfigStore(e deployment.Environment, ab deployment.AddressBook, c DeployLLOContractConfig) error {
nodes, err := deployment.NodeInfo(e.NodeIDs, e.Offchain)
if err != nil || len(nodes) == 0 {
e.Logger.Errorw("Failed to get node info", "err", err)
return err
}

for _, chainSel := range c.ChainsToDeploy {
chain, ok := e.Chains[chainSel]
if !ok {
return fmt.Errorf("Chain %d not found", chainSel)
}
_, err = deployChannelConfigStoreToChain(e, chain, ab)
if err != nil {
return err
}
chainAddresses, err := ab.AddressesForChain(chain.Selector)
if err != nil {
e.Logger.Errorw("Failed to get chain addresses", "err", err)
return err
}
chainState, err := LoadChainState(chain, chainAddresses)
if err != nil {
e.Logger.Errorw("Failed to load chain state", "err", err)
return err
}
if chainState.ChannelConfigStore == nil {
errNoCCS := errors.New("no ChannelConfigStore on chain")
e.Logger.Error(errNoCCS)
return errNoCCS
}
}

return nil
}

// deployChannelConfigStoreToChain deploys ChannelConfigStore to a specific chain.
//
// Note that this function modifies the given address book variable.
func deployChannelConfigStoreToChain(e deployment.Environment, chain deployment.Chain, ab deployment.AddressBook) (*ContractDeploy[*channel_config_store.ChannelConfigStore], error) {
return deployContract(e.Logger, chain, ab, func(chain deployment.Chain) ContractDeploy[*channel_config_store.ChannelConfigStore] {
ccsAddr, ccsTx, ccs, err := channel_config_store.DeployChannelConfigStore(
chain.DeployerKey,
chain.Client,
)
if err != nil {
return ContractDeploy[*channel_config_store.ChannelConfigStore]{
Err: err,
}
}
return ContractDeploy[*channel_config_store.ChannelConfigStore]{
Address: ccsAddr,
Contract: ccs,
Tx: ccsTx,
Tv: deployment.NewTypeAndVersion(ChannelConfigStore, deployment.Version1_0_0),
Err: nil,
}
})
}

func deployContract[C LLOContract](
lggr logger.Logger,
chain deployment.Chain,
addressBook deployment.AddressBook,
deploy func(chain deployment.Chain) ContractDeploy[C],
) (*ContractDeploy[C], error) {
contractDeploy := deploy(chain)
if contractDeploy.Err != nil {
lggr.Errorw("Failed to deploy contract", "err", contractDeploy.Err)
return nil, contractDeploy.Err
}
_, err := chain.Confirm(contractDeploy.Tx)
if err != nil {
lggr.Errorw("Failed to confirm deployment", "err", err)
return nil, err
}
err = addressBook.Save(chain.Selector, contractDeploy.Address.String(), contractDeploy.Tv)
if err != nil {
lggr.Errorw("Failed to save contract address", "err", err)
return nil, err
}
return &contractDeploy, nil
}
34 changes: 34 additions & 0 deletions integration-tests/deployment/llo/state.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package llo

import (
"fmt"

"github.com/ethereum/go-ethereum/common"

"github.com/smartcontractkit/chainlink/integration-tests/deployment"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/channel_config_store"
)

// LLOChainState holds a Go binding for all the currently deployed LLO contracts
// on a chain. If a binding is nil, it means here is no such contract on the chain.
type LLOChainState struct {
ChannelConfigStore *channel_config_store.ChannelConfigStore
}

// LoadChainState Loads all state for a chain into state
func LoadChainState(chain deployment.Chain, addresses map[string]deployment.TypeAndVersion) (LLOChainState, error) {
var state LLOChainState
for address, tvStr := range addresses {
switch tvStr.String() {
case deployment.NewTypeAndVersion(ChannelConfigStore, deployment.Version1_0_0).String():
ccs, err := channel_config_store.NewChannelConfigStore(common.HexToAddress(address), chain.Client)
if err != nil {
return state, err
}
state.ChannelConfigStore = ccs
default:
return state, fmt.Errorf("unknown contract %s", tvStr)
}
}
return state, nil
}
53 changes: 53 additions & 0 deletions integration-tests/deployment/llo/test_helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package llo

import (
"testing"

chain_selectors "github.com/smartcontractkit/chain-selectors"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zapcore"

"github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext"

"github.com/smartcontractkit/chainlink/integration-tests/deployment"
"github.com/smartcontractkit/chainlink/integration-tests/deployment/memory"
"github.com/smartcontractkit/chainlink/v2/core/logger"
)

type DeployedEnv struct {
Env deployment.Environment
Ab deployment.AddressBook
HomeChainSel uint64
FeedChainSel uint64
ReplayBlocks map[uint64]uint64
}

var (
// TestChain is the chain used by the in-memory environment.
TestChain = chain_selectors.Chain{
EvmChainID: 90000001,
Selector: 909606746561742123,
Name: "Test Chain",
VarName: "",
}
)

// NewMemoryEnvironment creates a new LLO environment with capreg, fee tokens, feeds and nodes set up.
func NewMemoryEnvironment(t *testing.T, lggr logger.Logger) DeployedEnv {
chains := memory.NewMemoryChains(t, 1)
rc := deployment.CapabilityRegistryConfig{}
nodes := memory.NewNodes(t, zapcore.InfoLevel, chains, 4, 1, rc)
ctx := testcontext.Get(t)
for _, node := range nodes {
require.NoError(t, node.App.Start(ctx))
t.Cleanup(func() {
require.NoError(t, node.App.Stop())
})
}

e := memory.NewMemoryEnvironmentFromChainsNodes(t, lggr, chains, nodes)
return DeployedEnv{
Ab: deployment.NewMemoryAddressBook(),
Env: e,
}
}

0 comments on commit a41e0f8

Please sign in to comment.