Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enforce uniqueness of timestamp nonce (backport #2082) #2094

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions protocol/lib/collections.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,13 @@ func MergeMaps[K comparable, V any](maps ...map[K]V) map[K]V {
}
return combinedMap
}

// Check if slice contains a particular value
func SliceContains[T comparable](list []T, value T) bool {
for _, v := range list {
if v == value {
return true
}
}
return false
}
12 changes: 12 additions & 0 deletions protocol/lib/collections_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -471,3 +471,15 @@ func TestMergeAllMapsWithDistinctKeys(t *testing.T) {
})
}
}

func TestSliceContains(t *testing.T) {
require.True(
t,
lib.SliceContains([]uint32{1, 2}, 1),
)

require.False(
t,
lib.SliceContains([]uint32{1, 2}, 3),
)
}
6 changes: 6 additions & 0 deletions protocol/x/accountplus/keeper/timestampnonce.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/cosmos/cosmos-sdk/telemetry"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/dydxprotocol/v4-chain/protocol/lib"
"github.com/dydxprotocol/v4-chain/protocol/lib/metrics"
"github.com/dydxprotocol/v4-chain/protocol/x/accountplus/types"
)
Expand Down Expand Up @@ -80,6 +81,11 @@ func AttemptTimestampNonceUpdate(
return false
}

// Must be unique
if lib.SliceContains(tsNonceDetails.TimestampNonces, tsNonce) {
return false
}

if len(tsNonceDetails.TimestampNonces) < MaxTimestampNonceArrSize {
tsNonceDetails.TimestampNonces = append(tsNonceDetails.TimestampNonces, tsNonce)
return true
Expand Down
44 changes: 37 additions & 7 deletions protocol/x/accountplus/keeper/timestampnonce_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func TestEjectStaleTsNonces(t *testing.T) {

func TestAttemptTimestampNonceUpdate(t *testing.T) {
startTs := keeper.TimestampNonceSequenceCutoff
t.Run("Will not update if ts nonce <= maxEjectedNonce", func(t *testing.T) {
t.Run("Will reject ts nonce <= maxEjectedNonce", func(t *testing.T) {
tsNonce := startTs + 10

var tsNonces []uint64
Expand Down Expand Up @@ -157,7 +157,37 @@ func TestAttemptTimestampNonceUpdate(t *testing.T) {
require.Equal(t, expectedAccountState, accountState)
})

t.Run("Will update if ts nonces has capacity (ts nonce > maxEjectedNonce)", func(t *testing.T) {
t.Run("Will reject duplicate ts nonce", func(t *testing.T) {
tsNonce := startTs + 20

var tsNonces []uint64
for i := range 5 {
tsNonces = append(tsNonces, startTs+uint64(i)+20)
}

accountState := types.AccountState{
Address: constants.AliceAccAddress.String(),
TimestampNonceDetails: types.TimestampNonceDetails{
TimestampNonces: tsNonces,
MaxEjectedNonce: startTs + 10,
},
}

expectedAccountState := types.AccountState{
Address: constants.AliceAccAddress.String(),
TimestampNonceDetails: types.TimestampNonceDetails{
TimestampNonces: tsNonces,
MaxEjectedNonce: startTs + 10,
},
}

updated := keeper.AttemptTimestampNonceUpdate(tsNonce, &accountState)

require.False(t, updated)
require.Equal(t, expectedAccountState, accountState)
})

t.Run("Will update if ts nonces has capacity (given ts unique and ts > maxEjectedNonce)", func(t *testing.T) {
tsNonce := startTs + 11

var tsNonces []uint64
Expand Down Expand Up @@ -188,9 +218,9 @@ func TestAttemptTimestampNonceUpdate(t *testing.T) {
})

t.Run(
"Will not update if ts nonce <= existing ts nonces (timestamp nonce > maxEjectedNonce)",
"Will not update if ts nonce <= existing ts nonces (given ts unique and ts > maxEjectedNonce)",
func(t *testing.T) {
tsNonce := startTs + 20
tsNonce := startTs + 19

var tsNonces []uint64
for i := range keeper.MaxTimestampNonceArrSize {
Expand Down Expand Up @@ -220,15 +250,15 @@ func TestAttemptTimestampNonceUpdate(t *testing.T) {
})

t.Run(
"Will update if ts nonce larger than at least one existing ts nonce (timestamp nonce > maxEjectedNonce)",
"Will update if ts nonce larger than at least one existing ts nonce (given ts unique and ts > maxEjectedNonce)",
func(t *testing.T) {
tsNonce := startTs + 21

var tsNonces []uint64
for i := range keeper.MaxTimestampNonceArrSize {
tsNonces = append(tsNonces, startTs+uint64(i)+20)
}

tsNonce := tsNonces[len(tsNonces)-1] + 1

accountState := types.AccountState{
Address: constants.AliceAccAddress.String(),
TimestampNonceDetails: types.TimestampNonceDetails{
Expand Down
Loading