-
Notifications
You must be signed in to change notification settings - Fork 557
/
simctx.go
174 lines (149 loc) · 4.53 KB
/
simctx.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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
package simtypes
import (
"crypto/sha256"
"encoding/binary"
"fmt"
"math/rand"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/simulation"
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
ibctestingtypes "github.com/cosmos/ibc-go/v7/testing/types"
)
// TODO: Contemplate name better
//
//nolint:structcheck
type SimCtx struct {
rm randManager
Accounts []simulation.Account
Cdc codec.JSONCodec // application codec
app App
chainID string
txbuilder func(ctx sdk.Context, msg sdk.Msg, msgName string) (sdk.Tx, error)
}
func NewSimCtx(r *rand.Rand, app App, accounts []simulation.Account, chainID string) *SimCtx {
sim := &SimCtx{
rm: newRandManager(r),
app: app,
Accounts: accounts,
chainID: chainID,
}
sim.txbuilder = sim.defaultTxBuilder
return sim
}
// TODO: Consider rename to Rand()
func (sim *SimCtx) GetRand() *rand.Rand {
return sim.rm.GetRand()
}
// TODO: Consider rename to SeededRand()
// or DomainSeparatedRand
func (sim *SimCtx) GetSeededRand(seed string) *rand.Rand {
return sim.rm.GetSeededRand(seed)
}
// WrapRand returns a new sim object and a cleanup function to write
// Accounts changes to the parent (and invalidate the prior)
func (sim *SimCtx) WrapRand(domainSeparator string) (wrappedSim *SimCtx, cleanup func()) {
wrappedSim = &SimCtx{
rm: sim.rm.WrapRand(domainSeparator),
app: sim.app,
Accounts: sim.Accounts,
Cdc: sim.Cdc,
chainID: sim.chainID,
txbuilder: sim.txbuilder,
}
cleanup = func() {
sim.Accounts = wrappedSim.Accounts
wrappedSim.Accounts = nil
}
return wrappedSim, cleanup
}
func (sim SimCtx) ChainID() string {
return sim.chainID
}
func (sim SimCtx) BaseApp() *baseapp.BaseApp {
return sim.app.GetBaseApp()
}
func (sim SimCtx) AppCodec() codec.Codec {
return sim.app.AppCodec()
}
func (sim SimCtx) AccountKeeper() AccountKeeper {
return sim.app.GetAccountKeeper()
}
func (sim SimCtx) BankKeeper() BankKeeper {
return sim.app.GetBankKeeper()
}
func (sim SimCtx) SDKStakingKeeper() stakingkeeper.Keeper {
return sim.app.GetSDKStakingKeeper()
}
func (sim SimCtx) StakingKeeper() ibctestingtypes.StakingKeeper {
return sim.app.GetStakingKeeper()
}
func (sim SimCtx) PoolManagerKeeper() PoolManagerKeeper {
return sim.app.GetPoolManagerKeeper()
}
// randManager is built to give API's for randomness access
// which allow the caller to avoid "butterfly effects".
// e.g. in the Simulator, I don't want adding one new rand call to a message
// to create an entirely new "run" shape.
type randManager struct {
// TODO: delete this, once we cleanup simulator initialization logic,
// and can then setup SimCtx with base seed.
internalSeed int64
rCounter int64
seededMap map[string]*rand.Rand
// if debug = true, we maintain a list of "seen" calls to Wrap,
// to ensure no duplicates ever get made.
// TODO: Find a way to expose this to executor.
// Perhaps we move this to an internal package?
debug bool
seenWraps map[string]bool
}
// TODO: Refactor to take in seed as API's improve
func newRandManager(r *rand.Rand) randManager {
return randManager{
internalSeed: r.Int63(),
rCounter: 0,
seededMap: map[string]*rand.Rand{},
debug: false,
seenWraps: map[string]bool{},
}
}
func stringToSeed(s string) int64 {
// take first 8 bytes of the sha256 hash of s.
// We use this for seeding our rand instances.
// We use a hash just for convenience, we don't need cryptographic collision resistance,
// just simple collisions being unlikely.
bz := sha256.Sum256([]byte(s))
seedInt := binary.BigEndian.Uint64(bz[:8])
return int64(seedInt)
}
func (rm *randManager) WrapRand(domainSeparator string) randManager {
if rm.debug {
if _, found := rm.seenWraps[domainSeparator]; found {
panic(fmt.Sprintf("domain separator %s reused!", domainSeparator))
}
rm.seenWraps[domainSeparator] = true
}
sepInt := stringToSeed(domainSeparator)
newSeed := rm.internalSeed + sepInt
r := rand.New(rand.NewSource(newSeed))
return newRandManager(r)
}
func (rm *randManager) GetRand() *rand.Rand {
rm.rCounter += 1
r := rand.New(rand.NewSource(rm.internalSeed + rm.rCounter))
return r
}
// TODO: Consider rename to DomainSeparatedRand
func (rm *randManager) GetSeededRand(seed string) *rand.Rand {
// use value in map if present
if r, ok := rm.seededMap[seed]; ok {
return r
}
seedInt := stringToSeed(seed)
newSeed := rm.internalSeed + seedInt
r := rand.New(rand.NewSource(newSeed))
rm.seededMap[seed] = r
return r
}