github.com/koko1123/flow-go-1@v0.29.6/model/flow/chain.go (about)

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