-
Notifications
You must be signed in to change notification settings - Fork 16
/
verify.go
107 lines (93 loc) · 3.1 KB
/
verify.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package header
import (
"errors"
"fmt"
"time"
)
// Verify verifies untrusted Header against trusted following general Header checks and
// custom user-specific checks defined in Header.Verify.
//
// Given headers must be non-zero
// Always returns VerifyError.
func Verify[H Header[H]](trstd, untrstd H) error {
// general mandatory verification
err := verify[H](trstd, untrstd)
if err != nil {
return &VerifyError{Reason: err}
}
// user defined verification
err = trstd.Verify(untrstd)
if err == nil {
return nil
}
// if that's an error, ensure we always return VerifyError
var verErr *VerifyError
if !errors.As(err, &verErr) {
verErr = &VerifyError{Reason: err}
}
// check adjacency of failed verification
adjacent := untrstd.Height() == trstd.Height()+1
if !adjacent {
// if non-adjacent, we don't know if the header is *really* wrong
// so set as soft
verErr.SoftFailure = true
}
// we trust adjacent verification to it's fullest
// if verification fails - the header is *really* wrong
return verErr
}
// verify is a little bro of Verify yet performs mandatory Header checks
// for any Header implementation.
func verify[H Header[H]](trstd, untrstd H) error {
if trstd.IsZero() {
return ErrZeroHeader
}
if untrstd.IsZero() {
return ErrZeroHeader
}
if untrstd.ChainID() != trstd.ChainID() {
return fmt.Errorf("%w: '%s' != '%s'", ErrWrongChainID, untrstd.ChainID(), trstd.ChainID())
}
if untrstd.Time().Before(trstd.Time()) {
return fmt.Errorf("%w: timestamp '%s' < current '%s'", ErrUnorderedTime, formatTime(untrstd.Time()), formatTime(trstd.Time()))
}
now := time.Now()
if untrstd.Time().After(now.Add(clockDrift)) {
return fmt.Errorf("%w: timestamp '%s' > now '%s', clock_drift '%v'", ErrFromFuture, formatTime(untrstd.Time()), formatTime(now), clockDrift)
}
known := untrstd.Height() <= trstd.Height()
if known {
return fmt.Errorf("%w: '%d' <= current '%d'", ErrKnownHeader, untrstd.Height(), trstd.Height())
}
return nil
}
var (
ErrZeroHeader = errors.New("zero header")
ErrWrongChainID = errors.New("wrong chain id")
ErrUnorderedTime = errors.New("unordered headers")
ErrFromFuture = errors.New("header is from the future")
ErrKnownHeader = errors.New("known header")
)
// VerifyError is thrown if a Header failed verification.
type VerifyError struct {
// Reason why verification failed as inner error.
Reason error
// SoftFailure means verification did not have enough information to definitively conclude a
// Header was correct or not.
// May happen with recent Headers during unfinished historical sync or because of local errors.
// TODO(@Wondertan): Better be part of signature Header.Verify() (bool, error), but kept here
// not to break
SoftFailure bool
}
func (vr *VerifyError) Error() string {
return fmt.Sprintf("header verification failed: %s", vr.Reason)
}
func (vr *VerifyError) Unwrap() error {
return vr.Reason
}
// 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
func formatTime(t time.Time) string {
return t.UTC().Format(time.RFC3339)
}