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  }