/
storage.go
129 lines (116 loc) · 4.35 KB
/
storage.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package replay
import (
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/substate"
"github.com/urfave/cli/v2"
)
// record-replay: substate-cli storage command
var GetStorageUpdateSizeCommand = cli.Command{
Action: getStorageUpdateSizeAction,
Name: "storage-size",
Usage: "returns changes in storage size by transactions in the specified block range",
ArgsUsage: "<blockNumFirst> <blockNumLast>",
Flags: []cli.Flag{
&substate.WorkersFlag,
&substate.SubstateDirFlag,
&ChainIDFlag,
},
Description: `
The substate-cli storage-size command requires two arguments:
<blockNumFirst> <blockNumLast>
<blockNumFirst> and <blockNumLast> are the first and
last block of the inclusive range of blocks to replay transactions.
Output log format: (block, timestamp, transaction, account, storage update size, storage size in input substate, storage size in output substate)`,
}
// computeStorageSize computes the number of non-zero storage entries
func computeStorageSizes(inUpdateSet map[common.Hash]common.Hash, outUpdateSet map[common.Hash]common.Hash) (int64, uint64, uint64) {
deltaSize := int64(0)
inUpdateSize := uint64(0)
outUpdateSize := uint64(0)
wordSize := uint64(32) //bytes
for address, outValue := range outUpdateSet {
if inValue, found := inUpdateSet[address]; found {
if (inValue == common.Hash{} && outValue != common.Hash{}) {
// storage increases by one new cell
// (cell is empty in in-storage)
deltaSize++
} else if (inValue != common.Hash{} && outValue == common.Hash{}) {
// storage shrinks by one new cell
// (cell is empty in out-storage)
deltaSize--
}
} else {
// storage increases by one new cell
// (cell is not found in in-storage but found in out-storage)
if (outValue != common.Hash{}) {
deltaSize++
}
}
// compute update size
if (outValue != common.Hash{}) {
outUpdateSize++
}
}
for address, inValue := range inUpdateSet {
if _, found := outUpdateSet[address]; !found {
// storage shrinks by one cell
// (The cell does not exist for an address in in-storage)
if (inValue != common.Hash{}) {
deltaSize--
}
}
if (inValue != common.Hash{}) {
inUpdateSize++
}
}
return deltaSize * int64(wordSize), inUpdateSize * wordSize, outUpdateSize * wordSize
}
// getStorageUpdateSizeTask replays storage access of accounts in each transaction
func getStorageUpdateSizeTask(block uint64, tx int, st *substate.Substate, taskPool *substate.SubstateTaskPool) error {
timestamp := st.Env.Timestamp
for wallet, outputAccount := range st.OutputAlloc {
var (
deltaSize int64
inUpdateSize uint64
outUpdateSize uint64
)
// account exists in both input substate and output substate
if inputAccount, found := st.InputAlloc[wallet]; found {
deltaSize, inUpdateSize, outUpdateSize = computeStorageSizes(inputAccount.Storage, outputAccount.Storage)
// account exists in output substate but not input substate
} else {
deltaSize, inUpdateSize, outUpdateSize = computeStorageSizes(map[common.Hash]common.Hash{}, outputAccount.Storage)
}
fmt.Printf("metric: %v,%v,%v,%v,%v,%v,%v\n", block, timestamp, tx, wallet.Hex(), deltaSize, inUpdateSize, outUpdateSize)
}
// account exists in input substate but not output substate
for wallet, inputAccount := range st.InputAlloc {
if _, found := st.OutputAlloc[wallet]; !found {
deltaSize, inUpdateSize, outUpdateSize := computeStorageSizes(inputAccount.Storage, map[common.Hash]common.Hash{})
fmt.Printf("metric: %v,%v,%v,%v,%v,%v,%v\n", block, timestamp, tx, wallet.Hex(), deltaSize, inUpdateSize, outUpdateSize)
}
}
return nil
}
// func getStorageUpdateSizeAction for replay-storage command
func getStorageUpdateSizeAction(ctx *cli.Context) error {
var err error
if ctx.Args().Len() != 2 {
return fmt.Errorf("substate-cli storage command requires exactly 2 arguments")
}
chainID = ctx.Int(ChainIDFlag.Name)
fmt.Printf("chain-id: %v\n", chainID)
fmt.Printf("git-date: %v\n", gitDate)
fmt.Printf("git-commit: %v\n", gitCommit)
first, last, argErr := SetBlockRange(ctx.Args().Get(0), ctx.Args().Get(1))
if argErr != nil {
return argErr
}
substate.SetSubstateFlags(ctx)
substate.OpenSubstateDBReadOnly()
defer substate.CloseSubstateDB()
taskPool := substate.NewSubstateTaskPool("substate-cli storage", getStorageUpdateSizeTask, first, last, ctx)
err = taskPool.Execute()
return err
}