forked from cosmos/cosmos-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(collections): add alternative value codec (cosmos#16773)
Co-authored-by: unknown unknown <unknown@unknown>
- Loading branch information
1 parent
f0aec3f
commit 309ed1a
Showing
12 changed files
with
337 additions
and
4 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
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
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,47 @@ | ||
package codec | ||
|
||
// NewAltValueCodec returns a new AltValueCodec. canonicalValueCodec is the codec that you want the value | ||
// to be encoded and decoded as, alternativeDecoder is a function that will attempt to decode the value | ||
// in case the canonicalValueCodec fails to decode it. | ||
func NewAltValueCodec[V any](canonicalValueCodec ValueCodec[V], alternativeDecoder func([]byte) (V, error)) ValueCodec[V] { | ||
return AltValueCodec[V]{ | ||
canonicalValueCodec: canonicalValueCodec, | ||
alternativeDecoder: alternativeDecoder, | ||
} | ||
} | ||
|
||
// AltValueCodec is a codec that can decode a value from state in an alternative format. | ||
// This is useful for migrating data from one format to another. For example, in x/bank | ||
// balances were initially encoded as sdk.Coin, now they are encoded as math.Int. | ||
// The AltValueCodec will be trying to decode the value as math.Int, and if that fails, | ||
// it will attempt to decode it as sdk.Coin. | ||
// NOTE: if the canonical format can also decode the alternative format, then this codec | ||
// will produce undefined and undesirable behavior. | ||
type AltValueCodec[V any] struct { | ||
canonicalValueCodec ValueCodec[V] | ||
alternativeDecoder func([]byte) (V, error) | ||
} | ||
|
||
// Decode will attempt to decode the value from state using the canonical value codec. | ||
// If it fails to decode, it will attempt to decode the value using the alternative decoder. | ||
func (a AltValueCodec[V]) Decode(b []byte) (V, error) { | ||
v, err := a.canonicalValueCodec.Decode(b) | ||
if err != nil { | ||
return a.alternativeDecoder(b) | ||
} | ||
return v, nil | ||
} | ||
|
||
// Below there is the implementation of ValueCodec relying on the canonical value codec. | ||
|
||
func (a AltValueCodec[V]) Encode(value V) ([]byte, error) { return a.canonicalValueCodec.Encode(value) } | ||
|
||
func (a AltValueCodec[V]) EncodeJSON(value V) ([]byte, error) { | ||
return a.canonicalValueCodec.EncodeJSON(value) | ||
} | ||
|
||
func (a AltValueCodec[V]) DecodeJSON(b []byte) (V, error) { return a.canonicalValueCodec.DecodeJSON(b) } | ||
|
||
func (a AltValueCodec[V]) Stringify(value V) string { return a.canonicalValueCodec.Stringify(value) } | ||
|
||
func (a AltValueCodec[V]) ValueType() string { return a.canonicalValueCodec.ValueType() } |
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,53 @@ | ||
package codec_test | ||
|
||
import ( | ||
"encoding/json" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
|
||
"cosmossdk.io/collections/codec" | ||
"cosmossdk.io/collections/colltest" | ||
) | ||
|
||
type altValue struct { | ||
Value uint64 `json:"value"` | ||
} | ||
|
||
func TestAltValueCodec(t *testing.T) { | ||
// we assume we want to migrate the value from json(altValue) to just be | ||
// the raw value uint64. | ||
canonical := codec.KeyToValueCodec(codec.NewUint64Key[uint64]()) | ||
alternative := func(v []byte) (uint64, error) { | ||
var alt altValue | ||
err := json.Unmarshal(v, &alt) | ||
if err != nil { | ||
return 0, err | ||
} | ||
return alt.Value, nil | ||
} | ||
|
||
cdc := codec.NewAltValueCodec(canonical, alternative) | ||
|
||
t.Run("decodes alternative value", func(t *testing.T) { | ||
expected := uint64(100) | ||
alternativeEncodedBytes, err := json.Marshal(altValue{Value: expected}) | ||
require.NoError(t, err) | ||
got, err := cdc.Decode(alternativeEncodedBytes) | ||
require.NoError(t, err) | ||
require.Equal(t, expected, got) | ||
}) | ||
|
||
t.Run("decodes canonical value", func(t *testing.T) { | ||
expected := uint64(100) | ||
canonicalEncodedBytes, err := cdc.Encode(expected) | ||
require.NoError(t, err) | ||
got, err := cdc.Decode(canonicalEncodedBytes) | ||
require.NoError(t, err) | ||
require.Equal(t, expected, got) | ||
}) | ||
|
||
t.Run("conformance", func(t *testing.T) { | ||
colltest.TestValueCodec(t, cdc, uint64(100)) | ||
}) | ||
} |
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
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
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
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
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
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
Oops, something went wrong.