Skip to content

Commit be533da

Browse files
authored
Merge pull request #7917 from onflow/fxamacker/add-account-key-validation-to-storage-health-check
Update storage health check to add account key validation, etc.
2 parents 26e1d22 + b2a6b61 commit be533da

File tree

4 files changed

+175
-12
lines changed

4 files changed

+175
-12
lines changed

cmd/util/cmd/check-storage/cmd.go

Lines changed: 160 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,25 @@ package check_storage
33
import (
44
"context"
55
"fmt"
6+
"strings"
67

78
"github.com/onflow/atree"
89
"github.com/onflow/cadence/interpreter"
10+
"github.com/onflow/crypto/hash"
911
"github.com/rs/zerolog/log"
1012
"github.com/spf13/cobra"
1113
"golang.org/x/sync/errgroup"
1214

1315
"github.com/onflow/cadence/common"
1416
"github.com/onflow/cadence/runtime"
1517

18+
"github.com/onflow/flow-go/cmd/util/ledger/migrations"
1619
"github.com/onflow/flow-go/cmd/util/ledger/reporters"
1720
"github.com/onflow/flow-go/cmd/util/ledger/util"
1821
"github.com/onflow/flow-go/cmd/util/ledger/util/registers"
22+
"github.com/onflow/flow-go/fvm/environment"
1923
"github.com/onflow/flow-go/fvm/evm/emulator/state"
24+
storageState "github.com/onflow/flow-go/fvm/storage/state"
2025
"github.com/onflow/flow-go/fvm/systemcontracts"
2126
"github.com/onflow/flow-go/ledger"
2227
"github.com/onflow/flow-go/model/flow"
@@ -32,6 +37,8 @@ var (
3237
flagNWorker int
3338
flagHasAccountFormatV1 bool
3439
flagHasAccountFormatV2 bool
40+
flagIsAccountStatusV4 bool
41+
flagCheckContract bool
3542
)
3643

3744
var (
@@ -109,9 +116,23 @@ func init() {
109116
Cmd.Flags().BoolVar(
110117
&flagHasAccountFormatV2,
111118
"account-format-v2",
112-
false,
119+
true,
113120
"State contains accounts in v2 format",
114121
)
122+
123+
Cmd.Flags().BoolVar(
124+
&flagIsAccountStatusV4,
125+
"account-status-v4",
126+
false,
127+
"State is migrated to account status v4 format",
128+
)
129+
130+
Cmd.Flags().BoolVar(
131+
&flagCheckContract,
132+
"check-contract",
133+
false,
134+
"check stored contract",
135+
)
115136
}
116137

117138
func run(*cobra.Command, []string) {
@@ -397,7 +418,48 @@ func checkAccountStorageHealth(accountRegisters *registers.AccountRegisters, nWo
397418
)
398419
}
399420

400-
// TODO: check health of non-atree registers
421+
if flagIsAccountStatusV4 {
422+
// Validate account public key storage
423+
err = migrations.ValidateAccountPublicKeyV4(address, accountRegisters)
424+
if err != nil {
425+
issues = append(
426+
issues,
427+
accountStorageIssue{
428+
Address: address.Hex(),
429+
Kind: storageErrorKindString[accountKeyErrorKind],
430+
Msg: err.Error(),
431+
},
432+
)
433+
}
434+
435+
// Check account public keys
436+
err = checkAccountPublicKeys(address, accountRegisters)
437+
if err != nil {
438+
issues = append(
439+
issues,
440+
accountStorageIssue{
441+
Address: address.Hex(),
442+
Kind: storageErrorKindString[accountKeyErrorKind],
443+
Msg: err.Error(),
444+
},
445+
)
446+
}
447+
}
448+
449+
if flagCheckContract {
450+
// Check contracts
451+
err = checkContracts(address, accountRegisters)
452+
if err != nil {
453+
issues = append(
454+
issues,
455+
accountStorageIssue{
456+
Address: address.Hex(),
457+
Kind: storageErrorKindString[contractErrorKind],
458+
Msg: err.Error(),
459+
},
460+
)
461+
}
462+
}
401463

402464
return issues
403465
}
@@ -409,12 +471,16 @@ const (
409471
cadenceAtreeStorageErrorKind
410472
evmAtreeStorageErrorKind
411473
storageFormatErrorKind
474+
accountKeyErrorKind
475+
contractErrorKind
412476
)
413477

414478
var storageErrorKindString = map[storageErrorKind]string{
415479
otherErrorKind: "error_check_storage_failed",
416480
cadenceAtreeStorageErrorKind: "error_cadence_atree_storage",
417481
evmAtreeStorageErrorKind: "error_evm_atree_storage",
482+
accountKeyErrorKind: "error_account_public_key",
483+
contractErrorKind: "error_contract",
418484
}
419485

420486
type accountStorageIssue struct {
@@ -484,3 +550,95 @@ func checkAccountFormat(
484550

485551
return nil
486552
}
553+
554+
func checkAccountPublicKeys(
555+
address common.Address,
556+
accountRegisters *registers.AccountRegisters,
557+
) error {
558+
// Skip empty address because it doesn't have any account status registers.
559+
if len(address) == 0 || address == common.ZeroAddress {
560+
return nil
561+
}
562+
563+
accounts := newAccounts(accountRegisters)
564+
565+
keyCount, err := accounts.GetAccountPublicKeyCount(flow.BytesToAddress(address.Bytes()))
566+
if err != nil {
567+
return err
568+
}
569+
570+
// Check keys
571+
for keyIndex := range keyCount {
572+
_, err = accounts.GetAccountPublicKey(flow.BytesToAddress(address.Bytes()), keyIndex)
573+
if err != nil {
574+
return err
575+
}
576+
}
577+
578+
// NOTE: don't need to check unreachable keys because it is checked in ValidateAccountPublicKeyV4().
579+
580+
return nil
581+
}
582+
583+
func checkContracts(
584+
address common.Address,
585+
accountRegisters *registers.AccountRegisters,
586+
) error {
587+
if len(address) == 0 || address == common.ZeroAddress {
588+
return nil
589+
}
590+
591+
accounts := newAccounts(accountRegisters)
592+
593+
contractNames, err := accounts.GetContractNames(flow.BytesToAddress(address.Bytes()))
594+
if err != nil {
595+
return err
596+
}
597+
598+
// Check contract
599+
contractRegisterKeys := make(map[string]bool)
600+
for _, contractName := range contractNames {
601+
contractRegisterKeys[flow.ContractKey(contractName)] = true
602+
603+
_, err = accounts.GetContract(contractName, flow.BytesToAddress(address.Bytes()))
604+
if err != nil {
605+
return err
606+
}
607+
}
608+
609+
// Check unreachable contract registers
610+
err = accountRegisters.ForEachKey(func(key string) error {
611+
if strings.HasPrefix(key, flow.CodeKeyPrefix) && !contractRegisterKeys[key] {
612+
return fmt.Errorf("found unreachable contract register %s, contract names %v", key, contractNames)
613+
}
614+
return nil
615+
})
616+
617+
return err
618+
}
619+
620+
func newAccounts(accountRegisters *registers.AccountRegisters) environment.Accounts {
621+
// Create a new transaction state with a dummy hasher
622+
// because we do not need spock proofs for migrations.
623+
transactionState := storageState.NewTransactionStateFromExecutionState(
624+
storageState.NewExecutionStateWithSpockStateHasher(
625+
registers.StorageSnapshot{
626+
Registers: accountRegisters,
627+
},
628+
storageState.DefaultParameters(),
629+
func() hash.Hasher {
630+
return dummyHasher{}
631+
},
632+
),
633+
)
634+
return environment.NewAccounts(transactionState)
635+
}
636+
637+
type dummyHasher struct{}
638+
639+
func (d dummyHasher) Algorithm() hash.HashingAlgorithm { return hash.UnknownHashingAlgorithm }
640+
func (d dummyHasher) Size() int { return 0 }
641+
func (d dummyHasher) ComputeHash([]byte) hash.Hash { return nil }
642+
func (d dummyHasher) Write([]byte) (int, error) { return 0, nil }
643+
func (d dummyHasher) SumHash() hash.Hash { return nil }
644+
func (d dummyHasher) Reset() {}

cmd/util/ledger/migrations/account_key_deduplication_migration.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ func NewAccountPublicKeyDeduplicationMigration(
9898
chainID: chainID,
9999
reporter: rwf.ReportWriter("account-public-key-deduplication-migration_summary"),
100100
outputDir: outputDir,
101+
validate: validate,
101102
}
102103

103104
if validate {
@@ -131,7 +132,7 @@ func (m *AccountPublicKeyDeduplicationMigration) MigrateAccount(
131132
}
132133

133134
if m.validate {
134-
err := validateAccountPublicKeyV4(address, accountRegisters)
135+
err := ValidateAccountPublicKeyV4(address, accountRegisters)
135136
if err != nil {
136137
m.validationReporter.Write(validationError{
137138
Address: address.Hex(),

cmd/util/ledger/migrations/account_key_deduplication_migration_test.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ func TestMigration(t *testing.T) {
232232
require.Equal(t, byte(0x40), encodedAccountStatusV4[0])
233233
require.Equal(t, encodedAccountStatusV3[1:], encodedAccountStatusV4[1:])
234234

235-
err = validateAccountPublicKeyV4(common.Address(owner), accountRegisters)
235+
err = ValidateAccountPublicKeyV4(common.Address(owner), accountRegisters)
236236
require.NoError(t, err)
237237
})
238238

@@ -276,7 +276,7 @@ func TestMigration(t *testing.T) {
276276
require.NoError(t, err)
277277
require.Equal(t, encodedPk1, encodedAccountPublicKey0)
278278

279-
err = validateAccountPublicKeyV4(common.Address(owner), accountRegisters)
279+
err = ValidateAccountPublicKeyV4(common.Address(owner), accountRegisters)
280280
require.NoError(t, err)
281281
})
282282

@@ -321,7 +321,7 @@ func TestMigration(t *testing.T) {
321321
require.NoError(t, err)
322322
require.Equal(t, encodedPk1, encodedAccountPublicKey0)
323323

324-
err = validateAccountPublicKeyV4(common.Address(owner), accountRegisters)
324+
err = ValidateAccountPublicKeyV4(common.Address(owner), accountRegisters)
325325
require.NoError(t, err)
326326
})
327327

@@ -399,7 +399,7 @@ func TestMigration(t *testing.T) {
399399
require.NoError(t, err)
400400
require.ElementsMatch(t, [][]byte{{}, encodedSpk2}, encodedPks)
401401

402-
err = validateAccountPublicKeyV4(common.Address(owner), accountRegisters)
402+
err = ValidateAccountPublicKeyV4(common.Address(owner), accountRegisters)
403403
require.NoError(t, err)
404404
})
405405

@@ -488,7 +488,7 @@ func TestMigration(t *testing.T) {
488488
require.NoError(t, err)
489489
require.Equal(t, uint64(2), seqNum)
490490

491-
err = validateAccountPublicKeyV4(common.Address(owner), accountRegisters)
491+
err = ValidateAccountPublicKeyV4(common.Address(owner), accountRegisters)
492492
require.NoError(t, err)
493493
})
494494

@@ -548,7 +548,7 @@ func TestMigration(t *testing.T) {
548548
require.NoError(t, err)
549549
require.Equal(t, encodedPk1, encodedAccountPublicKey0)
550550

551-
err = validateAccountPublicKeyV4(common.Address(owner), accountRegisters)
551+
err = ValidateAccountPublicKeyV4(common.Address(owner), accountRegisters)
552552
require.NoError(t, err)
553553
})
554554

@@ -618,7 +618,7 @@ func TestMigration(t *testing.T) {
618618
require.NoError(t, err)
619619
require.Equal(t, uint64(1), seqNum)
620620

621-
err = validateAccountPublicKeyV4(common.Address(owner), accountRegisters)
621+
err = ValidateAccountPublicKeyV4(common.Address(owner), accountRegisters)
622622
require.NoError(t, err)
623623
})
624624
}

cmd/util/ledger/migrations/account_key_deduplication_migration_validation.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,14 @@ import (
1313
"github.com/onflow/flow-go/model/flow"
1414
)
1515

16-
func validateAccountPublicKeyV4(
16+
func ValidateAccountPublicKeyV4(
1717
address common.Address,
1818
accountRegisters *registers.AccountRegisters,
1919
) error {
20+
// Skip empty address because it doesn't have account status register.
21+
if len(address) == 0 || address == common.ZeroAddress {
22+
return nil
23+
}
2024

2125
// Validate account status register
2226
accountPublicKeyCount, storedKeyCount, deduplicated, err := validateAccountStatusV4Register(address, accountRegisters)
@@ -90,7 +94,7 @@ func validateAccountStatusV4Register(
9094
deduplicated bool,
9195
err error,
9296
) {
93-
owner := string(address[:])
97+
owner := flow.AddressToRegisterOwner(flow.Address(address[:]))
9498

9599
encodedAccountStatus, err := accountRegisters.Get(owner, flow.AccountStatusKey)
96100
if err != nil {

0 commit comments

Comments
 (0)