v1-content
?
You are viewing an older version of the content, click here to switch to the current version

# Store Field - Keep an Up-To-Date Game Deadline

Make sure you have everything you need before proceeding:

In this section, you will:

  • Implement a deadline.
  • Work with dates.
  • Extend your unit tests.

In the previous section you introduced a FIFO that keeps the oldest games at its head and the most recently updated games at its tail.

Just because a game has not been updated in a while does not mean that it has expired. To ascertain this you need to add a new field to a game, deadline, and test against it.

# New information

To prepare the field, add in the StoredGame's Protobuf definition:

Copy message StoredGame { ... string deadline = 10; } proto checkers stored_game.proto View source

To have Ignite CLI and Protobuf recompile this file, use:

Copy $ ignite generate proto-go

On each update the deadline will always be now plus a fixed duration. In this context, now refers to the block's time. You cannot use a non-deterministic Date.now(), which would be different on each execution. Declare this duration as a new constant, plus how the date is to be represented, i.e. encoded in the saved game as a string:

Copy const ( MaxTurnDuration = time.Duration(24 * 3_600 * 1000_000_000) // 1 day DeadlineLayout = "2006-01-02 15:04:05.999999999 +0000 UTC" ) x checkers types keys.go View source

# Date manipulation

Helper functions can encode and decode the deadline in the storage.

  1. Define a new error:

    Copy ErrInvalidDeadline = sdkerrors.Register(ModuleName, 1110, "deadline cannot be parsed: %s") x checkers types errors.go View source
  2. Add your date helpers. A reasonable location to pick is full_game.go:

    Copy func (storedGame *StoredGame) GetDeadlineAsTime() (deadline time.Time, err error) { deadline, errDeadline := time.Parse(DeadlineLayout, storedGame.Deadline) return deadline, sdkerrors.Wrapf(errDeadline, ErrInvalidDeadline.Error(), storedGame.Deadline) } func FormatDeadline(deadline time.Time) string { return deadline.UTC().Format(DeadlineLayout) } x checkers types full_game.go View source

    Note that sdkerrors.Wrapf(err, ...) conveniently returns nil if err is nil.

  3. Add a function that encapsulates how the next deadline is calculated in the same file:

    Copy func GetNextDeadline(ctx sdk.Context) time.Time { return ctx.BlockTime().Add(MaxTurnDuration) } x checkers types full_game.go View source

# Updated deadline

Next, you need to update this new field with its appropriate value:

  1. At creation, in the message handler for game creation:

    Copy ... storedGame := types.StoredGame{ ... Deadline: types.FormatDeadline(types.GetNextDeadline(ctx)), } x checkers keeper msg_server_create_game.go View source
  2. After a move, in the message handler:

    Copy ... storedGame.MoveCount++ storedGame.Deadline = types.FormatDeadline(types.GetNextDeadline(ctx)) ... x checkers keeper msg_server_play_move.go View source

Confirm that your project still compiles:

Copy $ ignite chain build

# Unit tests

After these changes, your previous unit tests fail. Fix them by adding Deadline wherever it should be. Do not forget that the time is taken from the block's timestamp. In the case of tests, it is stored in the context's ctx.BlockTime(). In effect, you need to add this single line:

Copy ctx := sdk.UnwrapSDKContext(context) ... require.EqualValues(t, types.StoredGame{ ... Deadline: types.FormatDeadline(ctx.BlockTime().Add(types.MaxTurnDuration)), }, game) x checkers keeper msg_server_reject_game_fifo_test.go View source

# Interact via the CLI

There is not much to test here. Remember that you added a new field, but if your blockchain state already contains games then they are missing the new field:

Copy $ checkersd query checkers show-stored-game 1

This demonstrates some missing information:

Copy ... deadline: "" ...

In effect, your blockchain state is broken. Examine the section on migrations to see how to update your blockchain state to avoid such a breaking change. This broken state still lets you test the update of the deadline on play:

Copy $ checkersd tx checkers play-move 1 1 2 2 3 --from $bob $ checkersd query checkers show-stored-game 1

This contains:

Copy ... deadline: 2022-02-05 15:26:26.832533 +0000 UTC ...

In the same vein, you can create a new game and confirm it contains the deadline.

# Next up

You have created and updated the deadline. The section two steps ahead describes how to use the deadline.

Before you can do that, there is one other field you need to add. Discover which in the next section.