Skip to content

Commit f1572fe

Browse files
Merge branch 'master' into jp/support-private-registry
2 parents d00343d + 74f1f9f commit f1572fe

File tree

9 files changed

+506
-21
lines changed

9 files changed

+506
-21
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -859,6 +859,7 @@ docker-all-tools: tool-util tool-remove-execution-fork
859859
PHONY: docker-build-util
860860
docker-build-util:
861861
docker build -f cmd/Dockerfile --build-arg TARGET=./cmd/util --build-arg GOARCH=$(GOARCH) --build-arg VERSION=$(IMAGE_TAG) --build-arg CGO_FLAG=$(DISABLE_ADX) --target production \
862+
--secret id=cadence_deploy_key,env=CADENCE_DEPLOY_KEY --build-arg GOPRIVATE=$(GOPRIVATE) \
862863
-t "$(CONTAINER_REGISTRY)/util:latest" \
863864
-t "$(CONTAINER_REGISTRY)/util:$(IMAGE_TAG)" .
864865

cmd/util/cmd/export-evm-state/cmd.go

Lines changed: 63 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@ package evm_exporter
33
import (
44
"fmt"
55
"os"
6+
"path/filepath"
67

78
"github.com/rs/zerolog/log"
89
"github.com/spf13/cobra"
910

11+
"github.com/onflow/atree"
12+
1013
"github.com/onflow/flow-go/cmd/util/ledger/util"
1114
"github.com/onflow/flow-go/fvm/evm"
1215
"github.com/onflow/flow-go/fvm/evm/emulator/state"
16+
"github.com/onflow/flow-go/fvm/evm/testutils"
1317
"github.com/onflow/flow-go/ledger"
1418
"github.com/onflow/flow-go/ledger/common/convert"
1519
"github.com/onflow/flow-go/model/flow"
@@ -20,6 +24,8 @@ var (
2024
flagExecutionStateDir string
2125
flagOutputDir string
2226
flagStateCommitment string
27+
flagEVMStateGobDir string
28+
flagEVMStateGobHeight uint64
2329
)
2430

2531
var Cmd = &cobra.Command{
@@ -34,21 +40,33 @@ func init() {
3440

3541
Cmd.Flags().StringVar(&flagExecutionStateDir, "execution-state-dir", "",
3642
"Execution Node state dir (where WAL logs are written")
37-
_ = Cmd.MarkFlagRequired("execution-state-dir")
3843

3944
Cmd.Flags().StringVar(&flagOutputDir, "output-dir", "",
4045
"Directory to write new Execution State to")
4146
_ = Cmd.MarkFlagRequired("output-dir")
4247

4348
Cmd.Flags().StringVar(&flagStateCommitment, "state-commitment", "",
4449
"State commitment (hex-encoded, 64 characters)")
50+
51+
Cmd.Flags().StringVar(&flagEVMStateGobDir, "evm_state_gob_dir", "/var/flow/data/evm_state_gob",
52+
"directory that stores the evm state gob files as checkpoint")
53+
54+
Cmd.Flags().Uint64Var(&flagEVMStateGobHeight, "evm_state_gob_height", 0,
55+
"the flow height of the evm state gob files")
4556
}
4657

4758
func run(*cobra.Command, []string) {
4859
log.Info().Msg("start exporting evm state")
49-
err := ExportEVMState(flagChain, flagExecutionStateDir, flagStateCommitment, flagOutputDir)
50-
if err != nil {
51-
log.Fatal().Err(err).Msg("cannot get export evm state")
60+
if flagExecutionStateDir != "" {
61+
err := ExportEVMState(flagChain, flagExecutionStateDir, flagStateCommitment, flagOutputDir)
62+
if err != nil {
63+
log.Fatal().Err(err).Msg("cannot get export evm state")
64+
}
65+
} else if flagEVMStateGobDir != "" {
66+
err := ExportEVMStateFromGob(flagChain, flagEVMStateGobDir, flagEVMStateGobHeight, flagOutputDir)
67+
if err != nil {
68+
log.Fatal().Err(err).Msg("cannot get export evm state from gob files")
69+
}
5270
}
5371
}
5472

@@ -83,7 +101,40 @@ func ExportEVMState(
83101

84102
payloadsLedger := util.NewPayloadsLedger(filteredPayloads)
85103

86-
exporter, err := state.NewExporter(payloadsLedger, storageRoot)
104+
return ExportEVMStateFromPayloads(payloadsLedger, storageRoot, outputPath)
105+
}
106+
107+
func ExportEVMStateFromGob(
108+
chainName string,
109+
evmStateGobDir string,
110+
flowHeight uint64,
111+
outputPath string) error {
112+
113+
valueFileName, allocatorFileName := evmStateGobFileNamesByEndHeight(evmStateGobDir, flowHeight)
114+
chainID := flow.ChainID(chainName)
115+
116+
storageRoot := evm.StorageAccountAddress(chainID)
117+
valuesGob, err := testutils.DeserializeState(valueFileName)
118+
if err != nil {
119+
return err
120+
}
121+
122+
allocatorGobs, err := testutils.DeserializeAllocator(allocatorFileName)
123+
if err != nil {
124+
return err
125+
}
126+
127+
store := testutils.GetSimpleValueStorePopulated(valuesGob, allocatorGobs)
128+
129+
return ExportEVMStateFromPayloads(store, storageRoot, outputPath)
130+
}
131+
132+
func ExportEVMStateFromPayloads(
133+
ledger atree.Ledger,
134+
storageRoot flow.Address,
135+
outputPath string,
136+
) error {
137+
exporter, err := state.NewExporter(ledger, storageRoot)
87138
if err != nil {
88139
return fmt.Errorf("failed to create exporter: %w", err)
89140
}
@@ -95,15 +146,15 @@ func ExportEVMState(
95146
}
96147
}
97148

98-
fi, err := os.Create(outputPath)
99-
if err != nil {
100-
return err
101-
}
102-
defer fi.Close()
103-
104-
err = exporter.Export(outputPath)
149+
err = exporter.ExportGob(outputPath)
105150
if err != nil {
106151
return fmt.Errorf("failed to export: %w", err)
107152
}
108153
return nil
109154
}
155+
156+
func evmStateGobFileNamesByEndHeight(evmStateGobDir string, endHeight uint64) (string, string) {
157+
valueFileName := filepath.Join(evmStateGobDir, fmt.Sprintf("values-%d.gob", endHeight))
158+
allocatorFileName := filepath.Join(evmStateGobDir, fmt.Sprintf("allocators-%d.gob", endHeight))
159+
return valueFileName, allocatorFileName
160+
}

fvm/evm/emulator/state/base.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,13 @@ func NewBaseView(ledger atree.Ledger, rootAddress flow.Address) (*BaseView, erro
7474
// fetch the account collection, if not exist, create one
7575
view.accounts, view.accountSetupOnCommit, err = view.fetchOrCreateCollection(AccountsStorageIDKey)
7676
if err != nil {
77-
return nil, err
77+
return nil, fmt.Errorf("failed to fetch or create account collection with key %v: %w", AccountsStorageIDKey, err)
7878
}
7979

8080
// fetch the code collection, if not exist, create one
8181
view.codes, view.codeSetupOnCommit, err = view.fetchOrCreateCollection(CodesStorageIDKey)
8282
if err != nil {
83-
return nil, err
83+
return nil, fmt.Errorf("failed to fetch or create code collection with key %v: %w", CodesStorageIDKey, err)
8484
}
8585

8686
return view, nil
@@ -485,7 +485,10 @@ func (v *BaseView) fetchOrCreateCollection(path string) (collection *Collection,
485485
}
486486
if len(collectionID) == 0 {
487487
collection, err = v.collectionProvider.NewCollection()
488-
return collection, true, err
488+
if err != nil {
489+
return collection, true, fmt.Errorf("fail to create collection with key %v: %w", path, err)
490+
}
491+
return collection, true, nil
489492
}
490493
collection, err = v.collectionProvider.CollectionByID(collectionID)
491494
return collection, false, err

fvm/evm/emulator/state/diff.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package state
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
)
7+
8+
func AccountEqual(a, b *Account) bool {
9+
if a.Address != b.Address {
10+
return false
11+
}
12+
if !bytes.Equal(a.Balance.Bytes(), b.Balance.Bytes()) {
13+
return false
14+
}
15+
if a.Nonce != b.Nonce {
16+
return false
17+
}
18+
if a.CodeHash != b.CodeHash {
19+
return false
20+
}
21+
22+
// CollectionID could be different
23+
return true
24+
}
25+
26+
// find the difference and return as error
27+
func Diff(a *EVMState, b *EVMState) []error {
28+
var differences []error
29+
30+
// Compare Accounts
31+
for addr, accA := range a.Accounts {
32+
if accB, exists := b.Accounts[addr]; exists {
33+
if !AccountEqual(accA, accB) {
34+
differences = append(differences, fmt.Errorf("account %s differs, accA %v, accB %v", addr.Hex(), accA, accB))
35+
}
36+
} else {
37+
differences = append(differences, fmt.Errorf("account %s exists in a but not in b", addr.Hex()))
38+
}
39+
}
40+
for addr := range b.Accounts {
41+
if _, exists := a.Accounts[addr]; !exists {
42+
differences = append(differences, fmt.Errorf("account %s exists in b but not in a", addr.Hex()))
43+
}
44+
}
45+
46+
// Compare Slots
47+
for addr, slotsA := range a.Slots {
48+
slotsB, exists := b.Slots[addr]
49+
if !exists {
50+
differences = append(differences, fmt.Errorf("slots for address %s exist in a but not in b", addr.Hex()))
51+
continue
52+
}
53+
for key, valueA := range slotsA {
54+
if valueB, exists := slotsB[key]; exists {
55+
if valueA.Value != valueB.Value {
56+
differences = append(differences, fmt.Errorf("slot value for address %s and key %s differs", addr.Hex(), key.Hex()))
57+
}
58+
} else {
59+
differences = append(differences, fmt.Errorf("slot with key %s for address %s exists in a but not in b", key.Hex(), addr.Hex()))
60+
}
61+
}
62+
for key := range slotsB {
63+
if _, exists := slotsA[key]; !exists {
64+
differences = append(differences, fmt.Errorf("slot with key %s for address %s exists in b but not in a", key.Hex(), addr.Hex()))
65+
}
66+
}
67+
}
68+
for addr := range b.Slots {
69+
if _, exists := a.Slots[addr]; !exists {
70+
differences = append(differences, fmt.Errorf("slots for address %s exist in b but not in a", addr.Hex()))
71+
}
72+
}
73+
74+
// Compare Codes
75+
for hash, codeA := range a.Codes {
76+
if codeB, exists := b.Codes[hash]; exists {
77+
if !bytes.Equal(codeA.Code, codeB.Code) {
78+
differences = append(differences, fmt.Errorf("code for hash %s differs", hash.Hex()))
79+
}
80+
} else {
81+
differences = append(differences, fmt.Errorf("code with hash %s exists in a but not in b", hash.Hex()))
82+
}
83+
}
84+
for hash := range b.Codes {
85+
if _, exists := a.Codes[hash]; !exists {
86+
differences = append(differences, fmt.Errorf("code with hash %s exists in b but not in a", hash.Hex()))
87+
}
88+
}
89+
90+
return differences
91+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package state_test
2+
3+
import (
4+
"fmt"
5+
"path/filepath"
6+
"testing"
7+
8+
"github.com/stretchr/testify/require"
9+
10+
"github.com/onflow/flow-go/fvm/evm"
11+
"github.com/onflow/flow-go/fvm/evm/emulator/state"
12+
"github.com/onflow/flow-go/fvm/evm/testutils"
13+
"github.com/onflow/flow-go/model/flow"
14+
)
15+
16+
func StateDiff(t *testing.T) {
17+
offchainState, err := state.ImportEVMStateFromGob("/var/flow2/evm-state-from-gobs-218215348/")
18+
require.NoError(t, err)
19+
20+
enState, err := state.ImportEVMStateFromGob("/var/flow2/evm-state-from-gobs-218215348/")
21+
require.NoError(t, err)
22+
23+
differences := state.Diff(enState, offchainState)
24+
25+
require.Len(t, differences, 0)
26+
}
27+
28+
func EVMStateDiff(t *testing.T) {
29+
30+
state1 := EVMStateFromReplayGobDir(t, "/var/flow2/evm-state-from-gobs-218215348/", uint64(218215348))
31+
// state2 := EVMStateFromReplayGobDir(t, "/var/flow2/evm-state-from-gobs-218215348/", uint64(218215348))
32+
state2 := EVMStateFromCheckpointExtract(t, "/var/flow2/evm-state-from-checkpoint-218215348/")
33+
34+
differences := state.Diff(state1, state2)
35+
36+
for i, diff := range differences {
37+
fmt.Printf("Difference %d: %v\n", i, diff)
38+
}
39+
40+
require.Len(t, differences, 0)
41+
}
42+
43+
func EVMStateFromCheckpointExtract(t *testing.T, dir string) *state.EVMState {
44+
enState, err := state.ImportEVMStateFromGob("/var/flow2/evm-state-from-gobs-218215348/")
45+
require.NoError(t, err)
46+
return enState
47+
}
48+
49+
func EVMStateFromReplayGobDir(t *testing.T, gobDir string, flowHeight uint64) *state.EVMState {
50+
valueFileName, allocatorFileName := evmStateGobFileNamesByEndHeight(gobDir, flowHeight)
51+
chainID := flow.Testnet
52+
53+
allocatorGobs, err := testutils.DeserializeAllocator(allocatorFileName)
54+
require.NoError(t, err)
55+
56+
storageRoot := evm.StorageAccountAddress(chainID)
57+
valuesGob, err := testutils.DeserializeState(valueFileName)
58+
require.NoError(t, err)
59+
60+
store := testutils.GetSimpleValueStorePopulated(valuesGob, allocatorGobs)
61+
62+
bv, err := state.NewBaseView(store, storageRoot)
63+
require.NoError(t, err)
64+
65+
evmState, err := state.Extract(storageRoot, bv)
66+
require.NoError(t, err)
67+
return evmState
68+
}
69+
70+
func evmStateGobFileNamesByEndHeight(evmStateGobDir string, endHeight uint64) (string, string) {
71+
valueFileName := filepath.Join(evmStateGobDir, fmt.Sprintf("values-%d.gob", endHeight))
72+
allocatorFileName := filepath.Join(evmStateGobDir, fmt.Sprintf("allocators-%d.gob", endHeight))
73+
return valueFileName, allocatorFileName
74+
}

0 commit comments

Comments
 (0)