Skip to content

Commit 5be1546

Browse files
authored
Merge pull request #434 from onflow/fxamacker/add-func-to-check-cbor-tag-numbers
Add functions to check availablility of CBOR tag numbers
2 parents bb0f1aa + 89e51a3 commit 5be1546

File tree

2 files changed

+96
-1
lines changed

2 files changed

+96
-1
lines changed

storable.go

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,28 @@ func hasPointer(storable Storable) bool {
7373
const (
7474
// WARNING: tag numbers defined in here in github.com/onflow/atree
7575
// MUST not overlap with tag numbers used by Cadence internal value encoding.
76-
// As of Oct. 2, 2023, Cadence uses tag numbers from 128 to 224.
76+
// As of Aug. 14, 2024, Cadence uses tag numbers from 128 to 230.
7777
// See runtime/interpreter/encode.go at github.com/onflow/cadence.
7878

79+
// Atree reserves CBOR tag numbers [240, 255] for internal use.
80+
// Applications must use non-overlapping CBOR tag numbers to encode
81+
// elements managed by atree containers.
82+
minInternalCBORTagNumber = 240
83+
maxInternalCBORTagNumber = 255
84+
85+
// Reserved CBOR tag numbers for atree internal use.
86+
87+
// Replace _ when new tag number is needed (use higher tag numbers first).
88+
// Atree will use higher tag numbers first because Cadence will use lower tag numbers first.
89+
// This approach allows more flexibility in case we need to revisit ranges used by Atree and Cadence.
90+
91+
_ = 240
92+
_ = 241
93+
_ = 242
94+
_ = 243
95+
_ = 244
96+
_ = 245
97+
7998
CBORTagTypeInfoRef = 246
8099

81100
CBORTagInlinedArrayExtraData = 247
@@ -92,6 +111,22 @@ const (
92111
CBORTagSlabID = 255
93112
)
94113

114+
// IsCBORTagNumberRangeAvailable returns true if the specified range is not reserved for internal use by atree.
115+
// Applications must only use available (unreserved) CBOR tag numbers to encode elements in atree managed containers.
116+
func IsCBORTagNumberRangeAvailable(minTagNum, maxTagNum uint64) (bool, error) {
117+
if minTagNum > maxTagNum {
118+
return false, NewUserError(fmt.Errorf("min CBOR tag number %d must be <= max CBOR tag number %d", minTagNum, maxTagNum))
119+
}
120+
121+
return maxTagNum < minInternalCBORTagNumber || minTagNum > maxInternalCBORTagNumber, nil
122+
}
123+
124+
// ReservedCBORTagNumberRange returns minTagNum and maxTagNum of the range of CBOR tag numbers
125+
// reserved for internal use by atree.
126+
func ReservedCBORTagNumberRange() (minTagNum, maxTagNum uint64) {
127+
return minInternalCBORTagNumber, maxInternalCBORTagNumber
128+
}
129+
95130
type SlabIDStorable SlabID
96131

97132
var _ ContainerStorable = SlabIDStorable{}

storable_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ import (
2222
"encoding/binary"
2323
"fmt"
2424
"math"
25+
"testing"
2526

2627
"github.com/fxamacker/cbor/v2"
28+
"github.com/stretchr/testify/require"
2729
)
2830

2931
// This file contains value implementations for testing purposes
@@ -37,6 +39,64 @@ const (
3739
cborTagHashableMap = 166
3840
)
3941

42+
func TestIsCBORTagNumberRangeAvailable(t *testing.T) {
43+
t.Run("error", func(t *testing.T) {
44+
_, err := IsCBORTagNumberRangeAvailable(maxInternalCBORTagNumber, minInternalCBORTagNumber)
45+
var userError *UserError
46+
require.ErrorAs(t, err, &userError)
47+
})
48+
49+
t.Run("identical", func(t *testing.T) {
50+
available, err := IsCBORTagNumberRangeAvailable(minInternalCBORTagNumber, maxInternalCBORTagNumber)
51+
require.NoError(t, err)
52+
require.False(t, available)
53+
})
54+
55+
t.Run("subrange", func(t *testing.T) {
56+
available, err := IsCBORTagNumberRangeAvailable(minInternalCBORTagNumber, maxInternalCBORTagNumber-1)
57+
require.NoError(t, err)
58+
require.False(t, available)
59+
60+
available, err = IsCBORTagNumberRangeAvailable(minInternalCBORTagNumber+1, maxInternalCBORTagNumber)
61+
require.NoError(t, err)
62+
require.False(t, available)
63+
})
64+
65+
t.Run("partial overlap", func(t *testing.T) {
66+
available, err := IsCBORTagNumberRangeAvailable(minInternalCBORTagNumber-1, maxInternalCBORTagNumber-1)
67+
require.NoError(t, err)
68+
require.False(t, available)
69+
70+
available, err = IsCBORTagNumberRangeAvailable(minInternalCBORTagNumber+1, maxInternalCBORTagNumber+1)
71+
require.NoError(t, err)
72+
require.False(t, available)
73+
})
74+
75+
t.Run("non-overlap", func(t *testing.T) {
76+
available, err := IsCBORTagNumberRangeAvailable(minInternalCBORTagNumber-10, minInternalCBORTagNumber-1)
77+
require.NoError(t, err)
78+
require.True(t, available)
79+
80+
available, err = IsCBORTagNumberRangeAvailable(minInternalCBORTagNumber-1, minInternalCBORTagNumber-1)
81+
require.NoError(t, err)
82+
require.True(t, available)
83+
84+
available, err = IsCBORTagNumberRangeAvailable(maxInternalCBORTagNumber+1, maxInternalCBORTagNumber+10)
85+
require.NoError(t, err)
86+
require.True(t, available)
87+
88+
available, err = IsCBORTagNumberRangeAvailable(maxInternalCBORTagNumber+10, maxInternalCBORTagNumber+10)
89+
require.NoError(t, err)
90+
require.True(t, available)
91+
})
92+
}
93+
94+
func TestReservedCBORTagNumberRange(t *testing.T) {
95+
minTagNum, maxTagNum := ReservedCBORTagNumberRange()
96+
require.Equal(t, uint64(minInternalCBORTagNumber), minTagNum)
97+
require.Equal(t, uint64(maxInternalCBORTagNumber), maxTagNum)
98+
}
99+
40100
type HashableValue interface {
41101
Value
42102
HashInput(scratch []byte) ([]byte, error)

0 commit comments

Comments
 (0)