-
Notifications
You must be signed in to change notification settings - Fork 557
/
upgrade.go
135 lines (112 loc) · 3.87 KB
/
upgrade.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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package types
import (
"errors"
"fmt"
"slices"
"strings"
errorsmod "cosmossdk.io/errors"
connectiontypes "github.com/cosmos/ibc-go/v8/modules/core/03-connection/types"
)
// NewUpgrade creates a new Upgrade instance.
func NewUpgrade(upgradeFields UpgradeFields, timeout Timeout, nextSequenceSend uint64) Upgrade {
return Upgrade{
Fields: upgradeFields,
Timeout: timeout,
NextSequenceSend: nextSequenceSend,
}
}
// NewUpgradeFields returns a new ModifiableUpgradeFields instance.
func NewUpgradeFields(ordering Order, connectionHops []string, version string) UpgradeFields {
return UpgradeFields{
Ordering: ordering,
ConnectionHops: connectionHops,
Version: version,
}
}
// ValidateBasic performs a basic validation of the upgrade fields
func (u Upgrade) ValidateBasic() error {
if err := u.Fields.ValidateBasic(); err != nil {
return errorsmod.Wrap(err, "proposed upgrade fields are invalid")
}
if !u.Timeout.IsValid() {
return errorsmod.Wrap(ErrInvalidUpgrade, "upgrade timeout height and upgrade timeout timestamp cannot both be 0")
}
return nil
}
// ValidateBasic performs a basic validation of the proposed upgrade fields
func (uf UpgradeFields) ValidateBasic() error {
if !slices.Contains(connectiontypes.SupportedOrderings, uf.Ordering.String()) {
return errorsmod.Wrap(ErrInvalidChannelOrdering, uf.Ordering.String())
}
if len(uf.ConnectionHops) != 1 {
return errorsmod.Wrap(ErrTooManyConnectionHops, "current IBC version only supports one connection hop")
}
if strings.TrimSpace(uf.Version) == "" {
return errorsmod.Wrap(ErrInvalidChannelVersion, "version cannot be empty")
}
return nil
}
// UpgradeError defines an error that occurs during an upgrade.
type UpgradeError struct {
// err is the underlying error that caused the upgrade to fail.
// this error should not be written to state.
err error
// sequence is the upgrade sequence number of the upgrade that failed.
sequence uint64
}
var _ error = &UpgradeError{}
// NewUpgradeError returns a new UpgradeError instance.
func NewUpgradeError(upgradeSequence uint64, err error) *UpgradeError {
return &UpgradeError{
err: err,
sequence: upgradeSequence,
}
}
// Error implements the error interface, returning the underlying error which caused the upgrade to fail.
func (u *UpgradeError) Error() string {
return u.err.Error()
}
// Is returns true if the underlying error is of the given err type.
func (u *UpgradeError) Is(err error) bool {
return errors.Is(u.err, err)
}
// Unwrap returns the base error that caused the upgrade to fail.
func (u *UpgradeError) Unwrap() error {
baseError := u.err
for {
if err := errors.Unwrap(baseError); err != nil {
baseError = err
} else {
return baseError
}
}
}
// Cause implements the sdk error interface which uses this function to unwrap the error in various functions such as `wrappedError.Is()`.
// Cause returns the underlying error which caused the upgrade to fail.
func (u *UpgradeError) Cause() error {
return u.err
}
// GetErrorReceipt returns an error receipt with the code from the underlying error type stripped.
func (u *UpgradeError) GetErrorReceipt() ErrorReceipt {
// restoreErrorString defines a string constant included in error receipts.
// NOTE: Changing this const is state machine breaking as it is written into state.
const restoreErrorString = "restored channel to pre-upgrade state"
_, code, _ := errorsmod.ABCIInfo(u, false) // discard non-determinstic codespace and log values
return ErrorReceipt{
Sequence: u.sequence,
Message: fmt.Sprintf("ABCI code: %d: %s", code, restoreErrorString),
}
}
// IsUpgradeError returns true if err is of type UpgradeError or contained
// in the error chain of err and false otherwise.
func IsUpgradeError(err error) bool {
for {
_, ok := err.(*UpgradeError)
if ok {
return true
}
if err = errors.Unwrap(err); err == nil {
return false
}
}
}