Skip to content

Commit

Permalink
Implement ExpiryEntry (#3382)
Browse files Browse the repository at this point in the history
  • Loading branch information
StephenButtolph authored and michaelkaplan13 committed Sep 11, 2024
1 parent 48d2f9c commit 07f13c6
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 0 deletions.
57 changes: 57 additions & 0 deletions vms/platformvm/state/expiry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package state

import (
"encoding/binary"
"fmt"

"github.com/google/btree"

"github.com/ava-labs/avalanchego/database"
"github.com/ava-labs/avalanchego/ids"
)

// expiryEntry = [timestamp] + [validationID]
const expiryEntryLength = database.Uint64Size + ids.IDLen

var (
errUnexpectedExpiryEntryLength = fmt.Errorf("expected expiry entry length %d", expiryEntryLength)

_ btree.LessFunc[ExpiryEntry] = ExpiryEntry.Less
)

type ExpiryEntry struct {
Timestamp uint64
ValidationID ids.ID
}

func (e *ExpiryEntry) Marshal() []byte {
data := make([]byte, expiryEntryLength)
binary.BigEndian.PutUint64(data, e.Timestamp)
copy(data[database.Uint64Size:], e.ValidationID[:])
return data
}

func (e *ExpiryEntry) Unmarshal(data []byte) error {
if len(data) != expiryEntryLength {
return errUnexpectedExpiryEntryLength
}

e.Timestamp = binary.BigEndian.Uint64(data)
copy(e.ValidationID[:], data[database.Uint64Size:])
return nil
}

// Invariant: Less produces the same ordering as the marshalled bytes.
func (e ExpiryEntry) Less(o ExpiryEntry) bool {
switch {
case e.Timestamp < o.Timestamp:
return true
case e.Timestamp > o.Timestamp:
return false
default:
return e.ValidationID.Compare(o.ValidationID) == -1
}
}
62 changes: 62 additions & 0 deletions vms/platformvm/state/expiry_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package state

import (
"bytes"
"testing"

"github.com/stretchr/testify/require"
"github.com/thepudds/fzgen/fuzzer"
)

func FuzzExpiryEntryMarshal(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
require := require.New(t)

var entry ExpiryEntry
fz := fuzzer.NewFuzzer(data)
fz.Fill(&entry)

marshalledData := entry.Marshal()

var parsedEntry ExpiryEntry
require.NoError(parsedEntry.Unmarshal(marshalledData))
require.Equal(entry, parsedEntry)
})
}

func FuzzExpiryEntryLessAndMarshalOrdering(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
var (
entry0 ExpiryEntry
entry1 ExpiryEntry
)
fz := fuzzer.NewFuzzer(data)
fz.Fill(&entry0, &entry1)

key0 := entry0.Marshal()
key1 := entry1.Marshal()
require.Equal(
t,
entry0.Less(entry1),
bytes.Compare(key0, key1) == -1,
)
})
}

func FuzzExpiryEntryUnmarshal(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
require := require.New(t)

var entry ExpiryEntry
if err := entry.Unmarshal(data); err != nil {
require.ErrorIs(err, errUnexpectedExpiryEntryLength)
return
}

marshalledData := entry.Marshal()
require.Equal(data, marshalledData)
})
}

0 comments on commit 07f13c6

Please # to comment.