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 }