diff --git a/collections/CHANGELOG.md b/collections/CHANGELOG.md index d49418598a79..b69be22353e9 100644 --- a/collections/CHANGELOG.md +++ b/collections/CHANGELOG.md @@ -32,4 +32,5 @@ Ref: https://keepachangelog.com/en/1.0.0/ ## [Unreleased] * [#14134](https://github.com/cosmos/cosmos-sdk/pull/14134) Initialise core (Prefix, KeyEncoder, ValueEncoder, Map). -* [#14351](https://github.com/cosmos/cosmos-sdk/pull/14351) Add keyset \ No newline at end of file +* [#14351](https://github.com/cosmos/cosmos-sdk/pull/14351) Add keyset +* [#14364](https://github.com/cosmos/cosmos-sdk/pull/14364) Add sequence \ No newline at end of file diff --git a/collections/sequence.go b/collections/sequence.go new file mode 100644 index 000000000000..61be844fa45d --- /dev/null +++ b/collections/sequence.go @@ -0,0 +1,49 @@ +package collections + +import ( + "context" + "errors" +) + +// DefaultSequenceStart defines the default starting number of a sequence. +const DefaultSequenceStart uint64 = 1 + +// Sequence builds on top of an Item, and represents a monotonically increasing number. +type Sequence Item[uint64] + +// NewSequence instantiates a new sequence given +// a Schema, a Prefix and humanised name for the sequence. +func NewSequence(schema Schema, prefix Prefix, name string) Sequence { + return (Sequence)(NewItem(schema, prefix, name, Uint64Value)) +} + +// Peek returns the current sequence value, if no number +// is set then the DefaultSequenceStart is returned. +// Errors on encoding issues. +func (s Sequence) Peek(ctx context.Context) (uint64, error) { + n, err := (Item[uint64])(s).Get(ctx) + switch { + case err == nil: + return n, nil + case errors.Is(err, ErrNotFound): + return DefaultSequenceStart, nil + default: + return 0, err + } +} + +// Next returns the next sequence number, and sets the next expected sequence. +// Errors on encoding issues. +func (s Sequence) Next(ctx context.Context) (uint64, error) { + seq, err := s.Peek(ctx) + if err != nil { + return 0, err + } + return seq, s.Set(ctx, seq+1) +} + +// Set hard resets the sequence to the provided value. +// Errors on encoding issues. +func (s Sequence) Set(ctx context.Context, value uint64) error { + return (Item[uint64])(s).Set(ctx, value) +} diff --git a/collections/sequence_test.go b/collections/sequence_test.go new file mode 100644 index 000000000000..082e3000669c --- /dev/null +++ b/collections/sequence_test.go @@ -0,0 +1,32 @@ +package collections + +import ( + "github.com/stretchr/testify/require" + "testing" +) + +func TestSequence(t *testing.T) { + sk, ctx := deps() + schema := NewSchema(sk) + seq := NewSequence(schema, NewPrefix(0), "sequence") + // initially the first available number is DefaultSequenceStart + n, err := seq.Peek(ctx) + require.NoError(t, err) + require.Equal(t, DefaultSequenceStart, n) + + // when we call next when sequence is still unset the first expected value is DefaultSequenceStart + n, err = seq.Next(ctx) + require.NoError(t, err) + require.Equal(t, DefaultSequenceStart, n) + // when we call peek after the first number is set, then the next expected sequence is DefaultSequenceStart + 1 + n, err = seq.Peek(ctx) + require.NoError(t, err) + require.Equal(t, DefaultSequenceStart+1, n) + + // set + err = seq.Set(ctx, 10) + require.NoError(t, err) + n, err = seq.Peek(ctx) + require.NoError(t, err) + require.Equal(t, n, uint64(10)) +}