From 016c15e675ae792288b2d78809457f0a1ae48275 Mon Sep 17 00:00:00 2001 From: Ganesha Upadhyaya Date: Tue, 6 Dec 2022 16:08:57 +0530 Subject: [PATCH 1/3] implementing generic header interface for rollmint header --- types/header.go | 159 +++++++++++++++++++++++++++++++++++++++++++ types/header_test.go | 49 +++++++++++++ types/validation.go | 14 +++- 3 files changed, 220 insertions(+), 2 deletions(-) create mode 100644 types/header.go create mode 100644 types/header_test.go diff --git a/types/header.go b/types/header.go new file mode 100644 index 0000000000..308f523d17 --- /dev/null +++ b/types/header.go @@ -0,0 +1,159 @@ +package types + +import ( + "bytes" + "encoding" + "fmt" + "time" +) + +// Header abstracts all methods required to perform header sync. +type H interface { + // New creates new instance of a header. + New() Header + // Hash returns hash of a header. + Hash() [32]byte + // Height returns the height of a header. + // Height() int64 + // LastHeader returns the hash of last header before this header (aka. previous header hash). + LastHeader() [32]byte + // Time returns time when header was created. + // Time() time.Time + // IsRecent checks if header is recent against the given blockTime. + IsRecent(duration time.Duration) bool + // IsExpired checks if header is expired against trusting period. + IsExpired() bool + // VerifyAdjacent validates adjacent untrusted header against trusted header. + VerifyAdjacent(Header) error + // VerifyNonAdjacent validates non-adjacent untrusted header against trusted header. + VerifyNonAdjacent(Header) error + // Verify performs basic verification of untrusted header. + Verify(Header) error + // Validate performs basic validation to check for missed/incorrect fields. + Validate() error + + encoding.BinaryMarshaler + encoding.BinaryUnmarshaler +} + +var _ H = (*Header)(nil) + +// New creates new instance of a header. +func (h *Header) New() Header { + return Header{} +} + +// LastHeader returns the hash of last header before this header (aka. previous header hash). +func (h *Header) LastHeader() [32]byte { + return h.LastHeaderHash +} + +// IsRecent checks if header is recent against the given blockTime. +func (h *Header) IsRecent(blockTime time.Duration) bool { + return time.Since(time.Unix(0, int64(h.Time))) <= blockTime +} + +// TODO(@Wondertan): We should request TrustingPeriod from the network's state params or +// listen for network params changes to always have a topical value. + +// TrustingPeriod is period through which we can trust a header's validators set. +// +// Should be significantly less than the unbonding period (e.g. unbonding +// period = 3 weeks, trusting period = 2 weeks). +// +// More specifically, trusting period + time needed to check headers + time +// needed to report and punish misbehavior should be less than the unbonding +// period. +var TrustingPeriod = 168 * time.Hour + +// IsExpired checks if header is expired against trusting period. +func (h *Header) IsExpired() bool { + expirationTime := time.Unix(0, int64(h.Time)).Add(TrustingPeriod) + return !expirationTime.After(time.Now()) +} + +// VerifyError is thrown on during VerifyAdjacent and VerifyNonAdjacent if verification fails. +type VerifyError struct { + Reason error +} + +func (vr *VerifyError) Error() string { + return fmt.Sprintf("header: verify: %s", vr.Reason.Error()) +} + +// ErrNonAdjacent is returned when Store is appended with a header not adjacent to the stored head. +type ErrNonAdjacent struct { + Head uint64 + Attempted uint64 +} + +func (ena *ErrNonAdjacent) Error() string { + return fmt.Sprintf("header/store: non-adjacent: head %d, attempted %d", ena.Head, ena.Attempted) +} + +// VerifyAdjacent validates adjacent untrusted header against trusted header. +func (h *Header) VerifyAdjacent(untrst Header) error { + if untrst.Height != h.Height+1 { + return &ErrNonAdjacent{ + Head: h.Height, + Attempted: untrst.Height, + } + } + + if err := h.Verify(untrst); err != nil { + return &VerifyError{Reason: err} + } + + // Check the validator hashes are the same + if !bytes.Equal(untrst.AggregatorsHash[:], h.AggregatorsHash[:]) { + return &VerifyError{ + fmt.Errorf("expected old header next validators (%X) to match those from new header (%X)", + h.AggregatorsHash, + untrst.AggregatorsHash, + ), + } + } + + return nil +} + +// VerifyNonAdjacent validates non-adjacent untrusted header against trusted header. +func (h *Header) VerifyNonAdjacent(untrst Header) error { + if err := h.Verify(untrst); err != nil { + return &VerifyError{Reason: err} + } + + // Ensure that untrusted commit has enough of trusted commit's power. + // err := h.ValidatorSet.VerifyCommitLightTrusting(eh.ChainID, untrst.Commit, light.DefaultTrustLevel) + // if err != nil { + // return &VerifyError{err} + // } + + return nil +} + +// clockDrift defines how much new header's time can drift into +// the future relative to the now time during verification. +var clockDrift = 10 * time.Second + +// Verify performs basic verification of untrusted header. +func (h *Header) Verify(untrst Header) error { + untrstTime := time.Unix(0, int64(untrst.Time)) + hTime := time.Unix(0, int64(h.Time)) + if !untrstTime.After(hTime) { + return fmt.Errorf("expected new untrusted header time %v to be after old header time %v", untrstTime, hTime) + } + + now := time.Now() + if !untrstTime.Before(now.Add(clockDrift)) { + return fmt.Errorf( + "new untrusted header has a time from the future %v (now: %v, clockDrift: %v)", untrstTime, now, clockDrift) + } + + return nil +} + +// Validate performs basic validation to check for missed/incorrect fields. +func (h *Header) Validate() error { + return h.ValidateBasic() +} diff --git a/types/header_test.go b/types/header_test.go new file mode 100644 index 0000000000..c3a082bd63 --- /dev/null +++ b/types/header_test.go @@ -0,0 +1,49 @@ +package types + +import ( + "testing" +) + +func TestNew(t *testing.T) { + +} + +func TestHash(t *testing.T) { + +} + +func TestHeight(t *testing.T) { + +} + +func TestLastHeader(t *testing.T) { + +} + +func TestTime(t *testing.T) { + +} + +func TestIsRecent(t *testing.T) { + +} + +func TestIsExpired(t *testing.T) { + +} + +func TestVerifyAdjacent(t *testing.T) { + +} + +func TestVerifyNonAdjacent(t *testing.T) { + +} + +func TestVerify(t *testing.T) { + +} + +func TestValidate(t *testing.T) { + +} diff --git a/types/validation.go b/types/validation.go index a6498e30fe..fd4b1a0810 100644 --- a/types/validation.go +++ b/types/validation.go @@ -4,6 +4,9 @@ import ( "encoding/hex" "errors" "fmt" + + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/version" ) // ValidateBasic performs basic validation of a block. @@ -28,8 +31,15 @@ func (b *Block) ValidateBasic() error { // ValidateBasic performs basic validation of a header. func (h *Header) ValidateBasic() error { - if len(h.ProposerAddress) == 0 { - return errors.New("no proposer address") + if h.Version.Block != version.BlockProtocol { + return fmt.Errorf("block protocol is incorrect: got: %d, want: %d ", h.Version.Block, version.BlockProtocol) + } + + if len(h.ProposerAddress) != crypto.AddressSize { + return fmt.Errorf( + "invalid ProposerAddress length; got: %d, expected: %d", + len(h.ProposerAddress), crypto.AddressSize, + ) } return nil From 9833f48359621751f5fbb826d222fd15f272dea2 Mon Sep 17 00:00:00 2001 From: Ganesha Upadhyaya Date: Wed, 7 Dec 2022 20:31:29 +0530 Subject: [PATCH 2/3] change interface methods from Height, Time, to Number, Timestamp, and add tests --- types/header.go | 168 ++++++++++++++++++++++++--------- types/header_test.go | 217 ++++++++++++++++++++++++++++++++++++++++++- types/validation.go | 14 +-- 3 files changed, 341 insertions(+), 58 deletions(-) diff --git a/types/header.go b/types/header.go index 308f523d17..3f339ca30e 100644 --- a/types/header.go +++ b/types/header.go @@ -3,32 +3,62 @@ package types import ( "bytes" "encoding" + "encoding/hex" "fmt" + "strings" "time" ) +// TODO: remove Hash and H definition after importing go-header package +type Hash []byte + +func (h Hash) String() string { + return strings.ToUpper(hex.EncodeToString(h)) +} + +func (h Hash) MarshalJSON() ([]byte, error) { + s := strings.ToUpper(hex.EncodeToString(h)) + jbz := make([]byte, len(s)+2) + jbz[0] = '"' + copy(jbz[1:], s) + jbz[len(jbz)-1] = '"' + return jbz, nil +} + +func (h *Hash) UnmarshalJSON(data []byte) error { + if len(data) < 2 || data[0] != '"' || data[len(data)-1] != '"' { + return fmt.Errorf("invalid hex string: %s", data) + } + bz2, err := hex.DecodeString(string(data[1 : len(data)-1])) + if err != nil { + return err + } + *h = bz2 + return nil +} + // Header abstracts all methods required to perform header sync. type H interface { // New creates new instance of a header. - New() Header + New() H // Hash returns hash of a header. Hash() [32]byte - // Height returns the height of a header. - // Height() int64 + // Number returns the height of a header. + Number() int64 // LastHeader returns the hash of last header before this header (aka. previous header hash). LastHeader() [32]byte - // Time returns time when header was created. - // Time() time.Time + // Timestamp returns time when header was created. + Timestamp() time.Time // IsRecent checks if header is recent against the given blockTime. IsRecent(duration time.Duration) bool // IsExpired checks if header is expired against trusting period. IsExpired() bool // VerifyAdjacent validates adjacent untrusted header against trusted header. - VerifyAdjacent(Header) error + VerifyAdjacent(H) error // VerifyNonAdjacent validates non-adjacent untrusted header against trusted header. - VerifyNonAdjacent(Header) error + VerifyNonAdjacent(H) error // Verify performs basic verification of untrusted header. - Verify(Header) error + Verify(H) error // Validate performs basic validation to check for missed/incorrect fields. Validate() error @@ -39,8 +69,18 @@ type H interface { var _ H = (*Header)(nil) // New creates new instance of a header. -func (h *Header) New() Header { - return Header{} +func (h *Header) New() H { + return &Header{} +} + +// Number returns the height of a header. +func (h *Header) Number() int64 { + return int64(h.Height) +} + +// Timestamp returns time when header was created. +func (h *Header) Timestamp() time.Time { + return time.Unix(int64(h.Time), 0) } // LastHeader returns the hash of last header before this header (aka. previous header hash). @@ -50,7 +90,7 @@ func (h *Header) LastHeader() [32]byte { // IsRecent checks if header is recent against the given blockTime. func (h *Header) IsRecent(blockTime time.Duration) bool { - return time.Since(time.Unix(0, int64(h.Time))) <= blockTime + return time.Since(time.Unix(int64(h.Time), 0)) <= blockTime } // TODO(@Wondertan): We should request TrustingPeriod from the network's state params or @@ -68,7 +108,7 @@ var TrustingPeriod = 168 * time.Hour // IsExpired checks if header is expired against trusting period. func (h *Header) IsExpired() bool { - expirationTime := time.Unix(0, int64(h.Time)).Add(TrustingPeriod) + expirationTime := time.Unix(int64(h.Time), 0).Add(TrustingPeriod) return !expirationTime.After(time.Now()) } @@ -81,35 +121,32 @@ func (vr *VerifyError) Error() string { return fmt.Sprintf("header: verify: %s", vr.Reason.Error()) } -// ErrNonAdjacent is returned when Store is appended with a header not adjacent to the stored head. -type ErrNonAdjacent struct { - Head uint64 - Attempted uint64 -} - -func (ena *ErrNonAdjacent) Error() string { - return fmt.Sprintf("header/store: non-adjacent: head %d, attempted %d", ena.Head, ena.Attempted) -} - // VerifyAdjacent validates adjacent untrusted header against trusted header. -func (h *Header) VerifyAdjacent(untrst Header) error { - if untrst.Height != h.Height+1 { - return &ErrNonAdjacent{ - Head: h.Height, - Attempted: untrst.Height, +func (h *Header) VerifyAdjacent(untrst H) error { + untrstH, ok := untrst.(*Header) + if !ok { + return &VerifyError{ + fmt.Errorf("%T is not of type %T", untrst, h), + } + } + + if untrstH.Height != h.Height+1 { + return &VerifyError{ + fmt.Errorf("headers must be adjacent in height: trusted %d, untrusted %d", h.Height, untrstH.Height), } } - if err := h.Verify(untrst); err != nil { + if err := verifyNewHeaderAndVals(h, untrstH); err != nil { return &VerifyError{Reason: err} } // Check the validator hashes are the same - if !bytes.Equal(untrst.AggregatorsHash[:], h.AggregatorsHash[:]) { + // TODO: next validator set is not available + if !bytes.Equal(untrstH.AggregatorsHash[:], h.AggregatorsHash[:]) { return &VerifyError{ fmt.Errorf("expected old header next validators (%X) to match those from new header (%X)", h.AggregatorsHash, - untrst.AggregatorsHash, + untrstH.AggregatorsHash, ), } } @@ -118,8 +155,24 @@ func (h *Header) VerifyAdjacent(untrst Header) error { } // VerifyNonAdjacent validates non-adjacent untrusted header against trusted header. -func (h *Header) VerifyNonAdjacent(untrst Header) error { - if err := h.Verify(untrst); err != nil { +func (h *Header) VerifyNonAdjacent(untrst H) error { + untrstH, ok := untrst.(*Header) + if !ok { + return &VerifyError{ + fmt.Errorf("%T is not of type %T", untrst, h), + } + } + if untrstH.Height == h.Height+1 { + return &VerifyError{ + fmt.Errorf( + "headers must be non adjacent in height: trusted %d, untrusted %d", + h.Height, + untrstH.Height, + ), + } + } + + if err := verifyNewHeaderAndVals(h, untrstH); err != nil { return &VerifyError{Reason: err} } @@ -134,25 +187,52 @@ func (h *Header) VerifyNonAdjacent(untrst Header) error { // clockDrift defines how much new header's time can drift into // the future relative to the now time during verification. -var clockDrift = 10 * time.Second - -// Verify performs basic verification of untrusted header. -func (h *Header) Verify(untrst Header) error { - untrstTime := time.Unix(0, int64(untrst.Time)) - hTime := time.Unix(0, int64(h.Time)) - if !untrstTime.After(hTime) { - return fmt.Errorf("expected new untrusted header time %v to be after old header time %v", untrstTime, hTime) +var maxClockDrift = 10 * time.Second + +// verifyNewHeaderAndVals performs basic verification of untrusted header. +func verifyNewHeaderAndVals(trusted, untrusted *Header) error { + if err := untrusted.ValidateBasic(); err != nil { + return fmt.Errorf("untrusted.ValidateBasic failed: %w", err) + } + + if untrusted.Height <= trusted.Height { + return fmt.Errorf("expected new header height %d to be greater than one of old header %d", + untrusted.Height, + trusted.Height) + } + + if !untrusted.Timestamp().After(trusted.Timestamp()) { + return fmt.Errorf("expected new header time %v to be after old header time %v", + untrusted.Time, + trusted.Time) } - now := time.Now() - if !untrstTime.Before(now.Add(clockDrift)) { - return fmt.Errorf( - "new untrusted header has a time from the future %v (now: %v, clockDrift: %v)", untrstTime, now, clockDrift) + if !untrusted.Timestamp().Before(time.Now().Add(maxClockDrift)) { + return fmt.Errorf("new header has a time from the future %v (now: %v; max clock drift: %v)", + untrusted.Time, + time.Now(), + maxClockDrift) } return nil } +// Verify combines both VerifyAdjacent and VerifyNonAdjacent functions. +func (h *Header) Verify(untrst H) error { + untrstH, ok := untrst.(*Header) + if !ok { + return &VerifyError{ + fmt.Errorf("%T is not of type %T", untrst, h), + } + } + + if untrstH.Height != h.Height+1 { + return h.VerifyNonAdjacent(untrst) + } + + return h.VerifyAdjacent(untrst) +} + // Validate performs basic validation to check for missed/incorrect fields. func (h *Header) Validate() error { return h.ValidateBasic() diff --git a/types/header_test.go b/types/header_test.go index c3a082bd63..45ca914065 100644 --- a/types/header_test.go +++ b/types/header_test.go @@ -1,49 +1,262 @@ package types import ( + "crypto/rand" "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) +func TestInterfaceCompatible1(t *testing.T) { + t.Parallel() + + require := require.New(t) + + // create random hashes + h := [][32]byte{} + for i := 0; i < 8; i++ { + var h1 [32]byte + n, err := rand.Read(h1[:]) + require.Equal(32, n) + require.NoError(err) + h = append(h, h1) + } + + cases := []struct { + name string + input *Header + }{ + {"empty", &Header{}}, + {"first", &Header{ + Version: Version{ + Block: 1, + App: 2, + }, + NamespaceID: NamespaceID{0, 1, 2, 3, 4, 5, 6, 7}, + Height: 3, + Time: 4567, + LastHeaderHash: h[0], + LastCommitHash: h[1], + DataHash: h[2], + ConsensusHash: h[3], + AppHash: h[4], + LastResultsHash: h[5], + ProposerAddress: []byte{4, 3, 2, 1}, + AggregatorsHash: h[6], + }, + }, + {"next", &Header{ + Version: Version{ + Block: 1, + App: 2, + }, + NamespaceID: NamespaceID{0, 1, 2, 3, 4, 5, 6, 7}, + Height: 4, + Time: 4567, + LastHeaderHash: h[0], + LastCommitHash: h[1], + DataHash: h[2], + ConsensusHash: h[3], + AppHash: h[4], + LastResultsHash: h[5], + ProposerAddress: []byte{4, 3, 2, 1}, + AggregatorsHash: h[6], + }, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + assert := assert.New(t) + var h H = c.input + _, ok := h.(H) + assert.True(ok) + }) + } +} + +type A struct { +} + +func (a *A) New() H { + return &A{} +} + +func (a *A) Number() int64 { + return 0 +} + +func (a *A) Timestamp() time.Time { + return time.Now() +} + +func (a *A) Hash() [32]byte { + return [32]byte{} +} + +func (a *A) IsExpired() bool { + return false +} + +func (a *A) IsRecent(duration time.Duration) bool { + return true +} + +func (a *A) LastHeader() [32]byte { + return [32]byte{} +} + +func (a *A) Verify(h H) error { + return nil +} + +func (a *A) VerifyAdjacent(h H) error { + return nil +} + +func (a *A) VerifyNonAdjacent(h H) error { + return nil +} + +func (a *A) Validate() error { + return nil +} + +func (a *A) MarshalBinary() ([]byte, error) { + return []byte{}, nil +} + +func (a *A) UnmarshalBinary(data []byte) error { + return nil +} + +func TestInterfaceCompatible(t *testing.T) { + assert := assert.New(t) + h := &Header{} + var a H = &A{} + err := h.VerifyAdjacent(a) + assert.Error(err) +} + func TestNew(t *testing.T) { + assert := assert.New(t) + h1 := &Header{Time: 123456} + h2 := h1.New() + assert.NotNil(h2) } func TestHash(t *testing.T) { + assert := assert.New(t) + h1 := &Header{Height: 123456} + + h := [32]byte{104, 232, 90, 84, 228, 141, 242, 116, 213, 125, 19, 72, 23, 49, 5, 255, 5, 82, 174, 209, 213, 171, 106, 106, 156, 227, 119, 225, 24, 130, 129, 185} + assert.Equal(h, h1.Hash()) } -func TestHeight(t *testing.T) { +func TestNumber(t *testing.T) { + assert := assert.New(t) + h1 := &Header{Height: 123456} + assert.Equal(h1.Number(), int64(123456)) } func TestLastHeader(t *testing.T) { } -func TestTime(t *testing.T) { +func TestTimestamp(t *testing.T) { + assert := assert.New(t) + h1 := &Header{Time: 123456} + t1 := h1.Timestamp() + assert.Equal(t1.Unix(), int64(123456)) } func TestIsRecent(t *testing.T) { + assert := assert.New(t) + h1 := &Header{Time: 123456} + t2 := time.Now() + h2 := &Header{Time: uint64(t2.Unix())} + + recent := h1.IsRecent(time.Hour) + assert.False(recent) + recent = h2.IsRecent(time.Hour) + assert.True(recent) } func TestIsExpired(t *testing.T) { + assert := assert.New(t) + h1 := &Header{Time: 123456} + t2 := time.Now() + h2 := &Header{Time: uint64(t2.Unix())} + + expired := h1.IsExpired() + assert.True(expired) + + expired = h2.IsExpired() + assert.False(expired) +} +var case_adj = []struct { + name string + input *Header +}{ + {"trusted", &Header{ProposerAddress: []byte("123"), Time: 123, Height: 1}}, + {"untrusted", &Header{ProposerAddress: []byte("123"), Time: 456, Height: 2}}, +} + +var case_non_adj = []struct { + name string + input *Header +}{ + {"trusted", &Header{ProposerAddress: []byte("123"), Time: 123, Height: 1}}, + {"untrusted", &Header{ProposerAddress: []byte("123"), Time: 456, Height: 3}}, } func TestVerifyAdjacent(t *testing.T) { + assert := assert.New(t) + + err := case_adj[0].input.VerifyAdjacent(case_adj[1].input) + assert.NoError(err) + err = case_non_adj[0].input.VerifyAdjacent(case_non_adj[1].input) + assert.Error(err) } func TestVerifyNonAdjacent(t *testing.T) { + assert := assert.New(t) + err := case_non_adj[0].input.VerifyNonAdjacent(case_non_adj[1].input) + assert.NoError(err) + + err = case_adj[0].input.VerifyNonAdjacent(case_adj[1].input) + assert.Error(err) } func TestVerify(t *testing.T) { + assert := assert.New(t) + + err := case_adj[0].input.Verify(case_adj[1].input) + assert.NoError(err) + err = case_non_adj[0].input.Verify(case_non_adj[1].input) + assert.NoError(err) } func TestValidate(t *testing.T) { + assert := assert.New(t) + h := &Header{} + err := h.Validate() + assert.Error(err) + h = &Header{ + ProposerAddress: []byte("123"), + } + err = h.Validate() + assert.NoError(err) } diff --git a/types/validation.go b/types/validation.go index fd4b1a0810..a6498e30fe 100644 --- a/types/validation.go +++ b/types/validation.go @@ -4,9 +4,6 @@ import ( "encoding/hex" "errors" "fmt" - - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/version" ) // ValidateBasic performs basic validation of a block. @@ -31,15 +28,8 @@ func (b *Block) ValidateBasic() error { // ValidateBasic performs basic validation of a header. func (h *Header) ValidateBasic() error { - if h.Version.Block != version.BlockProtocol { - return fmt.Errorf("block protocol is incorrect: got: %d, want: %d ", h.Version.Block, version.BlockProtocol) - } - - if len(h.ProposerAddress) != crypto.AddressSize { - return fmt.Errorf( - "invalid ProposerAddress length; got: %d, expected: %d", - len(h.ProposerAddress), crypto.AddressSize, - ) + if len(h.ProposerAddress) == 0 { + return errors.New("no proposer address") } return nil From a0c9a40ccb5f7f79341367d5ddebe182e6e2c7ad Mon Sep 17 00:00:00 2001 From: Ganesha Upadhyaya Date: Thu, 8 Dec 2022 10:29:30 +0530 Subject: [PATCH 3/3] fix linter bug, remove unwanted test, add lastheader test --- types/header_test.go | 74 +++----------------------------------------- 1 file changed, 4 insertions(+), 70 deletions(-) diff --git a/types/header_test.go b/types/header_test.go index 45ca914065..398c42c1e3 100644 --- a/types/header_test.go +++ b/types/header_test.go @@ -1,82 +1,12 @@ package types import ( - "crypto/rand" "testing" "time" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) -func TestInterfaceCompatible1(t *testing.T) { - t.Parallel() - - require := require.New(t) - - // create random hashes - h := [][32]byte{} - for i := 0; i < 8; i++ { - var h1 [32]byte - n, err := rand.Read(h1[:]) - require.Equal(32, n) - require.NoError(err) - h = append(h, h1) - } - - cases := []struct { - name string - input *Header - }{ - {"empty", &Header{}}, - {"first", &Header{ - Version: Version{ - Block: 1, - App: 2, - }, - NamespaceID: NamespaceID{0, 1, 2, 3, 4, 5, 6, 7}, - Height: 3, - Time: 4567, - LastHeaderHash: h[0], - LastCommitHash: h[1], - DataHash: h[2], - ConsensusHash: h[3], - AppHash: h[4], - LastResultsHash: h[5], - ProposerAddress: []byte{4, 3, 2, 1}, - AggregatorsHash: h[6], - }, - }, - {"next", &Header{ - Version: Version{ - Block: 1, - App: 2, - }, - NamespaceID: NamespaceID{0, 1, 2, 3, 4, 5, 6, 7}, - Height: 4, - Time: 4567, - LastHeaderHash: h[0], - LastCommitHash: h[1], - DataHash: h[2], - ConsensusHash: h[3], - AppHash: h[4], - LastResultsHash: h[5], - ProposerAddress: []byte{4, 3, 2, 1}, - AggregatorsHash: h[6], - }, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - assert := assert.New(t) - var h H = c.input - _, ok := h.(H) - assert.True(ok) - }) - } -} - type A struct { } @@ -165,7 +95,11 @@ func TestNumber(t *testing.T) { } func TestLastHeader(t *testing.T) { + assert := assert.New(t) + h := [32]byte{104, 232, 90, 84, 228, 141, 242, 116, 213, 125, 19, 72, 23, 49, 5, 255, 5, 82, 174, 209, 213, 171, 106, 106, 156, 227, 119, 225, 24, 130, 129, 185} + h1 := &Header{LastHeaderHash: h} + assert.Equal(h1.LastHeader(), h) } func TestTimestamp(t *testing.T) {