From aab15aa01c1f0658b10b70eb438aad90b4b0b70c Mon Sep 17 00:00:00 2001 From: Rootul P Date: Wed, 7 Feb 2024 15:41:19 -0500 Subject: [PATCH] revert!: rsmt2d#277 and rsmt2d#287 (#295) While attempting to bump celestia-app to the v0.12.0-rc2, I noticed that the `RegisterTree` design leaks an implementation detail to celestia-app: the registering and managing of `treeName`s. Celestia-app has two categories of of trees: 1. erasured namespaced merkle tree in [nmt_wrapper.go](https://github.com/celestiaorg/celestia-app/blob/main/pkg/wrapper/nmt_wrapper.go) 2. EDS subtree root cacher [nmt_caching.go](https://github.com/celestiaorg/celestia-app/blob/main/pkg/inclusion/nmt_caching.go) Each of those categories has trees based on square size and NMT options. Celestia-app needs to be careful to register all the appropriate trees once (and only once) before they are used (via `Compute` or `Import`). I'd like to explore a less breaking option to get celestia-node the original desired feature which was https://github.com/celestiaorg/rsmt2d/issues/275. In the meantime, I think we should revert the two big breaking changes so that main can remain release-able. Revert https://github.com/celestiaorg/rsmt2d/pull/277 Revert https://github.com/celestiaorg/rsmt2d/pull/287 Closes https://github.com/celestiaorg/rsmt2d/pull/295 because no longer relevant if we merge this. --- default_tree.go | 48 ------------ extendeddatacrossword_test.go | 20 ++--- extendeddatasquare.go | 36 ++------- extendeddatasquare_test.go | 133 +++++++++------------------------- rsmt2d_test.go | 12 +-- testdata/edsCustomTree.json | 22 ------ tree.go | 54 +++++++------- tree_test.go | 119 ------------------------------ 8 files changed, 79 insertions(+), 365 deletions(-) delete mode 100644 default_tree.go delete mode 100644 testdata/edsCustomTree.json delete mode 100644 tree_test.go diff --git a/default_tree.go b/default_tree.go deleted file mode 100644 index deb9224..0000000 --- a/default_tree.go +++ /dev/null @@ -1,48 +0,0 @@ -package rsmt2d - -import ( - "crypto/sha256" - "fmt" - - "github.com/celestiaorg/merkletree" -) - -var DefaultTreeName = "default-tree" - -func init() { - err := RegisterTree(DefaultTreeName, NewDefaultTree) - if err != nil { - panic(fmt.Sprintf("%s already registered", DefaultTreeName)) - } -} - -var _ Tree = &DefaultTree{} - -type DefaultTree struct { - *merkletree.Tree - leaves [][]byte - root []byte -} - -func NewDefaultTree(_ Axis, _ uint) Tree { - return &DefaultTree{ - Tree: merkletree.New(sha256.New()), - leaves: make([][]byte, 0, 128), - } -} - -func (d *DefaultTree) Push(data []byte) error { - // ignore the idx, as this implementation doesn't need that info - d.leaves = append(d.leaves, data) - return nil -} - -func (d *DefaultTree) Root() ([]byte, error) { - if d.root == nil { - for _, l := range d.leaves { - d.Tree.Push(l) - } - d.root = d.Tree.Root() - } - return d.root, nil -} diff --git a/extendeddatacrossword_test.go b/extendeddatacrossword_test.go index d360f68..6266b79 100644 --- a/extendeddatacrossword_test.go +++ b/extendeddatacrossword_test.go @@ -42,7 +42,7 @@ func TestRepairExtendedDataSquare(t *testing.T) { flattened[12], flattened[13] = nil, nil // Re-import the data square. - eds, err := ImportExtendedDataSquare(flattened, codec, DefaultTreeName) + eds, err := ImportExtendedDataSquare(flattened, codec, NewDefaultTree) if err != nil { t.Errorf("ImportExtendedDataSquare failed: %v", err) } @@ -67,7 +67,7 @@ func TestRepairExtendedDataSquare(t *testing.T) { flattened[12], flattened[13], flattened[14] = nil, nil, nil // Re-import the data square. - eds, err := ImportExtendedDataSquare(flattened, codec, DefaultTreeName) + eds, err := ImportExtendedDataSquare(flattened, codec, NewDefaultTree) if err != nil { t.Errorf("ImportExtendedDataSquare failed: %v", err) } @@ -237,7 +237,7 @@ func BenchmarkRepair(b *testing.B) { // Generate a new range original data square then extend it square := genRandDS(originalDataWidth, shareSize) - eds, err := ComputeExtendedDataSquare(square, codec, DefaultTreeName) + eds, err := ComputeExtendedDataSquare(square, codec, NewDefaultTree) if err != nil { b.Error(err) } @@ -275,7 +275,7 @@ func BenchmarkRepair(b *testing.B) { } // Re-import the data square. - eds, _ = ImportExtendedDataSquare(flattened, codec, DefaultTreeName) + eds, _ = ImportExtendedDataSquare(flattened, codec, NewDefaultTree) b.StartTimer() @@ -301,7 +301,7 @@ func createTestEds(codec Codec, shareSize int) *ExtendedDataSquare { eds, err := ComputeExtendedDataSquare([][]byte{ ones, twos, threes, fours, - }, codec, DefaultTreeName) + }, codec, NewDefaultTree) if err != nil { panic(err) } @@ -390,14 +390,8 @@ func TestCorruptedEdsReturnsErrByzantineData_UnorderedShares(t *testing.T) { codec := NewLeoRSCodec() - edsWidth := 4 // number of shares per row/column in the extended data square - odsWidth := edsWidth / 2 // number of shares per row/column in the original data square - err := RegisterTree("testing-tree", newConstructor(uint64(odsWidth), nmt.NamespaceIDSize(namespaceSize))) - assert.NoError(t, err) - // create a DA header eds := createTestEdsWithNMT(t, codec, shareSize, namespaceSize, 1, 2, 3, 4) - assert.NotNil(t, eds) dAHeaderRoots, err := eds.getRowRoots() assert.NoError(t, err) @@ -442,8 +436,10 @@ func createTestEdsWithNMT(t *testing.T, codec Codec, shareSize, namespaceSize in for i, shareValue := range sharesValue { shares[i] = bytes.Repeat([]byte{byte(shareValue)}, shareSize) } + edsWidth := 4 // number of shares per row/column in the extended data square + odsWidth := edsWidth / 2 // number of shares per row/column in the original data square - eds, err := ComputeExtendedDataSquare(shares, codec, "testing-tree") + eds, err := ComputeExtendedDataSquare(shares, codec, newConstructor(uint64(odsWidth), nmt.NamespaceIDSize(namespaceSize))) require.NoError(t, err) return eds diff --git a/extendeddatasquare.go b/extendeddatasquare.go index 99b6a75..d076dd1 100644 --- a/extendeddatasquare.go +++ b/extendeddatasquare.go @@ -15,7 +15,6 @@ import ( type ExtendedDataSquare struct { *dataSquare codec Codec - treeName string originalDataWidth uint } @@ -23,11 +22,9 @@ func (eds *ExtendedDataSquare) MarshalJSON() ([]byte, error) { return json.Marshal(&struct { DataSquare [][]byte `json:"data_square"` Codec string `json:"codec"` - Tree string `json:"tree"` }{ DataSquare: eds.dataSquare.Flattened(), Codec: eds.codec.Name(), - Tree: eds.treeName, }) } @@ -35,19 +32,12 @@ func (eds *ExtendedDataSquare) UnmarshalJSON(b []byte) error { var aux struct { DataSquare [][]byte `json:"data_square"` Codec string `json:"codec"` - Tree string `json:"tree"` } - err := json.Unmarshal(b, &aux) - if err != nil { + if err := json.Unmarshal(b, &aux); err != nil { return err } - - if aux.Tree == "" { - aux.Tree = DefaultTreeName - } - - importedEds, err := ImportExtendedDataSquare(aux.DataSquare, codecs[aux.Codec], aux.Tree) + importedEds, err := ImportExtendedDataSquare(aux.DataSquare, codecs[aux.Codec], NewDefaultTree) if err != nil { return err } @@ -60,7 +50,7 @@ func (eds *ExtendedDataSquare) UnmarshalJSON(b []byte) error { func ComputeExtendedDataSquare( data [][]byte, codec Codec, - treeName string, + treeCreatorFn TreeConstructorFn, ) (*ExtendedDataSquare, error) { if len(data) > codec.MaxChunks() { return nil, errors.New("number of chunks exceeds the maximum") @@ -71,18 +61,12 @@ func ComputeExtendedDataSquare( if err != nil { return nil, err } - - treeCreatorFn, err := TreeFn(treeName) - if err != nil { - return nil, err - } - ds, err := newDataSquare(data, treeCreatorFn, uint(chunkSize)) if err != nil { return nil, err } - eds := ExtendedDataSquare{dataSquare: ds, codec: codec, treeName: treeName} + eds := ExtendedDataSquare{dataSquare: ds, codec: codec} err = eds.erasureExtendSquare(codec) if err != nil { return nil, err @@ -95,7 +79,7 @@ func ComputeExtendedDataSquare( func ImportExtendedDataSquare( data [][]byte, codec Codec, - treeName string, + treeCreatorFn TreeConstructorFn, ) (*ExtendedDataSquare, error) { if len(data) > 4*codec.MaxChunks() { return nil, errors.New("number of chunks exceeds the maximum") @@ -106,18 +90,12 @@ func ImportExtendedDataSquare( if err != nil { return nil, err } - - treeCreatorFn, err := TreeFn(treeName) - if err != nil { - return nil, err - } - ds, err := newDataSquare(data, treeCreatorFn, uint(chunkSize)) if err != nil { return nil, err } - eds := ExtendedDataSquare{dataSquare: ds, codec: codec, treeName: treeName} + eds := ExtendedDataSquare{dataSquare: ds, codec: codec} err = validateEdsWidth(eds.width) if err != nil { return nil, err @@ -248,7 +226,7 @@ func (eds *ExtendedDataSquare) erasureExtendCol(codec Codec, i uint) error { } func (eds *ExtendedDataSquare) deepCopy(codec Codec) (ExtendedDataSquare, error) { - imported, err := ImportExtendedDataSquare(eds.Flattened(), codec, eds.treeName) + imported, err := ImportExtendedDataSquare(eds.Flattened(), codec, eds.createTreeFn) return *imported, err } diff --git a/extendeddatasquare_test.go b/extendeddatasquare_test.go index f4fa3cb..057e78f 100644 --- a/extendeddatasquare_test.go +++ b/extendeddatasquare_test.go @@ -3,7 +3,6 @@ package rsmt2d import ( "bytes" "crypto/rand" - _ "embed" "encoding/json" "fmt" "reflect" @@ -26,9 +25,6 @@ var ( fifteens = bytes.Repeat([]byte{15}, shareSize) ) -//go:embed testdata/edsCustomTree.json -var edsCustomTree []byte - func TestComputeExtendedDataSquare(t *testing.T) { codec := NewLeoRSCodec() @@ -63,7 +59,7 @@ func TestComputeExtendedDataSquare(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - result, err := ComputeExtendedDataSquare(tc.data, codec, DefaultTreeName) + result, err := ComputeExtendedDataSquare(tc.data, codec, NewDefaultTree) assert.NoError(t, err) assert.Equal(t, tc.want, result.squareRow) }) @@ -71,7 +67,7 @@ func TestComputeExtendedDataSquare(t *testing.T) { t.Run("returns an error if chunkSize is not a multiple of 64", func(t *testing.T) { chunk := bytes.Repeat([]byte{1}, 65) - _, err := ComputeExtendedDataSquare([][]byte{chunk}, NewLeoRSCodec(), DefaultTreeName) + _, err := ComputeExtendedDataSquare([][]byte{chunk}, NewLeoRSCodec(), NewDefaultTree) assert.Error(t, err) }) } @@ -79,96 +75,39 @@ func TestComputeExtendedDataSquare(t *testing.T) { func TestImportExtendedDataSquare(t *testing.T) { t.Run("is able to import an EDS", func(t *testing.T) { eds := createExampleEds(t, shareSize) - got, err := ImportExtendedDataSquare(eds.Flattened(), NewLeoRSCodec(), DefaultTreeName) + got, err := ImportExtendedDataSquare(eds.Flattened(), NewLeoRSCodec(), NewDefaultTree) assert.NoError(t, err) assert.Equal(t, eds.Flattened(), got.Flattened()) }) t.Run("returns an error if chunkSize is not a multiple of 64", func(t *testing.T) { chunk := bytes.Repeat([]byte{1}, 65) - _, err := ImportExtendedDataSquare([][]byte{chunk}, NewLeoRSCodec(), DefaultTreeName) + _, err := ImportExtendedDataSquare([][]byte{chunk}, NewLeoRSCodec(), NewDefaultTree) assert.Error(t, err) }) } func TestMarshalJSON(t *testing.T) { - original, err := ComputeExtendedDataSquare([][]byte{ones, twos, threes, fours}, NewLeoRSCodec(), DefaultTreeName) - require.NoError(t, err) - - edsBytes, err := original.MarshalJSON() - require.NoError(t, err) - - var got ExtendedDataSquare - err = json.Unmarshal(edsBytes, &got) - require.NoError(t, err) - - assert.Equal(t, original.dataSquare.Flattened(), got.dataSquare.Flattened()) - assert.Equal(t, original.codec.Name(), got.codec.Name()) - assert.Equal(t, original.treeName, got.treeName) -} - -func TestUnmarshalJSON(t *testing.T) { - t.Run("throws an error when unmarshaling an unregistered custom tree", func(t *testing.T) { - var eds ExtendedDataSquare - err := eds.UnmarshalJSON(edsCustomTree) - - assert.Error(t, err) - assert.Contains(t, err.Error(), "custom-tree not registered yet") - }) - - type testCase struct { - name string - original *ExtendedDataSquare - want *ExtendedDataSquare - wantErr bool + codec := NewLeoRSCodec() + result, err := ComputeExtendedDataSquare([][]byte{ + ones, twos, + threes, fours, + }, codec, NewDefaultTree) + if err != nil { + panic(err) } - defaultEDS := exampleEds(t, DefaultTreeName) - - // The tree name is intentionally set to empty to test whether the - // Unmarshal process appropriately falls back to the default tree - defaultEDSWithoutTreeName := exampleEds(t, DefaultTreeName) - defaultEDSWithoutTreeName.treeName = "" - - customTreeName := "custom-tree" - err := RegisterTree(customTreeName, sudoConstructorFn) - require.NoError(t, err) - defer cleanUp(customTreeName) - customEDS := exampleEds(t, customTreeName) - - testCases := []testCase{ - { - name: "can unmarshal the default EDS", - original: defaultEDS, - want: defaultEDS, - wantErr: false, - }, - { - name: "can unmarshal the default EDS even if tree name is removed", - original: defaultEDSWithoutTreeName, - want: defaultEDS, - wantErr: false, - }, - { - name: "can unmarshal an EDS with a custom tree", - original: customEDS, - want: customEDS, - wantErr: false, - }, + edsBytes, err := json.Marshal(result) + if err != nil { + t.Errorf("failed to marshal EDS: %v", err) } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - edsBytes, err := json.Marshal(tc.original) - assert.NoError(t, err) - - var got ExtendedDataSquare - err = got.UnmarshalJSON(edsBytes) - assert.NoError(t, err) - - assert.Equal(t, tc.want.dataSquare.Flattened(), got.dataSquare.Flattened()) - assert.Equal(t, tc.want.codec.Name(), got.codec.Name()) - assert.Equal(t, tc.want.treeName, got.treeName) - }) + var eds ExtendedDataSquare + err = json.Unmarshal(edsBytes, &eds) + if err != nil { + t.Errorf("failed to marshal EDS: %v", err) + } + if !reflect.DeepEqual(result.squareRow, eds.squareRow) { + t.Errorf("eds not equal after json marshal/unmarshal") } } @@ -223,7 +162,7 @@ func TestImmutableRoots(t *testing.T) { result, err := ComputeExtendedDataSquare([][]byte{ ones, twos, threes, fours, - }, codec, DefaultTreeName) + }, codec, NewDefaultTree) if err != nil { panic(err) } @@ -258,7 +197,7 @@ func TestEDSRowColImmutable(t *testing.T) { result, err := ComputeExtendedDataSquare([][]byte{ ones, twos, threes, fours, - }, codec, DefaultTreeName) + }, codec, NewDefaultTree) if err != nil { panic(err) } @@ -281,7 +220,7 @@ func TestRowRoots(t *testing.T) { eds, err := ComputeExtendedDataSquare([][]byte{ ones, twos, threes, fours, - }, NewLeoRSCodec(), DefaultTreeName) + }, NewLeoRSCodec(), NewDefaultTree) require.NoError(t, err) rowRoots, err := eds.RowRoots() @@ -293,7 +232,7 @@ func TestRowRoots(t *testing.T) { eds, err := ComputeExtendedDataSquare([][]byte{ ones, twos, threes, fours, - }, NewLeoRSCodec(), DefaultTreeName) + }, NewLeoRSCodec(), NewDefaultTree) require.NoError(t, err) // set a cell to nil to make the EDS incomplete @@ -309,7 +248,7 @@ func TestColRoots(t *testing.T) { eds, err := ComputeExtendedDataSquare([][]byte{ ones, twos, threes, fours, - }, NewLeoRSCodec(), DefaultTreeName) + }, NewLeoRSCodec(), NewDefaultTree) require.NoError(t, err) colRoots, err := eds.ColRoots() @@ -321,7 +260,7 @@ func TestColRoots(t *testing.T) { eds, err := ComputeExtendedDataSquare([][]byte{ ones, twos, threes, fours, - }, NewLeoRSCodec(), DefaultTreeName) + }, NewLeoRSCodec(), NewDefaultTree) require.NoError(t, err) // set a cell to nil to make the EDS incomplete @@ -351,7 +290,7 @@ func BenchmarkExtensionEncoding(b *testing.B) { fmt.Sprintf("%s %dx%dx%d ODS", codecName, i, i, len(square[0])), func(b *testing.B) { for n := 0; n < b.N; n++ { - eds, err := ComputeExtendedDataSquare(square, codec, DefaultTreeName) + eds, err := ComputeExtendedDataSquare(square, codec, NewDefaultTree) if err != nil { b.Error(err) } @@ -378,7 +317,7 @@ func BenchmarkExtensionWithRoots(b *testing.B) { fmt.Sprintf("%s %dx%dx%d ODS", codecName, i, i, len(square[0])), func(b *testing.B) { for n := 0; n < b.N; n++ { - eds, err := ComputeExtendedDataSquare(square, codec, DefaultTreeName) + eds, err := ComputeExtendedDataSquare(square, codec, NewDefaultTree) if err != nil { b.Error(err) } @@ -457,7 +396,7 @@ func TestEquals(t *testing.T) { unequalChunkSize := createExampleEds(t, shareSize*2) - unequalEds, err := ComputeExtendedDataSquare([][]byte{ones}, NewLeoRSCodec(), DefaultTreeName) + unequalEds, err := ComputeExtendedDataSquare([][]byte{ones}, NewLeoRSCodec(), NewDefaultTree) require.NoError(t, err) testCases := []testCase{ @@ -492,7 +431,7 @@ func TestRoots(t *testing.T) { eds, err := ComputeExtendedDataSquare([][]byte{ ones, twos, threes, fours, - }, NewLeoRSCodec(), DefaultTreeName) + }, NewLeoRSCodec(), NewDefaultTree) require.NoError(t, err) roots, err := eds.Roots() @@ -519,7 +458,7 @@ func TestRoots(t *testing.T) { eds, err := ComputeExtendedDataSquare([][]byte{ ones, twos, threes, fours, - }, NewLeoRSCodec(), DefaultTreeName) + }, NewLeoRSCodec(), NewDefaultTree) require.NoError(t, err) // set a cell to nil to make the EDS incomplete @@ -540,13 +479,7 @@ func createExampleEds(t *testing.T, chunkSize int) (eds *ExtendedDataSquare) { threes, fours, } - eds, err := ComputeExtendedDataSquare(ods, NewLeoRSCodec(), DefaultTreeName) - require.NoError(t, err) - return eds -} - -func exampleEds(t *testing.T, treeName string) *ExtendedDataSquare { - eds, err := ComputeExtendedDataSquare([][]byte{ones, twos, threes, fours}, NewLeoRSCodec(), treeName) + eds, err := ComputeExtendedDataSquare(ods, NewLeoRSCodec(), NewDefaultTree) require.NoError(t, err) return eds } diff --git a/rsmt2d_test.go b/rsmt2d_test.go index 2561c7e..417ee89 100644 --- a/rsmt2d_test.go +++ b/rsmt2d_test.go @@ -35,7 +35,7 @@ func TestEdsRepairRoundtripSimple(t *testing.T) { threes, fours, }, tt.codec, - rsmt2d.DefaultTreeName, + rsmt2d.NewDefaultTree, ) if err != nil { t.Errorf("ComputeExtendedDataSquare failed: %v", err) @@ -56,7 +56,7 @@ func TestEdsRepairRoundtripSimple(t *testing.T) { flattened[12], flattened[13] = nil, nil // Re-import the data square. - eds, err = rsmt2d.ImportExtendedDataSquare(flattened, tt.codec, rsmt2d.DefaultTreeName) + eds, err = rsmt2d.ImportExtendedDataSquare(flattened, tt.codec, rsmt2d.NewDefaultTree) if err != nil { t.Errorf("ImportExtendedDataSquare failed: %v", err) } @@ -97,7 +97,7 @@ func TestEdsRepairTwice(t *testing.T) { threes, fours, }, tt.codec, - rsmt2d.DefaultTreeName, + rsmt2d.NewDefaultTree, ) if err != nil { t.Errorf("ComputeExtendedDataSquare failed: %v", err) @@ -120,7 +120,7 @@ func TestEdsRepairTwice(t *testing.T) { flattened[12], flattened[13] = nil, nil // Re-import the data square. - eds, err = rsmt2d.ImportExtendedDataSquare(flattened, tt.codec, rsmt2d.DefaultTreeName) + eds, err = rsmt2d.ImportExtendedDataSquare(flattened, tt.codec, rsmt2d.NewDefaultTree) if err != nil { t.Errorf("ImportExtendedDataSquare failed: %v", err) } @@ -139,7 +139,7 @@ func TestEdsRepairTwice(t *testing.T) { copy(flattened[1], missing) // Re-import the data square. - eds, err = rsmt2d.ImportExtendedDataSquare(flattened, tt.codec, rsmt2d.DefaultTreeName) + eds, err = rsmt2d.ImportExtendedDataSquare(flattened, tt.codec, rsmt2d.NewDefaultTree) if err != nil { t.Errorf("ImportExtendedDataSquare failed: %v", err) } @@ -205,7 +205,7 @@ func createExampleEds(t *testing.T, chunkSize int) (eds *rsmt2d.ExtendedDataSqua threes, fours, } - eds, err := rsmt2d.ComputeExtendedDataSquare(ods, rsmt2d.NewLeoRSCodec(), rsmt2d.DefaultTreeName) + eds, err := rsmt2d.ComputeExtendedDataSquare(ods, rsmt2d.NewLeoRSCodec(), rsmt2d.NewDefaultTree) require.NoError(t, err) return eds } diff --git a/testdata/edsCustomTree.json b/testdata/edsCustomTree.json deleted file mode 100644 index c6a23e3..0000000 --- a/testdata/edsCustomTree.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "data_square": [ - "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE=", - "AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI=", - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", - "AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM=", - "AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM=", - "BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ=", - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg=", - "Dw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8=", - "AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI=", - "CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCws=", - "DQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0=", - "BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ=", - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", - "DQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0=", - "BQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQU=", - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg=" - ], - "codec": "Leopard", - "tree": "custom-tree" -} diff --git a/tree.go b/tree.go index 1980f15..f8dcc66 100644 --- a/tree.go +++ b/tree.go @@ -1,8 +1,9 @@ package rsmt2d import ( - "fmt" - "sync" + "crypto/sha256" + + "github.com/celestiaorg/merkletree" ) // TreeConstructorFn creates a fresh Tree instance to be used as the Merkle tree @@ -21,38 +22,33 @@ type Tree interface { Root() ([]byte, error) } -// treeFns is a global map used for keeping track of registered tree constructors for JSON serialization -// The keys of this map should be kebab cased. E.g. "default-tree" -var treeFns = sync.Map{} - -// RegisterTree must be called in the init function -func RegisterTree(treeName string, treeConstructor TreeConstructorFn) error { - if _, ok := treeFns.Load(treeName); ok { - return fmt.Errorf("%s already registered", treeName) - } +var _ Tree = &DefaultTree{} - treeFns.Store(treeName, treeConstructor) - - return nil +type DefaultTree struct { + *merkletree.Tree + leaves [][]byte + root []byte } -// TreeFn get tree constructor function by tree name from the global map registry -func TreeFn(treeName string) (TreeConstructorFn, error) { - var treeFn TreeConstructorFn - v, ok := treeFns.Load(treeName) - if !ok { - return nil, fmt.Errorf("%s not registered yet", treeName) - } - treeFn, ok = v.(TreeConstructorFn) - if !ok { - return nil, fmt.Errorf("key %s has invalid interface", treeName) +func NewDefaultTree(_ Axis, _ uint) Tree { + return &DefaultTree{ + Tree: merkletree.New(sha256.New()), + leaves: make([][]byte, 0, 128), } +} - return treeFn, nil +func (d *DefaultTree) Push(data []byte) error { + // ignore the idx, as this implementation doesn't need that info + d.leaves = append(d.leaves, data) + return nil } -// removeTreeFn removes a treeConstructorFn by treeName. -// Only use for test cleanup. Proceed with caution. -func removeTreeFn(treeName string) { - treeFns.Delete(treeName) +func (d *DefaultTree) Root() ([]byte, error) { + if d.root == nil { + for _, l := range d.leaves { + d.Tree.Push(l) + } + d.root = d.Tree.Root() + } + return d.root, nil } diff --git a/tree_test.go b/tree_test.go deleted file mode 100644 index 5fddf52..0000000 --- a/tree_test.go +++ /dev/null @@ -1,119 +0,0 @@ -package rsmt2d - -import ( - "fmt" - "reflect" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// TestRegisterTree tests the RegisterTree function for adding -// a tree constructor function for a given tree name into treeFns -// global map. -func TestRegisterTree(t *testing.T) { - treeName := "testing_register_tree" - treeConstructorFn := sudoConstructorFn - - tests := []struct { - name string - expectErr error - }{ - // The tree has not been registered yet in the treeFns global map. - {"register successfully", nil}, - // The tree has already been registered in the treeFns global map. - {"register unsuccessfully", fmt.Errorf("%s already registered", treeName)}, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - err := RegisterTree(treeName, treeConstructorFn) - if test.expectErr != nil { - require.Equal(t, test.expectErr, err) - } - - treeFn, err := TreeFn(treeName) - require.NoError(t, err) - assert.True(t, reflect.DeepEqual(reflect.ValueOf(treeFn), reflect.ValueOf(treeConstructorFn))) - }) - } - - cleanUp(treeName) -} - -// TestTreeFn test the TreeFn function which fetches the -// tree constructor function from the treeFns golbal map. -func TestTreeFn(t *testing.T) { - treeName := "testing_treeFn_tree" - treeConstructorFn := sudoConstructorFn - invalidCaseTreeName := "testing_invalid_register_tree" - invalidTreeConstructorFn := "invalid constructor fn" - - tests := []struct { - name string - treeName string - malleate func() - expectErr error - }{ - // The tree constructor function is successfully fetched - // from the global map. - { - "get successfully", - treeName, - func() { - err := RegisterTree(treeName, treeConstructorFn) - require.NoError(t, err) - }, - nil, - }, - // Unable to fetch the tree constructor function for an - // unregisted tree name. - { - "get unregisted tree name", - "unregistered_tree", - func() {}, - fmt.Errorf("%s not registered yet", "unregistered_tree"), - }, - // Value returned from the global map is an invalid value that - // cannot be type asserted into TreeConstructorFn type. - { - "get invalid interface value", - invalidCaseTreeName, - func() { - // Seems like this case has low probability of happening - // since all register has been done through RegisterTree func - // which have strict type check as argument. - treeFns.Store(invalidCaseTreeName, invalidTreeConstructorFn) - }, - fmt.Errorf("key %s has invalid interface", invalidCaseTreeName), - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - test.malleate() - - treeFn, err := TreeFn(test.treeName) - if test.expectErr != nil { - require.Equal(t, test.expectErr, err) - } else { - require.NoError(t, err) - require.True(t, reflect.DeepEqual(reflect.ValueOf(treeFn), reflect.ValueOf(treeConstructorFn))) - } - }) - - cleanUp(test.treeName) - } -} - -// Avoid duplicate with default_tree treeConstructorFn -// registered during init. -func sudoConstructorFn(_ Axis, _ uint) Tree { - return &DefaultTree{} -} - -// Clear tested tree constructor function in the global map. -func cleanUp(treeName string) { - removeTreeFn(treeName) -}