Weighted votes migrate in-place migrations (#8663)
* Weighted votes migrate in-place migrations * Rename to pb.go * Fix some lint * Fix lint Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
parent
810605689d
commit
30f58b5662
|
@ -17,5 +17,5 @@ func NewMigrator(keeper Keeper) Migrator {
|
|||
|
||||
// Migrate1to2 migrates from version 1 to 2.
|
||||
func (m Migrator) Migrate1to2(ctx sdk.Context) error {
|
||||
return v042.MigrateStore(ctx, m.keeper.storeKey)
|
||||
return v042.MigrateStore(ctx, m.keeper.storeKey, m.keeper.cdc)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,302 @@
|
|||
// Package v040 is take from:
|
||||
// https://github.com/cosmos/cosmos-sdk/blob/v0.41.1/x/gov/types/gov.pb.go
|
||||
// by copy-pasted only the relevants parts for Vote.
|
||||
package v040
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
math_bits "math/bits"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/x/gov/types"
|
||||
)
|
||||
|
||||
type Vote struct {
|
||||
ProposalId uint64 `protobuf:"varint,1,opt,name=proposal_id,json=proposalId,proto3" json:"proposal_id,omitempty" yaml:"proposal_id"` //nolint:golint
|
||||
Voter string `protobuf:"bytes,2,opt,name=voter,proto3" json:"voter,omitempty"`
|
||||
Option types.VoteOption `protobuf:"varint,3,opt,name=option,proto3,enum=cosmos.gov.v1beta1.VoteOption" json:"option,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Vote) Reset() { *m = Vote{} }
|
||||
func (*Vote) ProtoMessage() {}
|
||||
|
||||
func (m *Vote) Size() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
var l int
|
||||
_ = l
|
||||
if m.ProposalId != 0 {
|
||||
n += 1 + sovGov(m.ProposalId)
|
||||
}
|
||||
l = len(m.Voter)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovGov(uint64(l))
|
||||
}
|
||||
if m.Option != 0 {
|
||||
n += 1 + sovGov(uint64(m.Option))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *Vote) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dAtA[:n], nil
|
||||
}
|
||||
|
||||
func (m *Vote) MarshalTo(dAtA []byte) (int, error) {
|
||||
size := m.Size()
|
||||
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *Vote) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i := len(dAtA)
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if m.Option != 0 {
|
||||
i = encodeVarintGov(dAtA, i, uint64(m.Option))
|
||||
i--
|
||||
dAtA[i] = 0x18
|
||||
}
|
||||
if len(m.Voter) > 0 {
|
||||
i -= len(m.Voter)
|
||||
copy(dAtA[i:], m.Voter)
|
||||
i = encodeVarintGov(dAtA, i, uint64(len(m.Voter)))
|
||||
i--
|
||||
dAtA[i] = 0x12
|
||||
}
|
||||
if m.ProposalId != 0 {
|
||||
i = encodeVarintGov(dAtA, i, m.ProposalId)
|
||||
i--
|
||||
dAtA[i] = 0x8
|
||||
}
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func encodeVarintGov(dAtA []byte, offset int, v uint64) int {
|
||||
offset -= sovGov(v)
|
||||
base := offset
|
||||
for v >= 1<<7 {
|
||||
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||
v >>= 7
|
||||
offset++
|
||||
}
|
||||
dAtA[offset] = uint8(v)
|
||||
return base
|
||||
}
|
||||
|
||||
func sovGov(x uint64) (n int) {
|
||||
return (math_bits.Len64(x|1) + 6) / 7
|
||||
}
|
||||
|
||||
func (m *Vote) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowGov
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: Vote: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: Vote: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field ProposalId", wireType)
|
||||
}
|
||||
m.ProposalId = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowGov
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.ProposalId |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Voter", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowGov
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthGov
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthGov
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Voter = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 3:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Option", wireType)
|
||||
}
|
||||
m.Option = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowGov
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.Option |= types.VoteOption(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipGov(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
return ErrInvalidLengthGov
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func skipGov(dAtA []byte) (n int, err error) {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
depth := 0
|
||||
for iNdEx < l {
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowGov
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
wireType := int(wire & 0x7)
|
||||
switch wireType {
|
||||
case 0:
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowGov
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx++
|
||||
if dAtA[iNdEx-1] < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
iNdEx += 8
|
||||
case 2:
|
||||
var length int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowGov
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
length |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if length < 0 {
|
||||
return 0, ErrInvalidLengthGov
|
||||
}
|
||||
iNdEx += length
|
||||
case 3:
|
||||
depth++
|
||||
case 4:
|
||||
if depth == 0 {
|
||||
return 0, ErrUnexpectedEndOfGroupGov
|
||||
}
|
||||
depth--
|
||||
case 5:
|
||||
iNdEx += 4
|
||||
default:
|
||||
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
||||
}
|
||||
if iNdEx < 0 {
|
||||
return 0, ErrInvalidLengthGov
|
||||
}
|
||||
if depth == 0 {
|
||||
return iNdEx, nil
|
||||
}
|
||||
}
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvalidLengthGov = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
ErrIntOverflowGov = fmt.Errorf("proto: integer overflow")
|
||||
ErrUnexpectedEndOfGroupGov = fmt.Errorf("proto: unexpected end of group")
|
||||
)
|
|
@ -0,0 +1,78 @@
|
|||
// Package v040 is copy-pasted from:
|
||||
// https://github.com/cosmos/cosmos-sdk/blob/v0.41.1/x/gov/types/vote.go
|
||||
package v040
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov/types"
|
||||
)
|
||||
|
||||
// NewVote creates a new Vote instance
|
||||
//nolint:interfacer
|
||||
func NewVote(proposalID uint64, voter sdk.AccAddress, option types.VoteOption) Vote {
|
||||
return Vote{proposalID, voter.String(), option}
|
||||
}
|
||||
|
||||
func (v Vote) String() string {
|
||||
out, _ := yaml.Marshal(v)
|
||||
return string(out)
|
||||
}
|
||||
|
||||
// Votes is a collection of Vote objects
|
||||
type Votes []Vote
|
||||
|
||||
// Equal returns true if two slices (order-dependant) of votes are equal.
|
||||
func (v Votes) Equal(other Votes) bool {
|
||||
if len(v) != len(other) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i, vote := range v {
|
||||
if vote.String() != other[i].String() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (v Votes) String() string {
|
||||
if len(v) == 0 {
|
||||
return "[]"
|
||||
}
|
||||
out := fmt.Sprintf("Votes for Proposal %d:", v[0].ProposalId)
|
||||
for _, vot := range v {
|
||||
out += fmt.Sprintf("\n %s: %s", vot.Voter, vot.Option)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Empty returns whether a vote is empty.
|
||||
func (v Vote) Empty() bool {
|
||||
return v.String() == Vote{}.String()
|
||||
}
|
||||
|
||||
// VoteOptionFromString returns a VoteOption from a string. It returns an error
|
||||
// if the string is invalid.
|
||||
func VoteOptionFromString(str string) (types.VoteOption, error) {
|
||||
option, ok := types.VoteOption_value[str]
|
||||
if !ok {
|
||||
return types.OptionEmpty, fmt.Errorf("'%s' is not a valid vote option", str)
|
||||
}
|
||||
return types.VoteOption(option), nil
|
||||
}
|
||||
|
||||
// ValidVoteOption returns true if the vote option is valid and false otherwise.
|
||||
func ValidVoteOption(option types.VoteOption) bool {
|
||||
if option == types.OptionYes ||
|
||||
option == types.OptionAbstain ||
|
||||
option == types.OptionNo ||
|
||||
option == types.OptionNoWithVeto {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -1,10 +1,12 @@
|
|||
package v042
|
||||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/store/prefix"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/address"
|
||||
v040gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v040"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov/types"
|
||||
)
|
||||
|
||||
const proposalIDLen = 8
|
||||
|
@ -30,14 +32,41 @@ func migratePrefixProposalAddress(store sdk.KVStore, prefixBz []byte) {
|
|||
}
|
||||
}
|
||||
|
||||
// migrateWeightedVotes migrates the ADR-037 weighted votes.
|
||||
func migrateWeightedVotes(store sdk.KVStore, cdc codec.BinaryMarshaler) error {
|
||||
iterator := sdk.KVStorePrefixIterator(store, v040gov.VotesKeyPrefix)
|
||||
|
||||
defer iterator.Close()
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
var oldVote v040gov.Vote
|
||||
err := cdc.UnmarshalBinaryBare(iterator.Value(), &oldVote)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newVote := &types.Vote{
|
||||
ProposalId: oldVote.ProposalId,
|
||||
Voter: oldVote.Voter,
|
||||
Options: []types.WeightedVoteOption{{Option: oldVote.Option, Weight: sdk.NewDec(1)}},
|
||||
}
|
||||
bz, err := cdc.MarshalBinaryBare(newVote)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
store.Set(iterator.Key(), bz)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MigrateStore performs in-place store migrations from v0.40 to v0.42. The
|
||||
// migration includes:
|
||||
//
|
||||
// - Change addresses to be length-prefixed.
|
||||
func MigrateStore(ctx sdk.Context, storeKey sdk.StoreKey) error {
|
||||
func MigrateStore(ctx sdk.Context, storeKey sdk.StoreKey, cdc codec.BinaryMarshaler) error {
|
||||
store := ctx.KVStore(storeKey)
|
||||
migratePrefixProposalAddress(store, v040gov.DepositsKeyPrefix)
|
||||
migratePrefixProposalAddress(store, v040gov.VotesKeyPrefix)
|
||||
|
||||
return nil
|
||||
return migrateWeightedVotes(store, cdc)
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/simapp"
|
||||
"github.com/cosmos/cosmos-sdk/testutil"
|
||||
"github.com/cosmos/cosmos-sdk/testutil/testdata"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
@ -16,6 +17,7 @@ import (
|
|||
)
|
||||
|
||||
func TestStoreMigration(t *testing.T) {
|
||||
cdc := simapp.MakeTestEncodingConfig().Marshaler
|
||||
govKey := sdk.NewKVStoreKey("gov")
|
||||
ctx := testutil.DefaultContext(govKey, sdk.NewTransientStoreKey("transient_test"))
|
||||
store := ctx.KVStore(govKey)
|
||||
|
@ -23,53 +25,57 @@ func TestStoreMigration(t *testing.T) {
|
|||
_, _, addr1 := testdata.KeyTestPubAddr()
|
||||
proposalID := uint64(6)
|
||||
now := time.Now()
|
||||
// Use dummy value for all keys.
|
||||
value := []byte("foo")
|
||||
// Use dummy value for keys where we don't test values.
|
||||
dummyValue := []byte("foo")
|
||||
// Use real values for votes, as we're testing weighted votes.
|
||||
oldVote := v040gov.Vote{ProposalId: 1, Voter: "foobar", Option: types.OptionNoWithVeto}
|
||||
oldVoteValue := cdc.MustMarshalBinaryBare(&oldVote)
|
||||
newVote := types.Vote{ProposalId: 1, Voter: "foobar", Options: types.WeightedVoteOptions{{Option: types.OptionNoWithVeto, Weight: sdk.NewDec(1)}}}
|
||||
newVoteValue := cdc.MustMarshalBinaryBare(&newVote)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
oldKey []byte
|
||||
newKey []byte
|
||||
name string
|
||||
oldKey, oldValue, newKey, newValue []byte
|
||||
}{
|
||||
{
|
||||
"ProposalKey",
|
||||
v040gov.ProposalKey(proposalID),
|
||||
types.ProposalKey(proposalID),
|
||||
v040gov.ProposalKey(proposalID), dummyValue,
|
||||
types.ProposalKey(proposalID), dummyValue,
|
||||
},
|
||||
{
|
||||
"ActiveProposalQueue",
|
||||
v040gov.ActiveProposalQueueKey(proposalID, now),
|
||||
types.ActiveProposalQueueKey(proposalID, now),
|
||||
v040gov.ActiveProposalQueueKey(proposalID, now), dummyValue,
|
||||
types.ActiveProposalQueueKey(proposalID, now), dummyValue,
|
||||
},
|
||||
{
|
||||
"InactiveProposalQueue",
|
||||
v040gov.InactiveProposalQueueKey(proposalID, now),
|
||||
types.InactiveProposalQueueKey(proposalID, now),
|
||||
v040gov.InactiveProposalQueueKey(proposalID, now), dummyValue,
|
||||
types.InactiveProposalQueueKey(proposalID, now), dummyValue,
|
||||
},
|
||||
{
|
||||
"ProposalIDKey",
|
||||
v040gov.ProposalIDKey,
|
||||
types.ProposalIDKey,
|
||||
v040gov.ProposalIDKey, dummyValue,
|
||||
types.ProposalIDKey, dummyValue,
|
||||
},
|
||||
{
|
||||
"DepositKey",
|
||||
v040gov.DepositKey(proposalID, addr1),
|
||||
types.DepositKey(proposalID, addr1),
|
||||
v040gov.DepositKey(proposalID, addr1), dummyValue,
|
||||
types.DepositKey(proposalID, addr1), dummyValue,
|
||||
},
|
||||
{
|
||||
"VotesKeyPrefix",
|
||||
v040gov.VoteKey(proposalID, addr1),
|
||||
types.VoteKey(proposalID, addr1),
|
||||
v040gov.VoteKey(proposalID, addr1), oldVoteValue,
|
||||
types.VoteKey(proposalID, addr1), newVoteValue,
|
||||
},
|
||||
}
|
||||
|
||||
// Set all the old keys to the store
|
||||
for _, tc := range testCases {
|
||||
store.Set(tc.oldKey, value)
|
||||
store.Set(tc.oldKey, tc.oldValue)
|
||||
}
|
||||
|
||||
// Run migrations.
|
||||
err := v042gov.MigrateStore(ctx, govKey)
|
||||
err := v042gov.MigrateStore(ctx, govKey, cdc)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Make sure the new keys are set and old keys are deleted.
|
||||
|
@ -79,7 +85,7 @@ func TestStoreMigration(t *testing.T) {
|
|||
if bytes.Compare(tc.oldKey, tc.newKey) != 0 {
|
||||
require.Nil(t, store.Get(tc.oldKey))
|
||||
}
|
||||
require.Equal(t, value, store.Get(tc.newKey))
|
||||
require.Equal(t, tc.newValue, store.Get(tc.newKey))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue