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  }