@@ -3,20 +3,25 @@ package check_storage
3
3
import (
4
4
"context"
5
5
"fmt"
6
+ "strings"
6
7
7
8
"github.com/onflow/atree"
8
9
"github.com/onflow/cadence/interpreter"
10
+ "github.com/onflow/crypto/hash"
9
11
"github.com/rs/zerolog/log"
10
12
"github.com/spf13/cobra"
11
13
"golang.org/x/sync/errgroup"
12
14
13
15
"github.com/onflow/cadence/common"
14
16
"github.com/onflow/cadence/runtime"
15
17
18
+ "github.com/onflow/flow-go/cmd/util/ledger/migrations"
16
19
"github.com/onflow/flow-go/cmd/util/ledger/reporters"
17
20
"github.com/onflow/flow-go/cmd/util/ledger/util"
18
21
"github.com/onflow/flow-go/cmd/util/ledger/util/registers"
22
+ "github.com/onflow/flow-go/fvm/environment"
19
23
"github.com/onflow/flow-go/fvm/evm/emulator/state"
24
+ storageState "github.com/onflow/flow-go/fvm/storage/state"
20
25
"github.com/onflow/flow-go/fvm/systemcontracts"
21
26
"github.com/onflow/flow-go/ledger"
22
27
"github.com/onflow/flow-go/model/flow"
32
37
flagNWorker int
33
38
flagHasAccountFormatV1 bool
34
39
flagHasAccountFormatV2 bool
40
+ flagIsAccountStatusV4 bool
41
+ flagCheckContract bool
35
42
)
36
43
37
44
var (
@@ -109,9 +116,23 @@ func init() {
109
116
Cmd .Flags ().BoolVar (
110
117
& flagHasAccountFormatV2 ,
111
118
"account-format-v2" ,
112
- false ,
119
+ true ,
113
120
"State contains accounts in v2 format" ,
114
121
)
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
+ )
115
136
}
116
137
117
138
func run (* cobra.Command , []string ) {
@@ -397,7 +418,48 @@ func checkAccountStorageHealth(accountRegisters *registers.AccountRegisters, nWo
397
418
)
398
419
}
399
420
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
+ }
401
463
402
464
return issues
403
465
}
@@ -409,12 +471,16 @@ const (
409
471
cadenceAtreeStorageErrorKind
410
472
evmAtreeStorageErrorKind
411
473
storageFormatErrorKind
474
+ accountKeyErrorKind
475
+ contractErrorKind
412
476
)
413
477
414
478
var storageErrorKindString = map [storageErrorKind ]string {
415
479
otherErrorKind : "error_check_storage_failed" ,
416
480
cadenceAtreeStorageErrorKind : "error_cadence_atree_storage" ,
417
481
evmAtreeStorageErrorKind : "error_evm_atree_storage" ,
482
+ accountKeyErrorKind : "error_account_public_key" ,
483
+ contractErrorKind : "error_contract" ,
418
484
}
419
485
420
486
type accountStorageIssue struct {
@@ -484,3 +550,95 @@ func checkAccountFormat(
484
550
485
551
return nil
486
552
}
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 () {}
0 commit comments