-
Notifications
You must be signed in to change notification settings - Fork 178
/
chain.go
320 lines (272 loc) · 8.24 KB
/
chain.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
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
package flow
import (
"fmt"
"github.com/pkg/errors"
"github.com/onflow/flow-go/utils/slices"
)
// A ChainID is a unique identifier for a specific Flow network instance.
//
// Chain IDs are used used to prevent replay attacks and to support network-specific address generation.
type ChainID string
type ChainIDList []ChainID
const (
// Mainnet is the chain ID for the mainnet chain.
Mainnet ChainID = "flow-mainnet"
// Long-lived test networks
// Testnet is the chain ID for the testnet chain.
Testnet ChainID = "flow-testnet"
// Sandboxnet is the chain ID for internal sandboxnet chain.
Sandboxnet ChainID = "flow-sandboxnet"
// Previewet is the chain ID for an external preview chain.
Previewnet ChainID = "flow-previewnet"
// Transient test networks
// Benchnet is the chain ID for the transient benchmarking chain.
Benchnet ChainID = "flow-benchnet"
// Localnet is the chain ID for the local development chain.
Localnet ChainID = "flow-localnet"
// Emulator is the chain ID for the emulated chain.
Emulator ChainID = "flow-emulator"
// BftTestnet is the chain ID for testing attack vector scenarios.
BftTestnet ChainID = "flow-bft-test-net"
// MonotonicEmulator is the chain ID for the emulated node chain with monotonic address generation.
MonotonicEmulator ChainID = "flow-emulator-monotonic"
)
// AllChainIDs returns a list of all supported chain IDs.
func AllChainIDs() ChainIDList {
return ChainIDList{
Mainnet,
Testnet,
Sandboxnet,
Previewnet,
Benchnet,
Localnet,
Emulator,
BftTestnet,
MonotonicEmulator,
}
}
// Transient returns whether the chain ID is for a transient network.
func (c ChainID) Transient() bool {
return c == Emulator || c == Localnet || c == Benchnet || c == BftTestnet || c == Previewnet
}
// getChainCodeWord derives the network type used for address generation from the globally
// configured chain ID.
func (c ChainID) getChainCodeWord() uint64 {
switch c {
case Mainnet:
return 0
case Testnet:
return invalidCodeTestNetwork
case Sandboxnet:
return invalidCodeSandboxNetwork
case Previewnet:
return invalidCodePreviewNetwork
case Emulator, Localnet, Benchnet, BftTestnet:
return invalidCodeTransientNetwork
default:
panic(fmt.Sprintf("chain ID [%s] is invalid or does not support linear code address generation", c))
}
}
type chainImpl interface {
newAddressGeneratorAtIndex(index uint64) AddressGenerator
// IsValid returns true if a given address is a valid account address on a given chain,
// and false otherwise.
//
// This is an off-chain check that only tells whether the address format is
// valid. If the function returns true, this does not mean
// a Flow account with this address has been generated. Such a test would
// require an on-chain check.
// zeroAddress() fails the check. Although it has a valid format, no account
// in Flow is assigned to zeroAddress().
IsValid(address Address) bool
// IndexFromAddress extracts the index used to generate the given address
IndexFromAddress(address Address) (uint64, error)
chain() ChainID
}
// monotonicImpl is a simple implementation of adress generation
// where addresses are simply the index of the account.
type monotonicImpl struct{}
func (m *monotonicImpl) newAddressGeneratorAtIndex(index uint64) AddressGenerator {
return &MonotonicAddressGenerator{
index: index,
}
}
// IsValid checks the validity of an address
func (m *monotonicImpl) IsValid(address Address) bool {
return address.uint64() > 0 && address.uint64() <= maxIndex
}
// IndexFromAddress returns the index used to generate the address
func (m *monotonicImpl) IndexFromAddress(address Address) (uint64, error) {
if !m.IsValid(address) {
return 0, errors.New("address is invalid")
}
return address.uint64(), nil
}
func (m *monotonicImpl) chain() ChainID {
return MonotonicEmulator
}
// linearCodeImpl is an implementation of the address generation
// using linear codes.
type linearCodeImpl struct {
chainID ChainID
}
func (l *linearCodeImpl) newAddressGeneratorAtIndex(index uint64) AddressGenerator {
return &linearCodeAddressGenerator{
index: index,
chainCodeWord: l.chainID.getChainCodeWord(),
}
}
// IsValid checks the validity of an address
func (l *linearCodeImpl) IsValid(address Address) bool {
codeWord := address.uint64()
codeWord ^= uint64(l.chainID.getChainCodeWord())
// zero is not a valid address
if codeWord == 0 {
return false
}
// check if address is a valid codeWord
return isValidCodeWord(codeWord)
}
// IndexFromAddress returns the index used to generate the address.
// It returns an error if the input is not a valid address.
func (l *linearCodeImpl) IndexFromAddress(address Address) (uint64, error) {
codeWord := address.uint64()
codeWord ^= uint64(l.chainID.getChainCodeWord())
// zero is not a valid address
if codeWord == 0 {
return 0, errors.New("address is invalid")
}
// check the address is valid code word
if !isValidCodeWord(codeWord) {
return 0, errors.New("address is invalid")
}
return decodeCodeWord(codeWord), nil
}
func (l *linearCodeImpl) chain() ChainID {
return l.chainID
}
type addressedChain struct {
chainImpl
}
var mainnet = &addressedChain{
chainImpl: &linearCodeImpl{
chainID: Mainnet,
},
}
var testnet = &addressedChain{
chainImpl: &linearCodeImpl{
chainID: Testnet,
},
}
var bftTestNet = &addressedChain{
chainImpl: &linearCodeImpl{
chainID: BftTestnet,
},
}
var sandboxnet = &addressedChain{
chainImpl: &linearCodeImpl{
chainID: Sandboxnet,
},
}
var previewnet = &addressedChain{
chainImpl: &linearCodeImpl{
chainID: Previewnet,
},
}
var benchnet = &addressedChain{
chainImpl: &linearCodeImpl{
chainID: Benchnet,
},
}
var localnet = &addressedChain{
chainImpl: &linearCodeImpl{
chainID: Localnet,
},
}
var emulator = &addressedChain{
chainImpl: &linearCodeImpl{
chainID: Emulator,
},
}
var monotonicEmulator = &addressedChain{
chainImpl: &monotonicImpl{},
}
// Chain returns the Chain corresponding to the string input
func (c ChainID) Chain() Chain {
switch c {
case Mainnet:
return mainnet
case Testnet:
return testnet
case Sandboxnet:
return sandboxnet
case Previewnet:
return previewnet
case Benchnet:
return benchnet
case Localnet:
return localnet
case Emulator:
return emulator
case MonotonicEmulator:
return monotonicEmulator
case BftTestnet:
return bftTestNet
default:
panic(fmt.Sprintf("chain ID [%s] is invalid ", c))
}
}
func (c ChainID) String() string {
return string(c)
}
// Chain is the interface for address generation implementations.
type Chain interface {
NewAddressGenerator() AddressGenerator
AddressAtIndex(index uint64) (Address, error)
ServiceAddress() Address
BytesToAddressGenerator(b []byte) AddressGenerator
IsValid(Address) bool
IndexFromAddress(address Address) (uint64, error)
String() string
ChainID() ChainID
// required for tests
zeroAddress() Address
newAddressGeneratorAtIndex(index uint64) AddressGenerator
}
// NewAddressGenerator returns a new AddressGenerator with an
// initialized index.
func (id *addressedChain) NewAddressGenerator() AddressGenerator {
return id.newAddressGeneratorAtIndex(0)
}
// AddressAtIndex returns the index-th generated account address.
func (id *addressedChain) AddressAtIndex(index uint64) (Address, error) {
if index > maxIndex {
return EmptyAddress, fmt.Errorf("index must be less or equal to %x", maxIndex)
}
return id.newAddressGeneratorAtIndex(index).CurrentAddress(), nil
}
// ServiceAddress returns the root (first) generated account address.
func (id *addressedChain) ServiceAddress() Address {
// returned error is guaranteed to be nil
address, _ := id.AddressAtIndex(1)
return address
}
// zeroAddress returns the "zero address" (account that no one owns).
func (id *addressedChain) zeroAddress() Address {
// returned error is guaranteed to be nil
address, _ := id.AddressAtIndex(0)
return address
}
// BytesToAddressGenerator converts an array of bytes into an address index
func (id *addressedChain) BytesToAddressGenerator(b []byte) AddressGenerator {
bytes := slices.EnsureByteSliceSize(b, addressIndexLength)
index := uint48(bytes[:])
return id.newAddressGeneratorAtIndex(index)
}
// ChainID returns the chain ID of the chain.
func (id *addressedChain) ChainID() ChainID {
return id.chain()
}
func (id *addressedChain) String() string {
return string(id.chain())
}