Skip to content

Commit

Permalink
add proposal loader utils (#251)
Browse files Browse the repository at this point in the history
  • Loading branch information
akhilchainani authored Jan 27, 2025
1 parent a11a0ee commit a7fddd4
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 49 deletions.
36 changes: 36 additions & 0 deletions proposal.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package mcms

import (
"encoding/json"
"errors"
"fmt"
"io"
"maps"
"os"
"slices"
"strings"
"time"
Expand All @@ -22,6 +24,40 @@ import (

const SignMsgABI = `[{"type":"bytes32"},{"type":"uint32"}]`

type ProposalInterface interface {
AppendSignature(signature types.Signature)
Validate() error
}

func LoadProposal(proposalType types.ProposalKind, filePath string) (ProposalInterface, error) {
switch proposalType {
case types.KindProposal:
// Open the file
file, err := os.Open(filePath)
if err != nil {
return nil, err
}

// Ensure the file is closed when done
defer file.Close()

return NewProposal(file)
case types.KindTimelockProposal:
// Open the file
file, err := os.Open(filePath)
if err != nil {
return nil, err
}

// Ensure the file is closed when done
defer file.Close()

return NewTimelockProposal(file)
default:
return nil, errors.New("unknown proposal type")
}
}

// BaseProposal is the base struct for all MCMS proposals, contains shared fields for all proposal types.
type BaseProposal struct {
Version string `json:"version" validate:"required,oneof=v1"`
Expand Down
101 changes: 82 additions & 19 deletions proposal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"io"
"math"
"os"
"strings"
"testing"

Expand All @@ -20,7 +21,25 @@ import (
)

var (
TestAddress = "0x1234567890abcdef"
TestAddress = "0x1234567890abcdef"
ValidProposal = `{
"version": "v1",
"kind": "Proposal",
"validUntil": 2004259681,
"chainMetadata": {
"3379446385462418246": {}
},
"operations": [
{
"chainSelector": 3379446385462418246,
"transaction": {
"to": "0xsomeaddress",
"data": "EjM=",
"additionalFields": {"value": 0}
}
}
]
}`
)

func Test_BaseProposal_AppendSignature(t *testing.T) {
Expand Down Expand Up @@ -48,24 +67,7 @@ func Test_NewProposal(t *testing.T) {
}{
{
name: "success: initializes a proposal from an io.Reader",
give: `{
"version": "v1",
"kind": "Proposal",
"validUntil": 2004259681,
"chainMetadata": {
"3379446385462418246": {}
},
"operations": [
{
"chainSelector": 3379446385462418246,
"transaction": {
"to": "0xsomeaddress",
"data": "EjM=",
"additionalFields": {"value": 0}
}
}
]
}`,
give: ValidProposal,
want: Proposal{
BaseProposal: BaseProposal{
Version: "v1",
Expand Down Expand Up @@ -211,6 +213,67 @@ func Test_WriteProposal(t *testing.T) {
}
}

func TestLoadProposal(t *testing.T) {
t.Parallel()
t.Run("valid file path with KindProposal", func(t *testing.T) {
t.Parallel()

tempFile, err := os.CreateTemp("", "valid_proposal.txt")
require.NoError(t, err)

err = os.WriteFile(tempFile.Name(), []byte(ValidProposal), 0600)
require.NoError(t, err)
defer os.Remove(tempFile.Name())

proposal, err := LoadProposal(types.KindProposal, tempFile.Name())

require.NoError(t, err)
assert.NotNil(t, proposal)
})

t.Run("valid file path with KindTimelockProposal", func(t *testing.T) {
t.Parallel()

tempFile, err := os.CreateTemp("", "valid_timelock_proposal.txt")
require.NoError(t, err)

err = os.WriteFile(tempFile.Name(), []byte(ValidTimelockProposal), 0600)
require.NoError(t, err)
defer os.Remove(tempFile.Name())

proposal, err := LoadProposal(types.KindTimelockProposal, tempFile.Name())

require.NoError(t, err)
assert.NotNil(t, proposal)
})

t.Run("unknown proposal type", func(t *testing.T) {
t.Parallel()

filePath := "testdata/valid_proposal.txt"
err := os.WriteFile(filePath, []byte(ValidProposal), 0600)
require.Error(t, err)
defer os.Remove(filePath)

proposal, err := LoadProposal(types.ProposalKind("unknown"), filePath)

require.Error(t, err)
assert.Nil(t, proposal)
assert.Equal(t, "unknown proposal type", err.Error())
})

t.Run("invalid file path", func(t *testing.T) {
t.Parallel()

filePath := "invalid_path.txt"

proposal, err := LoadProposal(types.KindProposal, filePath)

require.Error(t, err)
assert.Nil(t, proposal)
})
}

func Test_Proposal_Validate(t *testing.T) {
t.Parallel()

Expand Down
62 changes: 32 additions & 30 deletions timelock_proposal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,37 @@ import (
"github.com/smartcontractkit/mcms/types"
)

var ValidTimelockProposal = `{
"version": "v1",
"kind": "TimelockProposal",
"validUntil": 2004259681,
"chainMetadata": {
"16015286601757825753": {
"mcmAddress": "0x0000000000000000000000000000000000000000",
"startingOpCount": 0
}
},
"description": "Test proposal",
"overridePreviousRoot": false,
"action": "schedule",
"delay": "1h",
"timelockAddresses": {
"16015286601757825753": "0x01"
},
"operations": [
{
"chainSelector": 16015286601757825753,
"transactions": [
{
"to": "0x0000000000000000000000000000000000000000",
"additionalFields": {"value": 0},
"data": "ZGF0YQ=="
}
]
}
]
}`

func Test_NewTimelockProposal(t *testing.T) {
t.Parallel()

Expand All @@ -30,36 +61,7 @@ func Test_NewTimelockProposal(t *testing.T) {
}{
{
name: "success: initializes a proposal from an io.Reader",
give: `{
"version": "v1",
"kind": "TimelockProposal",
"validUntil": 2004259681,
"chainMetadata": {
"16015286601757825753": {
"mcmAddress": "0x0000000000000000000000000000000000000000",
"startingOpCount": 0
}
},
"description": "Test proposal",
"overridePreviousRoot": false,
"action": "schedule",
"delay": "1h",
"timelockAddresses": {
"16015286601757825753": "0x01"
},
"operations": [
{
"chainSelector": 16015286601757825753,
"transactions": [
{
"to": "0x0000000000000000000000000000000000000000",
"additionalFields": {"value": 0},
"data": "ZGF0YQ=="
}
]
}
]
}`,
give: ValidTimelockProposal,
want: TimelockProposal{
BaseProposal: BaseProposal{
Version: "v1",
Expand Down
6 changes: 6 additions & 0 deletions types/proposal_kind.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,9 @@ const (
// KindTimelockProposal is a proposal type for the MCMS contract with RBACTimelock.
KindTimelockProposal ProposalKind = "TimelockProposal"
)

// StringToProposalKind converts a string to a ProposalKind.
var StringToProposalKind = map[string]ProposalKind{
"Proposal": KindProposal,
"TimelockProposal": KindTimelockProposal,
}

0 comments on commit a7fddd4

Please sign in to comment.