5
5
"math"
6
6
"time"
7
7
8
- "github.com/cockroachdb/apd/v2"
9
-
10
8
sdk "github.com/cosmos/cosmos-sdk/types"
11
9
auth "github.com/cosmos/cosmos-sdk/x/auth/types"
12
10
vesting "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
@@ -16,14 +14,14 @@ import (
16
14
// Account is an internal representation of a genesis regen account
17
15
type Account struct {
18
16
Address sdk.AccAddress
19
- TotalRegen apd. Decimal
17
+ TotalRegen Dec
20
18
Distributions []Distribution
21
19
}
22
20
23
21
// Distribution is an internal representation of a genesis vesting distribution of regen
24
22
type Distribution struct {
25
23
Time time.Time
26
- Regen apd. Decimal
24
+ Regen Dec
27
25
}
28
26
29
27
func (a Account ) String () string {
@@ -34,22 +32,51 @@ func (d Distribution) String() string {
34
32
return fmt .Sprintf ("Distribution{%s, %sregen}" , d .Time .Format (time .RFC3339 ), d .Regen .String ())
35
33
}
36
34
37
- var Dec128Context = apd.Context {
38
- Precision : 34 ,
39
- MaxExponent : apd .MaxExponent ,
40
- MinExponent : apd .MinExponent ,
41
- Traps : apd .DefaultTraps ,
35
+ func (acc Account ) Validate () error {
36
+ if acc .Address .Empty () {
37
+ return fmt .Errorf ("empty address" )
38
+ }
39
+
40
+ if ! acc .TotalRegen .IsPositive () {
41
+ return fmt .Errorf ("expected positive balance, got %s" , acc .TotalRegen .String ())
42
+ }
43
+
44
+ var calcTotal Dec
45
+ for _ , dist := range acc .Distributions {
46
+ err := dist .Validate ()
47
+ if err != nil {
48
+ return err
49
+ }
50
+
51
+ calcTotal , err = calcTotal .Add (dist .Regen )
52
+ if err != nil {
53
+ return err
54
+ }
55
+ }
56
+
57
+ if ! acc .TotalRegen .IsEqual (calcTotal ) {
58
+ return fmt .Errorf ("incorrect balance, expected %s, got %s" , acc .TotalRegen .String (), calcTotal .String ())
59
+ }
60
+
61
+ return nil
62
+ }
63
+
64
+ func (d Distribution ) Validate () error {
65
+ if d .Time .IsZero () {
66
+ return fmt .Errorf ("time is zero" )
67
+ }
68
+ return nil
42
69
}
43
70
44
71
func RecordToAccount (rec Record , genesisTime time.Time ) (Account , error ) {
45
72
amount := rec .TotalAmount
46
73
distTime := rec .StartTime
47
74
if distTime .IsZero () {
48
- distTime = genesisTime
75
+ return Account {}, fmt . Errorf ( "require a non-zero distribution time" )
49
76
}
50
77
numDist := rec .NumMonthlyDistributions
51
78
if numDist < 1 {
52
- numDist = 1
79
+ return Account {}, fmt . Errorf ( " numDist must be > = 1, got %d" , numDist )
53
80
}
54
81
55
82
if numDist == 1 && ! distTime .After (genesisTime ) {
@@ -65,17 +92,19 @@ func RecordToAccount(rec Record, genesisTime time.Time) (Account, error) {
65
92
}, nil
66
93
}
67
94
95
+ // calculate dust, which represents an uregen-integral remainder, represented as a decimal value of regen
96
+ // from dividing `amount` by `numDist`
68
97
distAmount , dust , err := distAmountAndDust (amount , numDist )
69
98
if err != nil {
70
99
return Account {}, err
71
100
}
72
101
73
102
var distributions []Distribution
74
- var genesisAmount apd. Decimal
103
+ var genesisAmount Dec
75
104
76
105
// collapse all pre-genesis distributions into a genesis distribution
77
106
for ; numDist > 0 && ! distTime .After (genesisTime ); numDist -- {
78
- _ , err = apd . BaseContext . Add (& genesisAmount , & genesisAmount , & distAmount )
107
+ genesisAmount , err = genesisAmount . Add (distAmount )
79
108
if err != nil {
80
109
return Account {}, err
81
110
}
@@ -100,7 +129,7 @@ func RecordToAccount(rec Record, genesisTime time.Time) (Account, error) {
100
129
}
101
130
102
131
// add dust to first distribution
103
- _ , err = apd . BaseContext . Add ( & distributions [0 ].Regen , & distributions [0 ].Regen , & dust )
132
+ distributions [0 ].Regen , err = distributions [0 ].Regen . Add ( dust )
104
133
if err != nil {
105
134
return Account {}, err
106
135
}
@@ -112,36 +141,42 @@ func RecordToAccount(rec Record, genesisTime time.Time) (Account, error) {
112
141
}, nil
113
142
}
114
143
115
- func distAmountAndDust (amount apd.Decimal , numDist int ) (distAmount apd.Decimal , dust apd.Decimal , err error ) {
116
- if numDist <= 1 {
144
+ func distAmountAndDust (amount Dec , numDist int ) (distAmount Dec , dust Dec , err error ) {
145
+ if numDist < 1 {
146
+ return Dec {}, Dec {}, fmt .Errorf ("num must be >= 1, got %d" , numDist )
147
+ }
148
+
149
+ if numDist == 1 {
117
150
return amount , dust , nil
118
151
}
119
152
120
- var numDistDec apd.Decimal
121
- numDistDec = * numDistDec .SetInt64 (int64 (numDist ))
153
+ numDistDec := NewDecFromInt64 (int64 (numDist ))
122
154
123
- _ , err = Dec128Context .Mul (& amount , & amount , tenE6 )
155
+ // convert amount from regen to uregen, so we can perform integral arithmetic on uregen
156
+ amount , err = amount .Mul (tenE6 )
124
157
if err != nil {
125
158
return distAmount , dust , err
126
159
}
127
160
128
- // each distribution is an integral amount of regen
129
- _ , err = Dec128Context .QuoInteger (& distAmount , & amount , & numDistDec )
161
+ // each distribution is an integral amount of uregen
162
+ distAmount , err = amount .QuoInteger (numDistDec )
130
163
if err != nil {
131
164
return distAmount , dust , err
132
165
}
133
166
134
- _ , err = Dec128Context .Rem (& dust , & amount , & numDistDec )
167
+ dust , err = amount .Rem (numDistDec )
135
168
if err != nil {
136
169
return distAmount , dust , err
137
170
}
138
171
139
- _ , err = Dec128Context .Quo (& distAmount , & distAmount , tenE6 )
172
+ // convert distAmount from uregen back to regen
173
+ distAmount , err = distAmount .Quo (tenE6 )
140
174
if err != nil {
141
175
return distAmount , dust , err
142
176
}
143
177
144
- _ , err = Dec128Context .Quo (& dust , & dust , tenE6 )
178
+ // convert dust from uregen back to regen
179
+ dust , err = dust .Quo (tenE6 )
145
180
if err != nil {
146
181
return distAmount , dust , err
147
182
}
@@ -153,18 +188,15 @@ const (
153
188
URegenDenom = "uregen"
154
189
)
155
190
156
- var tenE6 * apd. Decimal
191
+ var tenE6 Dec = NewDecFromInt64 ( 1000000 )
157
192
158
- func init () {
159
- var err error
160
- tenE6 , _ , err = apd .NewFromString ("1000000" )
193
+ func ToCosmosAccount (acc Account , genesisTime time.Time ) (auth.AccountI , * bank.Balance , error ) {
194
+ err := acc .Validate ()
161
195
if err != nil {
162
- panic ( err )
196
+ return nil , nil , err
163
197
}
164
- }
165
198
166
- func ToCosmosAccount (acc Account , genesisTime time.Time ) (auth.AccountI , * bank.Balance , error ) {
167
- totalCoins , err := RegenToCoins (& acc .TotalRegen )
199
+ totalCoins , err := RegenToCoins (acc .TotalRegen )
168
200
if err != nil {
169
201
return nil , nil , err
170
202
}
@@ -183,24 +215,13 @@ func ToCosmosAccount(acc Account, genesisTime time.Time) (auth.AccountI, *bank.B
183
215
184
216
// if we have one distribution and it happens before or at genesis return a basic BaseAccount
185
217
if len (acc .Distributions ) == 1 && ! startTime .After (genesisTime ) {
186
- if acc .TotalRegen .Cmp (& acc .Distributions [0 ].Regen ) != 0 {
187
- return nil , nil , fmt .Errorf ("incorrect balance, expected %s, got %s" , acc .TotalRegen .String (), acc .Distributions [0 ].Regen .String ())
188
- }
189
-
190
218
return & auth.BaseAccount {Address : addrStr }, balance , nil
191
219
} else {
192
220
periodStart := startTime
193
221
194
222
var periods []vesting.Period
195
- var calcTotal apd.Decimal
196
-
197
223
for _ , dist := range acc .Distributions {
198
- _ , err = apd .BaseContext .Add (& calcTotal , & calcTotal , & dist .Regen )
199
- if err != nil {
200
- return nil , nil , err
201
- }
202
-
203
- coins , err := RegenToCoins (& dist .Regen )
224
+ coins , err := RegenToCoins (dist .Regen )
204
225
if err != nil {
205
226
return nil , nil , err
206
227
}
@@ -214,10 +235,6 @@ func ToCosmosAccount(acc Account, genesisTime time.Time) (auth.AccountI, *bank.B
214
235
})
215
236
}
216
237
217
- if acc .TotalRegen .Cmp (& calcTotal ) != 0 {
218
- return nil , nil , fmt .Errorf ("incorrect balance, expected %s, got %s" , acc .TotalRegen .String (), calcTotal .String ())
219
- }
220
-
221
238
return & vesting.PeriodicVestingAccount {
222
239
BaseVestingAccount : & vesting.BaseVestingAccount {
223
240
BaseAccount : & auth.BaseAccount {
@@ -232,9 +249,8 @@ func ToCosmosAccount(acc Account, genesisTime time.Time) (auth.AccountI, *bank.B
232
249
}
233
250
}
234
251
235
- func RegenToCoins (regenAmount * apd.Decimal ) (sdk.Coins , error ) {
236
- var uregen apd.Decimal
237
- _ , err := apd .BaseContext .Mul (& uregen , regenAmount , tenE6 )
252
+ func RegenToCoins (regenAmount Dec ) (sdk.Coins , error ) {
253
+ uregen , err := regenAmount .Mul (tenE6 )
238
254
if err != nil {
239
255
return nil , err
240
256
}
@@ -246,3 +262,28 @@ func RegenToCoins(regenAmount *apd.Decimal) (sdk.Coins, error) {
246
262
247
263
return sdk .NewCoins (sdk .NewCoin (URegenDenom , sdk .NewInt (uregenInt64 ))), nil
248
264
}
265
+
266
+ func ValidateVestingAccount (acc auth.AccountI ) error {
267
+ vacc , ok := acc .(* vesting.PeriodicVestingAccount )
268
+ if ! ok {
269
+ return nil
270
+ }
271
+
272
+ orig := vacc .OriginalVesting
273
+ time := vacc .StartTime
274
+ var total sdk.Coins
275
+ for _ , period := range vacc .VestingPeriods {
276
+ total = total .Add (period .Amount ... )
277
+ time += period .Length
278
+ }
279
+
280
+ if ! orig .IsEqual (total ) {
281
+ return fmt .Errorf ("vesting account error: expected %s coins, got %s" , orig .String (), total .String ())
282
+ }
283
+
284
+ if vacc .EndTime != time {
285
+ return fmt .Errorf ("vesting account error: expected %d end time, got %d" , vacc .EndTime , time )
286
+ }
287
+
288
+ return nil
289
+ }
0 commit comments