Skip to content

Commit

Permalink
feat!: reserve the last 256 namespaces for protocol use (backport #2257
Browse files Browse the repository at this point in the history
…) (#2261)
  • Loading branch information
rootulp authored Aug 15, 2023
2 parents 906ef24 + 34d7cf0 commit 53b5dbf
Show file tree
Hide file tree
Showing 17 changed files with 274 additions and 115 deletions.
53 changes: 34 additions & 19 deletions pkg/namespace/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,35 +36,50 @@ var (
// NamespaceVersionZeroPrefix is the prefix of a namespace ID for version 0.
NamespaceVersionZeroPrefix = bytes.Repeat([]byte{0}, NamespaceVersionZeroPrefixSize)

// TxNamespace is the namespace reserved for transaction data.
TxNamespace = MustNewV0([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 1})
// TxNamespace is the namespace reserved for ordinary Cosmos SDK transactions.
TxNamespace = primaryReservedNamespace(0x01)

// IntermediateStateRootsNamespace is the namespace reserved for
// intermediate state root data.
IntermediateStateRootsNamespace = MustNewV0([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 2})
IntermediateStateRootsNamespace = primaryReservedNamespace(0x02)

// PayForBlobNamespace is the namespace reserved for PayForBlobs transactions.
PayForBlobNamespace = MustNewV0([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 4})
PayForBlobNamespace = primaryReservedNamespace(0x04)

// ReservedPaddingNamespace is the namespace used for padding after all
// reserved namespaces. In practice this padding is after transactions
// (ordinary and PFBs) but before blobs.
ReservedPaddingNamespace = MustNewV0([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 255})
// PrimaryReservedPaddingNamespace is the namespace used for padding after all
// primary reserved namespaces.
PrimaryReservedPaddingNamespace = primaryReservedNamespace(0xFF)

// MaxReservedNamespace is lexicographically the largest namespace that is
// reserved for protocol use.
MaxReservedNamespace = MustNewV0([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 255})
// MaxPrimaryReservedNamespace is the highest primary reserved namespace.
// Namespaces lower than this are reserved for protocol use.
MaxPrimaryReservedNamespace = primaryReservedNamespace(0xFF)

// MinSecondaryReservedNamespace is the lowest secondary reserved namespace
// reserved for protocol use. Namespaces higher than this are reserved for
// protocol use.
MinSecondaryReservedNamespace = secondaryReservedNamespace(0x00)

// TailPaddingNamespace is the namespace reserved for tail padding. All data
// with this namespace will be ignored.
TailPaddingNamespace = Namespace{
Version: math.MaxUint8,
ID: append(bytes.Repeat([]byte{0xFF}, NamespaceIDSize-1), 0xFE),
}
TailPaddingNamespace = secondaryReservedNamespace(0xFE)

// ParitySharesNamespace is the namespace reserved for erasure coded data.
ParitySharesNamespace = Namespace{
Version: math.MaxUint8,
ID: bytes.Repeat([]byte{0xFF}, NamespaceIDSize),
}
ParitySharesNamespace = secondaryReservedNamespace(0xFF)

// SupportedBlobNamespaceVersions is a list of namespace versions that can be specified by a user for blobs.
SupportedBlobNamespaceVersions = []uint8{NamespaceVersionZero}
)

func primaryReservedNamespace(lastByte byte) Namespace {
return Namespace{
Version: NamespaceVersionZero,
ID: append(bytes.Repeat([]byte{0x00}, NamespaceIDSize-1), lastByte),
}
}

func secondaryReservedNamespace(lastByte byte) Namespace {
return Namespace{
Version: NamespaceVersionMax,
ID: append(bytes.Repeat([]byte{0xFF}, NamespaceIDSize-1), lastByte),
}
}
38 changes: 15 additions & 23 deletions pkg/namespace/namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type Namespace struct {

// New returns a new namespace with the provided version and id.
func New(version uint8, id []byte) (Namespace, error) {
err := validateVersion(version)
err := validateVersionSupported(version)
if err != nil {
return Namespace{}, err
}
Expand Down Expand Up @@ -83,25 +83,8 @@ func (n Namespace) Bytes() []byte {
return append([]byte{n.Version}, n.ID...)
}

// ValidateBlobNamespace returns an error if this namespace is not a valid blob namespace.
func (n Namespace) ValidateBlobNamespace() error {
if n.IsReserved() {
return fmt.Errorf("invalid blob namespace: %v cannot use a reserved namespace ID, want > %v", n.Bytes(), MaxReservedNamespace.Bytes())
}

if n.IsParityShares() {
return fmt.Errorf("invalid blob namespace: %v cannot use parity shares namespace ID", n.Bytes())
}

if n.IsTailPadding() {
return fmt.Errorf("invalid blob namespace: %v cannot use tail padding namespace ID", n.Bytes())
}

return nil
}

// validateVersion returns an error if the version is not supported.
func validateVersion(version uint8) error {
// validateVersionSupported returns an error if the version is not supported.
func validateVersionSupported(version uint8) error {
if version != NamespaceVersionZero && version != NamespaceVersionMax {
return fmt.Errorf("unsupported namespace version %v", version)
}
Expand All @@ -121,8 +104,17 @@ func validateID(version uint8, id []byte) error {
return nil
}

// IsReserved returns true if the namespace is reserved for protocol-use.
func (n Namespace) IsReserved() bool {
return bytes.Compare(n.Bytes(), MaxReservedNamespace.Bytes()) < 1
return n.IsPrimaryReserved() || n.IsSecondaryReserved()
}

func (n Namespace) IsPrimaryReserved() bool {
return n.IsLessOrEqualThan(MaxPrimaryReservedNamespace)
}

func (n Namespace) IsSecondaryReserved() bool {
return n.IsGreaterOrEqualThan(MinSecondaryReservedNamespace)
}

func (n Namespace) IsParityShares() bool {
Expand All @@ -133,8 +125,8 @@ func (n Namespace) IsTailPadding() bool {
return bytes.Equal(n.Bytes(), TailPaddingNamespace.Bytes())
}

func (n Namespace) IsReservedPadding() bool {
return bytes.Equal(n.Bytes(), ReservedPaddingNamespace.Bytes())
func (n Namespace) IsPrimaryReservedPadding() bool {
return bytes.Equal(n.Bytes(), PrimaryReservedPaddingNamespace.Bytes())
}

func (n Namespace) IsTx() bool {
Expand Down
89 changes: 89 additions & 0 deletions pkg/namespace/namespace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package namespace

import (
"bytes"
"math"
"reflect"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -195,3 +197,90 @@ func TestBytes(t *testing.T) {

assert.Equal(t, want, got)
}

func TestLeftPad(t *testing.T) {
tests := []struct {
input []byte
size int
expected []byte
}{
// input smaller than pad size
{[]byte{1, 2, 3}, 10, []byte{0, 0, 0, 0, 0, 0, 0, 1, 2, 3}},
{[]byte{1}, 5, []byte{0, 0, 0, 0, 1}},
{[]byte{1, 2}, 4, []byte{0, 0, 1, 2}},

// input equal to pad size
{[]byte{1, 2, 3}, 3, []byte{1, 2, 3}},
{[]byte{1, 2, 3, 4}, 4, []byte{1, 2, 3, 4}},

// input larger than pad size
{[]byte{1, 2, 3, 4, 5}, 4, []byte{1, 2, 3, 4, 5}},
{[]byte{1, 2, 3, 4, 5, 6, 7}, 3, []byte{1, 2, 3, 4, 5, 6, 7}},

// input size 0
{[]byte{}, 8, []byte{0, 0, 0, 0, 0, 0, 0, 0}},
{[]byte{}, 0, []byte{}},
}

for _, test := range tests {
result := leftPad(test.input, test.size)
assert.True(t, reflect.DeepEqual(result, test.expected))
}
}

func TestIsReserved(t *testing.T) {
type testCase struct {
ns Namespace
want bool
}
testCases := []testCase{
{
ns: MustNewV0(bytes.Repeat([]byte{1}, NamespaceVersionZeroIDSize)),
want: false,
},
{
ns: TxNamespace,
want: true,
},
{
ns: IntermediateStateRootsNamespace,
want: true,
},
{
ns: PayForBlobNamespace,
want: true,
},
{
ns: PrimaryReservedPaddingNamespace,
want: true,
},
{
ns: MaxPrimaryReservedNamespace,
want: true,
},
{
ns: MinSecondaryReservedNamespace,
want: true,
},
{
ns: TailPaddingNamespace,
want: true,
},
{
ns: ParitySharesNamespace,
want: true,
},
{
ns: Namespace{
Version: math.MaxUint8,
ID: append(bytes.Repeat([]byte{0xFF}, NamespaceIDSize-1), 1),
},
want: true,
},
}

for _, tc := range testCases {
got := tc.ns.IsReserved()
assert.Equal(t, tc.want, got)
}
}
21 changes: 17 additions & 4 deletions pkg/namespace/random_blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package namespace

import (
tmrand "github.com/tendermint/tendermint/libs/rand"
"golang.org/x/exp/slices"
)

func RandomBlobNamespaceID() []byte {
Expand All @@ -22,11 +23,9 @@ func RandomBlobNamespaceWithPRG(prg *tmrand.Rand) Namespace {
for {
id := RandomBlobNamespaceIDWithPRG(prg)
namespace := MustNewV0(id)
err := namespace.ValidateBlobNamespace()
if err != nil {
continue
if isBlobNamespace(namespace) {
return namespace
}
return namespace
}
}

Expand All @@ -36,3 +35,17 @@ func RandomBlobNamespaces(rand *tmrand.Rand, count int) (namespaces []Namespace)
}
return namespaces
}

// isBlobNamespace returns an true if this namespace is a valid user-specifiable
// blob namespace.
func isBlobNamespace(ns Namespace) bool {
if ns.IsReserved() {
return false
}

if !slices.Contains(SupportedBlobNamespaceVersions, ns.Version) {
return false
}

return true
}
21 changes: 11 additions & 10 deletions pkg/shares/padding.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import (

// NamespacePaddingShare returns a share that acts as padding. Namespace padding
// shares follow a blob so that the next blob may start at an index that
// conforms to blob share commitment rules. The ns parameter provided should
// be the namespace of the blob that precedes this padding in the data square.
func NamespacePaddingShare(ns appns.Namespace) (Share, error) {
b, err := NewBuilder(ns, appconsts.ShareVersionZero, true).Init()
// conforms to blob share commitment rules. The ns and shareVersion parameters
// provided should be the namespace and shareVersion of the blob that precedes
// this padding in the data square.
func NamespacePaddingShare(ns appns.Namespace, shareVersion uint8) (Share, error) {
b, err := NewBuilder(ns, shareVersion, true).Init()
if err != nil {
return Share{}, err
}
Expand All @@ -32,14 +33,14 @@ func NamespacePaddingShare(ns appns.Namespace) (Share, error) {
}

// NamespacePaddingShares returns n namespace padding shares.
func NamespacePaddingShares(ns appns.Namespace, n int) ([]Share, error) {
func NamespacePaddingShares(ns appns.Namespace, shareVersion uint8, n int) ([]Share, error) {
var err error
if n < 0 {
return nil, errors.New("n must be positive")
}
shares := make([]Share, n)
for i := 0; i < n; i++ {
shares[i], err = NamespacePaddingShare(ns)
shares[i], err = NamespacePaddingShare(ns, shareVersion)
if err != nil {
return shares, err
}
Expand All @@ -52,7 +53,7 @@ func NamespacePaddingShares(ns appns.Namespace, n int) ([]Share, error) {
// first blob can start at an index that conforms to non-interactive default
// rules.
func ReservedPaddingShare() Share {
share, err := NamespacePaddingShare(appns.ReservedPaddingNamespace)
share, err := NamespacePaddingShare(appns.PrimaryReservedPaddingNamespace, appconsts.ShareVersionZero)
if err != nil {
panic(err)
}
Expand All @@ -61,7 +62,7 @@ func ReservedPaddingShare() Share {

// ReservedPaddingShare returns n reserved padding shares.
func ReservedPaddingShares(n int) []Share {
shares, err := NamespacePaddingShares(appns.ReservedPaddingNamespace, n)
shares, err := NamespacePaddingShares(appns.PrimaryReservedPaddingNamespace, appconsts.ShareVersionZero, n)
if err != nil {
panic(err)
}
Expand All @@ -72,7 +73,7 @@ func ReservedPaddingShares(n int) []Share {
// square size. Tail padding shares follow the last blob share in the data
// square.
func TailPaddingShare() Share {
share, err := NamespacePaddingShare(appns.TailPaddingNamespace)
share, err := NamespacePaddingShare(appns.TailPaddingNamespace, appconsts.ShareVersionZero)
if err != nil {
panic(err)
}
Expand All @@ -81,7 +82,7 @@ func TailPaddingShare() Share {

// TailPaddingShares returns n tail padding shares.
func TailPaddingShares(n int) []Share {
shares, err := NamespacePaddingShares(appns.TailPaddingNamespace, n)
shares, err := NamespacePaddingShares(appns.TailPaddingNamespace, appconsts.ShareVersionZero, n)
if err != nil {
panic(err)
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/shares/padding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ var nsOnePadding, _ = zeroPadIfNecessary(

var reservedPadding, _ = zeroPadIfNecessary(
append(
appns.ReservedPaddingNamespace.Bytes(),
appns.PrimaryReservedPaddingNamespace.Bytes(),
[]byte{
1, // info byte
0, 0, 0, 0, // sequence len
Expand All @@ -40,13 +40,13 @@ var tailPadding, _ = zeroPadIfNecessary(
), appconsts.ShareSize)

func TestNamespacePaddingShare(t *testing.T) {
got, err := NamespacePaddingShare(ns1)
got, err := NamespacePaddingShare(ns1, appconsts.ShareVersionZero)
assert.NoError(t, err)
assert.Equal(t, nsOnePadding, got.ToBytes())
}

func TestNamespacePaddingShares(t *testing.T) {
shares, err := NamespacePaddingShares(ns1, 2)
shares, err := NamespacePaddingShares(ns1, appconsts.ShareVersionZero, 2)
assert.NoError(t, err)
for _, share := range shares {
assert.Equal(t, nsOnePadding, share.ToBytes())
Expand Down
6 changes: 3 additions & 3 deletions pkg/shares/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func TestParseShares(t *testing.T) {
// because it takes more than one share to store a sequence of 1000 bytes
tooLargeSequenceLen := generateRawShare(t, ns1, true, uint32(1000))

ns1Padding, err := NamespacePaddingShare(ns1)
ns1Padding, err := NamespacePaddingShare(ns1, appconsts.ShareVersionZero)
require.NoError(t, err)

type testCase struct {
Expand Down Expand Up @@ -162,11 +162,11 @@ func TestParseShares(t *testing.T) {
ignorePadding: false,
want: []ShareSequence{
{
Namespace: appns.ReservedPaddingNamespace,
Namespace: appns.PrimaryReservedPaddingNamespace,
Shares: []Share{ReservedPaddingShare()},
},
{
Namespace: appns.ReservedPaddingNamespace,
Namespace: appns.PrimaryReservedPaddingNamespace,
Shares: []Share{ReservedPaddingShare()},
},
},
Expand Down
Loading

0 comments on commit 53b5dbf

Please sign in to comment.