-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added data feeds deployment changesets (#16323)
* add data feeds deployment changesets * fix lint issues pt1 * fix lint issues pt2 * fix major lint issues * fix goimports * replace if/else with switch in state * replace mcms with mcmsv2 * create BuildMCMProposal func * add acceptOwnership changeset * lint * add tests for mcms, accept_ownership changeset * fix lint issues * use changesetv2 * add test cases for mcms * fix lint issues * fix cache_deploy test * add mcms support to confirm/propose aggregator changesets. buildproposal batch * fix lint issues * remove custom wrapper for legacy changeset * add more complex changesets * fix lint * minor changes * fix lint * fix typo * update buildproposal env
- Loading branch information
1 parent
88a2f8f
commit 3e5ec23
Showing
40 changed files
with
3,171 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package changeset | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
|
||
mcmslib "github.com/smartcontractkit/mcms" | ||
|
||
"github.com/smartcontractkit/chainlink/deployment" | ||
commonChangesets "github.com/smartcontractkit/chainlink/deployment/common/changeset" | ||
"github.com/smartcontractkit/chainlink/deployment/data-feeds/changeset/types" | ||
) | ||
|
||
// AcceptOwnershipChangeset is a changeset that will create an MCM proposal to accept the ownership of a contract. | ||
// Returns an MSM proposal to accept the ownership of a contract. Doesn't return a new addressbook. | ||
// Once proposal is executed, new owned contract can be imported into the addressbook. | ||
var AcceptOwnershipChangeset = deployment.CreateChangeSet(acceptOwnershipLogic, acceptOwnershipPrecondition) | ||
|
||
func acceptOwnershipLogic(env deployment.Environment, c types.AcceptOwnershipConfig) (deployment.ChangesetOutput, error) { | ||
chain := env.Chains[c.ChainSelector] | ||
|
||
_, contract, err := commonChangesets.LoadOwnableContract(c.ContractAddress, chain.Client) | ||
if err != nil { | ||
return deployment.ChangesetOutput{}, fmt.Errorf("failed to load the contract %w", err) | ||
} | ||
|
||
tx, err := contract.AcceptOwnership(deployment.SimTransactOpts()) | ||
if err != nil { | ||
return deployment.ChangesetOutput{}, fmt.Errorf("failed to create accept transfer ownership tx %w", err) | ||
} | ||
|
||
proposal, err := BuildMCMProposals(env, "accept ownership to timelock", c.ChainSelector, []ProposalData{ | ||
{ | ||
contract: c.ContractAddress.Hex(), | ||
tx: tx, | ||
}, | ||
}, c.McmsConfig.MinDelay) | ||
if err != nil { | ||
return deployment.ChangesetOutput{}, fmt.Errorf("failed to build proposal: %w", err) | ||
} | ||
|
||
return deployment.ChangesetOutput{MCMSTimelockProposals: []mcmslib.TimelockProposal{*proposal}}, nil | ||
} | ||
|
||
func acceptOwnershipPrecondition(env deployment.Environment, c types.AcceptOwnershipConfig) error { | ||
_, ok := env.Chains[c.ChainSelector] | ||
if !ok { | ||
return fmt.Errorf("chain not found in env %d", c.ChainSelector) | ||
} | ||
|
||
if c.McmsConfig == nil { | ||
return errors.New("mcms config is required") | ||
} | ||
|
||
return ValidateMCMSAddresses(env.ExistingAddresses, c.ChainSelector) | ||
} |
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,65 @@ | ||
package changeset | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/ethereum/go-ethereum/common" | ||
"github.com/stretchr/testify/require" | ||
"go.uber.org/zap/zapcore" | ||
|
||
"github.com/smartcontractkit/chainlink/deployment/data-feeds/changeset/types" | ||
|
||
"github.com/smartcontractkit/chainlink/deployment" | ||
commonChangesets "github.com/smartcontractkit/chainlink/deployment/common/changeset" | ||
"github.com/smartcontractkit/chainlink/deployment/common/proposalutils" | ||
commonTypes "github.com/smartcontractkit/chainlink/deployment/common/types" | ||
|
||
"github.com/smartcontractkit/chainlink-common/pkg/logger" | ||
|
||
"github.com/smartcontractkit/chainlink/deployment/environment/memory" | ||
) | ||
|
||
func TestAcceptOwnership(t *testing.T) { | ||
t.Parallel() | ||
lggr := logger.Test(t) | ||
cfg := memory.MemoryEnvironmentConfig{ | ||
Nodes: 1, | ||
Chains: 1, | ||
} | ||
env := memory.NewMemoryEnvironment(t, lggr, zapcore.DebugLevel, cfg) | ||
|
||
chainSelector := env.AllChainSelectors()[0] | ||
chain := env.Chains[chainSelector] | ||
|
||
newEnv, err := commonChangesets.Apply(t, env, nil, | ||
commonChangesets.Configure( | ||
deployment.CreateLegacyChangeSet(commonChangesets.DeployMCMSWithTimelockV2), | ||
map[uint64]commonTypes.MCMSWithTimelockConfigV2{ | ||
chainSelector: proposalutils.SingleGroupTimelockConfigV2(t), | ||
}, | ||
), | ||
) | ||
require.NoError(t, err) | ||
|
||
timeLockAddress, err := deployment.SearchAddressBook(newEnv.ExistingAddresses, chainSelector, "RBACTimelock") | ||
require.NoError(t, err) | ||
|
||
cache, _ := DeployCache(chain, []string{}) | ||
tx, _ := cache.Contract.TransferOwnership(chain.DeployerKey, common.HexToAddress(timeLockAddress)) | ||
_, err = chain.Confirm(tx) | ||
require.NoError(t, err) | ||
|
||
_, err = commonChangesets.Apply(t, newEnv, nil, | ||
commonChangesets.Configure( | ||
AcceptOwnershipChangeset, | ||
types.AcceptOwnershipConfig{ | ||
ChainSelector: chainSelector, | ||
ContractAddress: cache.Contract.Address(), | ||
McmsConfig: &types.MCMSConfig{ | ||
MinDelay: 1, | ||
}, | ||
}, | ||
), | ||
) | ||
require.NoError(t, err) | ||
} |
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,69 @@ | ||
package changeset | ||
|
||
import ( | ||
"fmt" | ||
|
||
mcmslib "github.com/smartcontractkit/mcms" | ||
|
||
"github.com/smartcontractkit/chainlink/deployment" | ||
"github.com/smartcontractkit/chainlink/deployment/data-feeds/changeset/types" | ||
proxy "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/data-feeds/generated/aggregator_proxy" | ||
) | ||
|
||
// ConfirmAggregatorChangeset is a changeset that confirms a proposed aggregator on deployed AggregatorProxy contract | ||
// This changeset may return a timelock proposal if the MCMS config is provided, otherwise it will execute the transaction with the deployer key. | ||
var ConfirmAggregatorChangeset = deployment.CreateChangeSet(confirmAggregatorLogic, confirmAggregatorPrecondition) | ||
|
||
func confirmAggregatorLogic(env deployment.Environment, c types.ProposeConfirmAggregatorConfig) (deployment.ChangesetOutput, error) { | ||
chain := env.Chains[c.ChainSelector] | ||
|
||
aggregatorProxy, err := proxy.NewAggregatorProxy(c.ProxyAddress, chain.Client) | ||
if err != nil { | ||
return deployment.ChangesetOutput{}, fmt.Errorf("failed to load AggregatorProxy: %w", err) | ||
} | ||
|
||
txOpt := chain.DeployerKey | ||
if c.McmsConfig != nil { | ||
txOpt = deployment.SimTransactOpts() | ||
} | ||
|
||
tx, err := aggregatorProxy.ConfirmAggregator(txOpt, c.NewAggregatorAddress) | ||
if err != nil { | ||
return deployment.ChangesetOutput{}, fmt.Errorf("failed to execute ConfirmAggregator: %w", err) | ||
} | ||
|
||
if c.McmsConfig != nil { | ||
proposal, err := BuildMCMProposals(env, "proposal to confirm a new aggregator", c.ChainSelector, []ProposalData{ | ||
{ | ||
contract: aggregatorProxy.Address().Hex(), | ||
tx: tx, | ||
}, | ||
}, c.McmsConfig.MinDelay) | ||
if err != nil { | ||
return deployment.ChangesetOutput{}, fmt.Errorf("failed to build proposal: %w", err) | ||
} | ||
return deployment.ChangesetOutput{MCMSTimelockProposals: []mcmslib.TimelockProposal{*proposal}}, nil | ||
} | ||
|
||
_, err = chain.Confirm(tx) | ||
if err != nil { | ||
return deployment.ChangesetOutput{}, fmt.Errorf("failed to confirm transaction: %s, %w", tx.Hash().String(), err) | ||
} | ||
|
||
return deployment.ChangesetOutput{}, nil | ||
} | ||
|
||
func confirmAggregatorPrecondition(env deployment.Environment, c types.ProposeConfirmAggregatorConfig) error { | ||
_, ok := env.Chains[c.ChainSelector] | ||
if !ok { | ||
return fmt.Errorf("chain not found in env %d", c.ChainSelector) | ||
} | ||
|
||
if c.McmsConfig != nil { | ||
if err := ValidateMCMSAddresses(env.ExistingAddresses, c.ChainSelector); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
} |
118 changes: 118 additions & 0 deletions
118
deployment/data-feeds/changeset/confirm_aggregator_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,118 @@ | ||
package changeset_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/ethereum/go-ethereum/common" | ||
"github.com/stretchr/testify/require" | ||
"go.uber.org/zap/zapcore" | ||
|
||
"github.com/smartcontractkit/chainlink/deployment" | ||
"github.com/smartcontractkit/chainlink/deployment/common/proposalutils" | ||
commonTypes "github.com/smartcontractkit/chainlink/deployment/common/types" | ||
|
||
commonChangesets "github.com/smartcontractkit/chainlink/deployment/common/changeset" | ||
|
||
"github.com/smartcontractkit/chainlink-common/pkg/logger" | ||
"github.com/smartcontractkit/chainlink/deployment/data-feeds/changeset" | ||
"github.com/smartcontractkit/chainlink/deployment/data-feeds/changeset/types" | ||
"github.com/smartcontractkit/chainlink/deployment/environment/memory" | ||
) | ||
|
||
func TestConfirmAggregator(t *testing.T) { | ||
t.Parallel() | ||
lggr := logger.Test(t) | ||
cfg := memory.MemoryEnvironmentConfig{ | ||
Nodes: 1, | ||
Chains: 1, | ||
} | ||
env := memory.NewMemoryEnvironment(t, lggr, zapcore.DebugLevel, cfg) | ||
|
||
chainSelector := env.AllChainSelectors()[0] | ||
|
||
// without MCMS | ||
newEnv, err := commonChangesets.Apply(t, env, nil, | ||
// Deploy cache and aggregator proxy | ||
commonChangesets.Configure( | ||
changeset.DeployCacheChangeset, | ||
types.DeployConfig{ | ||
ChainsToDeploy: []uint64{chainSelector}, | ||
Labels: []string{"data-feeds"}, | ||
}, | ||
), | ||
commonChangesets.Configure( | ||
changeset.DeployAggregatorProxyChangeset, | ||
types.DeployAggregatorProxyConfig{ | ||
ChainsToDeploy: []uint64{chainSelector}, | ||
AccessController: []common.Address{common.HexToAddress("0x")}, | ||
}, | ||
), | ||
) | ||
require.NoError(t, err) | ||
|
||
proxyAddress, err := deployment.SearchAddressBook(newEnv.ExistingAddresses, chainSelector, "AggregatorProxy") | ||
require.NoError(t, err) | ||
|
||
newEnv, err = commonChangesets.Apply(t, newEnv, nil, | ||
// Propose and confirm new Aggregator | ||
commonChangesets.Configure( | ||
changeset.ProposeAggregatorChangeset, | ||
types.ProposeConfirmAggregatorConfig{ | ||
ChainSelector: chainSelector, | ||
ProxyAddress: common.HexToAddress(proxyAddress), | ||
NewAggregatorAddress: common.HexToAddress("0x123"), | ||
}, | ||
), | ||
commonChangesets.Configure( | ||
changeset.ConfirmAggregatorChangeset, | ||
types.ProposeConfirmAggregatorConfig{ | ||
ChainSelector: chainSelector, | ||
ProxyAddress: common.HexToAddress(proxyAddress), | ||
NewAggregatorAddress: common.HexToAddress("0x123"), | ||
}, | ||
), | ||
commonChangesets.Configure( | ||
deployment.CreateLegacyChangeSet(commonChangesets.DeployMCMSWithTimelockV2), | ||
map[uint64]commonTypes.MCMSWithTimelockConfigV2{ | ||
chainSelector: proposalutils.SingleGroupTimelockConfigV2(t), | ||
}, | ||
), | ||
) | ||
require.NoError(t, err) | ||
|
||
// with MCMS | ||
newEnv, err = commonChangesets.Apply(t, newEnv, nil, | ||
// propose new Aggregator | ||
commonChangesets.Configure( | ||
changeset.ProposeAggregatorChangeset, | ||
types.ProposeConfirmAggregatorConfig{ | ||
ChainSelector: chainSelector, | ||
ProxyAddress: common.HexToAddress(proxyAddress), | ||
NewAggregatorAddress: common.HexToAddress("0x124"), | ||
}, | ||
), | ||
// transfer proxy ownership to timelock | ||
commonChangesets.Configure( | ||
deployment.CreateLegacyChangeSet(commonChangesets.TransferToMCMSWithTimelockV2), | ||
commonChangesets.TransferToMCMSWithTimelockConfig{ | ||
ContractsByChain: map[uint64][]common.Address{ | ||
chainSelector: {common.HexToAddress(proxyAddress)}, | ||
}, | ||
MinDelay: 0, | ||
}, | ||
), | ||
// confirm from timelock | ||
commonChangesets.Configure( | ||
changeset.ConfirmAggregatorChangeset, | ||
types.ProposeConfirmAggregatorConfig{ | ||
ChainSelector: chainSelector, | ||
ProxyAddress: common.HexToAddress(proxyAddress), | ||
NewAggregatorAddress: common.HexToAddress("0x124"), | ||
McmsConfig: &types.MCMSConfig{ | ||
MinDelay: 0, | ||
}, | ||
}, | ||
), | ||
) | ||
require.NoError(t, err) | ||
} |
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,78 @@ | ||
package changeset | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/ethereum/go-ethereum/accounts/abi/bind" | ||
"github.com/ethereum/go-ethereum/common" | ||
|
||
"github.com/smartcontractkit/chainlink/deployment" | ||
"github.com/smartcontractkit/chainlink/deployment/data-feeds/changeset/types" | ||
proxy "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/data-feeds/generated/aggregator_proxy" | ||
cache "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/data-feeds/generated/data_feeds_cache" | ||
) | ||
|
||
func DeployCache(chain deployment.Chain, labels []string) (*types.DeployCacheResponse, error) { | ||
cacheAddr, tx, cacheContract, err := cache.DeployDataFeedsCache(chain.DeployerKey, chain.Client) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to deploy DataFeedsCache: %w", err) | ||
} | ||
|
||
_, err = chain.Confirm(tx) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to confirm DataFeedsCache: %w", err) | ||
} | ||
|
||
tvStr, err := cacheContract.TypeAndVersion(&bind.CallOpts{}) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to get type and version: %w", err) | ||
} | ||
|
||
tv, err := deployment.TypeAndVersionFromString(tvStr) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to parse type and version from %s: %w", tvStr, err) | ||
} | ||
|
||
for _, label := range labels { | ||
tv.Labels.Add(label) | ||
} | ||
|
||
resp := &types.DeployCacheResponse{ | ||
Address: cacheAddr, | ||
Tx: tx.Hash(), | ||
Tv: tv, | ||
Contract: cacheContract, | ||
} | ||
return resp, nil | ||
} | ||
|
||
func DeployAggregatorProxy(chain deployment.Chain, aggregator common.Address, accessController common.Address, labels []string) (*types.DeployProxyResponse, error) { | ||
proxyAddr, tx, proxyContract, err := proxy.DeployAggregatorProxy(chain.DeployerKey, chain.Client, aggregator, accessController) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to deploy AggregatorProxy: %w", err) | ||
} | ||
|
||
_, err = chain.Confirm(tx) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to confirm AggregatorProxy: %w", err) | ||
} | ||
|
||
// AggregatorProxy contract doesn't implement typeAndVersion interface, so we have to set it manually | ||
tvStr := "AggregatorProxy 1.0.0" | ||
tv, err := deployment.TypeAndVersionFromString(tvStr) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to parse type and version from %s: %w", tvStr, err) | ||
} | ||
|
||
for _, label := range labels { | ||
tv.Labels.Add(label) | ||
} | ||
|
||
resp := &types.DeployProxyResponse{ | ||
Address: proxyAddr, | ||
Tx: tx.Hash(), | ||
Tv: tv, | ||
Contract: proxyContract, | ||
} | ||
return resp, nil | ||
} |
Oops, something went wrong.