From 83679408b7d049114325c855272599a7b022f8ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zdyba=C5=82?= Date: Thu, 8 Jul 2021 12:22:52 +0200 Subject: [PATCH 1/6] Add CheckBlockAvailability method to DA layer client interface --- da/da.go | 19 +++++++++-- ...a-interface.md => adr-006-da-interface.md} | 32 +++++++++++++++++-- 2 files changed, 47 insertions(+), 4 deletions(-) rename docs/lazy-adr/{adr-005-da-interface.md => adr-006-da-interface.md} (55%) diff --git a/da/da.go b/da/da.go index bd9f68bec7..127cd55c94 100644 --- a/da/da.go +++ b/da/da.go @@ -2,6 +2,7 @@ package da import ( "github.com/lazyledger/optimint/log" + "github.com/lazyledger/optimint/store" "github.com/lazyledger/optimint/types" ) @@ -23,18 +24,29 @@ const ( type ResultSubmitBlock struct { // Code is to determine if the action succeeded. Code StatusCode - // Message may contain DA layer specific information (like detailed error message) + // Message may contain DA layer specific information (like detailed error message). Message string // Not sure if this needs to be bubbled up to other // parts of Optimint. // Hash hash.Hash } +// ResultCheckBlock contains information about block availability, returned from DA layer client. +type ResultCheckBlock struct { + // Code is to determine if data availability layer client was able to execute availability query. + Code StatusCode + // DataAvailable is the actual answer whether the block is available or not. + // It can be true if and only if Code is equal to StatusSuccess. + DataAvailable bool + // Message may contain DA layer specific information (like DA block height/hash, detailed error message, etc) + Message string +} + // DataAvailabilityLayerClient defines generic interface for DA layer block submission. // It also contains life-cycle methods. type DataAvailabilityLayerClient interface { // Init is called once to allow DA client to read configuration and initialize resources. - Init(config []byte, logger log.Logger) error + Init(config []byte, kvStore store.KVStore, logger log.Logger) error Start() error Stop() error @@ -43,4 +55,7 @@ type DataAvailabilityLayerClient interface { // This should create a transaction which (potentially) // triggers a state transition in the DA layer. SubmitBlock(block *types.Block) ResultSubmitBlock + + // CheckBlockAvailability queries DA layer to check data availability of block corresponding to given header. + CheckBlockAvailability(header *types.Header) ResultCheckBlock } diff --git a/docs/lazy-adr/adr-005-da-interface.md b/docs/lazy-adr/adr-006-da-interface.md similarity index 55% rename from docs/lazy-adr/adr-005-da-interface.md rename to docs/lazy-adr/adr-006-da-interface.md index 5568195c84..2f014fa6e9 100644 --- a/docs/lazy-adr/adr-005-da-interface.md +++ b/docs/lazy-adr/adr-006-da-interface.md @@ -4,6 +4,7 @@ - 2021.04.30: Initial draft - 2021.06.03: Init method added +- 2021.07.08: Added CheckBlockAvailability method, added KVStore to Init method, added missing result types ## Context @@ -25,7 +26,7 @@ Definition of interface: ```go type DataAvailabilityLayerClient interface { // Init is called once to allow DA client to read configuration and initialize resources. - Init(config []byte, logger log.Logger) error + Init(config []byte, kvStore store.KVStore, logger log.Logger) error Start() error Stop() error @@ -34,6 +35,9 @@ type DataAvailabilityLayerClient interface { // This should create a transaction which (potentially) // triggers a state transition in the DA layer. SubmitBlock(block *types.Block) ResultSubmitBlock + + // CheckBlockAvailability queries DA layer to check block's data availability. + CheckBlockAvailability(block *types.Block) ResultCheckBlock } // TODO define an enum of different non-happy-path cases @@ -41,11 +45,35 @@ type DataAvailabilityLayerClient interface { // the underlying DA chain. type StatusCode uint64 +// Data Availability return codes. const ( - StatusSuccess StatusCode = iota + StatusUnknown StatusCode = iota + StatusSuccess StatusTimeout StatusError ) + +// ResultSubmitBlock contains information returned from DA layer after block submission. +type ResultSubmitBlock struct { + // Code is to determine if the action succeeded. + Code StatusCode + // Message may contain DA layer specific information (like detailed error message). + Message string + // Not sure if this needs to be bubbled up to other + // parts of Optimint. + // Hash hash.Hash +} + +// ResultCheckBlock contains information about block availability, returned from DA layer client. +type ResultCheckBlock struct { + // Code is to determine if data availability layer client was able to execute availability query. + Code StatusCode + // DataAvailable is the actual answer whether the block is available or not. + // It can be true if and only if Code is equal to StatusSuccess. + DataAvailable bool + // Message may contain DA layer specific information (like DA block height/hash, detailed error message, etc) + Message string +} ``` > From ec31a9136536f57852776cc682d51d18e24d84af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zdyba=C5=82?= Date: Fri, 9 Jul 2021 12:57:55 +0200 Subject: [PATCH 2/6] BlockRetriever interface. --- da/da.go | 28 +++++++++++++++++------ docs/lazy-adr/adr-006-da-interface.md | 33 +++++++++++++++++++-------- 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/da/da.go b/da/da.go index 127cd55c94..f67cc12760 100644 --- a/da/da.go +++ b/da/da.go @@ -20,12 +20,16 @@ const ( StatusError ) -// ResultSubmitBlock contains information returned from DA layer after block submission. -type ResultSubmitBlock struct { +type DAResult struct { // Code is to determine if the action succeeded. Code StatusCode - // Message may contain DA layer specific information (like detailed error message). + // Message may contain DA layer specific information (like DA block height/hash, detailed error message, etc) Message string +} + +// ResultSubmitBlock contains information returned from DA layer after block submission. +type ResultSubmitBlock struct { + DAResult // Not sure if this needs to be bubbled up to other // parts of Optimint. // Hash hash.Hash @@ -33,13 +37,17 @@ type ResultSubmitBlock struct { // ResultCheckBlock contains information about block availability, returned from DA layer client. type ResultCheckBlock struct { - // Code is to determine if data availability layer client was able to execute availability query. - Code StatusCode + DAResult // DataAvailable is the actual answer whether the block is available or not. // It can be true if and only if Code is equal to StatusSuccess. DataAvailable bool - // Message may contain DA layer specific information (like DA block height/hash, detailed error message, etc) - Message string +} + +type ResultRetrieveBlock struct { + DAResult + // Block is the full block retrieved from Data Availability Layer. + // If Code is not equal to StatusSuccess, it has to be nil. + Block *types.Block } // DataAvailabilityLayerClient defines generic interface for DA layer block submission. @@ -59,3 +67,9 @@ type DataAvailabilityLayerClient interface { // CheckBlockAvailability queries DA layer to check data availability of block corresponding to given header. CheckBlockAvailability(header *types.Header) ResultCheckBlock } + +// BlockRetriever is additional interface that can be implemented by Data Availability Layer Client that is able to retrieve +// block data from DA layer. This gives the ability to use it for block synchronization. +type BlockRetriever interface { + RetrieveBlock(height uint64) ResultRetrieveBlock +} diff --git a/docs/lazy-adr/adr-006-da-interface.md b/docs/lazy-adr/adr-006-da-interface.md index 2f014fa6e9..c97e5ee323 100644 --- a/docs/lazy-adr/adr-006-da-interface.md +++ b/docs/lazy-adr/adr-006-da-interface.md @@ -4,7 +4,7 @@ - 2021.04.30: Initial draft - 2021.06.03: Init method added -- 2021.07.08: Added CheckBlockAvailability method, added KVStore to Init method, added missing result types +- 2021.07.09: Added CheckBlockAvailability method, added KVStore to Init method, added missing result types ## Context @@ -17,7 +17,8 @@ Optimint requires data availability layer. Different implementations are expecte ## Decision Defined interface should be very generic. -Interface should consist of 4 methods: `Init`, `Start`, `Stop`, `SubmitBlock`. +Interface should consist of 5 methods: `Init`, `Start`, `Stop`, `SubmitBlock`, `CheckBlockAvailability`. +There is also optional interface `BlockRetriever` for data availability layer clients that are also able to get block data. All the details are implementation-specific. ## Detailed Design @@ -40,6 +41,12 @@ type DataAvailabilityLayerClient interface { CheckBlockAvailability(block *types.Block) ResultCheckBlock } +// BlockRetriever is additional interface that can be implemented by Data Availability Layer Client that is able to retrieve +// block data from DA layer. This gives the ability to use it for block synchronization. +type BlockRetriever interface { + RetrieveBlock(height uint64) ResultRetrieveBlock +} + // TODO define an enum of different non-happy-path cases // that might need to be handled by Optimint independent of // the underlying DA chain. @@ -53,12 +60,16 @@ const ( StatusError ) -// ResultSubmitBlock contains information returned from DA layer after block submission. -type ResultSubmitBlock struct { +type DAResult struct { // Code is to determine if the action succeeded. Code StatusCode - // Message may contain DA layer specific information (like detailed error message). + // Message may contain DA layer specific information (like DA block height/hash, detailed error message, etc) Message string +} + +// ResultSubmitBlock contains information returned from DA layer after block submission. +type ResultSubmitBlock struct { + DAResult // Not sure if this needs to be bubbled up to other // parts of Optimint. // Hash hash.Hash @@ -66,13 +77,17 @@ type ResultSubmitBlock struct { // ResultCheckBlock contains information about block availability, returned from DA layer client. type ResultCheckBlock struct { - // Code is to determine if data availability layer client was able to execute availability query. - Code StatusCode + DAResult // DataAvailable is the actual answer whether the block is available or not. // It can be true if and only if Code is equal to StatusSuccess. DataAvailable bool - // Message may contain DA layer specific information (like DA block height/hash, detailed error message, etc) - Message string +} + +type ResultRetrieveBlock struct { + DAResult + // Block is the full block retrieved from Data Availability Layer. + // If Code is not equal to StatusSuccess, it has to be nil. + Block *types.Block } ``` > From 309097cd2344f2bae496f3b99a20f00671e49435 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zdyba=C5=82?= Date: Mon, 5 Jul 2021 10:16:27 +0200 Subject: [PATCH 3/6] Get rid of misleading `hash` package --- conv/abci/block.go | 8 ++------ hash/hashing.go | 16 ---------------- state/executor.go | 13 +++---------- store/store.go | 6 +----- store/store_test.go | 8 ++------ types/hashing.go | 24 ++++++++++++++++++++++++ 6 files changed, 32 insertions(+), 43 deletions(-) delete mode 100644 hash/hashing.go create mode 100644 types/hashing.go diff --git a/conv/abci/block.go b/conv/abci/block.go index 5aef542504..29fed26ddf 100644 --- a/conv/abci/block.go +++ b/conv/abci/block.go @@ -6,16 +6,12 @@ import ( tmproto "github.com/lazyledger/lazyledger-core/proto/tendermint/types" tmversion "github.com/lazyledger/lazyledger-core/proto/tendermint/version" - "github.com/lazyledger/optimint/hash" "github.com/lazyledger/optimint/types" ) // ToABCIHeader converts Optimint header to Header format defined in ABCI. func ToABCIHeader(header *types.Header) (tmproto.Header, error) { - h, err := hash.Hash(header) - if err != nil { - return tmproto.Header{}, err - } + hash := header.Hash() return tmproto.Header{ Version: tmversion.Consensus{ Block: uint64(header.Version.Block), @@ -25,7 +21,7 @@ func ToABCIHeader(header *types.Header) (tmproto.Header, error) { Height: int64(header.Height), Time: time.Unix(int64(header.Time), 0), LastBlockId: tmproto.BlockID{ - Hash: h[:], + Hash: hash[:], PartSetHeader: tmproto.PartSetHeader{ Total: 0, Hash: nil, diff --git a/hash/hashing.go b/hash/hashing.go deleted file mode 100644 index 4c2e88e273..0000000000 --- a/hash/hashing.go +++ /dev/null @@ -1,16 +0,0 @@ -package hash - -import ( - "encoding" - - "github.com/minio/sha256-simd" -) - -// Hash creates a SHA-256 hash of object that can be serialized into binary form. -func Hash(object encoding.BinaryMarshaler) ([32]byte, error) { - blob, err := object.MarshalBinary() - if err != nil { - return [32]byte{}, err - } - return sha256.Sum256(blob), nil -} diff --git a/state/executor.go b/state/executor.go index b867430d51..1b2c9d0b89 100644 --- a/state/executor.go +++ b/state/executor.go @@ -12,7 +12,6 @@ import ( lltypes "github.com/lazyledger/lazyledger-core/types" abciconv "github.com/lazyledger/optimint/conv/abci" - "github.com/lazyledger/optimint/hash" "github.com/lazyledger/optimint/log" "github.com/lazyledger/optimint/mempool" "github.com/lazyledger/optimint/types" @@ -102,10 +101,7 @@ func (e *BlockExecutor) ApplyBlock(ctx context.Context, state State, block *type } func (e *BlockExecutor) updateState(state State, block *types.Block, abciResponses *tmstate.ABCIResponses) (State, error) { - h, err := hash.Hash(&block.Header) - if err != nil { - return State{}, err - } + hash := block.Header.Hash() s := State{ Version: state.Version, ChainID: state.ChainID, @@ -113,7 +109,7 @@ func (e *BlockExecutor) updateState(state State, block *types.Block, abciRespons LastBlockHeight: int64(block.Header.Height), LastBlockTime: time.Unix(int64(block.Header.Time), 0), LastBlockID: lltypes.BlockID{ - Hash: h[:], + Hash: hash[:], // for now, we don't care about part set headers }, // skipped all "Validators" fields @@ -199,10 +195,7 @@ func (e *BlockExecutor) execute(ctx context.Context, state State, block *types.B } }) - hash, err := hash.Hash(block) - if err != nil { - return nil, err - } + hash := block.Hash() abciHeader, err := abciconv.ToABCIHeader(&block.Header) if err != nil { return nil, err diff --git a/store/store.go b/store/store.go index 18e86af307..817641ec8d 100644 --- a/store/store.go +++ b/store/store.go @@ -7,7 +7,6 @@ import ( "go.uber.org/multierr" - "github.com/lazyledger/optimint/hash" "github.com/lazyledger/optimint/types" ) @@ -44,10 +43,7 @@ func (s *DefaultStore) Height() uint64 { // SaveBlock adds block to the store along with corresponding commit. // Stored height is updated if block height is greater than stored value. func (s *DefaultStore) SaveBlock(block *types.Block, commit *types.Commit) error { - hash, err := hash.Hash(&block.Header) - if err != nil { - return err - } + hash := block.Header.Hash() blockBlob, err := block.MarshalBinary() if err != nil { return err diff --git a/store/store_test.go b/store/store_test.go index 17dd5a078a..b7a517eb65 100644 --- a/store/store_test.go +++ b/store/store_test.go @@ -7,7 +7,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/lazyledger/optimint/hash" "github.com/lazyledger/optimint/types" ) @@ -79,9 +78,7 @@ func TestBlockstoreLoad(t *testing.T) { bstore := New() for _, block := range c.blocks { - headerHash, err := hash.Hash(&block.Header) - require.NoError(err) - err = bstore.SaveBlock(block, &types.Commit{Height: block.Header.Height, HeaderHash: headerHash}) + err := bstore.SaveBlock(block, &types.Commit{Height: block.Header.Height, HeaderHash: block.Header.Hash()}) require.NoError(err) } @@ -95,8 +92,7 @@ func TestBlockstoreLoad(t *testing.T) { assert.NoError(err) assert.NotNil(commit) assert.Equal(expected.Header.Height, commit.Height) - headerHash, err := hash.Hash(&expected.Header) - require.NoError(err) + headerHash := expected.Header.Hash() assert.Equal(headerHash, commit.HeaderHash) } }) diff --git a/types/hashing.go b/types/hashing.go new file mode 100644 index 0000000000..23aaa8e185 --- /dev/null +++ b/types/hashing.go @@ -0,0 +1,24 @@ +package types + +import ( + "encoding" + + "github.com/minio/sha256-simd" +) + +func (h *Header) Hash() [32]byte { + return hash(h) +} + +func (b *Block) Hash() [32]byte { + return hash(b) +} + +func hash(obj encoding.BinaryMarshaler) [32]byte { + blob, err := obj.MarshalBinary() + if err != nil { + return [32]byte{} + } + return sha256.Sum256(blob) + +} From 8c9219598c614821e979b63e6424d600ffd7fcd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zdyba=C5=82?= Date: Fri, 9 Jul 2021 13:29:41 +0200 Subject: [PATCH 4/6] Update DA layer implementations --- da/lazyledger/lazyledger.go | 20 ++++++++++++++------ da/lazyledger/lazyledger_test.go | 4 ++-- da/mock/mock.go | 16 +++++++++++++--- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/da/lazyledger/lazyledger.go b/da/lazyledger/lazyledger.go index f6fc5ac23d..262e0e6b7b 100644 --- a/da/lazyledger/lazyledger.go +++ b/da/lazyledger/lazyledger.go @@ -16,6 +16,7 @@ import ( "github.com/lazyledger/optimint/da" "github.com/lazyledger/optimint/log" + "github.com/lazyledger/optimint/store" "github.com/lazyledger/optimint/types" ) @@ -49,8 +50,9 @@ type Config struct { // LazyLedger implements DataAvailabilityLayerClient interface. // It use lazyledger-app via gRPC. type LazyLedger struct { - config Config - logger log.Logger + config Config + kvStore store.KVStore + logger log.Logger keyring keyring.Keyring @@ -60,8 +62,9 @@ type LazyLedger struct { var _ da.DataAvailabilityLayerClient = &LazyLedger{} // Init is called once to allow DA client to read configuration and initialize resources. -func (ll *LazyLedger) Init(config []byte, logger log.Logger) error { +func (ll *LazyLedger) Init(config []byte, kvStore store.KVStore, logger log.Logger) error { ll.logger = logger + ll.kvStore = kvStore err := toml.Unmarshal(config, &ll.config) if err != nil { return err @@ -90,7 +93,7 @@ func (ll *LazyLedger) Stop() error { func (ll *LazyLedger) SubmitBlock(block *types.Block) da.ResultSubmitBlock { msg, err := ll.preparePayForMessage(block) if err != nil { - return da.ResultSubmitBlock{Code: da.StatusError, Message: err.Error()} + return da.ResultSubmitBlock{da.DAResult{Code: da.StatusError, Message: err.Error()}} } ctx, cancel := context.WithTimeout(context.TODO(), ll.config.Timeout) @@ -98,10 +101,15 @@ func (ll *LazyLedger) SubmitBlock(block *types.Block) da.ResultSubmitBlock { err = ll.callRPC(ctx, msg) if err != nil { - return da.ResultSubmitBlock{Code: da.StatusError, Message: err.Error()} + return da.ResultSubmitBlock{da.DAResult{Code: da.StatusError, Message: err.Error()}} } - return da.ResultSubmitBlock{Code: da.StatusSuccess} + return da.ResultSubmitBlock{da.DAResult{Code: da.StatusSuccess}} +} + +// CheckBlockAvailability queries DA layer to check data availability of block corresponding to given header. +func (l *LazyLedger) CheckBlockAvailability(header *types.Header) da.ResultCheckBlock { + panic("not implemented") // TODO: Implement } func (ll *LazyLedger) callRPC(ctx context.Context, msg *apptypes.MsgWirePayForMessage) error { diff --git a/da/lazyledger/lazyledger_test.go b/da/lazyledger/lazyledger_test.go index 5f0ea23dc2..a25f2e6b04 100644 --- a/da/lazyledger/lazyledger_test.go +++ b/da/lazyledger/lazyledger_test.go @@ -30,7 +30,7 @@ func TestConfiguration(t *testing.T) { t.Run(c.name, func(t *testing.T) { assert := assert.New(t) ll := &LazyLedger{} - err := ll.Init(c.input, nil) + err := ll.Init(c.input, nil, nil) if c.err != nil { assert.EqualError(err, c.err.Error()) @@ -56,7 +56,7 @@ func TestSubmission(t *testing.T) { key, err := kr.Key("test-account") require.NoError(err) conf := testConfig(key) - err = ll.Init([]byte(conf), nil) + err = ll.Init([]byte(conf), nil, nil) require.NoError(err) ll.keyring = kr diff --git a/da/mock/mock.go b/da/mock/mock.go index 52b883716f..cc5059f17e 100644 --- a/da/mock/mock.go +++ b/da/mock/mock.go @@ -3,6 +3,7 @@ package mock import ( "github.com/lazyledger/optimint/da" "github.com/lazyledger/optimint/log" + "github.com/lazyledger/optimint/store" "github.com/lazyledger/optimint/types" ) @@ -14,8 +15,10 @@ type MockDataAvailabilityLayerClient struct { Blocks []*types.Block } +var _ da.DataAvailabilityLayerClient = &MockDataAvailabilityLayerClient{} + // Init is called once to allow DA client to read configuration and initialize resources. -func (m *MockDataAvailabilityLayerClient) Init(config []byte, logger log.Logger) error { +func (m *MockDataAvailabilityLayerClient) Init(config []byte, kvStore store.KVStore, logger log.Logger) error { m.logger = logger return nil } @@ -39,7 +42,14 @@ func (m *MockDataAvailabilityLayerClient) SubmitBlock(block *types.Block) da.Res m.Blocks = append(m.Blocks, block) return da.ResultSubmitBlock{ - Code: da.StatusSuccess, - Message: "OK", + DAResult: da.DAResult{ + Code: da.StatusSuccess, + Message: "OK", + }, } } + +// CheckBlockAvailability queries DA layer to check data availability of block corresponding to given header. +func (m *MockDataAvailabilityLayerClient) CheckBlockAvailability(header *types.Header) da.ResultCheckBlock { + panic("not implemented") // TODO: Implement +} From 54b331813e653f0b9ce6cbb577b817c813d6a242 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zdyba=C5=82?= Date: Fri, 9 Jul 2021 23:11:26 +0200 Subject: [PATCH 5/6] Mock Data Availability Layer Client with tests + some refactoring --- da/da.go | 1 + da/mock/mock.go | 20 +++++-- da/mock/mock_test.go | 124 +++++++++++++++++++++++++++++++++++++++++++ log/test/loggers.go | 42 +++++++++++++++ p2p/client_test.go | 50 +++-------------- 5 files changed, 191 insertions(+), 46 deletions(-) create mode 100644 da/mock/mock_test.go create mode 100644 log/test/loggers.go diff --git a/da/da.go b/da/da.go index f67cc12760..d08e4eb019 100644 --- a/da/da.go +++ b/da/da.go @@ -71,5 +71,6 @@ type DataAvailabilityLayerClient interface { // BlockRetriever is additional interface that can be implemented by Data Availability Layer Client that is able to retrieve // block data from DA layer. This gives the ability to use it for block synchronization. type BlockRetriever interface { + // RetrieveBlock returns block at given height from data availability layer. RetrieveBlock(height uint64) ResultRetrieveBlock } diff --git a/da/mock/mock.go b/da/mock/mock.go index cc5059f17e..10e8cf8db2 100644 --- a/da/mock/mock.go +++ b/da/mock/mock.go @@ -12,14 +12,18 @@ import ( type MockDataAvailabilityLayerClient struct { logger log.Logger - Blocks []*types.Block + Blocks map[[32]byte]*types.Block + BlockIndex map[uint64][32]byte } var _ da.DataAvailabilityLayerClient = &MockDataAvailabilityLayerClient{} +var _ da.BlockRetriever = &MockDataAvailabilityLayerClient{} // Init is called once to allow DA client to read configuration and initialize resources. func (m *MockDataAvailabilityLayerClient) Init(config []byte, kvStore store.KVStore, logger log.Logger) error { m.logger = logger + m.Blocks = make(map[[32]byte]*types.Block) + m.BlockIndex = make(map[uint64][32]byte) return nil } @@ -39,7 +43,10 @@ func (m *MockDataAvailabilityLayerClient) Stop() error { // This should create a transaction which (potentially) // triggers a state transition in the DA layer. func (m *MockDataAvailabilityLayerClient) SubmitBlock(block *types.Block) da.ResultSubmitBlock { - m.Blocks = append(m.Blocks, block) + hash := block.Header.Hash() + + m.Blocks[hash] = block + m.BlockIndex[block.Header.Height] = hash return da.ResultSubmitBlock{ DAResult: da.DAResult{ @@ -51,5 +58,12 @@ func (m *MockDataAvailabilityLayerClient) SubmitBlock(block *types.Block) da.Res // CheckBlockAvailability queries DA layer to check data availability of block corresponding to given header. func (m *MockDataAvailabilityLayerClient) CheckBlockAvailability(header *types.Header) da.ResultCheckBlock { - panic("not implemented") // TODO: Implement + _, ok := m.Blocks[header.Hash()] + return da.ResultCheckBlock{DAResult: da.DAResult{Code: da.StatusSuccess}, DataAvailable: ok} +} + +// RetrieveBlock returns block at given height from data availability layer. +func (m *MockDataAvailabilityLayerClient) RetrieveBlock(height uint64) da.ResultRetrieveBlock { + hash := m.BlockIndex[height] + return da.ResultRetrieveBlock{DAResult: da.DAResult{Code: da.StatusSuccess}, Block: m.Blocks[hash]} } diff --git a/da/mock/mock_test.go b/da/mock/mock_test.go new file mode 100644 index 0000000000..8437b03de6 --- /dev/null +++ b/da/mock/mock_test.go @@ -0,0 +1,124 @@ +package mock + +import ( + "math/rand" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/lazyledger/optimint/da" + "github.com/lazyledger/optimint/log/test" + "github.com/lazyledger/optimint/types" +) + +func TestLifecycle(t *testing.T) { + var da da.DataAvailabilityLayerClient = &MockDataAvailabilityLayerClient{} + + require := require.New(t) + + err := da.Init([]byte{}, nil, &test.TestLogger{t}) + require.NoError(err) + + err = da.Start() + require.NoError(err) + + err = da.Stop() + require.NoError(err) +} + +func TestMockDALC(t *testing.T) { + var dalc da.DataAvailabilityLayerClient = &MockDataAvailabilityLayerClient{} + + require := require.New(t) + assert := assert.New(t) + + err := dalc.Init([]byte{}, nil, &test.TestLogger{t}) + require.NoError(err) + + err = dalc.Start() + require.NoError(err) + + // only blocks b1 and b2 will be submitted to DA + b1 := getRandomBlock(1, 10) + b2 := getRandomBlock(2, 10) + b3 := getRandomBlock(1, 10) + + resp := dalc.SubmitBlock(b1) + assert.Equal(da.StatusSuccess, resp.Code) + + resp = dalc.SubmitBlock(b2) + assert.Equal(da.StatusSuccess, resp.Code) + + check := dalc.CheckBlockAvailability(&b1.Header) + assert.Equal(da.StatusSuccess, check.Code) + assert.True(check.DataAvailable) + + check = dalc.CheckBlockAvailability(&b2.Header) + assert.Equal(da.StatusSuccess, check.Code) + assert.True(check.DataAvailable) + + // this block was never submitted to DA + check = dalc.CheckBlockAvailability(&b3.Header) + assert.Equal(da.StatusSuccess, check.Code) + assert.False(check.DataAvailable) +} + +func TestRetrieve(t *testing.T) { + mock := &MockDataAvailabilityLayerClient{} + var dalc da.DataAvailabilityLayerClient = mock + var retriever da.BlockRetriever = mock + + require := require.New(t) + assert := assert.New(t) + + err := dalc.Init([]byte{}, nil, &test.TestLogger{t}) + require.NoError(err) + + err = dalc.Start() + require.NoError(err) + + for i := uint64(0); i < 100; i++ { + b := getRandomBlock(i, rand.Int()%20) + resp := dalc.SubmitBlock(b) + assert.Equal(da.StatusSuccess, resp.Code) + + ret := retriever.RetrieveBlock(i) + assert.Equal(da.StatusSuccess, ret.Code) + assert.Equal(b, ret.Block) + } +} + +// copy-pasted from store/store_test.go +func getRandomBlock(height uint64, nTxs int) *types.Block { + block := &types.Block{ + Header: types.Header{ + Height: height, + }, + Data: types.Data{ + Txs: make(types.Txs, nTxs), + IntermediateStateRoots: types.IntermediateStateRoots{ + RawRootsList: make([][]byte, nTxs), + }, + }, + } + copy(block.Header.AppHash[:], getRandomBytes(32)) + + for i := 0; i < nTxs; i++ { + block.Data.Txs[i] = getRandomTx() + block.Data.IntermediateStateRoots.RawRootsList[i] = getRandomBytes(32) + } + + return block +} + +func getRandomTx() types.Tx { + size := rand.Int()%100 + 100 + return types.Tx(getRandomBytes(size)) +} + +func getRandomBytes(n int) []byte { + data := make([]byte, n) + _, _ = rand.Read(data) + return data +} diff --git a/log/test/loggers.go b/log/test/loggers.go new file mode 100644 index 0000000000..019ef72cb2 --- /dev/null +++ b/log/test/loggers.go @@ -0,0 +1,42 @@ +package test + +import ( + "fmt" + "testing" +) + +// TODO(tzdybal): move to some common place +type TestLogger struct { + T *testing.T +} + +func (t *TestLogger) Debug(msg string, keyvals ...interface{}) { + t.T.Helper() + t.T.Log(append([]interface{}{"DEBUG: " + msg}, keyvals...)...) +} + +func (t *TestLogger) Info(msg string, keyvals ...interface{}) { + t.T.Helper() + t.T.Log(append([]interface{}{"INFO: " + msg}, keyvals...)...) +} + +func (t *TestLogger) Error(msg string, keyvals ...interface{}) { + t.T.Helper() + t.T.Log(append([]interface{}{"ERROR: " + msg}, keyvals...)...) +} + +type MockLogger struct { + DebugLines, InfoLines, ErrLines []string +} + +func (t *MockLogger) Debug(msg string, keyvals ...interface{}) { + t.DebugLines = append(t.DebugLines, fmt.Sprint(append([]interface{}{msg}, keyvals...)...)) +} + +func (t *MockLogger) Info(msg string, keyvals ...interface{}) { + t.InfoLines = append(t.InfoLines, fmt.Sprint(append([]interface{}{msg}, keyvals...)...)) +} + +func (t *MockLogger) Error(msg string, keyvals ...interface{}) { + t.ErrLines = append(t.ErrLines, fmt.Sprint(append([]interface{}{msg}, keyvals...)...)) +} diff --git a/p2p/client_test.go b/p2p/client_test.go index a307f36856..bf688588a9 100644 --- a/p2p/client_test.go +++ b/p2p/client_test.go @@ -3,7 +3,6 @@ package p2p import ( "context" "crypto/rand" - "fmt" "sync" "testing" "time" @@ -16,47 +15,12 @@ import ( "github.com/stretchr/testify/require" "github.com/lazyledger/optimint/config" + "github.com/lazyledger/optimint/log/test" ) -// TODO(tzdybal): move to some common place -type TestLogger struct { - t *testing.T -} - -func (t *TestLogger) Debug(msg string, keyvals ...interface{}) { - t.t.Helper() - t.t.Log(append([]interface{}{"DEBUG: " + msg}, keyvals...)...) -} - -func (t *TestLogger) Info(msg string, keyvals ...interface{}) { - t.t.Helper() - t.t.Log(append([]interface{}{"INFO: " + msg}, keyvals...)...) -} - -func (t *TestLogger) Error(msg string, keyvals ...interface{}) { - t.t.Helper() - t.t.Log(append([]interface{}{"ERROR: " + msg}, keyvals...)...) -} - -type MockLogger struct { - debug, info, err []string -} - -func (t *MockLogger) Debug(msg string, keyvals ...interface{}) { - t.debug = append(t.debug, fmt.Sprint(append([]interface{}{msg}, keyvals...)...)) -} - -func (t *MockLogger) Info(msg string, keyvals ...interface{}) { - t.info = append(t.info, fmt.Sprint(append([]interface{}{msg}, keyvals...)...)) -} - -func (t *MockLogger) Error(msg string, keyvals ...interface{}) { - t.err = append(t.err, fmt.Sprint(append([]interface{}{msg}, keyvals...)...)) -} - func TestClientStartup(t *testing.T) { privKey, _, _ := crypto.GenerateEd25519Key(rand.Reader) - client, err := NewClient(config.P2PConfig{}, privKey, "TestChain", &TestLogger{t}) + client, err := NewClient(config.P2PConfig{}, privKey, "TestChain", &test.TestLogger{t}) assert := assert.New(t) assert.NoError(err) assert.NotNil(client) @@ -71,7 +35,7 @@ func TestBootstrapping(t *testing.T) { //log.SetDebugLogging() assert := assert.New(t) - logger := &TestLogger{t} + logger := &test.TestLogger{t} ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -91,7 +55,7 @@ func TestBootstrapping(t *testing.T) { func TestDiscovery(t *testing.T) { assert := assert.New(t) - logger := &TestLogger{t} + logger := &test.TestLogger{t} ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -111,7 +75,7 @@ func TestDiscovery(t *testing.T) { func TestGossiping(t *testing.T) { assert := assert.New(t) - logger := &TestLogger{t} + logger := &test.TestLogger{t} ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -200,7 +164,7 @@ func TestSeedStringParsing(t *testing.T) { t.Run(c.name, func(t *testing.T) { assert := assert.New(t) require := require.New(t) - logger := &MockLogger{} + logger := &test.MockLogger{} client, err := NewClient(config.P2PConfig{}, privKey, "TestNetwork", logger) require.NoError(err) require.NotNil(client) @@ -208,7 +172,7 @@ func TestSeedStringParsing(t *testing.T) { assert.NotNil(actual) assert.Equal(c.expected, actual) // ensure that errors are logged - assert.Equal(c.nErrors, len(logger.err)) + assert.Equal(c.nErrors, len(logger.ErrLines)) }) } } From e37f94540a4c0027cf1290e7cbd0511686e47d4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zdyba=C5=82?= Date: Fri, 9 Jul 2021 23:15:52 +0200 Subject: [PATCH 6/6] Fix linter errors --- da/lazyledger/lazyledger.go | 6 +++--- da/mock/mock_test.go | 6 +++--- p2p/client_test.go | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/da/lazyledger/lazyledger.go b/da/lazyledger/lazyledger.go index 262e0e6b7b..e31c3cbeab 100644 --- a/da/lazyledger/lazyledger.go +++ b/da/lazyledger/lazyledger.go @@ -93,7 +93,7 @@ func (ll *LazyLedger) Stop() error { func (ll *LazyLedger) SubmitBlock(block *types.Block) da.ResultSubmitBlock { msg, err := ll.preparePayForMessage(block) if err != nil { - return da.ResultSubmitBlock{da.DAResult{Code: da.StatusError, Message: err.Error()}} + return da.ResultSubmitBlock{DAResult: da.DAResult{Code: da.StatusError, Message: err.Error()}} } ctx, cancel := context.WithTimeout(context.TODO(), ll.config.Timeout) @@ -101,10 +101,10 @@ func (ll *LazyLedger) SubmitBlock(block *types.Block) da.ResultSubmitBlock { err = ll.callRPC(ctx, msg) if err != nil { - return da.ResultSubmitBlock{da.DAResult{Code: da.StatusError, Message: err.Error()}} + return da.ResultSubmitBlock{DAResult: da.DAResult{Code: da.StatusError, Message: err.Error()}} } - return da.ResultSubmitBlock{da.DAResult{Code: da.StatusSuccess}} + return da.ResultSubmitBlock{DAResult: da.DAResult{Code: da.StatusSuccess}} } // CheckBlockAvailability queries DA layer to check data availability of block corresponding to given header. diff --git a/da/mock/mock_test.go b/da/mock/mock_test.go index 8437b03de6..5ec2808338 100644 --- a/da/mock/mock_test.go +++ b/da/mock/mock_test.go @@ -17,7 +17,7 @@ func TestLifecycle(t *testing.T) { require := require.New(t) - err := da.Init([]byte{}, nil, &test.TestLogger{t}) + err := da.Init([]byte{}, nil, &test.TestLogger{T: t}) require.NoError(err) err = da.Start() @@ -33,7 +33,7 @@ func TestMockDALC(t *testing.T) { require := require.New(t) assert := assert.New(t) - err := dalc.Init([]byte{}, nil, &test.TestLogger{t}) + err := dalc.Init([]byte{}, nil, &test.TestLogger{T: t}) require.NoError(err) err = dalc.Start() @@ -72,7 +72,7 @@ func TestRetrieve(t *testing.T) { require := require.New(t) assert := assert.New(t) - err := dalc.Init([]byte{}, nil, &test.TestLogger{t}) + err := dalc.Init([]byte{}, nil, &test.TestLogger{T: t}) require.NoError(err) err = dalc.Start() diff --git a/p2p/client_test.go b/p2p/client_test.go index bf688588a9..2432619ec6 100644 --- a/p2p/client_test.go +++ b/p2p/client_test.go @@ -20,7 +20,7 @@ import ( func TestClientStartup(t *testing.T) { privKey, _, _ := crypto.GenerateEd25519Key(rand.Reader) - client, err := NewClient(config.P2PConfig{}, privKey, "TestChain", &test.TestLogger{t}) + client, err := NewClient(config.P2PConfig{}, privKey, "TestChain", &test.TestLogger{T: t}) assert := assert.New(t) assert.NoError(err) assert.NotNil(client) @@ -35,7 +35,7 @@ func TestBootstrapping(t *testing.T) { //log.SetDebugLogging() assert := assert.New(t) - logger := &test.TestLogger{t} + logger := &test.TestLogger{T: t} ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -55,7 +55,7 @@ func TestBootstrapping(t *testing.T) { func TestDiscovery(t *testing.T) { assert := assert.New(t) - logger := &test.TestLogger{t} + logger := &test.TestLogger{T: t} ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -75,7 +75,7 @@ func TestDiscovery(t *testing.T) { func TestGossiping(t *testing.T) { assert := assert.New(t) - logger := &test.TestLogger{t} + logger := &test.TestLogger{T: t} ctx, cancel := context.WithCancel(context.Background()) defer cancel()