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  }