Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
cc106630
Unverified
Commit
cc106630
authored
Mar 15, 2023
by
Matthew Slipper
Committed by
GitHub
Mar 15, 2023
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #5147 from ethereum-optimism/bugfix/revert-single-step
Revert single step OVM_ETH migration
parents
c6521ff4
e77d6b54
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
208 additions
and
340 deletions
+208
-340
cli.go
op-chain-ops/ether/cli.go
+10
-0
migrate.go
op-chain-ops/ether/migrate.go
+30
-109
migrate_test.go
op-chain-ops/ether/migrate_test.go
+0
-224
precheck.go
op-chain-ops/ether/precheck.go
+155
-0
check.go
op-chain-ops/genesis/check.go
+1
-0
db_migration.go
op-chain-ops/genesis/db_migration.go
+12
-7
No files found.
op-chain-ops/ether/cli.go
View file @
cc106630
...
@@ -20,3 +20,13 @@ func getOVMETHTotalSupplySlot() common.Hash {
...
@@ -20,3 +20,13 @@ func getOVMETHTotalSupplySlot() common.Hash {
key
:=
common
.
BytesToHash
(
common
.
LeftPadBytes
(
position
.
Bytes
(),
32
))
key
:=
common
.
BytesToHash
(
common
.
LeftPadBytes
(
position
.
Bytes
(),
32
))
return
key
return
key
}
}
func
GetOVMETHTotalSupplySlot
()
common
.
Hash
{
return
getOVMETHTotalSupplySlot
()
}
// getOVMETHBalance gets a user's OVM ETH balance from state by querying the
// appropriate storage slot directly.
func
getOVMETHBalance
(
db
*
state
.
StateDB
,
addr
common
.
Address
)
*
big
.
Int
{
return
db
.
GetState
(
OVMETHAddress
,
CalcOVMETHStorageKey
(
addr
))
.
Big
()
}
op-chain-ops/ether/migrate.go
View file @
cc106630
...
@@ -17,7 +17,7 @@ var (
...
@@ -17,7 +17,7 @@ var (
// OVMETHAddress is the address of the OVM ETH predeploy.
// OVMETHAddress is the address of the OVM ETH predeploy.
OVMETHAddress
=
common
.
HexToAddress
(
"0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000"
)
OVMETHAddress
=
common
.
HexToAddress
(
"0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000"
)
OVMETHI
gnoredSlots
=
map
[
common
.
Hash
]
bool
{
i
gnoredSlots
=
map
[
common
.
Hash
]
bool
{
// Total Supply
// Total Supply
common
.
HexToHash
(
"0x0000000000000000000000000000000000000000000000000000000000000002"
)
:
true
,
common
.
HexToHash
(
"0x0000000000000000000000000000000000000000000000000000000000000002"
)
:
true
,
// Name
// Name
...
@@ -29,14 +29,7 @@ var (
...
@@ -29,14 +29,7 @@ var (
}
}
)
)
// MigrateLegacyETH checks that the given list of addresses and allowances represents all storage
func
MigrateLegacyETH
(
db
*
state
.
StateDB
,
addresses
[]
common
.
Address
,
chainID
int
,
noCheck
bool
)
error
{
// slots in the LegacyERC20ETH contract. We don't have to filter out extra addresses like we do for
// withdrawals because we'll simply carry the balance of a given address to the new system, if the
// account is extra then it won't have any balance and nothing will happen. For each valid balance,
// this method will migrate into state. This method does the checking as part of the migration loop
// in order to avoid having to iterate over state twice. This saves approximately 40 minutes during
// the mainnet migration.
func
MigrateLegacyETH
(
db
*
state
.
StateDB
,
addresses
[]
common
.
Address
,
allowances
[]
*
crossdomain
.
Allowance
,
chainID
int
,
noCheck
bool
,
commit
bool
)
error
{
// Chain params to use for integrity checking.
// Chain params to use for integrity checking.
params
:=
crossdomain
.
ParamsByChainID
[
chainID
]
params
:=
crossdomain
.
ParamsByChainID
[
chainID
]
if
params
==
nil
{
if
params
==
nil
{
...
@@ -46,99 +39,38 @@ func MigrateLegacyETH(db *state.StateDB, addresses []common.Address, allowances
...
@@ -46,99 +39,38 @@ func MigrateLegacyETH(db *state.StateDB, addresses []common.Address, allowances
// Log the chain params for debugging purposes.
// Log the chain params for debugging purposes.
log
.
Info
(
"Chain params"
,
"chain-id"
,
chainID
,
"supply-delta"
,
params
.
ExpectedSupplyDelta
)
log
.
Info
(
"Chain params"
,
"chain-id"
,
chainID
,
"supply-delta"
,
params
.
ExpectedSupplyDelta
)
return
doMigration
(
db
,
addresses
,
allowances
,
params
.
ExpectedSupplyDelta
,
noCheck
,
commit
)
// Deduplicate the list of addresses by converting to a map.
}
deduped
:=
make
(
map
[
common
.
Address
]
bool
)
func
doMigration
(
db
*
state
.
StateDB
,
addresses
[]
common
.
Address
,
allowances
[]
*
crossdomain
.
Allowance
,
expSupplyDiff
*
big
.
Int
,
noCheck
bool
,
commit
bool
)
error
{
// We'll need to maintain a list of all addresses that we've seen along with all of the storage
// slots based on the witness data.
slotsAddrs
:=
make
(
map
[
common
.
Hash
]
common
.
Address
)
slotTypes
:=
make
(
map
[
common
.
Hash
]
int
)
// For each known address, compute its balance key and add it to the list of addresses.
// Mint events are instrumented as regular ETH events in the witness data, so we no longer
// need to iterate over mint events during the migration.
for
_
,
addr
:=
range
addresses
{
for
_
,
addr
:=
range
addresses
{
sk
:=
CalcOVMETHStorageKey
(
addr
)
deduped
[
addr
]
=
true
slotTypes
[
sk
]
=
1
slotsAddrs
[
sk
]
=
addr
}
// For each known allowance, compute its storage key and add it to the list of addresses.
for
_
,
allowance
:=
range
allowances
{
slotTypes
[
CalcAllowanceStorageKey
(
allowance
.
From
,
allowance
.
To
)]
=
2
}
}
// Add the old SequencerEntrypoint because someone sent it ETH a long time ago and it has a
// Migrate the legacy ETH to ETH.
// balance but none of our instrumentation could easily find it. Special case.
sequencerEntrypointAddr
:=
common
.
HexToAddress
(
"0x4200000000000000000000000000000000000005"
)
slotTypes
[
CalcOVMETHStorageKey
(
sequencerEntrypointAddr
)]
=
1
// Migrate the OVM_ETH to ETH.
log
.
Info
(
"Migrating legacy ETH to ETH"
,
"num-accounts"
,
len
(
addresses
))
log
.
Info
(
"Migrating legacy ETH to ETH"
,
"num-accounts"
,
len
(
addresses
))
totalMigrated
:=
new
(
big
.
Int
)
totalMigrated
:=
new
(
big
.
Int
)
logAccountProgress
:=
util
.
ProgressLogger
(
1000
,
"imported OVM_ETH storage slot"
)
logAccountProgress
:=
util
.
ProgressLogger
(
1000
,
"imported accounts"
)
var
innerErr
error
for
addr
:=
range
deduped
{
err
:=
db
.
ForEachStorage
(
predeploys
.
LegacyERC20ETHAddr
,
func
(
key
,
value
common
.
Hash
)
bool
{
// No accounts should have a balance in state. If they do, bail.
defer
logAccountProgress
()
if
db
.
GetBalance
(
addr
)
.
Sign
()
>
0
{
if
noCheck
{
// We can safely ignore specific slots (totalSupply, name, symbol).
log
.
Error
(
"account has non-zero balance in state - should never happen"
,
"addr"
,
addr
)
if
OVMETHIgnoredSlots
[
key
]
{
}
else
{
return
true
log
.
Crit
(
"account has non-zero balance in state - should never happen"
,
"addr"
,
addr
)
}
// Look up the slot type.
slotType
,
ok
:=
slotTypes
[
key
]
if
!
ok
{
log
.
Error
(
"unknown storage slot in state"
,
"slot"
,
key
.
String
())
if
!
noCheck
{
innerErr
=
fmt
.
Errorf
(
"unknown storage slot in state: %s"
,
key
.
String
())
return
false
}
}
}
}
switch
slotType
{
// Pull out the OVM ETH balance.
case
1
:
ovmBalance
:=
getOVMETHBalance
(
db
,
addr
)
// Balance slot.
bal
:=
value
.
Big
()
totalMigrated
.
Add
(
totalMigrated
,
bal
)
addr
:=
slotsAddrs
[
key
]
// There should never be any balances in state, so verify that here.
// Actually perform the migration by setting the appropriate values in state.
if
db
.
GetBalance
(
addr
)
.
Sign
()
>
0
{
db
.
SetBalance
(
addr
,
ovmBalance
)
log
.
Error
(
"account has non-zero balance in state - should never happen"
,
"addr"
,
addr
)
db
.
SetState
(
predeploys
.
LegacyERC20ETHAddr
,
CalcOVMETHStorageKey
(
addr
),
common
.
Hash
{})
if
!
noCheck
{
innerErr
=
fmt
.
Errorf
(
"account has non-zero balance in state - should never happen: %s"
,
addr
)
return
false
}
}
if
!
commit
{
return
true
}
// Set the balance, and delete the legacy slot.
// Bump the total OVM balance.
db
.
SetBalance
(
addr
,
bal
)
totalMigrated
=
totalMigrated
.
Add
(
totalMigrated
,
ovmBalance
)
db
.
SetState
(
predeploys
.
LegacyERC20ETHAddr
,
key
,
common
.
Hash
{})
case
2
:
// Allowance slot. Nothing to do here.
return
true
default
:
// Should never happen.
log
.
Error
(
"unknown slot type"
,
"slot"
,
key
.
String
(),
"type"
,
slotType
)
if
!
noCheck
{
innerErr
=
fmt
.
Errorf
(
"unknown slot type: %d"
,
slotType
)
return
false
}
}
return
true
// Log progress.
})
logAccountProgress
()
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to iterate over OVM_ETH storage: %w"
,
err
)
}
if
innerErr
!=
nil
{
return
fmt
.
Errorf
(
"error in migration: %w"
,
innerErr
)
}
}
// Make sure that the total supply delta matches the expected delta. This is equivalent to
// Make sure that the total supply delta matches the expected delta. This is equivalent to
...
@@ -146,44 +78,33 @@ func doMigration(db *state.StateDB, addresses []common.Address, allowances []*cr
...
@@ -146,44 +78,33 @@ func doMigration(db *state.StateDB, addresses []common.Address, allowances []*cr
// same check against the total found (a = b, b = c => a = c).
// same check against the total found (a = b, b = c => a = c).
totalSupply
:=
getOVMETHTotalSupply
(
db
)
totalSupply
:=
getOVMETHTotalSupply
(
db
)
delta
:=
new
(
big
.
Int
)
.
Sub
(
totalSupply
,
totalMigrated
)
delta
:=
new
(
big
.
Int
)
.
Sub
(
totalSupply
,
totalMigrated
)
if
delta
.
Cmp
(
expSupplyDiff
)
!=
0
{
if
delta
.
Cmp
(
params
.
ExpectedSupplyDelta
)
!=
0
{
if
noCheck
{
if
noCheck
{
log
.
Error
(
log
.
Error
(
"supply mismatch"
,
"supply mismatch"
,
"migrated"
,
totalMigrated
.
String
(),
"migrated"
,
totalMigrated
.
String
(),
"supply"
,
totalSupply
.
String
(),
"supply"
,
totalSupply
.
String
(),
"delta"
,
delta
.
String
(),
"delta"
,
delta
.
String
(),
"exp_delta"
,
expSupplyDiff
.
String
(),
"exp_delta"
,
params
.
ExpectedSupplyDelta
.
String
(),
)
)
}
else
{
}
else
{
log
.
Error
(
log
.
Crit
(
"supply mismatch"
,
"supply mismatch"
,
"migrated"
,
totalMigrated
.
String
(),
"migrated"
,
totalMigrated
.
String
(),
"supply"
,
totalSupply
.
String
(),
"supply"
,
totalSupply
.
String
(),
"delta"
,
delta
.
String
(),
"delta"
,
delta
.
String
(),
"exp_delta"
,
expSupplyDiff
.
String
(),
"exp_delta"
,
params
.
ExpectedSupplyDelta
.
String
(),
)
)
return
fmt
.
Errorf
(
"supply mismatch: exp delta %s != %s"
,
expSupplyDiff
.
String
(),
delta
.
String
())
}
}
}
}
// Supply is verified.
log
.
Info
(
"supply verified OK"
,
"migrated"
,
totalMigrated
.
String
(),
"supply"
,
totalSupply
.
String
(),
"delta"
,
delta
.
String
(),
"exp_delta"
,
expSupplyDiff
.
String
(),
)
// Set the total supply to 0. We do this because the total supply is necessarily going to be
// Set the total supply to 0. We do this because the total supply is necessarily going to be
// different than the sum of all balances since we no longer track balances inside the contract
// different than the sum of all balances since we no longer track balances inside the contract
// itself. The total supply is going to be weird no matter what, might as well set it to zero
// itself. The total supply is going to be weird no matter what, might as well set it to zero
// so it's explicitly weird instead of implicitly weird.
// so it's explicitly weird instead of implicitly weird.
if
commit
{
db
.
SetState
(
predeploys
.
LegacyERC20ETHAddr
,
getOVMETHTotalSupplySlot
(),
common
.
Hash
{})
db
.
SetState
(
predeploys
.
LegacyERC20ETHAddr
,
getOVMETHTotalSupplySlot
(),
common
.
Hash
{})
log
.
Info
(
"Set the totalSupply to 0"
)
log
.
Info
(
"Set the totalSupply to 0"
)
}
// Fin.
return
nil
return
nil
}
}
op-chain-ops/ether/migrate_test.go
deleted
100644 → 0
View file @
c6521ff4
package
ether
import
(
"math/big"
"testing"
"github.com/ethereum-optimism/optimism/op-chain-ops/crossdomain"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/trie"
"github.com/stretchr/testify/require"
)
func
TestMigrateLegacyETH
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
totalSupply
*
big
.
Int
expDiff
*
big
.
Int
stateBalances
map
[
common
.
Address
]
*
big
.
Int
stateAllowances
map
[
common
.
Address
]
common
.
Address
inputAddresses
[]
common
.
Address
inputAllowances
[]
*
crossdomain
.
Allowance
check
func
(
t
*
testing
.
T
,
db
*
state
.
StateDB
,
err
error
)
}{
{
name
:
"everything matches"
,
totalSupply
:
big
.
NewInt
(
3
),
expDiff
:
big
.
NewInt
(
0
),
stateBalances
:
map
[
common
.
Address
]
*
big
.
Int
{
common
.
HexToAddress
(
"0x123"
)
:
big
.
NewInt
(
1
),
common
.
HexToAddress
(
"0x456"
)
:
big
.
NewInt
(
2
),
},
stateAllowances
:
map
[
common
.
Address
]
common
.
Address
{
common
.
HexToAddress
(
"0x123"
)
:
common
.
HexToAddress
(
"0x456"
),
},
inputAddresses
:
[]
common
.
Address
{
common
.
HexToAddress
(
"0x123"
),
common
.
HexToAddress
(
"0x456"
),
},
inputAllowances
:
[]
*
crossdomain
.
Allowance
{
{
From
:
common
.
HexToAddress
(
"0x123"
),
To
:
common
.
HexToAddress
(
"0x456"
),
},
},
check
:
func
(
t
*
testing
.
T
,
db
*
state
.
StateDB
,
err
error
)
{
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
db
.
GetBalance
(
common
.
HexToAddress
(
"0x123"
)),
big
.
NewInt
(
1
))
require
.
Equal
(
t
,
db
.
GetBalance
(
common
.
HexToAddress
(
"0x456"
)),
big
.
NewInt
(
2
))
require
.
Equal
(
t
,
db
.
GetState
(
OVMETHAddress
,
CalcOVMETHStorageKey
(
common
.
HexToAddress
(
"0x123"
))),
common
.
Hash
{})
require
.
Equal
(
t
,
db
.
GetState
(
OVMETHAddress
,
CalcOVMETHStorageKey
(
common
.
HexToAddress
(
"0x456"
))),
common
.
Hash
{})
require
.
Equal
(
t
,
db
.
GetState
(
OVMETHAddress
,
getOVMETHTotalSupplySlot
()),
common
.
Hash
{})
},
},
{
name
:
"extra input addresses"
,
totalSupply
:
big
.
NewInt
(
1
),
expDiff
:
big
.
NewInt
(
0
),
stateBalances
:
map
[
common
.
Address
]
*
big
.
Int
{
common
.
HexToAddress
(
"0x123"
)
:
big
.
NewInt
(
1
),
},
inputAddresses
:
[]
common
.
Address
{
common
.
HexToAddress
(
"0x123"
),
common
.
HexToAddress
(
"0x456"
),
},
check
:
func
(
t
*
testing
.
T
,
db
*
state
.
StateDB
,
err
error
)
{
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
db
.
GetBalance
(
common
.
HexToAddress
(
"0x123"
)),
big
.
NewInt
(
1
))
require
.
Equal
(
t
,
db
.
GetState
(
OVMETHAddress
,
CalcOVMETHStorageKey
(
common
.
HexToAddress
(
"0x123"
))),
common
.
Hash
{})
require
.
Equal
(
t
,
db
.
GetState
(
OVMETHAddress
,
getOVMETHTotalSupplySlot
()),
common
.
Hash
{})
},
},
{
name
:
"extra input allowances"
,
totalSupply
:
big
.
NewInt
(
1
),
expDiff
:
big
.
NewInt
(
0
),
stateBalances
:
map
[
common
.
Address
]
*
big
.
Int
{
common
.
HexToAddress
(
"0x123"
)
:
big
.
NewInt
(
1
),
},
stateAllowances
:
map
[
common
.
Address
]
common
.
Address
{
common
.
HexToAddress
(
"0x123"
)
:
common
.
HexToAddress
(
"0x456"
),
},
inputAddresses
:
[]
common
.
Address
{
common
.
HexToAddress
(
"0x123"
),
common
.
HexToAddress
(
"0x456"
),
},
inputAllowances
:
[]
*
crossdomain
.
Allowance
{
{
From
:
common
.
HexToAddress
(
"0x123"
),
To
:
common
.
HexToAddress
(
"0x456"
),
},
{
From
:
common
.
HexToAddress
(
"0x123"
),
To
:
common
.
HexToAddress
(
"0x789"
),
},
},
check
:
func
(
t
*
testing
.
T
,
db
*
state
.
StateDB
,
err
error
)
{
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
db
.
GetBalance
(
common
.
HexToAddress
(
"0x123"
)),
big
.
NewInt
(
1
))
require
.
Equal
(
t
,
db
.
GetState
(
OVMETHAddress
,
CalcOVMETHStorageKey
(
common
.
HexToAddress
(
"0x123"
))),
common
.
Hash
{})
require
.
Equal
(
t
,
db
.
GetState
(
OVMETHAddress
,
getOVMETHTotalSupplySlot
()),
common
.
Hash
{})
},
},
{
name
:
"missing input addresses"
,
totalSupply
:
big
.
NewInt
(
2
),
expDiff
:
big
.
NewInt
(
0
),
stateBalances
:
map
[
common
.
Address
]
*
big
.
Int
{
common
.
HexToAddress
(
"0x123"
)
:
big
.
NewInt
(
1
),
common
.
HexToAddress
(
"0x456"
)
:
big
.
NewInt
(
1
),
},
inputAddresses
:
[]
common
.
Address
{
common
.
HexToAddress
(
"0x123"
),
},
check
:
func
(
t
*
testing
.
T
,
db
*
state
.
StateDB
,
err
error
)
{
require
.
Error
(
t
,
err
)
require
.
ErrorContains
(
t
,
err
,
"unknown storage slot"
)
},
},
{
name
:
"missing input allowances"
,
totalSupply
:
big
.
NewInt
(
2
),
expDiff
:
big
.
NewInt
(
0
),
stateBalances
:
map
[
common
.
Address
]
*
big
.
Int
{
common
.
HexToAddress
(
"0x123"
)
:
big
.
NewInt
(
1
),
},
stateAllowances
:
map
[
common
.
Address
]
common
.
Address
{
common
.
HexToAddress
(
"0x123"
)
:
common
.
HexToAddress
(
"0x456"
),
common
.
HexToAddress
(
"0x123"
)
:
common
.
HexToAddress
(
"0x789"
),
},
inputAddresses
:
[]
common
.
Address
{
common
.
HexToAddress
(
"0x123"
),
},
inputAllowances
:
[]
*
crossdomain
.
Allowance
{
{
From
:
common
.
HexToAddress
(
"0x123"
),
To
:
common
.
HexToAddress
(
"0x456"
),
},
},
check
:
func
(
t
*
testing
.
T
,
db
*
state
.
StateDB
,
err
error
)
{
require
.
Error
(
t
,
err
)
require
.
ErrorContains
(
t
,
err
,
"unknown storage slot"
)
},
},
{
name
:
"bad supply diff"
,
totalSupply
:
big
.
NewInt
(
4
),
expDiff
:
big
.
NewInt
(
0
),
stateBalances
:
map
[
common
.
Address
]
*
big
.
Int
{
common
.
HexToAddress
(
"0x123"
)
:
big
.
NewInt
(
1
),
common
.
HexToAddress
(
"0x456"
)
:
big
.
NewInt
(
2
),
},
inputAddresses
:
[]
common
.
Address
{
common
.
HexToAddress
(
"0x123"
),
common
.
HexToAddress
(
"0x456"
),
},
check
:
func
(
t
*
testing
.
T
,
db
*
state
.
StateDB
,
err
error
)
{
require
.
Error
(
t
,
err
)
require
.
ErrorContains
(
t
,
err
,
"supply mismatch"
)
},
},
{
name
:
"good supply diff"
,
totalSupply
:
big
.
NewInt
(
4
),
expDiff
:
big
.
NewInt
(
1
),
stateBalances
:
map
[
common
.
Address
]
*
big
.
Int
{
common
.
HexToAddress
(
"0x123"
)
:
big
.
NewInt
(
1
),
common
.
HexToAddress
(
"0x456"
)
:
big
.
NewInt
(
2
),
},
inputAddresses
:
[]
common
.
Address
{
common
.
HexToAddress
(
"0x123"
),
common
.
HexToAddress
(
"0x456"
),
},
check
:
func
(
t
*
testing
.
T
,
db
*
state
.
StateDB
,
err
error
)
{
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
db
.
GetBalance
(
common
.
HexToAddress
(
"0x123"
)),
big
.
NewInt
(
1
))
require
.
Equal
(
t
,
db
.
GetBalance
(
common
.
HexToAddress
(
"0x456"
)),
big
.
NewInt
(
2
))
require
.
Equal
(
t
,
db
.
GetState
(
OVMETHAddress
,
CalcOVMETHStorageKey
(
common
.
HexToAddress
(
"0x123"
))),
common
.
Hash
{})
require
.
Equal
(
t
,
db
.
GetState
(
OVMETHAddress
,
CalcOVMETHStorageKey
(
common
.
HexToAddress
(
"0x456"
))),
common
.
Hash
{})
require
.
Equal
(
t
,
db
.
GetState
(
OVMETHAddress
,
getOVMETHTotalSupplySlot
()),
common
.
Hash
{})
},
},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
db
:=
makeLegacyETH
(
t
,
tt
.
totalSupply
,
tt
.
stateBalances
,
tt
.
stateAllowances
)
err
:=
doMigration
(
db
,
tt
.
inputAddresses
,
tt
.
inputAllowances
,
tt
.
expDiff
,
false
,
true
)
tt
.
check
(
t
,
db
,
err
)
})
}
}
func
makeLegacyETH
(
t
*
testing
.
T
,
totalSupply
*
big
.
Int
,
balances
map
[
common
.
Address
]
*
big
.
Int
,
allowances
map
[
common
.
Address
]
common
.
Address
)
*
state
.
StateDB
{
db
,
err
:=
state
.
New
(
common
.
Hash
{},
state
.
NewDatabaseWithConfig
(
rawdb
.
NewMemoryDatabase
(),
&
trie
.
Config
{
Preimages
:
true
,
Cache
:
1024
,
}),
nil
)
require
.
NoError
(
t
,
err
)
db
.
CreateAccount
(
OVMETHAddress
)
db
.
SetState
(
OVMETHAddress
,
getOVMETHTotalSupplySlot
(),
common
.
BigToHash
(
totalSupply
))
for
slot
:=
range
OVMETHIgnoredSlots
{
if
slot
==
getOVMETHTotalSupplySlot
()
{
continue
}
db
.
SetState
(
OVMETHAddress
,
slot
,
common
.
Hash
{
31
:
0xff
})
}
for
addr
,
balance
:=
range
balances
{
db
.
SetState
(
OVMETHAddress
,
CalcOVMETHStorageKey
(
addr
),
common
.
BigToHash
(
balance
))
}
for
from
,
to
:=
range
allowances
{
db
.
SetState
(
OVMETHAddress
,
CalcAllowanceStorageKey
(
from
,
to
),
common
.
BigToHash
(
big
.
NewInt
(
1
)))
}
root
,
err
:=
db
.
Commit
(
false
)
require
.
NoError
(
t
,
err
)
err
=
db
.
Database
()
.
TrieDB
()
.
Commit
(
root
,
true
)
require
.
NoError
(
t
,
err
)
return
db
}
op-chain-ops/ether/precheck.go
0 → 100644
View file @
cc106630
package
ether
import
(
"errors"
"fmt"
"math/big"
"github.com/ethereum-optimism/optimism/op-chain-ops/crossdomain"
"github.com/ethereum-optimism/optimism/op-chain-ops/util"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
)
// PreCheckBalances checks that the given list of addresses and allowances represents all storage
// slots in the LegacyERC20ETH contract. We don't have to filter out extra addresses like we do for
// withdrawals because we'll simply carry the balance of a given address to the new system, if the
// account is extra then it won't have any balance and nothing will happen.
func
PreCheckBalances
(
ldb
ethdb
.
Database
,
db
*
state
.
StateDB
,
addresses
[]
common
.
Address
,
allowances
[]
*
crossdomain
.
Allowance
,
chainID
int
,
noCheck
bool
)
([]
common
.
Address
,
error
)
{
// Chain params to use for integrity checking.
params
:=
crossdomain
.
ParamsByChainID
[
chainID
]
if
params
==
nil
{
return
nil
,
fmt
.
Errorf
(
"no chain params for %d"
,
chainID
)
}
// We'll need to maintain a list of all addresses that we've seen along with all of the storage
// slots based on the witness data.
addrs
:=
make
([]
common
.
Address
,
0
)
slotsInp
:=
make
(
map
[
common
.
Hash
]
int
)
// For each known address, compute its balance key and add it to the list of addresses.
// Mint events are instrumented as regular ETH events in the witness data, so we no longer
// need to iterate over mint events during the migration.
for
_
,
addr
:=
range
addresses
{
addrs
=
append
(
addrs
,
addr
)
slotsInp
[
CalcOVMETHStorageKey
(
addr
)]
=
1
}
// For each known allowance, compute its storage key and add it to the list of addresses.
for
_
,
allowance
:=
range
allowances
{
addrs
=
append
(
addrs
,
allowance
.
From
)
slotsInp
[
CalcAllowanceStorageKey
(
allowance
.
From
,
allowance
.
To
)]
=
2
}
// Add the old SequencerEntrypoint because someone sent it ETH a long time ago and it has a
// balance but none of our instrumentation could easily find it. Special case.
sequencerEntrypointAddr
:=
common
.
HexToAddress
(
"0x4200000000000000000000000000000000000005"
)
addrs
=
append
(
addrs
,
sequencerEntrypointAddr
)
slotsInp
[
CalcOVMETHStorageKey
(
sequencerEntrypointAddr
)]
=
1
// Build a mapping of every storage slot in the LegacyERC20ETH contract, except the list of
// slots that we know we can ignore (totalSupply, name, symbol).
var
count
int
slotsAct
:=
make
(
map
[
common
.
Hash
]
common
.
Hash
)
progress
:=
util
.
ProgressLogger
(
1000
,
"Read OVM_ETH storage slot"
)
err
:=
db
.
ForEachStorage
(
predeploys
.
LegacyERC20ETHAddr
,
func
(
key
,
value
common
.
Hash
)
bool
{
progress
()
// We can safely ignore specific slots (totalSupply, name, symbol).
if
ignoredSlots
[
key
]
{
return
true
}
// Slot exists, so add it to the map.
slotsAct
[
key
]
=
value
count
++
return
true
})
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"cannot iterate over LegacyERC20ETHAddr: %w"
,
err
)
}
// Log how many slots were iterated over.
log
.
Info
(
"Iterated legacy balances"
,
"count"
,
count
)
// Iterate over the list of known slots and check that we have a slot for each one. We'll also
// keep track of the total balance to be migrated and throw if the total supply exceeds the
// expected supply delta.
totalFound
:=
new
(
big
.
Int
)
var
unknown
bool
for
slot
:=
range
slotsAct
{
slotType
,
ok
:=
slotsInp
[
slot
]
if
!
ok
{
if
noCheck
{
log
.
Error
(
"ignoring unknown storage slot in state"
,
"slot"
,
slot
.
String
())
}
else
{
unknown
=
true
log
.
Error
(
"unknown storage slot in state"
,
"slot"
,
slot
.
String
())
continue
}
}
// Add balances to the total found.
switch
slotType
{
case
1
:
// Balance slot.
totalFound
.
Add
(
totalFound
,
slotsAct
[
slot
]
.
Big
())
case
2
:
// Allowance slot.
continue
default
:
// Should never happen.
if
noCheck
{
log
.
Error
(
"unknown slot type"
,
"slot"
,
slot
,
"type"
,
slotType
)
}
else
{
log
.
Crit
(
"unknown slot type: %d"
,
slotType
)
}
}
}
if
unknown
{
return
nil
,
errors
.
New
(
"unknown storage slots in state (see logs for details)"
)
}
// Verify the supply delta. Recorded total supply in the LegacyERC20ETH contract may be higher
// than the actual migrated amount because self-destructs will remove ETH supply in a way that
// cannot be reflected in the contract. This is fine because self-destructs just mean the L2 is
// actually *overcollateralized* by some tiny amount.
totalSupply
:=
getOVMETHTotalSupply
(
db
)
delta
:=
new
(
big
.
Int
)
.
Sub
(
totalSupply
,
totalFound
)
if
delta
.
Cmp
(
params
.
ExpectedSupplyDelta
)
!=
0
{
if
noCheck
{
log
.
Error
(
"supply mismatch"
,
"migrated"
,
totalFound
.
String
(),
"supply"
,
totalSupply
.
String
(),
"delta"
,
delta
.
String
(),
"exp_delta"
,
params
.
ExpectedSupplyDelta
.
String
(),
)
}
else
{
log
.
Crit
(
"supply mismatch"
,
"migrated"
,
totalFound
.
String
(),
"supply"
,
totalSupply
.
String
(),
"delta"
,
delta
.
String
(),
"exp_delta"
,
params
.
ExpectedSupplyDelta
.
String
(),
)
}
}
// Supply is verified.
log
.
Info
(
"supply verified OK"
,
"migrated"
,
totalFound
.
String
(),
"supply"
,
totalSupply
.
String
(),
"delta"
,
delta
.
String
(),
"exp_delta"
,
params
.
ExpectedSupplyDelta
.
String
(),
)
// We know we have at least a superset of all addresses here since we know that we have every
// storage slot. It's fine to have extras because they won't have any balance.
return
addrs
,
nil
}
op-chain-ops/genesis/check.go
View file @
cc106630
...
@@ -31,6 +31,7 @@ const MaxSlotChecks = 1000
...
@@ -31,6 +31,7 @@ const MaxSlotChecks = 1000
type
StorageCheckMap
=
map
[
common
.
Hash
]
common
.
Hash
type
StorageCheckMap
=
map
[
common
.
Hash
]
common
.
Hash
var
(
var
(
L2XDMOwnerSlot
=
common
.
Hash
{
31
:
0x33
}
ProxyAdminOwnerSlot
=
common
.
Hash
{}
ProxyAdminOwnerSlot
=
common
.
Hash
{}
LegacyETHCheckSlots
=
map
[
common
.
Hash
]
common
.
Hash
{
LegacyETHCheckSlots
=
map
[
common
.
Hash
]
common
.
Hash
{
...
...
op-chain-ops/genesis/db_migration.go
View file @
cc106630
...
@@ -134,6 +134,16 @@ func MigrateDB(ldb ethdb.Database, config *DeployConfig, l1Block *types.Block, m
...
@@ -134,6 +134,16 @@ func MigrateDB(ldb ethdb.Database, config *DeployConfig, l1Block *types.Block, m
filteredWithdrawals
=
crossdomain
.
SafeFilteredWithdrawals
(
unfilteredWithdrawals
)
filteredWithdrawals
=
crossdomain
.
SafeFilteredWithdrawals
(
unfilteredWithdrawals
)
}
}
// We also need to verify that we have all of the storage slots for the LegacyERC20ETH contract
// that we expect to have. An error will be thrown if there are any missing storage slots.
// Unlike with withdrawals, we do not need to filter out extra addresses because their balances
// would necessarily be zero and therefore not affect the migration.
log
.
Info
(
"Checking addresses..."
,
"no-check"
,
noCheck
)
addrs
,
err
:=
ether
.
PreCheckBalances
(
ldb
,
db
,
migrationData
.
Addresses
(),
migrationData
.
OvmAllowances
,
int
(
config
.
L1ChainID
),
noCheck
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"addresses mismatch: %w"
,
err
)
}
// At this point we've fully verified the witness data for the migration, so we can begin the
// At this point we've fully verified the witness data for the migration, so we can begin the
// actual migration process. This involves modifying parts of the legacy database and inserting
// actual migration process. This involves modifying parts of the legacy database and inserting
// a transition block.
// a transition block.
...
@@ -182,15 +192,10 @@ func MigrateDB(ldb ethdb.Database, config *DeployConfig, l1Block *types.Block, m
...
@@ -182,15 +192,10 @@ func MigrateDB(ldb ethdb.Database, config *DeployConfig, l1Block *types.Block, m
return
nil
,
fmt
.
Errorf
(
"cannot migrate withdrawals: %w"
,
err
)
return
nil
,
fmt
.
Errorf
(
"cannot migrate withdrawals: %w"
,
err
)
}
}
// We also need to verify that we have all of the storage slots for the LegacyERC20ETH contract
// Finally we migrate the balances held inside the LegacyERC20ETH contract into the state trie.
// that we expect to have. An error will be thrown if there are any missing storage slots.
// Unlike with withdrawals, we do not need to filter out extra addresses because their balances
// would necessarily be zero and therefore not affect the migration.
//
// Once verified, we migrate the balances held inside the LegacyERC20ETH contract into the state trie.
// We also delete the balances from the LegacyERC20ETH contract.
// We also delete the balances from the LegacyERC20ETH contract.
log
.
Info
(
"Starting to migrate ERC20 ETH"
)
log
.
Info
(
"Starting to migrate ERC20 ETH"
)
err
=
ether
.
MigrateLegacyETH
(
db
,
migrationData
.
Addresses
(),
migrationData
.
OvmAllowances
,
int
(
config
.
L1ChainID
),
noCheck
,
commit
)
err
=
ether
.
MigrateLegacyETH
(
db
,
addrs
,
int
(
config
.
L1ChainID
),
noCheck
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"cannot migrate legacy eth: %w"
,
err
)
return
nil
,
fmt
.
Errorf
(
"cannot migrate legacy eth: %w"
,
err
)
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment