Skip to content
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

Port CLI to mcms for non CLD integrations #182

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ issues:
# Exclude the directory from linting because it will soon be removed and only contains
# generated code. Can be removed once the gethwrappers directory is removed.
- sdk/evm/bindings
- cmd
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should lint this code, or if there is a linter that is specifically not needed, we can exclude it explicitly.

I am very wary of blanket exclusions

exclude-rules:
- path: _test\.go
linters:
Expand Down
2 changes: 2 additions & 0 deletions cmd/.example.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
PRIVATE_KEY=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
RPC_URL=https://mainnet.infura.io/v3/0123456789abcdef0123456789abcdef
17 changes: 17 additions & 0 deletions cmd/main.go
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

most of the code under cmd/ was copied from chainlink-deployments/cmd/, correct?

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package main

import (
"fmt"
"os"

"github.com/smartcontractkit/mcms/cmd/mcms"
)

func main() {
rootCmd := mcms.BuildMCMSCmd()

if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
63 changes: 63 additions & 0 deletions cmd/mcms/check_quorum.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package mcms

import (
"errors"
"fmt"

chainsel "github.com/smartcontractkit/chain-selectors"
"github.com/smartcontractkit/mcms/sdk"
"github.com/smartcontractkit/mcms/sdk/evm"
"github.com/smartcontractkit/mcms/types"
"github.com/spf13/cobra"
)

func buildMCMSCheckQuorumCmd(proposalPath string, chainSelector uint64) *cobra.Command {
return &cobra.Command{
Use: "check-quorum",
Short: "Determines whether the provided signatures meet the quorum to set the root",
RunE: func(cmd *cobra.Command, args []string) error {
fmt.Printf("Checking quorum for proposal %s\n", proposalPath)
proposal, err := loadProposal(proposalPath)
if err != nil {
fmt.Printf("Error loading proposal: %s\n", err)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably bringing up a linting error, but you can you the print function on the cmd object instead of fmt

return err
}

// Get EVM chain ID
chain, exists := chainsel.ChainBySelector(chainSelector)
if !exists {
return errors.New("chain not found")
}

// Load RPCs
backends, err := loadRPCs([]types.ChainSelector{types.ChainSelector(chain.Selector)})
if err != nil {
return err
}

// Create Inspector
chainInspector := evm.NewInspector(backends[types.ChainSelector(chain.Selector)])

// Convert proposal to signable
s, err := proposal.Signable(map[types.ChainSelector]sdk.Inspector{
types.ChainSelector(chain.Selector): chainInspector,
})
if err != nil {
fmt.Printf("Error converting proposal to signable: %s\n", err)
return err
}
quorumMet, err := s.CheckQuorum(types.ChainSelector(chainSelector))
if err != nil {
fmt.Printf("Error checking quorum: %s\n", err)
return err
}
if quorumMet {
fmt.Printf("Signature Quorum met!")
} else {
fmt.Printf("Signature Quorum not met!")
return errors.New("signature Quorum not met")
}
return nil
},
}
}
62 changes: 62 additions & 0 deletions cmd/mcms/deploy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package mcms

import (
"context"
"errors"
"fmt"
"math/big"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
chainsel "github.com/smartcontractkit/chain-selectors"
"github.com/smartcontractkit/mcms/sdk/evm/bindings"
"github.com/smartcontractkit/mcms/types"
"github.com/spf13/cobra"
)

func buildDeployCmd(chainSelector uint64) *cobra.Command {
return &cobra.Command{
Use: "deploy",
Short: "Deploys the MCMS contract on the specified chain",
Long: ``,
RunE: func(cmd *cobra.Command, args []string) error {
// Get EVM chain ID
chain, exists := chainsel.ChainBySelector(chainSelector)
if !exists {
return errors.New("chain not found")
}

// Load Private Key
pk, err := loadPrivateKey()
if err != nil {
fmt.Printf("Error loading private key: %s\n", err)
return err
}

auth, err := bind.NewKeyedTransactorWithChainID(pk, big.NewInt(int64(chain.EvmChainID)))
if err != nil {
return err
}

// Load RPCs
backends, err := loadRPCs([]types.ChainSelector{types.ChainSelector(chain.Selector)})
if err != nil {
return err
}
backend := backends[types.ChainSelector(chain.Selector)]

// Deploy MCM contract
_, tx, instance, err := bindings.DeployManyChainMultiSig(auth, backend)
if err != nil {
return err
}

_, err = EVMConfirm(context.Background(), backend, tx.Hash().Hex())
if err != nil {
return err
}
fmt.Printf("Transaction %s confirmed\n", tx.Hash().Hex())
fmt.Printf("MCMS Contract deployed at %s\n", instance.Address().Hex())
return nil
},
}
}
94 changes: 94 additions & 0 deletions cmd/mcms/execute_chain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package mcms

import (
"context"
"errors"
"fmt"
"math/big"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
chainsel "github.com/smartcontractkit/chain-selectors"
"github.com/smartcontractkit/mcms/sdk"
"github.com/smartcontractkit/mcms/sdk/evm"
"github.com/smartcontractkit/mcms/types"
"github.com/spf13/cobra"
)

func buildExecuteChainCmd(proposalPath string, chainSelector uint64) *cobra.Command {
return &cobra.Command{
Use: "execute-chain",
Short: "Executes all operations for a given chain in an MCMS Proposal. Root must be set first.",
Long: ``,
RunE: func(cmd *cobra.Command, args []string) error {
proposal, err := loadProposal(proposalPath)
if err != nil {
fmt.Printf("Error loading proposal: %s\n", err)
return err
}

// Get EVM chain ID
chain, exists := chainsel.ChainBySelector(chainSelector)
if !exists {
return errors.New("chain not found")
}

encoders, err := proposal.GetEncoders()
if err != nil {
fmt.Printf("Error getting encoders: %s\n", err)
return err
}

// Load Private Key
pk, err := loadPrivateKey()
if err != nil {
fmt.Printf("Error loading private key: %s\n", err)
return err
}

auth, err := bind.NewKeyedTransactorWithChainID(pk, big.NewInt(int64(chain.EvmChainID)))
if err != nil {
return err
}

// Load RPCs
backends, err := loadRPCs([]types.ChainSelector{types.ChainSelector(chain.Selector)})
if err != nil {
return err
}
backend := backends[types.ChainSelector(chain.Selector)]

// Create Executor
chainExecutor := evm.NewExecutor(
encoders[types.ChainSelector(chain.Selector)].(*evm.Encoder),
backend,
auth,
)

// Convert proposal to executor
e, err := proposal.Executable(map[types.ChainSelector]sdk.Executor{
types.ChainSelector(chain.Selector): chainExecutor,
})
if err != nil {
fmt.Printf("Error converting proposal to executor: %s\n", err)
return err
}

for i, op := range e.Proposal.Operations {
if op.ChainSelector == types.ChainSelector(chainSelector) {
txHash, err := e.Execute(i)
if err != nil {
return err
}

fmt.Printf("Transaction sent: %s", txHash)
_, err = EVMConfirm(context.Background(), backend, txHash)
if err != nil {
return err
}

}
}
return nil
},
}
}
95 changes: 95 additions & 0 deletions cmd/mcms/execute_operation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package mcms

import (
"context"
"errors"
"fmt"
"math/big"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
chainsel "github.com/smartcontractkit/chain-selectors"
"github.com/smartcontractkit/mcms/sdk"
"github.com/smartcontractkit/mcms/sdk/evm"
"github.com/smartcontractkit/mcms/types"
"github.com/spf13/cobra"
)

func buildExecuteOperationCmd(proposalPath string, chainSelector uint64) *cobra.Command {
var index uint64

cmd := cobra.Command{
Use: "execute-operation",
Short: "Executes a single operation on a given chain in an MCMS Proposal. Root must be set first.",
Long: ``,
RunE: func(cmd *cobra.Command, args []string) error {
proposal, err := loadProposal(proposalPath)
if err != nil {
fmt.Printf("Error loading proposal: %s\n", err)
return err
}

// Get EVM chain ID
chain, exists := chainsel.ChainBySelector(chainSelector)
if !exists {
return errors.New("chain not found")
}

encoders, err := proposal.GetEncoders()
if err != nil {
fmt.Printf("Error getting encoders: %s\n", err)
return err
}

// Load Private Key
pk, err := loadPrivateKey()
if err != nil {
fmt.Printf("Error loading private key: %s\n", err)
return err
}

auth, err := bind.NewKeyedTransactorWithChainID(pk, big.NewInt(int64(chain.EvmChainID)))
if err != nil {
return err
}

// Load RPCs
backends, err := loadRPCs([]types.ChainSelector{types.ChainSelector(chain.Selector)})
if err != nil {
return err
}
backend := backends[types.ChainSelector(chain.Selector)]

// Create Executor
chainExecutor := evm.NewExecutor(
encoders[types.ChainSelector(chain.Selector)].(*evm.Encoder),
backend,
auth,
)

// Convert proposal to executor
e, err := proposal.Executable(map[types.ChainSelector]sdk.Executor{
types.ChainSelector(chain.Selector): chainExecutor,
})
if err != nil {
fmt.Printf("Error converting proposal to executor: %s\n", err)
return err
}

txHash, err := e.Execute(int(index))
if err != nil {
return err
}

fmt.Printf("Transaction sent: %s", txHash)
_, err = EVMConfirm(context.Background(), backend, txHash)
if err != nil {
return err
}
return nil
},
}

cmd.Flags().Uint64Var(&index, "index", 0, "Index of the operation to execute")

return &cmd
}
33 changes: 33 additions & 0 deletions cmd/mcms/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package mcms

import (
"github.com/spf13/cobra"
)

func BuildMCMSCmd() *cobra.Command {
var (
proposalPath string
chainSelector uint64
)

cmd := cobra.Command{
Use: "mcms",
Short: "Manage MCMS proposals",
Long: ``,
}

cmd.PersistentFlags().StringVar(&proposalPath, "proposal", "", "Absolute file path containing the proposal to be submitted")
cmd.MarkFlagRequired("proposal")
cmd.PersistentFlags().Uint64Var(&chainSelector, "selector", 0, "Chain selector for the command to connect to")

cmd.AddCommand(buildDeployCmd(chainSelector))
cmd.AddCommand(buildSetConfigCmd(chainSelector))
cmd.AddCommand(buildMCMSCheckQuorumCmd(proposalPath, chainSelector))
cmd.AddCommand(buildExecuteChainCmd(proposalPath, chainSelector))
cmd.AddCommand(buildExecuteOperationCmd(proposalPath, chainSelector))
cmd.AddCommand(buildSetRootCmd(proposalPath, chainSelector))
cmd.AddCommand(newSignPrivateKeyCmd(proposalPath))
cmd.AddCommand(newSignLedgerCmd(proposalPath))

return &cmd
}
Loading
Loading