Skip to content

Commit

Permalink
Merge pull request #1 from MalteHerrmann/make-more-variable
Browse files Browse the repository at this point in the history
imp: use variable keys and proposal ID from current chain state
  • Loading branch information
MalteHerrmann authored Aug 9, 2023
2 parents fb401bc + 3fb16a0 commit 01e956b
Show file tree
Hide file tree
Showing 8 changed files with 236 additions and 25 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,16 @@ Ref: https://keepachangelog.com/en/1.0.0/

# Changelog

## [v0.2.0](https://github.com/MalteHerrmann/upgrade-local-node-go/releases/tag/v0.2.0) - 2023-08-09

### Improvements

- [#1](https://github.com/MalteHerrmann/upgrade-local-node-go/pull/1) adaptively gets keys and current proposal ID from the local node

## [v0.1.0](https://github.com/MalteHerrmann/upgrade-local-node-go/releases/tag/v0.1.0) - 2023-08-01

### Features

- Gets current block height of local node (at `http://localhost:26657`)
- Submit a software upgrade proposal to a running local Evmos node for the target version
- Vote on the software proposal

8 changes: 8 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
module github.com/MalteHerrmann/upgrade-local-node-go

go 1.20

require github.com/stretchr/testify v1.8.4

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
36 changes: 36 additions & 0 deletions keys.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package main

import (
"fmt"
"regexp"
)

// getKeys returns the list of keys from the current running local node
func getKeys() ([]string, error) {
out, err := executeShellCommand([]string{"keys", "list"}, evmosdHome, "", false)
if err != nil {
return nil, err
}

return parseKeysFromOut(out)
}

func parseKeysFromOut(out string) ([]string, error) {
// Define the regular expression pattern
pattern := `\s+name:\s*(\w+)`

// Compile the regular expression
re := regexp.MustCompile(pattern)

matches := re.FindAllStringSubmatch(out, -1)
if len(matches) == 0 {
return nil, fmt.Errorf("no keys found in output")
}

var keys []string
for _, match := range matches {
keys = append(keys, match[1])
}

return keys, nil
}
62 changes: 62 additions & 0 deletions keys_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package main

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestParseKeysFromOut(t *testing.T) {
testcases := []struct {
name string
out string
expKeys []string
expError bool
}{
{
name: "pass",
out: ` - address: evmos19mx9kcksequm4m4xume5h0k9fquwgmea3yvu89
name: dev0
pubkey: '{"@type":"/ethermint.crypto.v1.ethsecp256k1.PubKey","key":"AmquZBW+CPcgHKx6D4YRDICzr0MNcRvl9Wm/jJn8wJxs"}'
type: local
- address: evmos18z7xfs864u49jcv6gkgajpteesjl5d7krpple6
name: dev1
pubkey: '{"@type":"/ethermint.crypto.v1.ethsecp256k1.PubKey","key":"AtY/rqJrmhKbXrQ02xSxq/t9JGgbP2T7HPGTZJIbuT8I"}'
type: local
- address: evmos12rrt7vcnxvhxad6gzz0vt5psdlnurtldety57n
name: dev2
pubkey: '{"@type":"/ethermint.crypto.v1.ethsecp256k1.PubKey","key":"A544btlGjv4zB/qpWT8dQqlAHrcmgZEvrFSgJnp7Yjt4"}'
type: local
- address: evmos1dln2gjtsfd2sny6gwdxzyxcsr0uu8sh5nwajun
name: testKey1
pubkey: '{"@type":"/ethermint.crypto.v1.ethsecp256k1.PubKey","key":"Amja5pRiVw+5vPkozo6Eo20AEbYVVBqOKBi5yP7EbxyJ"}'
type: local
- address: evmos1qdxgxz9g2la8g9eyjdq4srlpxgrmuqd6ty88zm
name: testKey2
pubkey: '{"@type":"/ethermint.crypto.v1.ethsecp256k1.PubKey","key":"A+ytKfWmkQiW0c6iOCXSL71e4b5njmJVUd1msONsPEnA"}'
type: local
- address: evmos1hduvvhjvu0pqu7m97pajymdsupqx3us3ntey9a
name: testKey3
pubkey: '{"@type":"/ethermint.crypto.v1.ethsecp256k1.PubKey","key":"AsdAPndEVttzhUz5iSm0/FoFxkzB0oZE7DuKf3NjzXkS"}'
type: local`,
expKeys: []string{"dev0", "dev1", "dev2", "testKey1", "testKey2", "testKey3"},
},
{
name: "fail - no keys",
out: "invalid output",
expError: true,
},
}

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
keys, err := parseKeysFromOut(tc.out)
if tc.expError {
require.Error(t, err, "expected error parsing keys")
} else {
require.NoError(t, err, "unexpected error parsing keys")
require.Equal(t, tc.expKeys, keys)
}
})
}
}
34 changes: 11 additions & 23 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ const (
defaultFees int = 1e18 // 1 aevmos
// The denomination used for the local node.
denom = "aevmos"
// proposalID is the ID of the proposal that will be created.
proposalID = 1
)

// evmosdHome is the home directory of the local node.
Expand Down Expand Up @@ -67,34 +65,24 @@ func upgradeLocalNode(targetVersion string) {
}

upgradeHeight := currentHeight + deltaHeight
upgradeProposal := buildUpgradeProposalCommand(targetVersion, upgradeHeight)
_, err = executeShellCommand(upgradeProposal, evmosdHome, "dev0", true)
fmt.Println("Submitting upgrade proposal...")
proposalID, err := submitUpgradeProposal(targetVersion, upgradeHeight)
if err != nil {
log.Fatalf("Error executing upgrade proposal: %v", err)
}
fmt.Printf("Scheduled upgrade to %s at height %d.\n", targetVersion, upgradeHeight)

wait(2)
if err = voteForProposal(proposalID, "dev0"); err != nil {
log.Fatalf("Error voting for upgrade: %v", err)
}

wait(2)
if err = voteForProposal(proposalID, "dev1"); err != nil {
log.Fatalf("Error voting for upgrade: %v", err)
availableKeys, err := getKeys()
if err != nil {
log.Fatalf("Error getting available keys: %v", err)
}

wait(2)
if err = voteForProposal(proposalID, "dev2"); err != nil {
log.Fatalf("Error voting for upgrade: %v", err)
wait(1)
for _, key := range availableKeys {
if err = voteForProposal(proposalID, key); err != nil {
log.Fatalf("Error voting for upgrade: %v", err)
}
}
fmt.Printf("Cast all votes for proposal %d.\n", proposalID)
}

// voteForProposal votes for the proposal with the given ID using the given account.
func voteForProposal(proposalID int, sender string) error {
_, err := executeShellCommand([]string{"tx", "gov", "vote", fmt.Sprintf("%d", proposalID), "yes"}, evmosdHome, sender, true)
return err
fmt.Printf("Cast all %d 'yes' votes for proposal %d.\n", len(availableKeys), proposalID)
}

// wait waits for the specified amount of seconds.
Expand Down
39 changes: 38 additions & 1 deletion proposal.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,37 @@
package main

import "fmt"
import (
"fmt"
"regexp"
"strconv"
)

// submitUpgradeProposal submits a software upgrade proposal with the given target version and upgrade height.
func submitUpgradeProposal(targetVersion string, upgradeHeight int) (int, error) {
upgradeProposal := buildUpgradeProposalCommand(targetVersion, upgradeHeight)
out, err := executeShellCommand(upgradeProposal, evmosdHome, "dev0", true)
if err != nil {
return 0, err
}

return getProposalID(out)
}

// getProposalID parses the proposal ID from the given output from submitting an upgrade proposal.
func getProposalID(out string) (int, error) {
// Define the regular expression pattern
pattern := `- key:\s*proposal_id\s*\n\s*value:\s*"([^"]+)"`

// Compile the regular expression
re := regexp.MustCompile(pattern)

match := re.FindStringSubmatch(out)
if len(match) != 2 {
return 0, fmt.Errorf("proposal ID not found in output")
}

return strconv.Atoi(match[1])
}

// buildUpgradeProposalCommand builds the command to submit a software upgrade proposal.
func buildUpgradeProposalCommand(targetVersion string, upgradeHeight int) []string {
Expand All @@ -13,3 +44,9 @@ func buildUpgradeProposalCommand(targetVersion string, upgradeHeight int) []stri
"--no-validate",
}
}

// voteForProposal votes for the proposal with the given ID using the given account.
func voteForProposal(proposalID int, sender string) error {
_, err := executeShellCommand([]string{"tx", "gov", "vote", fmt.Sprintf("%d", proposalID), "yes"}, evmosdHome, sender, true)
return err
}
65 changes: 65 additions & 0 deletions proposal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package main

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestGetProposalID(t *testing.T) {
testcases := []struct {
name string
out string
expID int
expError bool
}{
{
name: "pass",
out: `gas estimate: 850456
code: 0
codespace: ""
data: 12330A2D2F636F736D6F732E676F762E763162657461312E4D73675375626D697450726F706F73616C526573706F6E736512020804
events:
logs:
- events:
- attributes:
- key: amount
value: 1000000000000aevmos
- key: proposal_id
value: "4"
type: proposal_deposit
- attributes:
- key: proposal_id
value: "4"
- key: proposal_messages
value: ',/cosmos.gov.v1.MsgExecLegacyContent'
- key: voting_period_start
value: "4"
type: submit_proposal
type: transfer
log: ""
msg_index: 0
timestamp: ""
tx: null
txhash: A505158FF9EFB4E939CD4A9A94F731E0E34AEEF50C7E53A723226EEF33A1A89B`,
expID: 4,
},
{
name: "fail - no proposal ID",
out: "invalid output",
expError: true,
},
}

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
id, err := getProposalID(tc.out)
if tc.expError {
require.Error(t, err, "expected error parsing proposal ID")
} else {
require.NoError(t, err, "unexpected error parsing proposal ID")
require.Equal(t, tc.expID, id, "expected different proposal ID")
}
})
}
}

0 comments on commit 01e956b

Please sign in to comment.