Skip to content

Commit 4da949f

Browse files
committed
fix: address comments review
1 parent 225dc26 commit 4da949f

2 files changed

Lines changed: 122 additions & 63 deletions

File tree

pkg/storageincentives/staking/contract.go

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -166,15 +166,15 @@ func (c *contract) UpdateHeight(ctx context.Context) (common.Hash, bool, error)
166166
}
167167

168168
func (c *contract) GetPotentialStake(ctx context.Context) (*big.Int, error) {
169-
_, potential, err := c.getStake(ctx)
169+
_, potential, _, err := c.getStake(ctx)
170170
if err != nil {
171171
return nil, fmt.Errorf("staking contract: failed to get stake: %w", err)
172172
}
173173
return potential, nil
174174
}
175175

176176
func (c *contract) GetMinDeposit(ctx context.Context) (*big.Int, error) {
177-
committed, potential, err := c.getStake(ctx)
177+
committed, potential, stakeExists, err := c.getStake(ctx)
178178
if err != nil {
179179
return nil, fmt.Errorf("staking contract: failed to get stake: %w", err)
180180
}
@@ -187,20 +187,20 @@ func (c *contract) GetMinDeposit(ctx context.Context) (*big.Int, error) {
187187
}
188188
}
189189

190-
return calculateMinDeposit(potential, committed, price, c.height), nil
190+
return calculateMinDeposit(potential, committed, price, c.height, stakeExists), nil
191191
}
192192

193193
// calculateMinDeposit returns the minimum additional deposit in PLUR that manageStake will accept according to contract.
194-
func calculateMinDeposit(potential, committed *big.Int, price uint32, height uint8) *big.Int {
194+
// stakeExists mirrors Solidity's _stakingSet != 0 (lastUpdatedBlockNumber != 0).
195+
func calculateMinDeposit(potential, committed *big.Int, price uint32, height uint8, stakeExists bool) *big.Int {
195196
minAdd := big.NewInt(1)
196197

197-
// The contract applies the minimum stake floor only when creating a stake.
198-
if potential.Sign() == 0 {
199-
minAdd.Lsh(new(big.Int).Set(MinimumStakeAmount), uint(height))
198+
// Contract: BelowMinimumStake when addAmount < MIN_STAKE * 2^height && _stakingSet == 0.
199+
if !stakeExists {
200+
minAdd = new(big.Int).Lsh(new(big.Int).Set(MinimumStakeAmount), uint(height))
200201
}
201202

202203
if price != 0 && committed.Sign() > 0 {
203-
// User already has committed stake.
204204
// Commitment protection: required = committed * price * 2^height
205205
required := new(big.Int).SetUint64(uint64(price))
206206
required = new(big.Int).Lsh(required, uint(height)) // * 2^height
@@ -379,17 +379,17 @@ func (c *contract) sendManageStakeTransaction(ctx context.Context, stakedAmount
379379
return receipt, nil
380380
}
381381

382-
func (c *contract) getStake(ctx context.Context) (committed, potential *big.Int, err error) {
382+
func (c *contract) getStake(ctx context.Context) (committed, potential *big.Int, stakeExists bool, err error) {
383383
callData, err := c.stakingContractABI.Pack("stakes", c.owner)
384384
if err != nil {
385-
return nil, nil, err
385+
return nil, nil, false, err
386386
}
387387
result, err := c.transactionService.Call(ctx, &transaction.TxRequest{
388388
To: &c.stakingContractAddress,
389389
Data: callData,
390390
})
391391
if err != nil {
392-
return nil, nil, fmt.Errorf("get stakes: %w", err)
392+
return nil, nil, false, fmt.Errorf("get stakes: %w", err)
393393
}
394394

395395
// overlay bytes32,
@@ -398,16 +398,17 @@ func (c *contract) getStake(ctx context.Context) (committed, potential *big.Int,
398398
// lastUpdatedBlockNumber uint256,
399399
results, err := c.stakingContractABI.Unpack("stakes", result)
400400
if err != nil {
401-
return nil, nil, err
401+
return nil, nil, false, err
402402
}
403403

404404
if len(results) < 4 {
405-
return nil, nil, ErrUnexpectedLength
405+
return nil, nil, false, ErrUnexpectedLength
406406
}
407407

408408
committed = abi.ConvertType(results[1], new(big.Int)).(*big.Int)
409409
potential = abi.ConvertType(results[2], new(big.Int)).(*big.Int)
410-
return committed, potential, nil
410+
lastUpdated := abi.ConvertType(results[3], new(big.Int)).(*big.Int)
411+
return committed, potential, lastUpdated.Sign() != 0, nil
411412
}
412413

413414
func (c *contract) getCurrentPrice(ctx context.Context) (uint32, error) {

pkg/storageincentives/staking/contract_test.go

Lines changed: 107 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -37,76 +37,93 @@ func TestCalculateMinDeposit(t *testing.T) {
3737
committedAtMin := new(big.Int).Div(minStake, big.NewInt(1000)) // potential/price at price 1000
3838

3939
tests := []struct {
40-
name string
41-
potential *big.Int
42-
committed *big.Int
43-
price uint32
44-
height uint8
45-
want *big.Int
40+
name string
41+
potential *big.Int
42+
committed *big.Int
43+
price uint32
44+
height uint8
45+
stakeExists bool
46+
want *big.Int
4647
}{
4748
{
48-
name: "first deposit height 0",
49-
potential: big.NewInt(0),
50-
committed: big.NewInt(0),
51-
price: 1000,
52-
height: 0,
53-
want: minStake,
49+
name: "first deposit height 0",
50+
potential: big.NewInt(0),
51+
committed: big.NewInt(0),
52+
price: 1000,
53+
height: 0,
54+
stakeExists: false,
55+
want: minStake,
5456
},
5557
{
56-
name: "first deposit height 1",
57-
potential: big.NewInt(0),
58-
committed: big.NewInt(0),
59-
price: 1000,
60-
height: 1,
61-
want: new(big.Int).Mul(minStake, big.NewInt(2)),
58+
name: "first deposit height 1",
59+
potential: big.NewInt(0),
60+
committed: big.NewInt(0),
61+
price: 1000,
62+
height: 1,
63+
stakeExists: false,
64+
want: new(big.Int).Mul(minStake, big.NewInt(2)),
6265
},
6366
{
64-
name: "subsequent with surplus is one plur",
65-
potential: new(big.Int).Mul(minStake, big.NewInt(2)),
66-
committed: committedAtMin,
67-
price: 1000,
68-
height: 0,
69-
want: big.NewInt(1),
67+
name: "subsequent with surplus is one plur",
68+
potential: new(big.Int).Mul(minStake, big.NewInt(2)),
69+
committed: committedAtMin,
70+
price: 1000,
71+
height: 0,
72+
stakeExists: true,
73+
want: big.NewInt(1),
7074
},
7175
{
72-
name: "exact cover is one plur",
73-
potential: new(big.Int).Set(minStake),
74-
committed: committedAtMin,
75-
price: 1000,
76-
height: 0,
77-
want: big.NewInt(1),
76+
name: "exact cover is one plur",
77+
potential: new(big.Int).Set(minStake),
78+
committed: committedAtMin,
79+
price: 1000,
80+
height: 0,
81+
stakeExists: true,
82+
want: big.NewInt(1),
7883
},
7984
{
80-
name: "price increase requires gap",
81-
potential: new(big.Int).Set(minStake),
82-
committed: committedAtMin,
83-
price: 1001,
84-
height: 0,
85-
want: committedAtMin,
85+
name: "price increase requires gap",
86+
potential: new(big.Int).Set(minStake),
87+
committed: committedAtMin,
88+
price: 1001,
89+
height: 0,
90+
stakeExists: true,
91+
want: committedAtMin,
8692
},
8793
{
88-
name: "height doubles required potential",
89-
potential: new(big.Int).Mul(minStake, big.NewInt(2)),
90-
committed: committedAtMin,
91-
price: 1001,
92-
height: 1,
93-
want: new(big.Int).Mul(committedAtMin, big.NewInt(2)),
94+
name: "height doubles required potential",
95+
potential: new(big.Int).Mul(minStake, big.NewInt(2)),
96+
committed: committedAtMin,
97+
price: 1001,
98+
height: 1,
99+
stakeExists: true,
100+
want: new(big.Int).Mul(committedAtMin, big.NewInt(2)),
94101
},
95102
{
96-
name: "existing slashed stake does not restore initial floor",
97-
potential: new(big.Int).Div(minStake, big.NewInt(10)),
98-
committed: committedAtMin,
99-
price: 100,
100-
height: 0,
101-
want: big.NewInt(1),
103+
name: "existing slashed stake does not restore initial floor",
104+
potential: new(big.Int).Div(minStake, big.NewInt(10)),
105+
committed: committedAtMin,
106+
price: 100,
107+
height: 0,
108+
stakeExists: true,
109+
want: big.NewInt(1),
110+
},
111+
{
112+
name: "initialized stake with zero potential does not restore initial floor",
113+
potential: big.NewInt(0),
114+
committed: big.NewInt(0),
115+
price: 200,
116+
height: 0,
117+
stakeExists: true,
118+
want: big.NewInt(1),
102119
},
103120
}
104121

105122
for _, tc := range tests {
106123
t.Run(tc.name, func(t *testing.T) {
107124
t.Parallel()
108125

109-
got := staking.CalculateMinDeposit(tc.potential, tc.committed, tc.price, tc.height)
126+
got := staking.CalculateMinDeposit(tc.potential, tc.committed, tc.price, tc.height, tc.stakeExists)
110127
if got.Cmp(tc.want) != 0 {
111128
t.Fatalf("got %s, want %s", got, tc.want)
112129
}
@@ -1481,6 +1498,36 @@ func TestGetMinDeposit(t *testing.T) {
14811498
t.Fatalf("got %s, want %s", got, committed)
14821499
}
14831500
})
1501+
1502+
t.Run("initialized stake with zero potential", func(t *testing.T) {
1503+
t.Parallel()
1504+
1505+
contract := staking.New(
1506+
owner,
1507+
stakingAddress,
1508+
stakingContractABI,
1509+
bzzTokenAddress,
1510+
transactionMock.New(
1511+
transactionMock.WithCallFunc(func(ctx context.Context, request *transaction.TxRequest) (result []byte, err error) {
1512+
if *request.To == stakingAddress {
1513+
return getStakeResponseWithLastUpdated(t, big.NewInt(0), big.NewInt(0), big.NewInt(1000)), nil
1514+
}
1515+
return nil, errors.New("unexpected call")
1516+
}),
1517+
),
1518+
nonce,
1519+
0,
1520+
stakingHeight,
1521+
)
1522+
1523+
got, err := contract.GetMinDeposit(ctx)
1524+
if err != nil {
1525+
t.Fatal(err)
1526+
}
1527+
if got.Cmp(big.NewInt(1)) != 0 {
1528+
t.Fatalf("got %s, want 1", got)
1529+
}
1530+
})
14841531
}
14851532

14861533
func TestGetWithdrawableStake(t *testing.T) {
@@ -2207,10 +2254,21 @@ func newStakeCallFunc(
22072254
func getStakeResponse(t *testing.T, committed, potential *big.Int) []byte {
22082255
t.Helper()
22092256

2257+
lastUpdated := big.NewInt(0)
2258+
if committed.Sign() > 0 || potential.Sign() > 0 {
2259+
lastUpdated = big.NewInt(1)
2260+
}
2261+
return getStakeResponseWithLastUpdated(t, committed, potential, lastUpdated)
2262+
}
2263+
2264+
func getStakeResponseWithLastUpdated(t *testing.T, committed, potential, lastUpdated *big.Int) []byte {
2265+
t.Helper()
2266+
22102267
ret := make([]byte, 32*5)
22112268
copy(ret, swarm.RandAddress(t).Bytes())
22122269
copy(ret[32:], committed.FillBytes(make([]byte, 32)))
22132270
copy(ret[64:], potential.FillBytes(make([]byte, 32)))
2271+
copy(ret[96:], lastUpdated.FillBytes(make([]byte, 32)))
22142272

22152273
return ret
22162274
}

0 commit comments

Comments
 (0)