forked from strangelove-ventures/interchaintest
-
Notifications
You must be signed in to change notification settings - Fork 0
/
chainspec.go
255 lines (219 loc) · 7.52 KB
/
chainspec.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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
package interchaintest
import (
"errors"
"fmt"
"sort"
"strings"
"sync"
"sync/atomic"
"github.com/strangelove-ventures/interchaintest/v8/ibc"
"go.uber.org/zap"
)
// ChainSpec is a wrapper around an ibc.ChainConfig
// that allows callers to easily reference one of the built-in chain configs
// and optionally provide overrides for some settings.
type ChainSpec struct {
// Name is the name of the built-in config to use as a basis for this chain spec.
// Required unless every other field is set.
Name string
// ChainName sets the Name of the embedded ibc.ChainConfig, i.e. the name of the chain.
ChainName string
// Version of the docker image to use.
// Must be set.
Version string
// NoHostMount is a pointers in ChainSpec
// so zero-overrides can be detected from omitted overrides.
NoHostMount *bool
// Embedded ChainConfig to allow for simple JSON definition of a ChainSpec.
ibc.ChainConfig
// How many validators and how many full nodes to use
// when instantiating the chain.
// If unspecified, NumValidators defaults to 2 and NumFullNodes defaults to 1.
NumValidators, NumFullNodes *int
// Generate the automatic suffix on demand when needed.
autoSuffixOnce sync.Once
autoSuffix string
}
// Config returns the underlying ChainConfig,
// with any overrides applied.
func (s *ChainSpec) Config(log *zap.Logger) (*ibc.ChainConfig, error) {
if s.Version == "" {
// Version must be set at top-level if not set in inlined config.
if len(s.ChainConfig.Images) == 0 || s.ChainConfig.Images[0].Version == "" {
return nil, errors.New("ChainSpec.Version must not be empty")
}
}
if len(s.ChainConfig.Images) > 0 {
for i, image := range s.ChainConfig.Images {
if err := image.Validate(); err != nil {
return nil, fmt.Errorf("ChainConfig.Images[%d] is invalid: %s", i, err)
}
}
}
if len(s.ExposeAdditionalPorts) > 0 {
s.ChainConfig.ExposeAdditionalPorts = append(s.ChainConfig.ExposeAdditionalPorts, s.ExposeAdditionalPorts...)
}
// s.Name and chainConfig.Name are interchangeable
if s.Name == "" && s.ChainConfig.Name != "" {
s.Name = s.ChainConfig.Name
} else if s.Name != "" && s.ChainConfig.Name == "" {
s.ChainConfig.Name = s.Name
}
// Empty name is only valid with a fully defined chain config.
if s.Name == "" {
// If ChainName is provided and ChainConfig.Name is not set, set it.
if s.ChainConfig.Name == "" && s.ChainName != "" {
s.ChainConfig.Name = s.ChainName
}
if !s.ChainConfig.IsFullyConfigured() {
return nil, errors.New("ChainSpec.Name required when not all config fields are set")
}
return s.applyConfigOverrides(s.ChainConfig)
}
builtinChainConfigs, err := initBuiltinChainConfig(log)
if err != nil {
return nil, fmt.Errorf("failed to get pre-configured chains: %w", err)
}
// Get built-in config.
// If chain doesn't have built in config, but is fully configured, register chain label.
cfg, ok := builtinChainConfigs[s.Name]
if !ok {
if !s.ChainConfig.IsFullyConfigured() {
availableChains := make([]string, 0, len(builtinChainConfigs))
for k := range builtinChainConfigs {
availableChains = append(availableChains, k)
}
sort.Strings(availableChains)
return nil, fmt.Errorf("no chain configuration for %s (available chains are: %s)", s.Name, strings.Join(availableChains, ", "))
}
cfg = ibc.ChainConfig{}
}
cfg = cfg.Clone()
// Apply any overrides from this ChainSpec.
cfg = cfg.MergeChainSpecConfig(s.ChainConfig)
coinType, err := cfg.VerifyCoinType()
if err != nil {
return nil, err
}
cfg.CoinType = coinType
// Apply remaining top-level overrides.
return s.applyConfigOverrides(cfg)
}
func (s *ChainSpec) applyConfigOverrides(cfg ibc.ChainConfig) (*ibc.ChainConfig, error) {
// If no ChainName provided, generate one based on the spec name.
cfg.Name = s.ChainName
if cfg.Name == "" {
cfg.Name = s.Name + s.suffix()
}
// If no ChainID provided, generate one -- prefer chain name but fall back to spec name.
if cfg.ChainID == "" {
prefix := s.ChainName
if prefix == "" {
prefix = s.Name
}
cfg.ChainID = prefix + s.suffix()
}
if s.NoHostMount != nil {
cfg.NoHostMount = *s.NoHostMount
}
if s.SkipGenTx {
cfg.SkipGenTx = true
}
if s.ModifyGenesis != nil {
cfg.ModifyGenesis = s.ModifyGenesis
}
if s.PreGenesis != nil {
cfg.PreGenesis = s.PreGenesis
}
if s.ModifyGenesisAmounts != nil {
cfg.ModifyGenesisAmounts = s.ModifyGenesisAmounts
}
if s.HostPortOverride != nil {
cfg.HostPortOverride = s.HostPortOverride
}
if s.Genesis != nil {
cfg.Genesis = s.Genesis
}
cfg.UsingChainIDFlagCLI = s.UsingChainIDFlagCLI
if cfg.CoinDecimals == nil {
evm := int64(18)
cosmos := int64(6)
thorchain := int64(8)
bitcoin := int64(8)
switch cfg.CoinType {
case "0", "2", "3", "145":
cfg.CoinDecimals = &bitcoin
case "60":
cfg.CoinDecimals = &evm
case "118", "330", "529":
cfg.CoinDecimals = &cosmos
case "931":
cfg.CoinDecimals = &thorchain
}
}
// Set the version depending on the chain type.
switch cfg.Type {
case "cosmos", "namada":
if s.Version != "" && len(cfg.Images) > 0 {
cfg.Images[0].Version = s.Version
}
case "penumbra":
versionSplit := strings.Split(s.Version, ",")
if len(versionSplit) != 2 {
return nil, errors.New("penumbra version should be comma separated penumbra_version,tendermint_version")
}
if cfg.Images[0].Version == "" {
cfg.Images[0].Version = versionSplit[1]
}
if cfg.Images[1].Version == "" {
cfg.Images[1].Version = versionSplit[0]
}
case "polkadot":
// Only set if ChainSpec's Version is set, if not, Version from Images must be set.
if s.Version != "" {
versionSplit := strings.Split(s.Version, ",")
relayChainImageSplit := strings.Split(versionSplit[0], ":")
var relayChainVersion string
if len(relayChainImageSplit) > 1 {
if relayChainImageSplit[0] != "seunlanlege/centauri-polkadot" &&
relayChainImageSplit[0] != "polkadot" {
return nil, fmt.Errorf("only polkadot is supported as the relay chain node. got: %s", relayChainImageSplit[0])
}
relayChainVersion = relayChainImageSplit[1]
} else {
relayChainVersion = relayChainImageSplit[0]
}
cfg.Images[0].Version = relayChainVersion
switch {
case strings.Contains(s.Name, "composable"):
if len(versionSplit) != 2 {
return nil, fmt.Errorf("unexpected composable version: %s. should be comma separated polkadot:version,composable:version", s.Version)
}
imageSplit := strings.Split(versionSplit[1], ":")
if len(imageSplit) != 2 {
return nil, fmt.Errorf("parachain versions should be in the format parachain_name:parachain_version, got: %s", versionSplit[1])
}
if !strings.Contains(cfg.Images[1].Repository, imageSplit[0]) {
return nil, fmt.Errorf("unexpected parachain: %s", imageSplit[0])
}
cfg.Images[1].Version = imageSplit[1]
default:
return nil, fmt.Errorf("unexpected parachain: %s", s.Name)
}
} else if len(s.ChainConfig.Images) < 2 || s.ChainConfig.Images[1].Version == "" {
// Ensure there are at least two images and check the 2nd version is populated
return nil, fmt.Errorf("ChainCongfig.Images must be >1 and ChainConfig.Images[1].Version must not be empty")
}
}
return &cfg, nil
}
// suffix returns the automatically generated, concurrency-safe suffix for
// generating a chain name or chain ID.
func (s *ChainSpec) suffix() string {
s.autoSuffixOnce.Do(func() {
s.autoSuffix = fmt.Sprintf("-%d", atomic.AddInt32(&suffixCounter, 1))
})
return s.autoSuffix
}
// suffixCounter is a package-level counter for safely generating unique suffixes per execution environment.
var suffixCounter int32