-
Notifications
You must be signed in to change notification settings - Fork 0
/
trace.go
194 lines (158 loc) · 5.71 KB
/
trace.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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
package types
import (
"crypto/sha256"
"encoding/hex"
"fmt"
sdk "github.com/ci123chain/ci123chain/pkg/abci/types"
sdkerrors "github.com/ci123chain/ci123chain/pkg/abci/types/errors"
"github.com/ci123chain/ci123chain/pkg/ibc/core/host"
tmbytes "github.com/tendermint/tendermint/libs/bytes"
tmtypes "github.com/tendermint/tendermint/types"
"sort"
"strings"
)
// ParseHexHash parses a hex hash in string format to bytes and validates its correctness.
func ParseHexHash(hexHash string) (tmbytes.HexBytes, error) {
hash, err := hex.DecodeString(hexHash)
if err != nil {
return nil, err
}
if err := tmtypes.ValidateHash(hash); err != nil {
return nil, err
}
return hash, nil
}
// GetFullDenomPath returns the full denomination according to the ICS20 specification:
// tracePath + "/" + baseDenom
// If there exists no trace then the base denomination is returned.
func (dt DenomTrace) GetFullDenomPath() string {
if dt.Path == "" {
return dt.BaseDenom
}
return dt.GetPrefix() + dt.BaseDenom
}
// Hash returns the hex bytes of the SHA256 hash of the DenomTrace fields using the following formula:
//
// hash = sha256(tracePath + "/" + baseDenom)
func (dt DenomTrace) Hash() tmbytes.HexBytes {
hash := sha256.Sum256([]byte(dt.GetFullDenomPath()))
return hash[:]
}
// GetPrefix returns the receiving denomination prefix composed by the trace info and a separator.
func (dt DenomTrace) GetPrefix() string {
return dt.Path + "/"
}
// IBCDenom a coin denomination for an ICS20 fungible token in the format
// 'ibc/{hash(tracePath + baseDenom)}'. If the trace is empty, it will return the base denomination.
func (dt DenomTrace) IBCDenom() string {
if dt.Path != "" {
return fmt.Sprintf("%s/%s", DenomPrefix, dt.Hash())
}
return dt.BaseDenom
}
// Validate performs a basic validation of the DenomTrace fields.
func (dt DenomTrace) Validate() error {
// empty trace is accepted when token lives on the original chain
switch {
case dt.Path == "" && dt.BaseDenom != "":
return nil
case strings.TrimSpace(dt.BaseDenom) == "":
return fmt.Errorf("base denomination cannot be blank")
}
// NOTE: no base denomination validation
identifiers := strings.Split(dt.Path, "/")
return validateTraceIdentifiers(identifiers)
}
// ValidateIBCDenom validates that the given denomination is either:
//
// - A valid base denomination (eg: 'uatom')
// - A valid fungible token representation (i.e 'ibc/{hash}') per ADR 001 https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-001-coin-source-tracing.md
func ValidateIBCDenom(denom string) error {
if err := sdk.ValidateDenom(denom); err != nil {
return err
}
denomSplit := strings.SplitN(denom, "/", 2)
switch {
case strings.TrimSpace(denom) == "",
len(denomSplit) == 1 && denomSplit[0] == DenomPrefix,
len(denomSplit) == 2 && (denomSplit[0] != DenomPrefix || strings.TrimSpace(denomSplit[1]) == ""):
return sdkerrors.Wrapf(ErrInvalidDenomForTransfer, "denomination should be prefixed with the format 'ibc/{hash(trace + \"/\" + %s)}'", denom)
case denomSplit[0] == denom && strings.TrimSpace(denom) != "":
return nil
}
if _, err := ParseHexHash(denomSplit[1]); err != nil {
return sdkerrors.Wrapf(err, "invalid denom trace hash %s", denomSplit[1])
}
return nil
}
func validateTraceIdentifiers(identifiers []string) error {
if len(identifiers) == 0 || len(identifiers)%2 != 0 {
return fmt.Errorf("trace info must come in pairs of port and channel identifiers '{portID}/{channelID}', got the identifiers: %s", identifiers)
}
// validate correctness of port and channel identifiers
for i := 0; i < len(identifiers); i += 2 {
if err := host.PortIdentifierValidator(identifiers[i]); err != nil {
return sdkerrors.Wrapf(err, "invalid port ID at position %d", i)
}
if err := host.ChannelIdentifierValidator(identifiers[i+1]); err != nil {
return sdkerrors.Wrapf(err, "invalid channel ID at position %d", i)
}
}
return nil
}
// ParseDenomTrace parses a string with the ibc prefix (denom trace) and the base denomination
// into a DenomTrace type.
//
// Examples:
//
// - "portidone/channelidone/uatom" => DenomTrace{Path: "portidone/channelidone", BaseDenom: "uatom"}
// - "uatom" => DenomTrace{Path: "", BaseDenom: "uatom"}
func ParseDenomTrace(rawDenom string) DenomTrace {
denomSplit := strings.Split(rawDenom, "/")
if denomSplit[0] == rawDenom {
return DenomTrace{
Path: "",
BaseDenom: rawDenom,
}
}
return DenomTrace{
Path: strings.Join(denomSplit[:len(denomSplit)-1], "/"),
BaseDenom: denomSplit[len(denomSplit)-1],
}
}
type Traces []DenomTrace
// DefaultGenesisState returns a GenesisState with "transfer" as the default PortID.
func DefaultGenesisState() *GenesisState {
return &GenesisState{
PortId: PortID,
DenomTraces: Traces{},
Params: DefaultParams(),
}
}
// Validate performs a basic validation of each denomination trace info.
func (t Traces) Validate() error {
seenTraces := make(map[string]bool)
for i, trace := range t {
hash := trace.Hash().String()
if seenTraces[hash] {
return fmt.Errorf("duplicated denomination trace with hash %s", trace.Hash())
}
if err := trace.Validate(); err != nil {
return sdkerrors.Wrapf(err, "failed denom trace %d validation", i)
}
seenTraces[hash] = true
}
return nil
}
var _ sort.Interface = Traces{}
// Len implements sort.Interface for Traces
func (t Traces) Len() int { return len(t) }
// Less implements sort.Interface for Traces
func (t Traces) Less(i, j int) bool { return t[i].GetFullDenomPath() < t[j].GetFullDenomPath() }
// Swap implements sort.Interface for Traces
func (t Traces) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
// Sort is a helper function to sort the set of denomination traces in-place
func (t Traces) Sort() Traces {
sort.Sort(t)
return t
}