Skip to content

Commit 80bb75d

Browse files
aaroncclevinson
andauthored
Fix math (regen-network#15)
* Fix math * Update account.go Co-authored-by: Cory <[email protected]> * trigger ci Co-authored-by: Cory <[email protected]>
1 parent 1ca4ada commit 80bb75d

9 files changed

+529
-309
lines changed

account.go

Lines changed: 92 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ import (
55
"math"
66
"time"
77

8-
"github.com/cockroachdb/apd/v2"
9-
108
sdk "github.com/cosmos/cosmos-sdk/types"
119
auth "github.com/cosmos/cosmos-sdk/x/auth/types"
1210
vesting "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
@@ -16,14 +14,14 @@ import (
1614
// Account is an internal representation of a genesis regen account
1715
type Account struct {
1816
Address sdk.AccAddress
19-
TotalRegen apd.Decimal
17+
TotalRegen Dec
2018
Distributions []Distribution
2119
}
2220

2321
// Distribution is an internal representation of a genesis vesting distribution of regen
2422
type Distribution struct {
2523
Time time.Time
26-
Regen apd.Decimal
24+
Regen Dec
2725
}
2826

2927
func (a Account) String() string {
@@ -34,22 +32,51 @@ func (d Distribution) String() string {
3432
return fmt.Sprintf("Distribution{%s, %sregen}", d.Time.Format(time.RFC3339), d.Regen.String())
3533
}
3634

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
4269
}
4370

4471
func RecordToAccount(rec Record, genesisTime time.Time) (Account, error) {
4572
amount := rec.TotalAmount
4673
distTime := rec.StartTime
4774
if distTime.IsZero() {
48-
distTime = genesisTime
75+
return Account{}, fmt.Errorf("require a non-zero distribution time")
4976
}
5077
numDist := rec.NumMonthlyDistributions
5178
if numDist < 1 {
52-
numDist = 1
79+
return Account{}, fmt.Errorf("numDist must be >= 1, got %d", numDist)
5380
}
5481

5582
if numDist == 1 && !distTime.After(genesisTime) {
@@ -65,17 +92,19 @@ func RecordToAccount(rec Record, genesisTime time.Time) (Account, error) {
6592
}, nil
6693
}
6794

95+
// calculate dust, which represents an uregen-integral remainder, represented as a decimal value of regen
96+
// from dividing `amount` by `numDist`
6897
distAmount, dust, err := distAmountAndDust(amount, numDist)
6998
if err != nil {
7099
return Account{}, err
71100
}
72101

73102
var distributions []Distribution
74-
var genesisAmount apd.Decimal
103+
var genesisAmount Dec
75104

76105
// collapse all pre-genesis distributions into a genesis distribution
77106
for ; numDist > 0 && !distTime.After(genesisTime); numDist-- {
78-
_, err = apd.BaseContext.Add(&genesisAmount, &genesisAmount, &distAmount)
107+
genesisAmount, err = genesisAmount.Add(distAmount)
79108
if err != nil {
80109
return Account{}, err
81110
}
@@ -100,7 +129,7 @@ func RecordToAccount(rec Record, genesisTime time.Time) (Account, error) {
100129
}
101130

102131
// 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)
104133
if err != nil {
105134
return Account{}, err
106135
}
@@ -112,36 +141,42 @@ func RecordToAccount(rec Record, genesisTime time.Time) (Account, error) {
112141
}, nil
113142
}
114143

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 {
117150
return amount, dust, nil
118151
}
119152

120-
var numDistDec apd.Decimal
121-
numDistDec = *numDistDec.SetInt64(int64(numDist))
153+
numDistDec := NewDecFromInt64(int64(numDist))
122154

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)
124157
if err != nil {
125158
return distAmount, dust, err
126159
}
127160

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)
130163
if err != nil {
131164
return distAmount, dust, err
132165
}
133166

134-
_, err = Dec128Context.Rem(&dust, &amount, &numDistDec)
167+
dust, err = amount.Rem(numDistDec)
135168
if err != nil {
136169
return distAmount, dust, err
137170
}
138171

139-
_, err = Dec128Context.Quo(&distAmount, &distAmount, tenE6)
172+
// convert distAmount from uregen back to regen
173+
distAmount, err = distAmount.Quo(tenE6)
140174
if err != nil {
141175
return distAmount, dust, err
142176
}
143177

144-
_, err = Dec128Context.Quo(&dust, &dust, tenE6)
178+
// convert dust from uregen back to regen
179+
dust, err = dust.Quo(tenE6)
145180
if err != nil {
146181
return distAmount, dust, err
147182
}
@@ -153,18 +188,15 @@ const (
153188
URegenDenom = "uregen"
154189
)
155190

156-
var tenE6 *apd.Decimal
191+
var tenE6 Dec = NewDecFromInt64(1000000)
157192

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()
161195
if err != nil {
162-
panic(err)
196+
return nil, nil, err
163197
}
164-
}
165198

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)
168200
if err != nil {
169201
return nil, nil, err
170202
}
@@ -183,24 +215,13 @@ func ToCosmosAccount(acc Account, genesisTime time.Time) (auth.AccountI, *bank.B
183215

184216
// if we have one distribution and it happens before or at genesis return a basic BaseAccount
185217
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-
190218
return &auth.BaseAccount{Address: addrStr}, balance, nil
191219
} else {
192220
periodStart := startTime
193221

194222
var periods []vesting.Period
195-
var calcTotal apd.Decimal
196-
197223
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)
204225
if err != nil {
205226
return nil, nil, err
206227
}
@@ -214,10 +235,6 @@ func ToCosmosAccount(acc Account, genesisTime time.Time) (auth.AccountI, *bank.B
214235
})
215236
}
216237

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-
221238
return &vesting.PeriodicVestingAccount{
222239
BaseVestingAccount: &vesting.BaseVestingAccount{
223240
BaseAccount: &auth.BaseAccount{
@@ -232,9 +249,8 @@ func ToCosmosAccount(acc Account, genesisTime time.Time) (auth.AccountI, *bank.B
232249
}
233250
}
234251

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)
238254
if err != nil {
239255
return nil, err
240256
}
@@ -246,3 +262,28 @@ func RegenToCoins(regenAmount *apd.Decimal) (sdk.Coins, error) {
246262

247263
return sdk.NewCoins(sdk.NewCoin(URegenDenom, sdk.NewInt(uregenInt64))), nil
248264
}
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

Comments
 (0)