forked from ethereum-optimism/optimism
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'develop' into sc/liquidity-migration
- Loading branch information
Showing
47 changed files
with
1,998 additions
and
387 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
package foundry | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"maps" | ||
"math/big" | ||
"os" | ||
|
||
"github.com/holiman/uint256" | ||
|
||
"github.com/ethereum/go-ethereum/common" | ||
"github.com/ethereum/go-ethereum/common/hexutil" | ||
"github.com/ethereum/go-ethereum/core/state" | ||
"github.com/ethereum/go-ethereum/core/types" | ||
) | ||
|
||
type ForgeAllocs struct { | ||
Accounts types.GenesisAlloc | ||
} | ||
|
||
// FromState takes a geth StateDB, and dumps the accounts into the ForgeAllocs. | ||
// Any previous allocs contents are removed. | ||
// Warning: the state must be committed first, trie-key preimages must be present for iteration, | ||
// and a fresh state around the committed state-root must be presented, for the latest state-contents to be dumped. | ||
func (f *ForgeAllocs) FromState(stateDB StateDB) { | ||
f.Accounts = make(types.GenesisAlloc) | ||
stateDB.DumpToCollector((*forgeAllocsDump)(f), &state.DumpConfig{ | ||
OnlyWithAddresses: true, | ||
}) | ||
} | ||
|
||
// StateDB is a minimal interface to support dumping of Geth EVM state to ForgeAllocs. | ||
type StateDB interface { | ||
DumpToCollector(c state.DumpCollector, conf *state.DumpConfig) (nextKey []byte) | ||
} | ||
|
||
// Assert that the Geth StateDB implements this interface still. | ||
var _ StateDB = (*state.StateDB)(nil) | ||
|
||
// forgeAllocsDump is a wrapper to hide the error-prone state-dumping interface from public API. | ||
// Use ForgeAllocs.FromState to dump a state to forge-allocs. | ||
type forgeAllocsDump ForgeAllocs | ||
|
||
// ForgeAllocs implements state.DumpAllocator, such that the EVM state can be dumped into it: | ||
// with a StateDB.DumpToCollector call. | ||
var _ state.DumpCollector = (*forgeAllocsDump)(nil) | ||
|
||
func (d *forgeAllocsDump) OnRoot(hash common.Hash) { | ||
// Unlike the geth raw-state-dump, forge-allocs do not reference the state trie root. | ||
} | ||
|
||
func (d *forgeAllocsDump) OnAccount(address *common.Address, account state.DumpAccount) { | ||
if address == nil { | ||
return | ||
} | ||
if _, ok := d.Accounts[*address]; ok { | ||
panic(fmt.Errorf("cannot dump account %s twice", *address)) | ||
} | ||
balance, ok := new(big.Int).SetString(account.Balance, 0) | ||
if !ok { | ||
panic("invalid balance") | ||
} | ||
var storage map[common.Hash]common.Hash | ||
if len(account.Storage) > 0 { | ||
storage = make(map[common.Hash]common.Hash, len(account.Storage)) | ||
for k, v := range account.Storage { | ||
storage[k] = common.HexToHash(v) | ||
} | ||
} | ||
d.Accounts[*address] = types.Account{ | ||
Code: account.Code, | ||
Storage: storage, | ||
Balance: balance, | ||
Nonce: account.Nonce, | ||
} | ||
} | ||
|
||
func (d *ForgeAllocs) Copy() *ForgeAllocs { | ||
out := make(types.GenesisAlloc, len(d.Accounts)) | ||
maps.Copy(out, d.Accounts) | ||
return &ForgeAllocs{Accounts: out} | ||
} | ||
|
||
func (d *ForgeAllocs) UnmarshalJSON(b []byte) error { | ||
// forge, since integrating Alloy, likes to hex-encode everything. | ||
type forgeAllocAccount struct { | ||
Balance hexutil.U256 `json:"balance"` | ||
Nonce hexutil.Uint64 `json:"nonce"` | ||
Code hexutil.Bytes `json:"code,omitempty"` | ||
Storage map[common.Hash]common.Hash `json:"storage,omitempty"` | ||
} | ||
var allocs map[common.Address]forgeAllocAccount | ||
if err := json.Unmarshal(b, &allocs); err != nil { | ||
return err | ||
} | ||
d.Accounts = make(types.GenesisAlloc, len(allocs)) | ||
for addr, acc := range allocs { | ||
acc := acc | ||
d.Accounts[addr] = types.Account{ | ||
Code: acc.Code, | ||
Storage: acc.Storage, | ||
Balance: (*uint256.Int)(&acc.Balance).ToBig(), | ||
Nonce: (uint64)(acc.Nonce), | ||
PrivateKey: nil, | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func LoadForgeAllocs(allocsPath string) (*ForgeAllocs, error) { | ||
f, err := os.OpenFile(allocsPath, os.O_RDONLY, 0644) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to open forge allocs %q: %w", allocsPath, err) | ||
} | ||
defer f.Close() | ||
var out ForgeAllocs | ||
if err := json.NewDecoder(f).Decode(&out); err != nil { | ||
return nil, fmt.Errorf("failed to json-decode forge allocs %q: %w", allocsPath, err) | ||
} | ||
return &out, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package foundry | ||
|
||
import ( | ||
"os" | ||
"testing" | ||
|
||
"github.com/holiman/uint256" | ||
"github.com/stretchr/testify/require" | ||
|
||
oplog "github.com/ethereum-optimism/optimism/op-service/log" | ||
"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/core/tracing" | ||
"github.com/ethereum/go-ethereum/core/types" | ||
"github.com/ethereum/go-ethereum/crypto" | ||
"github.com/ethereum/go-ethereum/log" | ||
"github.com/ethereum/go-ethereum/triedb" | ||
"github.com/ethereum/go-ethereum/triedb/hashdb" | ||
) | ||
|
||
func TestForgeAllocs_FromState(t *testing.T) { | ||
// Internals of state-dumping of Geth have silent errors. | ||
cfg := oplog.DefaultCLIConfig() | ||
cfg.Level = log.LevelTrace | ||
oplog.SetGlobalLogHandler(oplog.NewLogHandler(os.Stdout, cfg)) | ||
|
||
rawDB := rawdb.NewMemoryDatabase() | ||
stateDB := state.NewDatabaseWithConfig(rawDB, &triedb.Config{ | ||
Preimages: true, | ||
IsVerkle: false, | ||
HashDB: hashdb.Defaults, | ||
PathDB: nil, | ||
}) | ||
st, err := state.New(types.EmptyRootHash, stateDB, nil) | ||
require.NoError(t, err) | ||
|
||
alice := common.HexToAddress("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266") | ||
st.CreateAccount(alice) | ||
st.SetBalance(alice, uint256.NewInt(123), tracing.BalanceChangeUnspecified) | ||
st.SetNonce(alice, 42) | ||
|
||
bob := common.HexToAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8") | ||
st.CreateAccount(bob) | ||
st.CreateContract(bob) | ||
st.SetBalance(bob, uint256.NewInt(100), tracing.BalanceChangeUnspecified) | ||
st.SetNonce(bob, 1) | ||
st.SetState(bob, common.Hash{0: 0x42}, common.Hash{0: 7}) | ||
|
||
contract := common.HexToAddress("0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC") | ||
st.CreateAccount(contract) | ||
st.CreateContract(contract) | ||
st.SetNonce(contract, 30) | ||
st.SetBalance(contract, uint256.NewInt(0), tracing.BalanceChangeUnspecified) | ||
st.SetCode(contract, []byte{10, 11, 12, 13, 14}) | ||
|
||
// Commit and make a new state, we cannot reuse the state after Commit | ||
// (see doc-comment in Commit, absolute footgun) | ||
root, err := st.Commit(0, false) | ||
require.NoError(t, err) | ||
st, err = state.New(root, stateDB, nil) | ||
require.NoError(t, err) | ||
|
||
st.SetState(contract, common.Hash{0: 0xa}, common.Hash{0: 1}) | ||
st.SetState(contract, crypto.Keccak256Hash([]byte("hello")), crypto.Keccak256Hash([]byte("world"))) | ||
|
||
root, err = st.Commit(0, false) | ||
require.NoError(t, err) | ||
st, err = state.New(root, stateDB, nil) | ||
require.NoError(t, err) | ||
|
||
var allocs ForgeAllocs | ||
allocs.FromState(st) | ||
|
||
require.Len(t, allocs.Accounts, 3) | ||
|
||
require.Contains(t, allocs.Accounts, alice) | ||
require.Nil(t, allocs.Accounts[alice].Code) | ||
require.Nil(t, allocs.Accounts[alice].Storage) | ||
require.Equal(t, "123", allocs.Accounts[alice].Balance.String()) | ||
require.Equal(t, uint64(42), allocs.Accounts[alice].Nonce) | ||
|
||
require.Contains(t, allocs.Accounts, bob) | ||
require.Nil(t, allocs.Accounts[bob].Code) | ||
require.Len(t, allocs.Accounts[bob].Storage, 1) | ||
require.Equal(t, common.Hash{0: 7}, allocs.Accounts[bob].Storage[common.Hash{0: 0x42}]) | ||
require.Equal(t, "100", allocs.Accounts[bob].Balance.String()) | ||
require.Equal(t, uint64(1), allocs.Accounts[bob].Nonce) | ||
|
||
require.Contains(t, allocs.Accounts, contract) | ||
require.Equal(t, []byte{10, 11, 12, 13, 14}, allocs.Accounts[contract].Code) | ||
require.Len(t, allocs.Accounts[contract].Storage, 2) | ||
require.Equal(t, common.Hash{0: 1}, allocs.Accounts[contract].Storage[common.Hash{0: 0xa}]) | ||
require.Equal(t, crypto.Keccak256Hash([]byte("world")), | ||
allocs.Accounts[contract].Storage[crypto.Keccak256Hash([]byte("hello"))]) | ||
require.Equal(t, "0", allocs.Accounts[contract].Balance.String()) | ||
require.Equal(t, uint64(30), allocs.Accounts[contract].Nonce) | ||
} |
Oops, something went wrong.