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