From 472834939667ab412ae02cb71359d7626b49cba6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zdyba=C5=82?= Date: Fri, 5 Aug 2022 21:55:16 +0200 Subject: [PATCH 1/3] feat: use gossiped commit in sync --- block/manager.go | 117 +++++++++++++++++++++++++++++++---------------- node/node.go | 10 ++++ 2 files changed, 88 insertions(+), 39 deletions(-) diff --git a/block/manager.go b/block/manager.go index 62deab6237..b75595f2bd 100644 --- a/block/manager.go +++ b/block/manager.go @@ -61,7 +61,7 @@ type Manager struct { CommitInCh chan *types.Commit CommitOutCh chan *types.Commit - lastCommit *types.Commit + lastCommit atomic.Value syncTarget uint64 blockInCh chan newBlockEvent @@ -207,7 +207,13 @@ func (m *Manager) SyncLoop(ctx context.Context) { } case commit := <-m.CommitInCh: // TODO(tzdybal): check if it's from right aggregator - m.lastCommit = commit + m.lastCommit.Store(commit) + err := m.trySyncNextBlock(ctx, 0) + if err != nil { + m.logger.Info("failed to sync next block", "error", err) + } else { + m.logger.Debug("synced using gossiped commit", "height", commit.Height) + } case blockEvent := <-m.blockInCh: block := blockEvent.block daHeight := blockEvent.daHeight @@ -218,42 +224,10 @@ func (m *Manager) SyncLoop(ctx context.Context) { ) m.syncCache[block.Header.Height] = block m.retrieveCond.Signal() - currentHeight := m.store.Height() // TODO(tzdybal): maybe store a copy in memory - b1, ok1 := m.syncCache[currentHeight+1] - b2, ok2 := m.syncCache[currentHeight+2] - if ok1 && ok2 { - m.logger.Info("Syncing block", "height", b1.Header.Height) - newState, responses, err := m.executor.ApplyBlock(ctx, m.lastState, b1) - if err != nil { - m.logger.Error("failed to ApplyBlock", "error", err) - continue - } - err = m.store.SaveBlock(b1, &b2.LastCommit) - if err != nil { - m.logger.Error("failed to save block", "error", err) - continue - } - _, _, err = m.executor.Commit(ctx, newState, b1, responses) - if err != nil { - m.logger.Error("failed to Commit", "error", err) - continue - } - m.store.SetHeight(b1.Header.Height) - - err = m.store.SaveBlockResponses(b1.Header.Height, responses) - if err != nil { - m.logger.Error("failed to save block responses", "error", err) - continue - } - newState.DAHeight = daHeight - m.lastState = newState - err = m.store.UpdateState(m.lastState) - if err != nil { - m.logger.Error("failed to save updated state", "error", err) - continue - } - delete(m.syncCache, currentHeight+1) + err := m.trySyncNextBlock(ctx, daHeight) + if err != nil { + m.logger.Info("failed to sync next block", "error", err) } case <-ctx.Done(): return @@ -261,6 +235,70 @@ func (m *Manager) SyncLoop(ctx context.Context) { } } +func (m *Manager) trySyncNextBlock(ctx context.Context, daHeight uint64) error { + var b1 *types.Block + var commit *types.Commit + currentHeight := m.store.Height() // TODO(tzdybal): maybe store a copy in memory + + b1, ok1 := m.syncCache[currentHeight+1] + if !ok1 { + return nil + } + b2, ok2 := m.syncCache[currentHeight+2] + if ok2 { + m.logger.Debug("using last commit from next block") + commit = &b2.LastCommit + } else { + lastCommit := m.getLastCommit() + if lastCommit != nil && lastCommit.Height == currentHeight+1 { + m.logger.Debug("using gossiped commit") + commit = lastCommit + } + } + + if b1 != nil && commit != nil { + m.logger.Info("Syncing block", "height", b1.Header.Height) + newState, responses, err := m.executor.ApplyBlock(ctx, m.lastState, b1) + if err != nil { + return fmt.Errorf("failed to ApplyBlock: %w", err) + } + err = m.store.SaveBlock(b1, commit) + if err != nil { + return fmt.Errorf("failed to save block: %w", err) + } + _, _, err = m.executor.Commit(ctx, newState, b1, responses) + if err != nil { + return fmt.Errorf("failed to Commit: %w", err) + } + m.store.SetHeight(b1.Header.Height) + + err = m.store.SaveBlockResponses(b1.Header.Height, responses) + if err != nil { + return fmt.Errorf("failed to save block responses: %w", err) + } + + if m.daHeight > newState.DAHeight { + newState.DAHeight = daHeight + } + m.lastState = newState + err = m.store.UpdateState(m.lastState) + if err != nil { + m.logger.Error("failed to save updated state", "error", err) + } + delete(m.syncCache, currentHeight+1) + } + + return nil +} + +func (m *Manager) getLastCommit() *types.Commit { + ptr := m.lastCommit.Load() + if ptr != nil { + return m.lastCommit.Load().(*types.Commit) + } + return nil +} + // RetrieveLoop is responsible for interacting with DA layer. func (m *Manager) RetrieveLoop(ctx context.Context) { // waitCh is used to signal the retrieve loop, that it should process next blocks @@ -365,6 +403,7 @@ func (m *Manager) publishBlock(ctx context.Context) error { } var block *types.Block + var commit *types.Commit // Check if there's an already stored block at a newer height // If there is use that instead of creating a new block @@ -385,7 +424,7 @@ func (m *Manager) publishBlock(ctx context.Context) error { if err != nil { return err } - commit := &types.Commit{ + commit = &types.Commit{ Height: block.Header.Height, HeaderHash: block.Header.Hash(), Signatures: []types.Signature{sign}, @@ -442,7 +481,7 @@ func (m *Manager) publishBlock(ctx context.Context) error { m.store.SetHeight(block.Header.Height) m.publishHeader(block) - m.publishCommit(lastCommit) + m.publishCommit(commit) return nil } diff --git a/node/node.go b/node/node.go index 37c7556528..f9e6ce931f 100644 --- a/node/node.go +++ b/node/node.go @@ -200,6 +200,15 @@ func (n *Node) headerPublishLoop(ctx context.Context) { if err != nil { n.Logger.Error("failed to gossip block header", "error", err) } + case commit := <-n.blockManager.CommitOutCh: + commitBytes, err := commit.MarshalBinary() + if err != nil { + n.Logger.Error("failed to serialize block commit", "error", err) + } + err = n.P2P.GossipCommit(ctx, commitBytes) + if err != nil { + n.Logger.Error("failed to gossip block commit", "error", err) + } case <-ctx.Done(): return } @@ -341,6 +350,7 @@ func (n *Node) newCommitValidator() p2p.GossipValidator { n.Logger.Error("failed to validate commit", "error", err) return false } + n.Logger.Debug("commit received", "height", commit.Height) n.blockManager.CommitInCh <- &commit return true } From ef0c9661bb3cf1350a99c15423b620eafb720eae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zdyba=C5=82?= Date: Fri, 5 Aug 2022 22:03:41 +0200 Subject: [PATCH 2/3] test: try to detect block synced with gossiped commit --- node/integration_test.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/node/integration_test.go b/node/integration_test.go index 58acdb4e04..f31ee00851 100644 --- a/node/integration_test.go +++ b/node/integration_test.go @@ -115,7 +115,9 @@ func TestTxGossipingAndAggregation(t *testing.T) { t.Fatal("failing after timeout") } - for _, n := range nodes { + require.NoError(nodes[0].Stop()) + time.Sleep(300 * time.Millisecond) + for _, n := range nodes[1:] { require.NoError(n.Stop()) } aggApp := apps[0] @@ -149,11 +151,11 @@ func TestTxGossipingAndAggregation(t *testing.T) { assert.GreaterOrEqual(commitCnt, adjustedHeight) // assert that all blocks known to node are same as produced by aggregator - for h := uint64(1); h <= nodes[i].Store.Height(); h++ { - nodeBlock, err := nodes[i].Store.LoadBlock(h) - require.NoError(err) + for h := uint64(1); h <= aggregatorHeight; h++ { aggBlock, err := nodes[0].Store.LoadBlock(h) require.NoError(err) + nodeBlock, err := nodes[i].Store.LoadBlock(h) + require.NoError(err) assert.Equal(aggBlock, nodeBlock) } } @@ -192,7 +194,7 @@ func createNode(n int, aggregator bool, dalc da.DataAvailabilityLayerClient, key ListenAddress: "/ip4/127.0.0.1/tcp/" + strconv.Itoa(startPort+n), } bmConfig := config.BlockManagerConfig{ - BlockTime: 1 * time.Second, + BlockTime: 300 * time.Millisecond, NamespaceID: [8]byte{8, 7, 6, 5, 4, 3, 2, 1}, } for i := 0; i < len(keys); i++ { From 358bf20a75725c08ef8acbe93b74426080caaa6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zdyba=C5=82?= Date: Tue, 23 Aug 2022 10:48:38 +0200 Subject: [PATCH 3/3] feat: broadcast SignedHeader --- block/manager.go | 46 ++--- node/integration_test.go | 7 +- node/node.go | 21 +- p2p/client.go | 4 +- proto/optimint/optimint.proto | 5 + types/block.go | 10 +- types/pb/dalc/dalc.pb.go | 6 +- types/pb/optimint/optimint.pb.go | 319 +++++++++++++++++++++++++++---- types/serialization.go | 40 ++++ types/validation.go | 27 ++- 10 files changed, 395 insertions(+), 90 deletions(-) diff --git a/block/manager.go b/block/manager.go index b75595f2bd..b2ae62220d 100644 --- a/block/manager.go +++ b/block/manager.go @@ -56,12 +56,11 @@ type Manager struct { // daHeight is the height of the latest processed DA block daHeight uint64 - HeaderOutCh chan *types.Header - HeaderInCh chan *types.Header + HeaderOutCh chan *types.SignedHeader + HeaderInCh chan *types.SignedHeader - CommitInCh chan *types.Commit - CommitOutCh chan *types.Commit - lastCommit atomic.Value + CommitInCh chan *types.Commit + lastCommit atomic.Value syncTarget uint64 blockInCh chan newBlockEvent @@ -138,10 +137,9 @@ func NewManager( retriever: dalc.(da.BlockRetriever), // TODO(tzdybal): do it in more gentle way (after MVP) daHeight: s.DAHeight, // channels are buffered to avoid blocking on input/output operations, buffer sizes are arbitrary - HeaderOutCh: make(chan *types.Header, 100), - HeaderInCh: make(chan *types.Header, 100), + HeaderOutCh: make(chan *types.SignedHeader, 100), + HeaderInCh: make(chan *types.SignedHeader, 100), CommitInCh: make(chan *types.Commit, 100), - CommitOutCh: make(chan *types.Commit, 100), blockInCh: make(chan newBlockEvent, 100), retrieveMtx: new(sync.Mutex), syncCache: make(map[uint64]*types.Block), @@ -195,8 +193,8 @@ func (m *Manager) SyncLoop(ctx context.Context) { case <-daTicker.C: m.retrieveCond.Signal() case header := <-m.HeaderInCh: - m.logger.Debug("block header received", "height", header.Height, "hash", header.Hash()) - newHeight := header.Height + m.logger.Debug("block header received", "height", header.Header.Height, "hash", header.Header.Hash()) + newHeight := header.Header.Height currentHeight := m.store.Height() // in case of client reconnecting after being offline // newHeight may be significantly larger than currentHeight @@ -205,6 +203,7 @@ func (m *Manager) SyncLoop(ctx context.Context) { atomic.StoreUint64(&m.syncTarget, newHeight) m.retrieveCond.Signal() } + m.CommitInCh <- &header.Commit case commit := <-m.CommitInCh: // TODO(tzdybal): check if it's from right aggregator m.lastCommit.Store(commit) @@ -235,6 +234,11 @@ func (m *Manager) SyncLoop(ctx context.Context) { } } +// trySyncNextBlock tries to progress one step (one block) in sync process. +// +// To be able to apply block and height h, we need to have its Commit. It is contained in block at height h+1. +// If block at height h+1 is not available, value of last gossiped commit is checked. +// If commit for block h is available, we proceed with sync process, and remove synced block from sync cache. func (m *Manager) trySyncNextBlock(ctx context.Context, daHeight uint64) error { var b1 *types.Block var commit *types.Commit @@ -277,7 +281,7 @@ func (m *Manager) trySyncNextBlock(ctx context.Context, daHeight uint64) error { return fmt.Errorf("failed to save block responses: %w", err) } - if m.daHeight > newState.DAHeight { + if daHeight > newState.DAHeight { newState.DAHeight = daHeight } m.lastState = newState @@ -293,10 +297,10 @@ func (m *Manager) trySyncNextBlock(ctx context.Context, daHeight uint64) error { func (m *Manager) getLastCommit() *types.Commit { ptr := m.lastCommit.Load() - if ptr != nil { - return m.lastCommit.Load().(*types.Commit) + if ptr == nil { + return nil } - return nil + return ptr.(*types.Commit) } // RetrieveLoop is responsible for interacting with DA layer. @@ -480,8 +484,7 @@ func (m *Manager) publishBlock(ctx context.Context) error { // Only update the stored height after successfully submitting to DA layer and committing to the DB m.store.SetHeight(block.Header.Height) - m.publishHeader(block) - m.publishCommit(commit) + m.publishSignedHeader(block, commit) return nil } @@ -507,8 +510,6 @@ func (m *Manager) submitBlockToDA(ctx context.Context, block *types.Block) error return fmt.Errorf("Failed to submit block to DA layer after %d attempts", maxSubmitAttempts) } - m.HeaderOutCh <- &block.Header - return nil } @@ -521,13 +522,8 @@ func (m *Manager) exponentialBackoff(backoff time.Duration) time.Duration { } // TODO(tzdybal): consider inlining -func (m *Manager) publishHeader(block *types.Block) { - m.HeaderOutCh <- &block.Header -} - -// TODO(tzdybal): consider inlining -func (m *Manager) publishCommit(commit *types.Commit) { - m.CommitOutCh <- commit +func (m *Manager) publishSignedHeader(block *types.Block, commit *types.Commit) { + m.HeaderOutCh <- &types.SignedHeader{Header: block.Header, Commit: *commit} } func updateState(s *types.State, res *abci.ResponseInitChain) { diff --git a/node/integration_test.go b/node/integration_test.go index f31ee00851..8d1dcdddd4 100644 --- a/node/integration_test.go +++ b/node/integration_test.go @@ -115,11 +115,10 @@ func TestTxGossipingAndAggregation(t *testing.T) { t.Fatal("failing after timeout") } - require.NoError(nodes[0].Stop()) - time.Sleep(300 * time.Millisecond) - for _, n := range nodes[1:] { + for _, n := range nodes { require.NoError(n.Stop()) } + time.Sleep(100 * time.Millisecond) aggApp := apps[0] apps = apps[1:] @@ -151,7 +150,7 @@ func TestTxGossipingAndAggregation(t *testing.T) { assert.GreaterOrEqual(commitCnt, adjustedHeight) // assert that all blocks known to node are same as produced by aggregator - for h := uint64(1); h <= aggregatorHeight; h++ { + for h := uint64(1); h <= nodes[i].Store.Height(); h++ { aggBlock, err := nodes[0].Store.LoadBlock(h) require.NoError(err) nodeBlock, err := nodes[i].Store.LoadBlock(h) diff --git a/node/node.go b/node/node.go index f9e6ce931f..bfb9b66f9a 100644 --- a/node/node.go +++ b/node/node.go @@ -191,23 +191,14 @@ func (n *Node) initGenesisChunks() error { func (n *Node) headerPublishLoop(ctx context.Context) { for { select { - case header := <-n.blockManager.HeaderOutCh: - headerBytes, err := header.MarshalBinary() + case signedHeader := <-n.blockManager.HeaderOutCh: + headerBytes, err := signedHeader.MarshalBinary() if err != nil { - n.Logger.Error("failed to serialize block header", "error", err) + n.Logger.Error("failed to serialize signed block header", "error", err) } - err = n.P2P.GossipHeader(ctx, headerBytes) + err = n.P2P.GossipSignedHeader(ctx, headerBytes) if err != nil { - n.Logger.Error("failed to gossip block header", "error", err) - } - case commit := <-n.blockManager.CommitOutCh: - commitBytes, err := commit.MarshalBinary() - if err != nil { - n.Logger.Error("failed to serialize block commit", "error", err) - } - err = n.P2P.GossipCommit(ctx, commitBytes) - if err != nil { - n.Logger.Error("failed to gossip block commit", "error", err) + n.Logger.Error("failed to gossip signed block header", "error", err) } case <-ctx.Done(): return @@ -318,7 +309,7 @@ func (n *Node) newTxValidator() p2p.GossipValidator { func (n *Node) newHeaderValidator() p2p.GossipValidator { return func(headerMsg *p2p.GossipMessage) bool { n.Logger.Debug("header received", "from", headerMsg.From, "bytes", len(headerMsg.Data)) - var header types.Header + var header types.SignedHeader err := header.UnmarshalBinary(headerMsg.Data) if err != nil { n.Logger.Error("failed to deserialize header", "error", err) diff --git a/p2p/client.go b/p2p/client.go index e97ed92559..f559dadcdc 100644 --- a/p2p/client.go +++ b/p2p/client.go @@ -160,8 +160,8 @@ func (c *Client) SetTxValidator(val GossipValidator) { c.txValidator = val } -// GossipHeader sends the block header to the P2P network. -func (c *Client) GossipHeader(ctx context.Context, headerBytes []byte) error { +// GossipSignedHeader sends the block header to the P2P network. +func (c *Client) GossipSignedHeader(ctx context.Context, headerBytes []byte) error { c.logger.Debug("Gossiping block header", "len", len(headerBytes)) return c.headerGossiper.Publish(ctx, headerBytes) } diff --git a/proto/optimint/optimint.proto b/proto/optimint/optimint.proto index e5ac1f73af..5489b5fcaf 100644 --- a/proto/optimint/optimint.proto +++ b/proto/optimint/optimint.proto @@ -63,6 +63,11 @@ message Commit { repeated bytes signatures = 3; } +message SignedHeader { + Header header = 1; + Commit commit = 2; +} + message Data { repeated bytes txs = 1; repeated bytes intermediate_state_roots = 2; diff --git a/types/block.go b/types/block.go index eb293439e3..02d4657bc0 100644 --- a/types/block.go +++ b/types/block.go @@ -74,13 +74,21 @@ type EvidenceData struct { Evidence []Evidence } -// Commit cointains evidence of block creation. +// Commit contains evidence of block creation. type Commit struct { Height uint64 HeaderHash [32]byte Signatures []Signature // most of the time this is a single signature } +// SignedHeader combines Header and its Commit. +// +// Used mostly for gossiping. +type SignedHeader struct { + Header Header + Commit Commit +} + // Signature represents signature of block creator. type Signature []byte diff --git a/types/pb/dalc/dalc.pb.go b/types/pb/dalc/dalc.pb.go index 561ede0d56..c89e04c32f 100644 --- a/types/pb/dalc/dalc.pb.go +++ b/types/pb/dalc/dalc.pb.go @@ -1218,7 +1218,7 @@ func (m *SubmitBlockResponse) Unmarshal(dAtA []byte) error { switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field BaseResult", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Result", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -1373,7 +1373,7 @@ func (m *CheckBlockAvailabilityResponse) Unmarshal(dAtA []byte) error { switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field BaseResult", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Result", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -1548,7 +1548,7 @@ func (m *RetrieveBlocksResponse) Unmarshal(dAtA []byte) error { switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field BaseResult", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Result", wireType) } var msglen int for shift := uint(0); ; shift += 7 { diff --git a/types/pb/optimint/optimint.pb.go b/types/pb/optimint/optimint.pb.go index c21e7d42e7..9a5112a048 100644 --- a/types/pb/optimint/optimint.pb.go +++ b/types/pb/optimint/optimint.pb.go @@ -290,6 +290,58 @@ func (m *Commit) GetSignatures() [][]byte { return nil } +type SignedHeader struct { + Header *Header `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"` + Commit *Commit `protobuf:"bytes,2,opt,name=commit,proto3" json:"commit,omitempty"` +} + +func (m *SignedHeader) Reset() { *m = SignedHeader{} } +func (m *SignedHeader) String() string { return proto.CompactTextString(m) } +func (*SignedHeader) ProtoMessage() {} +func (*SignedHeader) Descriptor() ([]byte, []int) { + return fileDescriptor_c876654a788c67ff, []int{3} +} +func (m *SignedHeader) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignedHeader) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignedHeader.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SignedHeader) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignedHeader.Merge(m, src) +} +func (m *SignedHeader) XXX_Size() int { + return m.Size() +} +func (m *SignedHeader) XXX_DiscardUnknown() { + xxx_messageInfo_SignedHeader.DiscardUnknown(m) +} + +var xxx_messageInfo_SignedHeader proto.InternalMessageInfo + +func (m *SignedHeader) GetHeader() *Header { + if m != nil { + return m.Header + } + return nil +} + +func (m *SignedHeader) GetCommit() *Commit { + if m != nil { + return m.Commit + } + return nil +} + type Data struct { Txs [][]byte `protobuf:"bytes,1,rep,name=txs,proto3" json:"txs,omitempty"` IntermediateStateRoots [][]byte `protobuf:"bytes,2,rep,name=intermediate_state_roots,json=intermediateStateRoots,proto3" json:"intermediate_state_roots,omitempty"` @@ -300,7 +352,7 @@ func (m *Data) Reset() { *m = Data{} } func (m *Data) String() string { return proto.CompactTextString(m) } func (*Data) ProtoMessage() {} func (*Data) Descriptor() ([]byte, []int) { - return fileDescriptor_c876654a788c67ff, []int{3} + return fileDescriptor_c876654a788c67ff, []int{4} } func (m *Data) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -360,7 +412,7 @@ func (m *Block) Reset() { *m = Block{} } func (m *Block) String() string { return proto.CompactTextString(m) } func (*Block) ProtoMessage() {} func (*Block) Descriptor() ([]byte, []int) { - return fileDescriptor_c876654a788c67ff, []int{4} + return fileDescriptor_c876654a788c67ff, []int{5} } func (m *Block) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -414,6 +466,7 @@ func init() { proto.RegisterType((*Version)(nil), "optimint.Version") proto.RegisterType((*Header)(nil), "optimint.Header") proto.RegisterType((*Commit)(nil), "optimint.Commit") + proto.RegisterType((*SignedHeader)(nil), "optimint.SignedHeader") proto.RegisterType((*Data)(nil), "optimint.Data") proto.RegisterType((*Block)(nil), "optimint.Block") } @@ -421,43 +474,45 @@ func init() { func init() { proto.RegisterFile("optimint/optimint.proto", fileDescriptor_c876654a788c67ff) } var fileDescriptor_c876654a788c67ff = []byte{ - // 568 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x54, 0x93, 0xc1, 0x6e, 0xd4, 0x30, - 0x10, 0x86, 0x9b, 0xee, 0x76, 0x77, 0x3b, 0x29, 0x65, 0x6b, 0xa1, 0x92, 0x52, 0x29, 0x94, 0x48, - 0x48, 0x05, 0xa4, 0xac, 0x5a, 0x84, 0xc4, 0x95, 0x02, 0x52, 0x11, 0xb7, 0x20, 0x71, 0xe0, 0xb2, - 0xf2, 0x26, 0xa3, 0xc4, 0x62, 0x13, 0x5b, 0xb6, 0xb7, 0x82, 0x37, 0x80, 0x1b, 0xaf, 0xc3, 0x1b, - 0x70, 0xec, 0x91, 0x23, 0x6a, 0x5f, 0x04, 0x79, 0x9c, 0x4d, 0xb6, 0x97, 0xc8, 0xfe, 0xff, 0x2f, - 0x13, 0xcf, 0xe4, 0x37, 0x3c, 0x94, 0xca, 0x8a, 0x5a, 0x34, 0x76, 0xb6, 0x5e, 0xa4, 0x4a, 0x4b, - 0x2b, 0xd9, 0x64, 0xbd, 0x7f, 0x74, 0x6c, 0xb1, 0x29, 0x50, 0x13, 0xc4, 0x17, 0xb9, 0x98, 0xd9, - 0xef, 0x0a, 0x8d, 0xc7, 0x92, 0x33, 0x18, 0x7f, 0x46, 0x6d, 0x84, 0x6c, 0xd8, 0x03, 0xd8, 0x59, - 0x2c, 0x65, 0xfe, 0x35, 0x0a, 0x4e, 0x82, 0xd3, 0x61, 0xe6, 0x37, 0x6c, 0x0a, 0x03, 0xae, 0x54, - 0xb4, 0x4d, 0x9a, 0x5b, 0x26, 0xbf, 0x07, 0x30, 0xba, 0x44, 0x5e, 0xa0, 0x66, 0x2f, 0x60, 0x7c, - 0xe5, 0xdf, 0xa6, 0x97, 0xc2, 0xf3, 0x83, 0xb4, 0x3b, 0x46, 0x5b, 0x36, 0x5b, 0x13, 0xec, 0x09, - 0xec, 0x35, 0xbc, 0x46, 0xa3, 0x78, 0x8e, 0x73, 0x51, 0x50, 0xc9, 0xbd, 0x2c, 0xec, 0xb4, 0x0f, - 0x05, 0x3b, 0x84, 0x51, 0x85, 0xa2, 0xac, 0x6c, 0x34, 0xa0, 0xef, 0xb5, 0x3b, 0xc6, 0x60, 0x68, - 0x45, 0x8d, 0xd1, 0x90, 0x54, 0x5a, 0xb3, 0x53, 0x98, 0x2e, 0xb9, 0xb1, 0xf3, 0x8a, 0x8e, 0x32, - 0xaf, 0xb8, 0xa9, 0xa2, 0x1d, 0x2a, 0xb9, 0xef, 0x74, 0x7f, 0xc2, 0x4b, 0x6e, 0xaa, 0x8e, 0xcc, - 0x65, 0x5d, 0x0b, 0xeb, 0xc9, 0x51, 0x4f, 0xbe, 0x25, 0x99, 0xc8, 0x63, 0xd8, 0x2d, 0xb8, 0xe5, - 0x1e, 0x19, 0x13, 0x32, 0x71, 0x02, 0x99, 0x4f, 0x61, 0x3f, 0x97, 0x8d, 0xc1, 0xc6, 0xac, 0x8c, - 0x27, 0x26, 0x44, 0xdc, 0xeb, 0x54, 0xc2, 0x8e, 0x60, 0xc2, 0x95, 0xf2, 0xc0, 0x2e, 0x01, 0x63, - 0xae, 0x14, 0x59, 0xcf, 0xe1, 0x80, 0x0e, 0xa2, 0xd1, 0xac, 0x96, 0xb6, 0x2d, 0x02, 0xc4, 0xdc, - 0x77, 0x46, 0xe6, 0x75, 0x62, 0x9f, 0xc1, 0x54, 0x69, 0xa9, 0xa4, 0x41, 0x3d, 0xe7, 0x45, 0xa1, - 0xd1, 0x98, 0x28, 0xf4, 0xe8, 0x5a, 0x7f, 0xe3, 0x65, 0x87, 0xf2, 0xb2, 0xd4, 0x58, 0x72, 0x2b, - 0x75, 0x5b, 0x75, 0xcf, 0xa3, 0x1b, 0xba, 0xab, 0x9a, 0x70, 0x18, 0xf9, 0x76, 0x37, 0x46, 0x1d, - 0xdc, 0x19, 0xf5, 0x63, 0x08, 0x37, 0x27, 0xea, 0x7f, 0x12, 0x54, 0xfd, 0x34, 0x63, 0x00, 0x23, - 0xca, 0x86, 0xdb, 0x95, 0x46, 0x13, 0x0d, 0x4e, 0x06, 0xce, 0xef, 0x95, 0xe4, 0x67, 0x00, 0xc3, - 0x77, 0xdc, 0x72, 0x97, 0x1c, 0xfb, 0xcd, 0x44, 0x01, 0x11, 0x6e, 0xc9, 0x5e, 0x43, 0x24, 0x1a, - 0x8b, 0xba, 0xc6, 0x42, 0x70, 0x8b, 0x73, 0x63, 0xdd, 0x53, 0x4b, 0x69, 0x4d, 0xb4, 0x4d, 0xd8, - 0xe1, 0xa6, 0xff, 0xc9, 0xd9, 0x99, 0x73, 0xd9, 0x2b, 0x98, 0xe0, 0x95, 0x28, 0xb0, 0xc9, 0x91, - 0x3e, 0x19, 0x9e, 0x1f, 0xa5, 0x7d, 0xac, 0x53, 0x17, 0xeb, 0xf4, 0x7d, 0x0b, 0x64, 0x1d, 0x9a, - 0xfc, 0x08, 0x60, 0xe7, 0x82, 0x62, 0x7c, 0xea, 0xda, 0x75, 0x3d, 0xb4, 0x41, 0x9d, 0xf6, 0x41, - 0xf5, 0x49, 0xc9, 0x5a, 0x9f, 0x25, 0x30, 0x74, 0xbf, 0x9c, 0x3a, 0x0f, 0xcf, 0xf7, 0x7b, 0xce, - 0x35, 0x95, 0x91, 0xc7, 0xce, 0x20, 0xdc, 0x48, 0x14, 0x85, 0xf5, 0x4e, 0x49, 0x3f, 0xe3, 0x0c, - 0xfa, 0x78, 0x5d, 0x7c, 0xfc, 0x73, 0x13, 0x07, 0xd7, 0x37, 0x71, 0xf0, 0xef, 0x26, 0x0e, 0x7e, - 0xdd, 0xc6, 0x5b, 0xd7, 0xb7, 0xf1, 0xd6, 0xdf, 0xdb, 0x78, 0xeb, 0xcb, 0x59, 0x29, 0x6c, 0xb5, - 0x5a, 0xa4, 0xb9, 0xac, 0x67, 0x39, 0x2e, 0xd1, 0x58, 0xc1, 0xa5, 0x2e, 0xbb, 0x0b, 0xed, 0xef, - 0xeb, 0x4c, 0x2d, 0x3a, 0x65, 0x31, 0xa2, 0xcb, 0xfb, 0xf2, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff, - 0xc5, 0xd1, 0xe5, 0xa6, 0xfe, 0x03, 0x00, 0x00, + // 593 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x94, 0xc1, 0x6e, 0xd3, 0x4c, + 0x10, 0xc7, 0xeb, 0x26, 0x4d, 0xd2, 0x49, 0xbe, 0x7e, 0xe9, 0x0a, 0x15, 0x97, 0x4a, 0xa6, 0x58, + 0x42, 0x2a, 0x20, 0x25, 0x6a, 0x11, 0x12, 0x57, 0x0a, 0x48, 0x45, 0xdc, 0x5c, 0x89, 0x03, 0x97, + 0x68, 0x6d, 0x8f, 0xec, 0x15, 0xb1, 0x77, 0xb5, 0xbb, 0xa9, 0xe0, 0x0d, 0xe0, 0xc6, 0xeb, 0xf0, + 0x06, 0x1c, 0x7b, 0xe4, 0x88, 0xda, 0x17, 0x41, 0x3b, 0xeb, 0xd8, 0xa9, 0xc4, 0x81, 0x4b, 0xb4, + 0xfb, 0xff, 0xff, 0x32, 0x3b, 0x33, 0x9e, 0x5d, 0xb8, 0x2f, 0x95, 0x15, 0x95, 0xa8, 0xed, 0x7c, + 0xbd, 0x98, 0x29, 0x2d, 0xad, 0x64, 0xa3, 0xf5, 0xfe, 0xc1, 0x91, 0xc5, 0x3a, 0x47, 0x4d, 0x10, + 0x4f, 0x33, 0x31, 0xb7, 0x5f, 0x14, 0x1a, 0x8f, 0xc5, 0xa7, 0x30, 0xfc, 0x80, 0xda, 0x08, 0x59, + 0xb3, 0x7b, 0xb0, 0x93, 0x2e, 0x65, 0xf6, 0x29, 0x0c, 0x8e, 0x83, 0x93, 0x7e, 0xe2, 0x37, 0x6c, + 0x0a, 0x3d, 0xae, 0x54, 0xb8, 0x4d, 0x9a, 0x5b, 0xc6, 0x3f, 0x7a, 0x30, 0xb8, 0x40, 0x9e, 0xa3, + 0x66, 0xcf, 0x60, 0x78, 0xe5, 0xff, 0x4d, 0x7f, 0x1a, 0x9f, 0xed, 0xcf, 0xda, 0x34, 0x9a, 0xb0, + 0xc9, 0x9a, 0x60, 0x8f, 0x60, 0x52, 0xf3, 0x0a, 0x8d, 0xe2, 0x19, 0x2e, 0x44, 0x4e, 0x21, 0x27, + 0xc9, 0xb8, 0xd5, 0xde, 0xe5, 0xec, 0x00, 0x06, 0x25, 0x8a, 0xa2, 0xb4, 0x61, 0x8f, 0xce, 0x6b, + 0x76, 0x8c, 0x41, 0xdf, 0x8a, 0x0a, 0xc3, 0x3e, 0xa9, 0xb4, 0x66, 0x27, 0x30, 0x5d, 0x72, 0x63, + 0x17, 0x25, 0xa5, 0xb2, 0x28, 0xb9, 0x29, 0xc3, 0x1d, 0x0a, 0xb9, 0xe7, 0x74, 0x9f, 0xe1, 0x05, + 0x37, 0x65, 0x4b, 0x66, 0xb2, 0xaa, 0x84, 0xf5, 0xe4, 0xa0, 0x23, 0x5f, 0x93, 0x4c, 0xe4, 0x11, + 0xec, 0xe6, 0xdc, 0x72, 0x8f, 0x0c, 0x09, 0x19, 0x39, 0x81, 0xcc, 0xc7, 0xb0, 0x97, 0xc9, 0xda, + 0x60, 0x6d, 0x56, 0xc6, 0x13, 0x23, 0x22, 0xfe, 0x6b, 0x55, 0xc2, 0x0e, 0x61, 0xc4, 0x95, 0xf2, + 0xc0, 0x2e, 0x01, 0x43, 0xae, 0x14, 0x59, 0x4f, 0x61, 0x9f, 0x12, 0xd1, 0x68, 0x56, 0x4b, 0xdb, + 0x04, 0x01, 0x62, 0xfe, 0x77, 0x46, 0xe2, 0x75, 0x62, 0x9f, 0xc0, 0x54, 0x69, 0xa9, 0xa4, 0x41, + 0xbd, 0xe0, 0x79, 0xae, 0xd1, 0x98, 0x70, 0xec, 0xd1, 0xb5, 0xfe, 0xca, 0xcb, 0x0e, 0xe5, 0x45, + 0xa1, 0xb1, 0xe0, 0x56, 0xea, 0x26, 0xea, 0xc4, 0xa3, 0x1b, 0xba, 0x8b, 0x1a, 0x73, 0x18, 0xf8, + 0x72, 0x37, 0x5a, 0x1d, 0xdc, 0x69, 0xf5, 0x43, 0x18, 0x6f, 0x76, 0xd4, 0x7f, 0x24, 0x28, 0xbb, + 0x6e, 0x46, 0x00, 0x46, 0x14, 0x35, 0xb7, 0x2b, 0x8d, 0x26, 0xec, 0x1d, 0xf7, 0x9c, 0xdf, 0x29, + 0x71, 0x0a, 0x93, 0x4b, 0x51, 0xd4, 0x98, 0x37, 0x33, 0x72, 0xe2, 0x0e, 0x72, 0xab, 0x66, 0x44, + 0xa6, 0xdd, 0x88, 0x78, 0x22, 0x69, 0x7c, 0x47, 0xfa, 0x4f, 0x44, 0xa7, 0xde, 0x21, 0x7d, 0xd2, + 0x49, 0xe3, 0xc7, 0xdf, 0x02, 0xe8, 0xbf, 0xe1, 0x96, 0xbb, 0xe9, 0xb4, 0x9f, 0x4d, 0x18, 0x50, + 0x16, 0x6e, 0xc9, 0x5e, 0x42, 0x28, 0x6a, 0x8b, 0xba, 0xc2, 0x5c, 0x70, 0x8b, 0x0b, 0x63, 0xdd, + 0xaf, 0x96, 0xd2, 0x9a, 0x70, 0x9b, 0xb0, 0x83, 0x4d, 0xff, 0xd2, 0xd9, 0x89, 0x73, 0xd9, 0x0b, + 0x18, 0xe1, 0x95, 0xc8, 0xb1, 0xce, 0x90, 0xca, 0x1a, 0x9f, 0x1d, 0xce, 0xba, 0xab, 0x33, 0x73, + 0x57, 0x67, 0xf6, 0xb6, 0x01, 0x92, 0x16, 0x8d, 0xbf, 0x06, 0xb0, 0x73, 0x4e, 0x57, 0xe5, 0xdf, + 0x2b, 0x8d, 0xa1, 0xef, 0xc6, 0xaa, 0xa9, 0x73, 0xaf, 0xe3, 0x5c, 0x51, 0x09, 0x79, 0xec, 0x14, + 0xc6, 0x1b, 0x53, 0x4b, 0x17, 0xe2, 0x6f, 0x2d, 0x81, 0x6e, 0x84, 0xcf, 0xdf, 0xff, 0xbc, 0x89, + 0x82, 0xeb, 0x9b, 0x28, 0xf8, 0x7d, 0x13, 0x05, 0xdf, 0x6f, 0xa3, 0xad, 0xeb, 0xdb, 0x68, 0xeb, + 0xd7, 0x6d, 0xb4, 0xf5, 0xf1, 0xb4, 0x10, 0xb6, 0x5c, 0xa5, 0xb3, 0x4c, 0x56, 0xf3, 0x0c, 0x97, + 0x68, 0xac, 0xe0, 0x52, 0x17, 0xed, 0xa3, 0xe1, 0xdf, 0x84, 0xb9, 0x4a, 0x5b, 0x25, 0x1d, 0xd0, + 0x03, 0xf1, 0xfc, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x99, 0xed, 0x29, 0xb9, 0x62, 0x04, 0x00, + 0x00, } func (m *Version) Marshal() (dAtA []byte, err error) { @@ -645,6 +700,53 @@ func (m *Commit) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *SignedHeader) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SignedHeader) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignedHeader) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Commit != nil { + { + size, err := m.Commit.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintOptimint(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.Header != nil { + { + size, err := m.Header.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintOptimint(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *Data) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -862,6 +964,23 @@ func (m *Commit) Size() (n int) { return n } +func (m *SignedHeader) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Header != nil { + l = m.Header.Size() + n += 1 + l + sovOptimint(uint64(l)) + } + if m.Commit != nil { + l = m.Commit.Size() + n += 1 + l + sovOptimint(uint64(l)) + } + return n +} + func (m *Data) Size() (n int) { if m == nil { return 0 @@ -1569,6 +1688,128 @@ func (m *Commit) Unmarshal(dAtA []byte) error { } return nil } +func (m *SignedHeader) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOptimint + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SignedHeader: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignedHeader: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOptimint + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthOptimint + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthOptimint + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Header == nil { + m.Header = &Header{} + } + if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Commit", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOptimint + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthOptimint + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthOptimint + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Commit == nil { + m.Commit = &Commit{} + } + if err := m.Commit.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipOptimint(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthOptimint + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *Data) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/types/serialization.go b/types/serialization.go index 9613b2fae2..5e5ddae737 100644 --- a/types/serialization.go +++ b/types/serialization.go @@ -62,6 +62,46 @@ func (c *Commit) UnmarshalBinary(data []byte) error { return err } +// ToProto converts SignedHeader into protobuf representation and returns it. +func (h *SignedHeader) ToProto() *pb.SignedHeader { + return &pb.SignedHeader{ + Header: h.Header.ToProto(), + Commit: h.Commit.ToProto(), + } +} + +// FromProto fills SignedHeader with data from protobuf representation. +func (h *SignedHeader) FromProto(other *pb.SignedHeader) error { + err := h.Header.FromProto(other.Header) + if err != nil { + return err + } + err = h.Commit.FromProto(other.Commit) + if err != nil { + return err + } + return nil +} + +// MarshalBinary encodes SignedHeader into binary form and returns it. +func (h *SignedHeader) MarshalBinary() ([]byte, error) { + return h.ToProto().Marshal() +} + +// UnmarshalBinary decodes binary form of SignedHeader into object. +func (h *SignedHeader) UnmarshalBinary(data []byte) error { + var pHeader pb.SignedHeader + err := pHeader.Unmarshal(data) + if err != nil { + return err + } + err = h.FromProto(&pHeader) + if err != nil { + return err + } + return nil +} + // ToProto converts Header into protobuf representation and returns it. func (h *Header) ToProto() *pb.Header { return &pb.Header{ diff --git a/types/validation.go b/types/validation.go index b55abfd83c..a6498e30fe 100644 --- a/types/validation.go +++ b/types/validation.go @@ -1,6 +1,10 @@ package types -import "errors" +import ( + "encoding/hex" + "errors" + "fmt" +) // ValidateBasic performs basic validation of a block. func (b *Block) ValidateBasic() error { @@ -46,3 +50,24 @@ func (c *Commit) ValidateBasic() error { } return nil } + +// ValidateBasic performs basic validation of a signed header. +func (h *SignedHeader) ValidateBasic() error { + err := h.Commit.ValidateBasic() + if err != nil { + return err + } + err = h.Header.ValidateBasic() + if err != nil { + return err + } + + if h.Commit.Height != h.Header.Height { + return fmt.Errorf("height missmatch - header height: %d, commit height: %d", h.Header.Height, h.Commit.Height) + } + if h.Commit.HeaderHash != h.Header.Hash() { + hash := h.Header.Hash() + return fmt.Errorf("hash missmatch - header hash: %s, commit hash: %s", hex.EncodeToString(hash[:]), hex.EncodeToString(h.Commit.HeaderHash[:])) + } + return nil +}