github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/cmd/fbchaind/mpt/iavl2mpt.go (about) 1 package mpt 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 8 ethcmn "github.com/ethereum/go-ethereum/common" 9 "github.com/ethereum/go-ethereum/core/rawdb" 10 ethstate "github.com/ethereum/go-ethereum/core/state" 11 ethcrypto "github.com/ethereum/go-ethereum/crypto" 12 "github.com/ethereum/go-ethereum/ethdb" 13 "github.com/ethereum/go-ethereum/rlp" 14 "github.com/ethereum/go-ethereum/trie" 15 "github.com/fibonacci-chain/fbc/app" 16 apptypes "github.com/fibonacci-chain/fbc/app/types" 17 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/server" 18 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store/mpt" 19 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 20 authexported "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/exported" 21 authtypes "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/types" 22 "github.com/fibonacci-chain/fbc/libs/iavl" 23 evmtypes "github.com/fibonacci-chain/fbc/x/evm/types" 24 "github.com/spf13/cobra" 25 ) 26 27 func iavl2mptCmd(ctx *server.Context) *cobra.Command { 28 cmd := &cobra.Command{ 29 Args: cobra.ExactArgs(1), 30 Use: "iavl2mpt acc/evm", 31 Short: "migrate data from iavl to mpt", 32 PreRunE: func(cmd *cobra.Command, args []string) error { 33 return checkValidKey(args[0]) 34 }, 35 Run: func(cmd *cobra.Command, args []string) { 36 log.Printf("--------- migrate %s start ---------\n", args[0]) 37 switch args[0] { 38 case accStoreKey: 39 migrateAccFromIavlToMpt(ctx) 40 case evmStoreKey: 41 migrateEvmFromIavlToMpt(ctx) 42 case legacyStoreKey: 43 migrateEvmLegacyFromIavlToIavl(ctx) 44 } 45 log.Printf("--------- migrate %s end ---------\n", args[0]) 46 }, 47 } 48 return cmd 49 } 50 51 // migrateAccFromIavlToMpt migrate acc data from iavl to mpt 52 func migrateAccFromIavlToMpt(ctx *server.Context) { 53 // 0.1 initialize App and context 54 migrationApp := newMigrationApp(ctx) 55 cmCtx := migrationApp.MockContext() 56 committedHeight := cmCtx.BlockHeight() - 1 57 58 // 0.1 initialize database of acc mpt 59 accMptDb := mpt.InstanceOfMptStore() 60 accTrie, err := accMptDb.OpenTrie(mpt.NilHash) 61 panicError(err) 62 63 // 0.2 initialize database of evm mpt 64 evmMptDb := mpt.InstanceOfMptStore() 65 evmTrie, err := evmMptDb.OpenTrie(mpt.NilHash) 66 panicError(err) 67 68 // 1.1 update GlobalNumber to mpt 69 accountNumber := migrationApp.AccountKeeper.GetNextAccountNumber(cmCtx) 70 err = accTrie.TryUpdate(authtypes.GlobalAccountNumberKey, migrationApp.Codec().MustMarshalBinaryLengthPrefixed(accountNumber)) 71 panicError(err) 72 fmt.Println("GlobalNumber", accountNumber) 73 74 // 1.2 update every account to mpt 75 count, contractCount := 0, 0 76 batch := evmMptDb.TrieDB().DiskDB().NewBatch() 77 migrationApp.AccountKeeper.MigrateAccounts(cmCtx, func(account authexported.Account, key, value []byte) (stop bool) { 78 count++ 79 if len(value) == 0 { 80 log.Printf("[warning] %s has nil value\n", account.GetAddress().String()) 81 } 82 83 // update acc mpt for every account 84 panicError(accTrie.TryUpdate(key, value)) 85 if count%100 == 0 { 86 pushData2Database(accMptDb, accTrie, committedHeight, false) 87 log.Println(count) 88 } 89 90 // check if the account is a contract account 91 if ethAcc, ok := account.(*apptypes.EthAccount); ok { 92 if !bytes.Equal(ethAcc.CodeHash, mpt.EmptyCodeHashBytes) { 93 contractCount++ 94 // update evm mpt. Key is the address of the contract; Value is the empty root hash 95 panicError(evmTrie.TryUpdate(ethAcc.EthAddress().Bytes(), mpt.EmptyRootHashBytes)) 96 if contractCount%100 == 0 { 97 pushData2Database(evmMptDb, evmTrie, committedHeight, true) 98 } 99 100 // write code to evm.db in direct 101 codeHash := ethcmn.BytesToHash(ethAcc.CodeHash) 102 rawdb.WriteCode(batch, codeHash, migrationApp.EvmKeeper.GetCodeByHash(cmCtx, codeHash)) 103 writeDataToRawdb(batch) 104 } 105 } 106 107 return false 108 }) 109 110 // 1.3 make sure the last data is committed to the database 111 pushData2Database(accMptDb, accTrie, committedHeight, false) 112 pushData2Database(evmMptDb, evmTrie, committedHeight, true) 113 114 fmt.Println(fmt.Sprintf("Successfully migrate %d account (include %d contract account) at version %d", count, contractCount, committedHeight)) 115 } 116 117 // migrateEvmFromIavlToMpt migrate evm data from iavl to mpt 118 func migrateEvmFromIavlToMpt(ctx *server.Context) { 119 // 0.1 initialize App and context 120 migrationApp := newMigrationApp(ctx) 121 cmCtx := migrationApp.MockContext() 122 123 // 0.1 initialize database of evm mpt, and open trie based on the latest root hash 124 evmMptDb := mpt.InstanceOfMptStore() 125 rootHash := migrationApp.EvmKeeper.GetMptRootHash(uint64(cmCtx.BlockHeight() - 1)) 126 evmTrie, err := evmMptDb.OpenTrie(rootHash) 127 panicError(err) 128 129 /* Here are prefix keys from evm module: 130 KeyPrefixBlockHash 131 KeyPrefixBloom 132 KeyPrefixCode 133 KeyPrefixStorage 134 KeyPrefixChainConfig 135 KeyPrefixHeightHash 136 KeyPrefixContractDeploymentWhitelist 137 KeyPrefixContractBlockedList 138 139 So, here are data list about the migration process: 140 1. Accounts -> accMpt 141 Code -> rawdb (note: done in iavl2mpt acc cmd) 142 Storage -> evmMpt 143 2. BlockHash、HeightHash -> rawdb 144 3. Bloom -> rawdb 145 146 4. ChainConfig -> iavl // do it in abci 147 5. ContractDeploymentWhitelist、ContractBlockedList -> iavl // do it in abci 148 */ 149 150 // 1. Migratess Accounts、Storage -> mpt 151 migrateContractToMpt(migrationApp, cmCtx, evmMptDb, evmTrie) 152 153 // 2. Migrates BlockHash、HeightHash -> rawdb 154 batch := evmMptDb.TrieDB().DiskDB().NewBatch() 155 miragteBlockHashesToDb(migrationApp, cmCtx, batch) 156 157 // 3. Migrates Bloom -> rawdb 158 miragteBloomsToDb(migrationApp, cmCtx, batch) 159 160 /* 161 // 4. save an empty evmlegacy iavl tree in mirgate height 162 upgradedPrefixDb := dbm.NewPrefixDB(migrationApp.GetDB(), []byte(iavlEvmLegacyKey)) 163 upgradedTree, err := iavl.NewMutableTreeWithOpts(upgradedPrefixDb, iavlstore.IavlCacheSize, nil) 164 panicError(err) 165 _, version, err := upgradedTree.SaveVersionSync(cmCtx.BlockHeight()-1, false) 166 panicError(err) 167 fmt.Printf("Successfully save an empty evmlegacy iavl tree in %d\n", version) 168 */ 169 } 170 171 // 1. migrateContractToMpt Migrates Accounts、Code、Storage 172 func migrateContractToMpt(migrationApp *app.FBChainApp, cmCtx sdk.Context, evmMptDb ethstate.Database, evmTrie ethstate.Trie) { 173 committedHeight := cmCtx.BlockHeight() - 1 174 count := 0 175 itr := trie.NewIterator(evmTrie.NodeIterator(nil)) 176 for itr.Next() { 177 count++ 178 179 addr := ethcmn.BytesToAddress(evmTrie.GetKey(itr.Key)) 180 // 1.1 get solo contract mpt 181 contractTrie := getStorageTrie(evmMptDb, ethcrypto.Keccak256Hash(addr[:]), mpt.NilHash) 182 183 _ = migrationApp.EvmKeeper.ForEachStorage(cmCtx, addr, func(key, value ethcmn.Hash) bool { 184 // Encoding []byte cannot fail, ok to ignore the error. 185 v, _ := rlp.EncodeToBytes(ethcmn.TrimLeftZeroes(value[:])) 186 if len(v) == 0 { 187 log.Printf("[warning] %s in %s has nil value\n", addr.String(), key.String()) 188 } 189 // 1.2 set every storage into solo 190 panicError(contractTrie.TryUpdate(key.Bytes(), v)) 191 return false 192 }) 193 // 1.3 calculate rootHash of contract mpt 194 rootHash, err := contractTrie.Commit(nil) 195 panicError(err) 196 // 1.4 set the rootHash of contract mpt into evm mpt 197 panicError(evmTrie.TryUpdate(addr[:], rootHash.Bytes())) 198 199 if count%100 == 0 { 200 pushData2Database(evmMptDb, evmTrie, committedHeight, true) 201 log.Println(count) 202 } 203 } 204 pushData2Database(evmMptDb, evmTrie, committedHeight, true) 205 fmt.Printf("Successfully migrate %d contract stroage at version %d\n", count, committedHeight) 206 } 207 208 // 2. miragteBlockHashesToDb Migrates BlockHash/HeightHash 209 func miragteBlockHashesToDb(migrationApp *app.FBChainApp, cmCtx sdk.Context, batch ethdb.Batch) { 210 count := 0 211 migrationApp.EvmKeeper.IterateBlockHash(cmCtx, func(key []byte, value []byte) bool { 212 count++ 213 panicError(batch.Put(key, value)) 214 panicError(batch.Put(append(evmtypes.KeyPrefixHeightHash, value...), key[1:])) 215 216 if count%1000 == 0 { 217 writeDataToRawdb(batch) 218 log.Printf("write block hash between %d~%d\n", count-1000, count) 219 } 220 return false 221 }) 222 writeDataToRawdb(batch) 223 fmt.Printf("Successfully migrate %d block-hashes\n", count) 224 } 225 226 // 3. miragteBloomsToDb Migrates Bloom 227 func miragteBloomsToDb(migrationApp *app.FBChainApp, cmCtx sdk.Context, batch ethdb.Batch) { 228 count := 0 229 migrationApp.EvmKeeper.IterateBlockBloom(cmCtx, func(key []byte, value []byte) bool { 230 count++ 231 panicError(batch.Put(key, value)) 232 233 if count%1000 == 0 { 234 writeDataToRawdb(batch) 235 log.Printf("write bloom between %d~%d\n", count-1000, count) 236 } 237 return false 238 }) 239 writeDataToRawdb(batch) 240 fmt.Printf("Successfully migrate %d blooms\n", count) 241 } 242 243 // migrateEvmLegacyFromIavlToIavl only used for test! 244 func migrateEvmLegacyFromIavlToIavl(ctx *server.Context) { 245 // 0.1 initialize App and context 246 migrationApp := newMigrationApp(ctx) 247 cmCtx := migrationApp.MockContext() 248 249 allParams := readAllParams(migrationApp) 250 upgradedTree := getUpgradedTree(migrationApp.GetDB(), []byte(KeyParams), true) 251 252 // 0. migrate latest params to pre-latest version 253 for key, value := range allParams { 254 upgradedTree.Set([]byte(key), value) 255 } 256 fmt.Printf("Successfully update all module params\n") 257 258 // 1. Migrates ChainConfig -> evmlegacy iavl 259 config, _ := migrationApp.EvmKeeper.GetChainConfig(cmCtx) 260 upgradedTree.Set(generateKeyForCustomParamStore(evmStoreKey, evmtypes.KeyPrefixChainConfig), 261 migrationApp.Codec().MustMarshalBinaryBare(config)) 262 fmt.Printf("Successfully migrate chain config\n") 263 264 // 2. Migrates ContractDeploymentWhitelist、ContractBlockedList 265 csdb := evmtypes.CreateEmptyCommitStateDB(migrationApp.EvmKeeper.GenerateCSDBParams(), cmCtx) 266 267 // 5.1、deploy white list 268 whiteList := csdb.GetContractDeploymentWhitelist() 269 for i := 0; i < len(whiteList); i++ { 270 upgradedTree.Set(generateKeyForCustomParamStore(evmStoreKey, evmtypes.GetContractDeploymentWhitelistMemberKey(whiteList[i])), 271 []byte("")) 272 } 273 274 // 5.2、deploy blocked list 275 blockedList := csdb.GetContractBlockedList() 276 for i := 0; i < len(blockedList); i++ { 277 upgradedTree.Set(generateKeyForCustomParamStore(evmStoreKey, evmtypes.GetContractBlockedListMemberKey(blockedList[i])), 278 []byte("")) 279 } 280 281 // 5.3、deploy blocked method list 282 bcml := csdb.GetContractMethodBlockedList() 283 count := 0 284 for i := 0; i < len(bcml); i++ { 285 if !bcml[i].IsAllMethodBlocked() { 286 count++ 287 evmtypes.SortContractMethods(bcml[i].BlockMethods) 288 value := migrationApp.Codec().MustMarshalJSON(bcml[i].BlockMethods) 289 sortedValue := sdk.MustSortJSON(value) 290 upgradedTree.Set(generateKeyForCustomParamStore(evmStoreKey, evmtypes.GetContractBlockedListMemberKey(bcml[i].Address)), 291 sortedValue) 292 } 293 } 294 295 fmt.Printf("Successfully migrate %d addresses in white list, %d addresses in blocked list, %d addresses in method block list\n", 296 len(whiteList), len(blockedList), count) 297 298 iavl.SetIgnoreVersionCheck(true) 299 hash, version, _, err := upgradedTree.SaveVersion(false) 300 panicError(err) 301 fmt.Printf("Successfully save evmlegacy, version: %d, hash: %s\n", version, ethcmn.BytesToHash(hash)) 302 303 } 304 305 func readAllParams(app *app.FBChainApp) map[string][]byte { 306 tree := getUpgradedTree(app.GetDB(), []byte(KeyParams), false) 307 308 paramsMap := make(map[string][]byte) 309 tree.IterateRange(nil, nil, true, func(key, value []byte) bool { 310 paramsMap[string(key)] = value 311 return false 312 }) 313 314 return paramsMap 315 } 316 317 func generateKeyForCustomParamStore(storeKey string, key []byte) []byte { 318 prefix := []byte("custom/" + storeKey + "/") 319 return append(prefix, key...) 320 }