Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

state-migration: Fail if account would be overwritten #202

Merged
merged 6 commits into from
Aug 1, 2024
Merged
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 54 additions & 28 deletions op-chain-ops/cmd/celo-migrate/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
Expand Down Expand Up @@ -94,7 +95,10 @@ func applyStateMigrationChanges(config *genesis.DeployConfig, genesis *core.Gene
}

// Apply the changes to the state DB.
applyAllocsToState(db, genesis, cfg)
err = applyAllocsToState(db, genesis, cfg)
if err != nil {
return nil, fmt.Errorf("cannot apply allocations to state: %w", err)
}

// Initialize the distribution schedule contract
// This uses the original config which won't enable recent hardforks (and things like the PUSH0 opcode)
Expand Down Expand Up @@ -242,48 +246,70 @@ func applyStateMigrationChanges(config *genesis.DeployConfig, genesis *core.Gene
// If an account already exists, it adds the balance of the new account to the existing balance.
// If the code of an existing account is different from the code in the genesis block, it logs a warning.
// This changes the state root, so `Commit` needs to be called after this function.
func applyAllocsToState(db *state.StateDB, genesis *core.Genesis, config *params.ChainConfig) {
func applyAllocsToState(db vm.StateDB, genesis *core.Genesis, config *params.ChainConfig) error {
log.Info("Starting to migrate OP contracts into state DB")

accountCounter := 0
copyCounter := 0
skipCounter := 0
overwriteCounter := 0
for k, v := range genesis.Alloc {
accountCounter++
whitelist := accountOverwriteWhitelist[config.ChainID.Uint64()]

balance := uint256.MustFromBig(v.Balance)
for k, v := range genesis.Alloc {
palango marked this conversation as resolved.
Show resolved Hide resolved
// Check that the balance of the account to written is zero,
// as we must not create new CELo tokens
if v.Balance.Cmp(big.NewInt(0)) != 0 {
log.Error("Account balance is not zero, would change celo supply", "address", k.Hex())
return fmt.Errorf("account balance is not zero, would change celo supply: %s", k.Hex())
}

if db.Exist(k) {
// If the account already has balance, add it to the balance of the new account
balance = balance.Add(balance, db.GetBalance(k))

currentCode := db.GetCode(k)
equalCode := bytes.Equal(currentCode, v.Code)
if currentCode != nil && !equalCode {
if whitelist, exists := accountOverwriteWhitelist[config.ChainID.Uint64()]; exists {
if db.GetCodeSize(k) == 0 {
// Check if it's an EOA, if so bail out
if db.GetNonce(k) > 0 {
log.Error("Account already exists with nonce > 0", "address", k.Hex())
return fmt.Errorf("account already exists with nonce > 0: %s", k.Hex())
} else {
copyAlloc(db, k, v, v.Nonce)
copyCounter++
}
} else { // account has code
equalCode := bytes.Equal(db.GetCode(k), v.Code)
if equalCode {
log.Info("Account already exists with same code", "address", k.Hex())
return fmt.Errorf("account already exists with same code, unclear what state/nonce to use: %s", k.Hex())
} else { // differing code
if _, ok := whitelist[k]; ok {
log.Info("Account already exists with different code and is whitelisted, overwriting...", "address", k)
log.Info("Account already exists with different code and is whitelisted, overwriting...", "address", k, "nonce", db.GetNonce(k))

// keep the existing nonce
copyAlloc(db, k, v, db.GetNonce(k))
overwriteCounter++
} else {
log.Warn("Account already exists with different code and is not whitelisted, overwriting...", "address", k, "oldCode", db.GetCode(k), "newCode", v.Code)
log.Error("Account already exists with different code and is not whitelisted", "address", k, "oldCode", db.GetCode(k), "newCode", v.Code)
return fmt.Errorf("account already exists with different code and is not whitelisted: %s", k.Hex())
palango marked this conversation as resolved.
Show resolved Hide resolved
}
} else {
log.Warn("Account already exists with different code and no whitelist exists", "address", k, "oldCode", db.GetCode(k), "newCode", v.Code)
}

overwriteCounter++
}
} else { // account does not exist
copyAlloc(db, k, v, v.Nonce)
copyCounter++
}
db.CreateAccount(k)
}

db.SetNonce(k, v.Nonce)
db.SetBalance(k, balance)
db.SetCode(k, v.Code)
for key, value := range v.Storage {
db.SetState(k, key, value)
}
log.Info("Migrated OP contracts into state DB", "totalAllocs", len(genesis.Alloc), "copiedAccounts", copyCounter, "skippedAccounts", skipCounter, "overwrittenAccounts", overwriteCounter)
return nil
}

func copyAlloc(db vm.StateDB, addr common.Address, v types.Account, nonce uint64) {
// This carries over any existing balance
db.CreateAccount(addr)

log.Info("Moved account", "address", k)
db.SetNonce(addr, nonce)
db.SetCode(addr, v.Code)
for key, value := range v.Storage {
db.SetState(addr, key, value)
}
log.Info("Migrated OP contracts into state DB", "copiedAccounts", accountCounter, "overwrittenAccounts", overwriteCounter)
log.Info("Copied account", "address", addr.Hex())
}

// setupDistributionSchedule sets up the distribution schedule contract with the correct balance
Expand Down
Loading