Skip to content

Commit

Permalink
Merge pull request #434 from onflow/fxamacker/add-func-to-check-cbor-…
Browse files Browse the repository at this point in the history
…tag-numbers

Add functions to check availablility of CBOR tag numbers
  • Loading branch information
fxamacker authored Aug 15, 2024
2 parents bb0f1aa + 89e51a3 commit 5be1546
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 1 deletion.
37 changes: 36 additions & 1 deletion storable.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,28 @@ func hasPointer(storable Storable) bool {
const (
// WARNING: tag numbers defined in here in github.com/onflow/atree
// MUST not overlap with tag numbers used by Cadence internal value encoding.
// As of Oct. 2, 2023, Cadence uses tag numbers from 128 to 224.
// As of Aug. 14, 2024, Cadence uses tag numbers from 128 to 230.
// See runtime/interpreter/encode.go at github.com/onflow/cadence.

// Atree reserves CBOR tag numbers [240, 255] for internal use.
// Applications must use non-overlapping CBOR tag numbers to encode
// elements managed by atree containers.
minInternalCBORTagNumber = 240
maxInternalCBORTagNumber = 255

// Reserved CBOR tag numbers for atree internal use.

// Replace _ when new tag number is needed (use higher tag numbers first).
// Atree will use higher tag numbers first because Cadence will use lower tag numbers first.
// This approach allows more flexibility in case we need to revisit ranges used by Atree and Cadence.

_ = 240
_ = 241
_ = 242
_ = 243
_ = 244
_ = 245

CBORTagTypeInfoRef = 246

CBORTagInlinedArrayExtraData = 247
Expand All @@ -92,6 +111,22 @@ const (
CBORTagSlabID = 255
)

// IsCBORTagNumberRangeAvailable returns true if the specified range is not reserved for internal use by atree.
// Applications must only use available (unreserved) CBOR tag numbers to encode elements in atree managed containers.
func IsCBORTagNumberRangeAvailable(minTagNum, maxTagNum uint64) (bool, error) {
if minTagNum > maxTagNum {
return false, NewUserError(fmt.Errorf("min CBOR tag number %d must be <= max CBOR tag number %d", minTagNum, maxTagNum))
}

return maxTagNum < minInternalCBORTagNumber || minTagNum > maxInternalCBORTagNumber, nil
}

// ReservedCBORTagNumberRange returns minTagNum and maxTagNum of the range of CBOR tag numbers
// reserved for internal use by atree.
func ReservedCBORTagNumberRange() (minTagNum, maxTagNum uint64) {
return minInternalCBORTagNumber, maxInternalCBORTagNumber
}

type SlabIDStorable SlabID

var _ ContainerStorable = SlabIDStorable{}
Expand Down
60 changes: 60 additions & 0 deletions storable_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ import (
"encoding/binary"
"fmt"
"math"
"testing"

"github.com/fxamacker/cbor/v2"
"github.com/stretchr/testify/require"
)

// This file contains value implementations for testing purposes
Expand All @@ -37,6 +39,64 @@ const (
cborTagHashableMap = 166
)

func TestIsCBORTagNumberRangeAvailable(t *testing.T) {
t.Run("error", func(t *testing.T) {
_, err := IsCBORTagNumberRangeAvailable(maxInternalCBORTagNumber, minInternalCBORTagNumber)
var userError *UserError
require.ErrorAs(t, err, &userError)
})

t.Run("identical", func(t *testing.T) {
available, err := IsCBORTagNumberRangeAvailable(minInternalCBORTagNumber, maxInternalCBORTagNumber)
require.NoError(t, err)
require.False(t, available)
})

t.Run("subrange", func(t *testing.T) {
available, err := IsCBORTagNumberRangeAvailable(minInternalCBORTagNumber, maxInternalCBORTagNumber-1)
require.NoError(t, err)
require.False(t, available)

available, err = IsCBORTagNumberRangeAvailable(minInternalCBORTagNumber+1, maxInternalCBORTagNumber)
require.NoError(t, err)
require.False(t, available)
})

t.Run("partial overlap", func(t *testing.T) {
available, err := IsCBORTagNumberRangeAvailable(minInternalCBORTagNumber-1, maxInternalCBORTagNumber-1)
require.NoError(t, err)
require.False(t, available)

available, err = IsCBORTagNumberRangeAvailable(minInternalCBORTagNumber+1, maxInternalCBORTagNumber+1)
require.NoError(t, err)
require.False(t, available)
})

t.Run("non-overlap", func(t *testing.T) {
available, err := IsCBORTagNumberRangeAvailable(minInternalCBORTagNumber-10, minInternalCBORTagNumber-1)
require.NoError(t, err)
require.True(t, available)

available, err = IsCBORTagNumberRangeAvailable(minInternalCBORTagNumber-1, minInternalCBORTagNumber-1)
require.NoError(t, err)
require.True(t, available)

available, err = IsCBORTagNumberRangeAvailable(maxInternalCBORTagNumber+1, maxInternalCBORTagNumber+10)
require.NoError(t, err)
require.True(t, available)

available, err = IsCBORTagNumberRangeAvailable(maxInternalCBORTagNumber+10, maxInternalCBORTagNumber+10)
require.NoError(t, err)
require.True(t, available)
})
}

func TestReservedCBORTagNumberRange(t *testing.T) {
minTagNum, maxTagNum := ReservedCBORTagNumberRange()
require.Equal(t, uint64(minInternalCBORTagNumber), minTagNum)
require.Equal(t, uint64(maxInternalCBORTagNumber), maxTagNum)
}

type HashableValue interface {
Value
HashInput(scratch []byte) ([]byte, error)
Expand Down

0 comments on commit 5be1546

Please sign in to comment.