github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/model/flow/chain.go (about) 1 package flow 2 3 import ( 4 "fmt" 5 6 "github.com/pkg/errors" 7 8 "github.com/onflow/flow-go/utils/slices" 9 ) 10 11 // A ChainID is a unique identifier for a specific Flow network instance. 12 // 13 // Chain IDs are used used to prevent replay attacks and to support network-specific address generation. 14 type ChainID string 15 type ChainIDList []ChainID 16 17 const ( 18 // Mainnet is the chain ID for the mainnet chain. 19 Mainnet ChainID = "flow-mainnet" 20 21 // Long-lived test networks 22 23 // Testnet is the chain ID for the testnet chain. 24 Testnet ChainID = "flow-testnet" 25 // Sandboxnet is the chain ID for internal sandboxnet chain. 26 Sandboxnet ChainID = "flow-sandboxnet" 27 // Previewet is the chain ID for an external preview chain. 28 Previewnet ChainID = "flow-previewnet" 29 30 // Transient test networks 31 32 // Benchnet is the chain ID for the transient benchmarking chain. 33 Benchnet ChainID = "flow-benchnet" 34 // Localnet is the chain ID for the local development chain. 35 Localnet ChainID = "flow-localnet" 36 // Emulator is the chain ID for the emulated chain. 37 Emulator ChainID = "flow-emulator" 38 // BftTestnet is the chain ID for testing attack vector scenarios. 39 BftTestnet ChainID = "flow-bft-test-net" 40 41 // MonotonicEmulator is the chain ID for the emulated node chain with monotonic address generation. 42 MonotonicEmulator ChainID = "flow-emulator-monotonic" 43 ) 44 45 // AllChainIDs returns a list of all supported chain IDs. 46 func AllChainIDs() ChainIDList { 47 return ChainIDList{ 48 Mainnet, 49 Testnet, 50 Sandboxnet, 51 Previewnet, 52 Benchnet, 53 Localnet, 54 Emulator, 55 BftTestnet, 56 MonotonicEmulator, 57 } 58 } 59 60 // Transient returns whether the chain ID is for a transient network. 61 func (c ChainID) Transient() bool { 62 return c == Emulator || c == Localnet || c == Benchnet || c == BftTestnet || c == Previewnet 63 } 64 65 // getChainCodeWord derives the network type used for address generation from the globally 66 // configured chain ID. 67 func (c ChainID) getChainCodeWord() uint64 { 68 switch c { 69 case Mainnet: 70 return 0 71 case Testnet: 72 return invalidCodeTestNetwork 73 case Sandboxnet: 74 return invalidCodeSandboxNetwork 75 case Previewnet: 76 return invalidCodePreviewNetwork 77 case Emulator, Localnet, Benchnet, BftTestnet: 78 return invalidCodeTransientNetwork 79 default: 80 panic(fmt.Sprintf("chain ID [%s] is invalid or does not support linear code address generation", c)) 81 } 82 } 83 84 type chainImpl interface { 85 newAddressGeneratorAtIndex(index uint64) AddressGenerator 86 // IsValid returns true if a given address is a valid account address on a given chain, 87 // and false otherwise. 88 // 89 // This is an off-chain check that only tells whether the address format is 90 // valid. If the function returns true, this does not mean 91 // a Flow account with this address has been generated. Such a test would 92 // require an on-chain check. 93 // zeroAddress() fails the check. Although it has a valid format, no account 94 // in Flow is assigned to zeroAddress(). 95 IsValid(address Address) bool 96 // IndexFromAddress extracts the index used to generate the given address 97 IndexFromAddress(address Address) (uint64, error) 98 chain() ChainID 99 } 100 101 // monotonicImpl is a simple implementation of adress generation 102 // where addresses are simply the index of the account. 103 type monotonicImpl struct{} 104 105 func (m *monotonicImpl) newAddressGeneratorAtIndex(index uint64) AddressGenerator { 106 return &MonotonicAddressGenerator{ 107 index: index, 108 } 109 } 110 111 // IsValid checks the validity of an address 112 func (m *monotonicImpl) IsValid(address Address) bool { 113 return address.uint64() > 0 && address.uint64() <= maxIndex 114 } 115 116 // IndexFromAddress returns the index used to generate the address 117 func (m *monotonicImpl) IndexFromAddress(address Address) (uint64, error) { 118 if !m.IsValid(address) { 119 return 0, errors.New("address is invalid") 120 } 121 return address.uint64(), nil 122 } 123 124 func (m *monotonicImpl) chain() ChainID { 125 return MonotonicEmulator 126 } 127 128 // linearCodeImpl is an implementation of the address generation 129 // using linear codes. 130 type linearCodeImpl struct { 131 chainID ChainID 132 } 133 134 func (l *linearCodeImpl) newAddressGeneratorAtIndex(index uint64) AddressGenerator { 135 return &linearCodeAddressGenerator{ 136 index: index, 137 chainCodeWord: l.chainID.getChainCodeWord(), 138 } 139 } 140 141 // IsValid checks the validity of an address 142 func (l *linearCodeImpl) IsValid(address Address) bool { 143 codeWord := address.uint64() 144 codeWord ^= uint64(l.chainID.getChainCodeWord()) 145 146 // zero is not a valid address 147 if codeWord == 0 { 148 return false 149 } 150 // check if address is a valid codeWord 151 return isValidCodeWord(codeWord) 152 } 153 154 // IndexFromAddress returns the index used to generate the address. 155 // It returns an error if the input is not a valid address. 156 func (l *linearCodeImpl) IndexFromAddress(address Address) (uint64, error) { 157 codeWord := address.uint64() 158 codeWord ^= uint64(l.chainID.getChainCodeWord()) 159 160 // zero is not a valid address 161 if codeWord == 0 { 162 return 0, errors.New("address is invalid") 163 } 164 165 // check the address is valid code word 166 if !isValidCodeWord(codeWord) { 167 return 0, errors.New("address is invalid") 168 } 169 return decodeCodeWord(codeWord), nil 170 } 171 172 func (l *linearCodeImpl) chain() ChainID { 173 return l.chainID 174 } 175 176 type addressedChain struct { 177 chainImpl 178 } 179 180 var mainnet = &addressedChain{ 181 chainImpl: &linearCodeImpl{ 182 chainID: Mainnet, 183 }, 184 } 185 186 var testnet = &addressedChain{ 187 chainImpl: &linearCodeImpl{ 188 chainID: Testnet, 189 }, 190 } 191 192 var bftTestNet = &addressedChain{ 193 chainImpl: &linearCodeImpl{ 194 chainID: BftTestnet, 195 }, 196 } 197 198 var sandboxnet = &addressedChain{ 199 chainImpl: &linearCodeImpl{ 200 chainID: Sandboxnet, 201 }, 202 } 203 204 var previewnet = &addressedChain{ 205 chainImpl: &linearCodeImpl{ 206 chainID: Previewnet, 207 }, 208 } 209 210 var benchnet = &addressedChain{ 211 chainImpl: &linearCodeImpl{ 212 chainID: Benchnet, 213 }, 214 } 215 216 var localnet = &addressedChain{ 217 chainImpl: &linearCodeImpl{ 218 chainID: Localnet, 219 }, 220 } 221 222 var emulator = &addressedChain{ 223 chainImpl: &linearCodeImpl{ 224 chainID: Emulator, 225 }, 226 } 227 228 var monotonicEmulator = &addressedChain{ 229 chainImpl: &monotonicImpl{}, 230 } 231 232 // Chain returns the Chain corresponding to the string input 233 func (c ChainID) Chain() Chain { 234 switch c { 235 case Mainnet: 236 return mainnet 237 case Testnet: 238 return testnet 239 case Sandboxnet: 240 return sandboxnet 241 case Previewnet: 242 return previewnet 243 case Benchnet: 244 return benchnet 245 case Localnet: 246 return localnet 247 case Emulator: 248 return emulator 249 case MonotonicEmulator: 250 return monotonicEmulator 251 case BftTestnet: 252 return bftTestNet 253 default: 254 panic(fmt.Sprintf("chain ID [%s] is invalid ", c)) 255 } 256 } 257 258 func (c ChainID) String() string { 259 return string(c) 260 } 261 262 // Chain is the interface for address generation implementations. 263 type Chain interface { 264 NewAddressGenerator() AddressGenerator 265 AddressAtIndex(index uint64) (Address, error) 266 ServiceAddress() Address 267 BytesToAddressGenerator(b []byte) AddressGenerator 268 IsValid(Address) bool 269 IndexFromAddress(address Address) (uint64, error) 270 String() string 271 ChainID() ChainID 272 // required for tests 273 zeroAddress() Address 274 newAddressGeneratorAtIndex(index uint64) AddressGenerator 275 } 276 277 // NewAddressGenerator returns a new AddressGenerator with an 278 // initialized index. 279 func (id *addressedChain) NewAddressGenerator() AddressGenerator { 280 return id.newAddressGeneratorAtIndex(0) 281 } 282 283 // AddressAtIndex returns the index-th generated account address. 284 func (id *addressedChain) AddressAtIndex(index uint64) (Address, error) { 285 if index > maxIndex { 286 return EmptyAddress, fmt.Errorf("index must be less or equal to %x", maxIndex) 287 } 288 return id.newAddressGeneratorAtIndex(index).CurrentAddress(), nil 289 } 290 291 // ServiceAddress returns the root (first) generated account address. 292 func (id *addressedChain) ServiceAddress() Address { 293 // returned error is guaranteed to be nil 294 address, _ := id.AddressAtIndex(1) 295 return address 296 } 297 298 // zeroAddress returns the "zero address" (account that no one owns). 299 func (id *addressedChain) zeroAddress() Address { 300 // returned error is guaranteed to be nil 301 address, _ := id.AddressAtIndex(0) 302 return address 303 } 304 305 // BytesToAddressGenerator converts an array of bytes into an address index 306 func (id *addressedChain) BytesToAddressGenerator(b []byte) AddressGenerator { 307 bytes := slices.EnsureByteSliceSize(b, addressIndexLength) 308 309 index := uint48(bytes[:]) 310 return id.newAddressGeneratorAtIndex(index) 311 } 312 313 // ChainID returns the chain ID of the chain. 314 func (id *addressedChain) ChainID() ChainID { 315 return id.chain() 316 } 317 318 func (id *addressedChain) String() string { 319 return string(id.chain()) 320 }