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

Add InfoReservedByte to compact and sparse shares #691

Merged
merged 40 commits into from
Sep 16, 2022
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
0ea94fe
Add ParseInfoReservedByte
rootulp Aug 31, 2022
29ab27e
Update split contiguous shares to account for info byte
rootulp Aug 31, 2022
243aa90
Merge branch 'main' into rp/universal-share-encoding
rootulp Sep 7, 2022
fd62420
Fix TestContigShareWriter test
rootulp Sep 7, 2022
522d6c2
Improve names in parseDataLength
rootulp Sep 7, 2022
1592824
Remove unused function parseDataLength
rootulp Sep 7, 2022
6705967
Merge branch 'main' into rp/universal-share-encoding
rootulp Sep 7, 2022
88fbe5f
revert local celestia-core replace
rootulp Sep 8, 2022
89ce0f6
docs: improve docs
rootulp Sep 13, 2022
c77951a
docs: fix doc comments
rootulp Sep 13, 2022
23ce09c
feat: move share consts from celestia-core
rootulp Sep 14, 2022
f616dee
feat: new consts for universal share encoding
rootulp Sep 14, 2022
2936923
Merge branch 'main' into rp/universal-share-constants
rootulp Sep 15, 2022
e2d3858
remove universal share constants
rootulp Sep 15, 2022
70dbf7c
Merge branch 'rp/rename-constants' into rp/universal-share-encoding
rootulp Sep 15, 2022
73dc2f5
fix: pass Test_parseSparseShares
rootulp Sep 15, 2022
75e2b9d
Rename appconsts to use `Compact` and `Sparse` terminology (#710)
rootulp Sep 15, 2022
0facaf6
Revert "Rename appconsts to use `Compact` and `Sparse` terminology (#…
rootulp Sep 15, 2022
9e2e504
Merge branch 'main' into rp/universal-share-encoding
rootulp Sep 15, 2022
027878c
revert change to exactMsgShareSize
rootulp Sep 15, 2022
efd1b8f
fix: add info byte to namespace padded shares
rootulp Sep 15, 2022
feee270
pass first two test cases of TestMerge
rootulp Sep 15, 2022
8f2d0f0
pass: TestMerge
rootulp Sep 15, 2022
f87d4ec
pass: TestTxInclusion by updating proof to use share pkg
rootulp Sep 15, 2022
ae55527
pass: TestTxSharePosition
rootulp Sep 15, 2022
1c32037
pass: Test_genRowShares
rootulp Sep 15, 2022
e0a2d3e
pass: Test_genOrigRowShares
rootulp Sep 15, 2022
502e8dd
Merge branch 'main' into rp/universal-share-encoding
rootulp Sep 15, 2022
501249c
use share package over types
rootulp Sep 15, 2022
063065a
pass: TestPrepareMessagesWithReservedNamespaces
rootulp Sep 16, 2022
ae2fc21
pass: TestCreateCommitment by updating expected bytes
rootulp Sep 16, 2022
e11055e
extract doc comments to separate PR
rootulp Sep 16, 2022
175469e
extract separate PR for specifying square size in shares_test.go
rootulp Sep 16, 2022
db0351f
test: add TestMsgShareContainsInfoByte
rootulp Sep 16, 2022
159ecc7
test: add TestContiguousMsgShareContainsInfoByte
rootulp Sep 16, 2022
2c6a761
test: add compact share tests
rootulp Sep 16, 2022
0650502
Merge branch 'main' into rp/universal-share-encoding
rootulp Sep 16, 2022
5c4fef7
throw error if isMessageStart != isNewMessage
rootulp Sep 16, 2022
297dd00
Merge branch 'main' into rp/universal-share-encoding
rootulp Sep 16, 2022
b067e48
throw error if compact share does not start with a share with isMessa…
rootulp Sep 16, 2022
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
2 changes: 1 addition & 1 deletion app/split_shares.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ func newShareSplitter(txConf client.TxConfig, squareSize uint64, data *core.Data
panic(err)
}

sqwr.txWriter = shares.NewCompactShareSplitter(appconsts.TxNamespaceID)
sqwr.txWriter = shares.NewCompactShareSplitter(appconsts.TxNamespaceID, appconsts.ShareVersion)
sqwr.msgWriter = shares.NewSparseShareSplitter()

return &sqwr
Expand Down
12 changes: 10 additions & 2 deletions pkg/appconsts/appconsts.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ const (
// NamespaceSize is the namespace size in bytes.
NamespaceSize = 8

// ShareInfoBytes is the number of bytes reserved for information. The info
// byte contains the share version and a start idicator.
ShareInfoBytes = 1

// ShareVersion is the current version of the share format
ShareVersion = uint8(0)

// See https://github.com/celestiaorg/celestia-app/pull/660#discussion_r958603307
// for the motivation behind `CompactShare` and `SparseShare` terminology.

Expand All @@ -26,10 +33,11 @@ const (

// CompactShareContentSize is the number of bytes usable for data in a compact
// (i.e. transactions, ISRs, evidence) share.
CompactShareContentSize = ShareSize - NamespaceSize - CompactShareReservedBytes
CompactShareContentSize = ShareSize - NamespaceSize - ShareInfoBytes - CompactShareReservedBytes

// SparseShareContentSize is the number of bytes usable for data in a sparse (i.e.
// message) share.
SparseShareContentSize = ShareSize - NamespaceSize
SparseShareContentSize = ShareSize - NamespaceSize - ShareInfoBytes

// MaxSquareSize is the maximum number of
// rows/columns of the original data shares in square layout.
Expand Down
2 changes: 1 addition & 1 deletion pkg/prove/proof_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ func joinByteSlices(s ...[]byte) string {
out := make([]string, len(s))
for i, sl := range s {
sl, _, _ := shares.ParseDelimiter(sl)
out[i] = string(sl[appconsts.NamespaceSize:])
out[i] = string(sl[appconsts.NamespaceSize+appconsts.ShareInfoBytes:])
}
return strings.Join(out, "")
}
Expand Down
42 changes: 41 additions & 1 deletion pkg/shares/compact_shares_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
func TestCompactShareWriter(t *testing.T) {
// note that this test is mainly for debugging purposes, the main round trip
// tests occur in TestMerge and Test_processCompactShares
w := NewCompactShareSplitter(appconsts.TxNamespaceID)
w := NewCompactShareSplitter(appconsts.TxNamespaceID, appconsts.ShareVersion)
txs := generateRandomCompactShares(33, 200)
for _, tx := range txs {
rawTx, _ := MarshalDelimitedTx(tx)
Expand Down Expand Up @@ -121,3 +121,43 @@ func Test_processCompactShares(t *testing.T) {
})
}
}

func TestCompactShareContainsInfoByte(t *testing.T) {
css := NewCompactShareSplitter(appconsts.TxNamespaceID, appconsts.ShareVersion)
txs := generateRandomCompactShares(1, 100)

for _, tx := range txs {
css.WriteTx(tx)
}

shares := css.Export().RawShares()
assert.Condition(t, func() bool { return len(shares) == 1 })

infoByte := shares[0][appconsts.NamespaceSize : appconsts.NamespaceSize+appconsts.ShareInfoBytes][0]

isMessageStart := true
want, err := NewInfoReservedByte(appconsts.ShareVersion, isMessageStart)

require.NoError(t, err)
assert.Equal(t, byte(want), infoByte)
}

func TestContiguousCompactShareContainsInfoByte(t *testing.T) {
css := NewCompactShareSplitter(appconsts.TxNamespaceID, appconsts.ShareVersion)
txs := generateRandomCompactShares(1, 1000)

for _, tx := range txs {
css.WriteTx(tx)
}

shares := css.Export().RawShares()
assert.Condition(t, func() bool { return len(shares) > 1 })

infoByte := shares[1][appconsts.NamespaceSize : appconsts.NamespaceSize+appconsts.ShareInfoBytes][0]

isMessageStart := false
want, err := NewInfoReservedByte(appconsts.ShareVersion, isMessageStart)

require.NoError(t, err)
assert.Equal(t, byte(want), infoByte)
}
Comment on lines +126 to +164
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[optional, prefer different PR due to timing]
I think this should be done in a different PR (if at all) since we want to optimize to merge into #692 asap

but we might refactor these tests into a table drive test that tests evidence and the rest of the shares that are exported. another super minor [nit], would be to use a formula to calculate the size of the random compact shares so that we can incorporate appconsts. (we don't do this other places, but should so that can probably be a separate PR too)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you please elaborate on

would be to use a formula to calculate the size of the random compact shares so that we can incorporate appconsts

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah sure,

instead of just using the number 100 or 1000, we could say appconsts.TxShareSize / 2 or appconsts.TxShareSize * 4

6 changes: 3 additions & 3 deletions pkg/shares/parse_compact_shares.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (

// parseCompactShares takes raw shares and extracts out transactions,
// intermediate state roots, or evidence. The returned [][]byte do not have
// namespaces or length delimiters and are ready to be unmarshalled
// namespaces, info bytes, or length delimiters and are ready to be unmarshalled
func parseCompactShares(shares [][]byte) (txs [][]byte, err error) {
if len(shares) == 0 {
return nil, nil
Expand Down Expand Up @@ -37,7 +37,7 @@ func (ss *shareStack) resolve() ([][]byte, error) {
if len(ss.shares) == 0 {
return nil, nil
}
err := ss.peel(ss.shares[0][appconsts.NamespaceSize+appconsts.CompactShareReservedBytes:], true)
err := ss.peel(ss.shares[0][appconsts.NamespaceSize+appconsts.ShareInfoBytes+appconsts.CompactShareReservedBytes:], true)
return ss.data, err
}

Expand Down Expand Up @@ -70,7 +70,7 @@ func (ss *shareStack) peel(share []byte, delimited bool) (err error) {
// add the next share to the current share to continue merging if possible
if len(ss.shares) > ss.cursor+1 {
ss.cursor++
share := append(share, ss.shares[ss.cursor][appconsts.NamespaceSize+appconsts.CompactShareReservedBytes:]...)
share := append(share, ss.shares[ss.cursor][appconsts.NamespaceSize+appconsts.ShareInfoBytes+appconsts.CompactShareReservedBytes:]...)
return ss.peel(share, false)
}
// collect any remaining data
Expand Down
10 changes: 5 additions & 5 deletions pkg/shares/parse_sparse_shares.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ func parseSparseShares(shares [][]byte) ([]coretypes.Message, error) {
dataLen = len(currentMsg.Data) + appconsts.SparseShareContentSize
switch {
case isNewMessage:
nextMsgChunk, nextMsgLen, err := ParseDelimiter(shares[i][appconsts.NamespaceSize:])
nextMsgChunk, nextMsgLen, err := ParseDelimiter(shares[i][appconsts.NamespaceSize+appconsts.ShareInfoBytes:])
if err != nil {
return nil, err
}
// the current share is namespaced padding so we ignore it
if bytes.Equal(shares[i][appconsts.NamespaceSize:], appconsts.NameSpacedPaddedShareBytes) {
if bytes.Equal(shares[i][appconsts.NamespaceSize+appconsts.ShareInfoBytes:], appconsts.NameSpacedPaddedShareBytes) {
continue
}
currentMsgLen = int(nextMsgLen)
Expand All @@ -58,12 +58,12 @@ func parseSparseShares(shares [][]byte) ([]coretypes.Message, error) {
isNewMessage = false
// this entire share contains a chunk of message that we need to save
case currentMsgLen > dataLen:
currentMsg.Data = append(currentMsg.Data, shares[i][appconsts.NamespaceSize:]...)
currentMsg.Data = append(currentMsg.Data, shares[i][appconsts.NamespaceSize+appconsts.ShareInfoBytes:]...)
// this share contains the last chunk of data needed to complete the
// message
case currentMsgLen <= dataLen:
remaining := currentMsgLen - len(currentMsg.Data) + appconsts.NamespaceSize
currentMsg.Data = append(currentMsg.Data, shares[i][appconsts.NamespaceSize:remaining]...)
remaining := currentMsgLen - len(currentMsg.Data) + appconsts.NamespaceSize + appconsts.ShareInfoBytes
currentMsg.Data = append(currentMsg.Data, shares[i][appconsts.NamespaceSize+appconsts.ShareInfoBytes:remaining]...)
saveMessage()
}
}
Expand Down
7 changes: 3 additions & 4 deletions pkg/shares/share_splitting.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,18 +86,17 @@ func ExtractShareIndexes(txs coretypes.Txs) []uint32 {
}

func SplitTxs(txs coretypes.Txs) [][]byte {
writer := NewCompactShareSplitter(appconsts.TxNamespaceID)
writer := NewCompactShareSplitter(appconsts.TxNamespaceID, appconsts.ShareVersion)
for _, tx := range txs {
writer.WriteTx(tx)
}
return writer.Export().RawShares()
}

func SplitEvidence(evd coretypes.EvidenceList) ([][]byte, error) {
writer := NewCompactShareSplitter(appconsts.EvidenceNamespaceID)
var err error
writer := NewCompactShareSplitter(appconsts.EvidenceNamespaceID, appconsts.ShareVersion)
for _, ev := range evd {
err = writer.WriteEvidence(ev)
err := writer.WriteEvidence(ev)
if err != nil {
return nil, err
}
Expand Down
34 changes: 34 additions & 0 deletions pkg/shares/sparse_shares_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,37 @@ func TestParsePaddedMsg(t *testing.T) {
require.NoError(t, err)
require.Equal(t, msgs.MessagesList, pmsgs)
}

func TestMsgShareContainsInfoByte(t *testing.T) {
sss := NewSparseShareSplitter()
smallMsg := generateRandomMessage(100)
sss.Write(smallMsg)

shares := sss.Export().RawShares()

got := shares[0][appconsts.NamespaceSize : appconsts.NamespaceSize+appconsts.ShareInfoBytes][0]

isMessageStart := true
want, err := NewInfoReservedByte(appconsts.ShareVersion, isMessageStart)

require.NoError(t, err)
assert.Equal(t, byte(want), got)
}

func TestContiguousMsgShareContainsInfoByte(t *testing.T) {
sss := NewSparseShareSplitter()
longMsg := generateRandomMessage(1000)
sss.Write(longMsg)

shares := sss.Export().RawShares()

// we expect longMsg to occupy more than one share
assert.Condition(t, func() bool { return len(shares) > 1 })
got := shares[1][appconsts.NamespaceSize : appconsts.NamespaceSize+appconsts.ShareInfoBytes][0]

isMessageStart := false
want, err := NewInfoReservedByte(appconsts.ShareVersion, isMessageStart)

require.NoError(t, err)
assert.Equal(t, byte(want), got)
}
40 changes: 30 additions & 10 deletions pkg/shares/split_compact_shares.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,19 @@ type CompactShareSplitter struct {
shares []NamespacedShare
pendingShare NamespacedShare
namespace namespace.ID
version uint8
}

// NewCompactShareSplitter returns a CompactShareSplitter using the provided
// namespace.
func NewCompactShareSplitter(ns namespace.ID) *CompactShareSplitter {
func NewCompactShareSplitter(ns namespace.ID, version uint8) *CompactShareSplitter {
pendingShare := NamespacedShare{ID: ns, Share: make([]byte, 0, appconsts.ShareSize)}
infoByte, err := NewInfoReservedByte(version, true)
if err != nil {
panic(err)
}
pendingShare.Share = append(pendingShare.Share, ns...)
pendingShare.Share = append(pendingShare.Share, byte(infoByte))
return &CompactShareSplitter{pendingShare: pendingShare, namespace: ns}
}

Expand Down Expand Up @@ -52,7 +58,7 @@ func (css *CompactShareSplitter) WriteEvidence(evd coretypes.Evidence) error {
func (css *CompactShareSplitter) WriteBytes(rawData []byte) {
// if this is the first time writing to a pending share, we must add the
// reserved bytes
if len(css.pendingShare.Share) == appconsts.NamespaceSize {
if len(css.pendingShare.Share) == appconsts.NamespaceSize+appconsts.ShareInfoBytes {
css.pendingShare.Share = append(css.pendingShare.Share, 0)
}

Expand All @@ -79,7 +85,7 @@ func (css *CompactShareSplitter) WriteBytes(rawData []byte) {
txCursor = len(rawData)

// add the share reserved bytes to the new pending share
pendingCursor := len(rawData) + appconsts.NamespaceSize + appconsts.CompactShareReservedBytes
pendingCursor := len(rawData) + appconsts.NamespaceSize + appconsts.ShareInfoBytes + appconsts.CompactShareReservedBytes
var reservedByte byte
if pendingCursor >= appconsts.ShareSize {
// the share reserve byte is zero when some compactly written
Expand All @@ -106,6 +112,11 @@ func (css *CompactShareSplitter) stackPending() {
css.shares = append(css.shares, css.pendingShare)
newPendingShare := make([]byte, 0, appconsts.ShareSize)
newPendingShare = append(newPendingShare, css.namespace...)
infoByte, err := NewInfoReservedByte(css.version, false)
if err != nil {
panic(err)
}
newPendingShare = append(newPendingShare, byte(infoByte))
css.pendingShare = NamespacedShare{
Share: newPendingShare,
ID: css.namespace,
Expand All @@ -115,7 +126,7 @@ func (css *CompactShareSplitter) stackPending() {
// Export finalizes and returns the underlying compact shares.
func (css *CompactShareSplitter) Export() NamespacedShares {
// add the pending share to the current shares before returning
if len(css.pendingShare.Share) > appconsts.NamespaceSize {
if len(css.pendingShare.Share) > appconsts.NamespaceSize+appconsts.ShareInfoBytes {
css.pendingShare.Share = zeroPadIfNecessary(css.pendingShare.Share, appconsts.ShareSize)
css.shares = append(css.shares, css.pendingShare)
}
Expand All @@ -131,7 +142,7 @@ func (css *CompactShareSplitter) Export() NamespacedShares {
// confusion for light clients parsing these shares, as the rest of the
// data after transaction is padding. See
// https://github.com/celestiaorg/celestia-specs/blob/master/src/specs/data_structures.md#share
rawLastShare[appconsts.NamespaceSize+i] = byte(0)
rawLastShare[appconsts.NamespaceSize+appconsts.ShareInfoBytes+i] = byte(0)
}

newLastShare := NamespacedShare{
Expand All @@ -144,18 +155,22 @@ func (css *CompactShareSplitter) Export() NamespacedShares {

// Count returns the current number of shares that will be made if exporting.
func (css *CompactShareSplitter) Count() (count, availableBytes int) {
if len(css.pendingShare.Share) > appconsts.NamespaceSize {
if len(css.pendingShare.Share) > appconsts.NamespaceSize+appconsts.ShareInfoBytes {
return len(css.shares), 0
}
availableBytes = appconsts.CompactShareContentSize - (len(css.pendingShare.Share) - appconsts.NamespaceSize)
// this doesn't account for the size of the reserved byte
availableBytes = appconsts.CompactShareContentSize - (len(css.pendingShare.Share) - appconsts.NamespaceSize - appconsts.ShareInfoBytes)
return len(css.shares), availableBytes
}

var tailPaddingInfo, _ = NewInfoReservedByte(appconsts.ShareVersion, false)

// tail is filler for all tail padded shares
// it is allocated once and used everywhere
var tailPaddingShare = append(
var tailPaddingShare = append(append(
append(make([]byte, 0, appconsts.ShareSize), appconsts.TailPaddingNamespaceID...),
bytes.Repeat([]byte{0}, appconsts.ShareSize-appconsts.NamespaceSize)...,
byte(tailPaddingInfo)),
bytes.Repeat([]byte{0}, appconsts.ShareSize-appconsts.NamespaceSize-appconsts.ShareInfoBytes)...,
)

func TailPaddingShares(n int) NamespacedShares {
Expand All @@ -170,11 +185,16 @@ func TailPaddingShares(n int) NamespacedShares {
}

func namespacedPaddedShares(ns []byte, count int) []NamespacedShare {
infoByte, err := NewInfoReservedByte(appconsts.ShareVersion, true)
if err != nil {
panic(err)
}
shares := make([]NamespacedShare, count)
for i := 0; i < count; i++ {
shares[i] = NamespacedShare{
Share: append(append(
Share: append(append(append(
make([]byte, 0, appconsts.ShareSize), ns...),
byte(infoByte)),
make([]byte, appconsts.SparseShareContentSize)...),
ID: ns,
}
Expand Down
23 changes: 19 additions & 4 deletions pkg/shares/split_sparse_shares.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,14 @@ func (sss *SparseShareSplitter) Count() int {
// Used for messages.
func AppendToShares(shares []NamespacedShare, nid namespace.ID, rawData []byte) []NamespacedShare {
if len(rawData) <= appconsts.SparseShareContentSize {
rawShare := append(append(
make([]byte, 0, len(nid)+len(rawData)),
infoByte, err := NewInfoReservedByte(appconsts.ShareVersion, true)
if err != nil {
panic(err)
}
rawShare := append(append(append(
make([]byte, 0, appconsts.ShareSize),
nid...),
byte(infoByte)),
rawData...,
)
paddedShare := zeroPadIfNecessary(rawShare, appconsts.ShareSize)
Expand All @@ -111,18 +116,28 @@ func AppendToShares(shares []NamespacedShare, nid namespace.ID, rawData []byte)
// namespaced shares
func splitMessage(rawData []byte, nid namespace.ID) NamespacedShares {
shares := make([]NamespacedShare, 0)
firstRawShare := append(append(
infoByte, err := NewInfoReservedByte(appconsts.ShareVersion, true)
if err != nil {
panic(err)
}
firstRawShare := append(append(append(
make([]byte, 0, appconsts.ShareSize),
nid...),
byte(infoByte)),
rawData[:appconsts.SparseShareContentSize]...,
)
shares = append(shares, NamespacedShare{firstRawShare, nid})
rawData = rawData[appconsts.SparseShareContentSize:]
for len(rawData) > 0 {
shareSizeOrLen := min(appconsts.SparseShareContentSize, len(rawData))
rawShare := append(append(
infoByte, err := NewInfoReservedByte(appconsts.ShareVersion, false)
if err != nil {
panic(err)
}
rawShare := append(append(append(
make([]byte, 0, appconsts.ShareSize),
nid...),
byte(infoByte)),
rawData[:shareSizeOrLen]...,
)
paddedShare := zeroPadIfNecessary(rawShare, appconsts.ShareSize)
Expand Down
2 changes: 1 addition & 1 deletion x/payment/types/payfordata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ func TestCreateCommitment(t *testing.T) {
squareSize: 4,
namespace: bytes.Repeat([]byte{0xFF}, 8),
message: bytes.Repeat([]byte{0xFF}, 11*ShareSize),
expected: []byte{0xe3, 0xaa, 0xdb, 0x1c, 0x2e, 0xc3, 0x77, 0x4a, 0x46, 0xb6, 0xb2, 0xf0, 0x4f, 0x6b, 0x3d, 0x4f, 0x17, 0x99, 0x82, 0x6b, 0xd6, 0xd4, 0x40, 0x5e, 0xbe, 0x1b, 0x1f, 0x29, 0x5a, 0x53, 0x3f, 0xbb},
expected: []byte{0x44, 0x7e, 0xa2, 0xf4, 0xee, 0xb, 0xad, 0x9d, 0x7f, 0xfb, 0x5e, 0x9e, 0xc6, 0xd4, 0xf6, 0x70, 0x4f, 0x36, 0x83, 0x1a, 0x58, 0xe, 0x13, 0xd8, 0x5a, 0x9d, 0x43, 0x11, 0x6a, 0x5f, 0xdd, 0xe1},
},
{
squareSize: 2,
Expand Down