github.com/onflow/flow-go@v0.33.17/cmd/util/ledger/migrations/change_contract_code_migration.go (about)

     1  package migrations
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"sync"
     7  
     8  	coreContracts "github.com/onflow/flow-core-contracts/lib/go/contracts"
     9  	ftContracts "github.com/onflow/flow-ft/lib/go/contracts"
    10  	nftContracts "github.com/onflow/flow-nft/lib/go/contracts"
    11  	"github.com/rs/zerolog"
    12  
    13  	sdk "github.com/onflow/flow-go-sdk"
    14  
    15  	"github.com/onflow/cadence/runtime/common"
    16  
    17  	evm "github.com/onflow/flow-go/fvm/evm/stdlib"
    18  	"github.com/onflow/flow-go/fvm/systemcontracts"
    19  	"github.com/onflow/flow-go/ledger"
    20  	"github.com/onflow/flow-go/ledger/common/convert"
    21  	"github.com/onflow/flow-go/model/flow"
    22  )
    23  
    24  type ChangeContractCodeMigration struct {
    25  	log       zerolog.Logger
    26  	mutex     sync.RWMutex
    27  	contracts map[common.Address]map[flow.RegisterID]string
    28  }
    29  
    30  var _ AccountBasedMigration = (*ChangeContractCodeMigration)(nil)
    31  
    32  func (d *ChangeContractCodeMigration) Close() error {
    33  	d.mutex.RLock()
    34  	defer d.mutex.RUnlock()
    35  
    36  	if len(d.contracts) > 0 {
    37  		return fmt.Errorf("failed to find all contract registers that need to be changed")
    38  	}
    39  
    40  	return nil
    41  }
    42  
    43  func (d *ChangeContractCodeMigration) InitMigration(
    44  	log zerolog.Logger,
    45  	_ []*ledger.Payload,
    46  	_ int,
    47  ) error {
    48  	d.log = log.
    49  		With().
    50  		Str("migration", "ChangeContractCodeMigration").
    51  		Logger()
    52  
    53  	return nil
    54  }
    55  
    56  func (d *ChangeContractCodeMigration) MigrateAccount(
    57  	_ context.Context,
    58  	address common.Address,
    59  	payloads []*ledger.Payload,
    60  ) ([]*ledger.Payload, error) {
    61  
    62  	contracts, ok := (func() (map[flow.RegisterID]string, bool) {
    63  		d.mutex.Lock()
    64  		defer d.mutex.Unlock()
    65  
    66  		contracts, ok := d.contracts[address]
    67  
    68  		// remove address from set of addresses
    69  		// to keep track of which addresses are left to change
    70  		delete(d.contracts, address)
    71  
    72  		return contracts, ok
    73  	})()
    74  
    75  	if !ok {
    76  		// no contracts to change on this address
    77  		return payloads, nil
    78  	}
    79  
    80  	for payloadIndex, payload := range payloads {
    81  		key, err := payload.Key()
    82  		if err != nil {
    83  			return nil, err
    84  		}
    85  
    86  		registerID, err := convert.LedgerKeyToRegisterID(key)
    87  		if err != nil {
    88  			return nil, err
    89  		}
    90  
    91  		newContract, ok := contracts[registerID]
    92  		if !ok {
    93  			// not a contract register, or
    94  			// not interested in this contract
    95  			continue
    96  		}
    97  
    98  		// change contract code
    99  		payloads[payloadIndex] = ledger.NewPayload(
   100  			key,
   101  			[]byte(newContract),
   102  		)
   103  
   104  		// TODO: maybe log diff between old and new
   105  
   106  		// remove contract from list of contracts to change
   107  		// to keep track of which contracts are left to change
   108  		delete(contracts, registerID)
   109  	}
   110  
   111  	if len(contracts) > 0 {
   112  		return nil, fmt.Errorf("failed to find all contract registers that need to be changed")
   113  	}
   114  
   115  	return payloads, nil
   116  }
   117  
   118  func (d *ChangeContractCodeMigration) RegisterContractChange(
   119  	address common.Address,
   120  	contractName string,
   121  	newContractCode string,
   122  ) (
   123  	previousNewContractCode string,
   124  ) {
   125  	d.mutex.Lock()
   126  	defer d.mutex.Unlock()
   127  
   128  	if d.contracts == nil {
   129  		d.contracts = map[common.Address]map[flow.RegisterID]string{}
   130  	}
   131  
   132  	if _, ok := d.contracts[address]; !ok {
   133  		d.contracts[address] = map[flow.RegisterID]string{}
   134  	}
   135  
   136  	registerID := flow.ContractRegisterID(flow.ConvertAddress(address), contractName)
   137  
   138  	previousNewContractCode = d.contracts[address][registerID]
   139  
   140  	d.contracts[address][registerID] = newContractCode
   141  
   142  	return
   143  }
   144  
   145  type SystemContractChange struct {
   146  	Address         common.Address
   147  	ContractName    string
   148  	NewContractCode string
   149  }
   150  
   151  func NewSystemContractChange(
   152  	systemContract systemcontracts.SystemContract,
   153  	newContractCode []byte,
   154  ) SystemContractChange {
   155  	return SystemContractChange{
   156  		Address:         common.Address(systemContract.Address),
   157  		ContractName:    systemContract.Name,
   158  		NewContractCode: string(newContractCode),
   159  	}
   160  }
   161  
   162  func SystemContractChanges(chainID flow.ChainID) []SystemContractChange {
   163  	systemContracts := systemcontracts.SystemContractsForChain(chainID)
   164  
   165  	var stakingCollectionAddress, stakingProxyAddress common.Address
   166  
   167  	switch chainID {
   168  	case flow.Mainnet:
   169  		stakingCollectionAddress = mustHexAddress("0x8d0e87b65159ae63")
   170  		stakingProxyAddress = mustHexAddress("0x62430cf28c26d095")
   171  
   172  	case flow.Testnet:
   173  		stakingCollectionAddress = mustHexAddress("0x95e019a17d0e23d7")
   174  		stakingProxyAddress = mustHexAddress("0x7aad92e5a0715d21")
   175  
   176  	default:
   177  		panic(fmt.Errorf("unsupported chain ID: %s", chainID))
   178  	}
   179  
   180  	lockedTokensAddress := stakingCollectionAddress
   181  	fungibleTokenMetadataViewsAddress := common.Address(systemContracts.FungibleToken.Address)
   182  	fungibleTokenSwitchboardAddress := common.Address(systemContracts.FungibleToken.Address)
   183  
   184  	return []SystemContractChange{
   185  		// epoch related contracts
   186  		NewSystemContractChange(
   187  			systemContracts.Epoch,
   188  			coreContracts.FlowEpoch(
   189  				systemContracts.FungibleToken.Address.HexWithPrefix(),
   190  				systemContracts.FlowToken.Address.HexWithPrefix(),
   191  				systemContracts.IDTableStaking.Address.HexWithPrefix(),
   192  				systemContracts.ClusterQC.Address.HexWithPrefix(),
   193  				systemContracts.DKG.Address.HexWithPrefix(),
   194  				systemContracts.FlowFees.Address.HexWithPrefix(),
   195  			),
   196  		),
   197  		NewSystemContractChange(
   198  			systemContracts.IDTableStaking,
   199  			coreContracts.FlowIDTableStaking(
   200  				systemContracts.FungibleToken.Address.HexWithPrefix(),
   201  				systemContracts.FlowToken.Address.HexWithPrefix(),
   202  				systemContracts.FlowFees.Address.HexWithPrefix(),
   203  				true,
   204  			),
   205  		),
   206  		NewSystemContractChange(
   207  			systemContracts.ClusterQC,
   208  			coreContracts.FlowQC(),
   209  		),
   210  		NewSystemContractChange(
   211  			systemContracts.DKG,
   212  			coreContracts.FlowDKG(),
   213  		),
   214  
   215  		// service account related contracts
   216  		NewSystemContractChange(
   217  			systemContracts.FlowServiceAccount,
   218  			coreContracts.FlowServiceAccount(
   219  				systemContracts.FungibleToken.Address.HexWithPrefix(),
   220  				systemContracts.FlowToken.Address.HexWithPrefix(),
   221  				systemContracts.FlowFees.Address.HexWithPrefix(),
   222  				systemContracts.FlowStorageFees.Address.HexWithPrefix(),
   223  			),
   224  		),
   225  		NewSystemContractChange(
   226  			systemContracts.NodeVersionBeacon,
   227  			coreContracts.NodeVersionBeacon(),
   228  		),
   229  		NewSystemContractChange(
   230  			systemContracts.RandomBeaconHistory,
   231  			coreContracts.RandomBeaconHistory(),
   232  		),
   233  		NewSystemContractChange(
   234  			systemContracts.FlowStorageFees,
   235  			coreContracts.FlowStorageFees(
   236  				systemContracts.FungibleToken.Address.HexWithPrefix(),
   237  				systemContracts.FlowToken.Address.HexWithPrefix(),
   238  			),
   239  		),
   240  		{
   241  			Address:      stakingCollectionAddress,
   242  			ContractName: "FlowStakingCollection",
   243  			NewContractCode: string(coreContracts.FlowStakingCollection(
   244  				systemContracts.FungibleToken.Address.HexWithPrefix(),
   245  				systemContracts.FlowToken.Address.HexWithPrefix(),
   246  				systemContracts.IDTableStaking.Address.HexWithPrefix(),
   247  				stakingProxyAddress.HexWithPrefix(),
   248  				lockedTokensAddress.HexWithPrefix(),
   249  				systemContracts.FlowStorageFees.Address.HexWithPrefix(),
   250  				systemContracts.ClusterQC.Address.HexWithPrefix(),
   251  				systemContracts.DKG.Address.HexWithPrefix(),
   252  				systemContracts.Epoch.Address.HexWithPrefix(),
   253  			)),
   254  		},
   255  		{
   256  			Address:         stakingProxyAddress,
   257  			ContractName:    "StakingProxy",
   258  			NewContractCode: string(coreContracts.FlowStakingProxy()),
   259  		},
   260  		{
   261  			Address:      lockedTokensAddress,
   262  			ContractName: "LockedTokens",
   263  			NewContractCode: string(coreContracts.FlowLockedTokens(
   264  				systemContracts.FungibleToken.Address.HexWithPrefix(),
   265  				systemContracts.FlowToken.Address.HexWithPrefix(),
   266  				systemContracts.IDTableStaking.Address.HexWithPrefix(),
   267  				stakingProxyAddress.HexWithPrefix(),
   268  				systemContracts.FlowStorageFees.Address.HexWithPrefix(),
   269  			)),
   270  		},
   271  
   272  		// token related contracts
   273  		NewSystemContractChange(
   274  			systemContracts.FlowFees,
   275  			coreContracts.FlowFees(
   276  				systemContracts.FungibleToken.Address.HexWithPrefix(),
   277  				systemContracts.FlowToken.Address.HexWithPrefix(),
   278  				systemContracts.FlowStorageFees.Address.HexWithPrefix(),
   279  			),
   280  		),
   281  		NewSystemContractChange(
   282  			systemContracts.FlowToken,
   283  			coreContracts.FlowToken(
   284  				systemContracts.FungibleToken.Address.HexWithPrefix(),
   285  				systemContracts.MetadataViews.Address.HexWithPrefix(),
   286  				systemContracts.ViewResolver.Address.HexWithPrefix(),
   287  			),
   288  		),
   289  		NewSystemContractChange(
   290  			systemContracts.FungibleToken,
   291  			ftContracts.FungibleToken(),
   292  		),
   293  		{
   294  			Address:      fungibleTokenMetadataViewsAddress,
   295  			ContractName: "FungibleTokenMetadataViews",
   296  			NewContractCode: string(ftContracts.FungibleTokenMetadataViews(
   297  				systemContracts.FungibleToken.Address.HexWithPrefix(),
   298  				systemContracts.MetadataViews.Address.HexWithPrefix(),
   299  			)),
   300  		},
   301  		{
   302  			Address:      fungibleTokenSwitchboardAddress,
   303  			ContractName: "FungibleTokenSwitchboard",
   304  			NewContractCode: string(ftContracts.FungibleTokenSwitchboard(
   305  				systemContracts.FungibleToken.Address.HexWithPrefix(),
   306  			)),
   307  		},
   308  
   309  		// NFT related contracts
   310  		NewSystemContractChange(
   311  			systemContracts.NonFungibleToken,
   312  			nftContracts.NonFungibleToken(),
   313  		),
   314  		NewSystemContractChange(
   315  			systemContracts.MetadataViews,
   316  			nftContracts.MetadataViews(
   317  				sdk.Address(systemContracts.FungibleToken.Address),
   318  				sdk.Address(systemContracts.NonFungibleToken.Address),
   319  			),
   320  		),
   321  		NewSystemContractChange(
   322  			systemContracts.ViewResolver,
   323  			nftContracts.Resolver(),
   324  		),
   325  
   326  		// EVM related contracts
   327  		NewSystemContractChange(
   328  			systemContracts.EVMContract,
   329  			evm.ContractCode(
   330  				systemContracts.FlowToken.Address,
   331  				true,
   332  			),
   333  		),
   334  	}
   335  }
   336  
   337  func mustHexAddress(hexAddress string) common.Address {
   338  	address, err := common.HexToAddress(hexAddress)
   339  	if err != nil {
   340  		panic(err)
   341  	}
   342  	return address
   343  }