Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions openapi/Swarm.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
openapi: 3.0.3

info:
version: 8.1.1
version: 8.2.0
title: Bee API
description: "API endpoints for interacting with the Swarm network, supporting file operations, messaging, and node management"

Expand Down Expand Up @@ -2558,7 +2558,11 @@ paths:
schema:
$ref: "SwarmCommon.yaml#/components/schemas/StakeTransactionResponse"
"400":
$ref: "SwarmCommon.yaml#/components/responses/400"
description: Deposit amount is below the required minimum.
content:
application/json:
schema:
$ref: "SwarmCommon.yaml#/components/schemas/StakeDepositErrorResponse"
"500":
$ref: "SwarmCommon.yaml#/components/responses/500"
default:
Expand All @@ -2567,7 +2571,7 @@ paths:
"/stake":
get:
summary: Get the staked amount.
description: This endpoint fetches the total staked amount from the blockchain.
description: This endpoint fetches the total staked amount and the minimum additional deposit from the blockchain. The first deposit is at least 10 BZZ times 2^height. Subsequent deposits are at least 1 PLUR, or more if the price oracle has increased since the last deposit.
tags:
- Staking
responses:
Expand Down
12 changes: 12 additions & 0 deletions openapi/SwarmCommon.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,18 @@ components:
properties:
stakedAmount:
$ref: "#/components/schemas/BigInt"
minimumDeposit:
$ref: "#/components/schemas/BigInt"

StakeDepositErrorResponse:
type: object
properties:
code:
type: integer
message:
type: string
minimumDeposit:
$ref: "#/components/schemas/BigInt"

GetWithdrawableResponse:
type: object
Expand Down
1 change: 1 addition & 0 deletions pkg/api/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ type (
WalletResponse = walletResponse
WalletTxResponse = walletTxResponse
GetStakeResponse = getStakeResponse
StakeDepositErrorResponse = stakeDepositErrorResponse
GetWithdrawableResponse = getWithdrawableResponse
StakeTransactionReponse = stakeTransactionReponse
StatusSnapshotResponse = statusSnapshotResponse
Expand Down
37 changes: 30 additions & 7 deletions pkg/api/staking.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
"math/big"
"net/http"

"github.com/ethersphere/bee/v2/pkg/bigint"
"github.com/gorilla/mux"

"github.com/ethersphere/bee/v2/pkg/bigint"
"github.com/ethersphere/bee/v2/pkg/jsonhttp"
"github.com/ethersphere/bee/v2/pkg/storageincentives/staking"
"github.com/gorilla/mux"
)

func (s *Service) stakingAccessHandler(h http.Handler) http.Handler {
Expand All @@ -31,7 +31,8 @@
}

type getStakeResponse struct {
StakedAmount *bigint.BigInt `json:"stakedAmount"`
StakedAmount *bigint.BigInt `json:"stakedAmount"`
MinimumDeposit *bigint.BigInt `json:"minimumDeposit"`
}

type getWithdrawableResponse struct {
Expand All @@ -41,6 +42,12 @@
TxHash string `json:"txHash"`
}

type stakeDepositErrorResponse struct {
Code int `json:"code"`
Message string `json:"message"`
MinimumDeposit *bigint.BigInt `json:"minimumDeposit"`
}

func (s *Service) stakingDepositHandler(w http.ResponseWriter, r *http.Request) {
logger := s.logger.WithName("post_stake_deposit").Build()

Expand All @@ -54,10 +61,15 @@

txHash, err := s.stakingContract.DepositStake(r.Context(), paths.Amount)
if err != nil {
if errors.Is(err, staking.ErrInsufficientStakeAmount) {
logger.Debug("insufficient stake amount", "minimum_stake", staking.MinimumStakeAmount, "error", err)
var minErr *staking.MinDepositError
if errors.As(err, &minErr) {
logger.Debug("insufficient stake amount", "minimum_deposit", minErr.Minimum, "error", err)

Check failure on line 66 in pkg/api/staking.go

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "insufficient stake amount" 3 times.

See more on https://sonarcloud.io/project/issues?id=ethersphere_bee&issues=AaAu7xRp88Jwv2L8_Lna&open=AaAu7xRp88Jwv2L8_Lna&pullRequest=5571
logger.Error(nil, "insufficient stake amount")
jsonhttp.BadRequest(w, "insufficient stake amount")
jsonhttp.BadRequest(w, stakeDepositErrorResponse{
Code: http.StatusBadRequest,
Message: "insufficient stake amount",
MinimumDeposit: bigint.Wrap(minErr.Minimum),
})
return
}
if errors.Is(err, staking.ErrNotImplemented) {
Expand Down Expand Up @@ -105,7 +117,18 @@
return
}

jsonhttp.OK(w, getStakeResponse{StakedAmount: bigint.Wrap(stakedAmount)})
minDeposit, err := s.stakingContract.GetMinDeposit(r.Context())
if err != nil {
logger.Debug("get minimum deposit failed", "overlayAddr", s.overlay, "error", err)

Check failure on line 122 in pkg/api/staking.go

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "get minimum deposit failed" 3 times.

See more on https://sonarcloud.io/project/issues?id=ethersphere_bee&issues=AaAupOq8Ek95ELe1cEIn&open=AaAupOq8Ek95ELe1cEIn&pullRequest=5571
logger.Error(nil, "get minimum deposit failed")
jsonhttp.InternalServerError(w, "get minimum deposit failed")
return
}

jsonhttp.OK(w, getStakeResponse{
StakedAmount: bigint.Wrap(stakedAmount),
MinimumDeposit: bigint.Wrap(minDeposit),
})
}

func (s *Service) getWithdrawableStakeHandler(w http.ResponseWriter, r *http.Request) {
Expand Down
37 changes: 30 additions & 7 deletions pkg/api/staking_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import (
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/ethersphere/bee/v2/pkg/bigint"

"github.com/ethersphere/bee/v2/pkg/api"
"github.com/ethersphere/bee/v2/pkg/bigint"
"github.com/ethersphere/bee/v2/pkg/jsonhttp"
"github.com/ethersphere/bee/v2/pkg/jsonhttp/jsonhttptest"
"github.com/ethersphere/bee/v2/pkg/sctx"
Expand Down Expand Up @@ -43,18 +43,22 @@ func TestDepositStake(t *testing.T) {
jsonhttptest.Request(t, ts, http.MethodPost, depositStake(minStake), http.StatusOK)
})

t.Run("with invalid stake amount", func(t *testing.T) {
t.Run("with insufficient amount reports minimum", func(t *testing.T) {
t.Parallel()

invalidMinStake := big.NewInt(0).String()
minDeposit := big.NewInt(123)
contract := stakingContractMock.New(
stakingContractMock.WithDepositStake(func(ctx context.Context, stakedAmount *big.Int) (common.Hash, error) {
return common.Hash{}, staking.ErrInsufficientStakeAmount
return common.Hash{}, &staking.MinDepositError{Minimum: minDeposit}
}),
)
ts, _, _, _ := newTestServer(t, testServerOptions{StakingContract: contract})
jsonhttptest.Request(t, ts, http.MethodPost, depositStake(invalidMinStake), http.StatusBadRequest,
jsonhttptest.WithExpectedJSONResponse(&jsonhttp.StatusResponse{Code: http.StatusBadRequest, Message: "insufficient stake amount"}))
jsonhttptest.Request(t, ts, http.MethodPost, depositStake("1"), http.StatusBadRequest,
jsonhttptest.WithExpectedJSONResponse(&api.StakeDepositErrorResponse{
Code: http.StatusBadRequest,
Message: "insufficient stake amount",
MinimumDeposit: bigint.Wrap(minDeposit),
}))
})

t.Run("out of funds", func(t *testing.T) {
Expand Down Expand Up @@ -134,7 +138,10 @@ func TestGetStakeCommitted(t *testing.T) {
)
ts, _, _, _ := newTestServer(t, testServerOptions{StakingContract: contract})
jsonhttptest.Request(t, ts, http.MethodGet, "/stake", http.StatusOK,
jsonhttptest.WithExpectedJSONResponse(&api.GetStakeResponse{StakedAmount: bigint.Wrap(big.NewInt(1))}))
jsonhttptest.WithExpectedJSONResponse(&api.GetStakeResponse{
StakedAmount: bigint.Wrap(big.NewInt(1)),
MinimumDeposit: bigint.Wrap(big.NewInt(1)),
}))
})

t.Run("with error", func(t *testing.T) {
Expand All @@ -149,6 +156,22 @@ func TestGetStakeCommitted(t *testing.T) {
jsonhttptest.Request(t, ts, http.MethodGet, "/stake", http.StatusInternalServerError,
jsonhttptest.WithExpectedJSONResponse(&jsonhttp.StatusResponse{Code: http.StatusInternalServerError, Message: "get staked amount failed"}))
})

t.Run("minimum deposit error", func(t *testing.T) {
t.Parallel()

contractWithError := stakingContractMock.New(
stakingContractMock.WithGetStake(func(ctx context.Context) (*big.Int, error) {
return big.NewInt(1), nil
}),
stakingContractMock.WithGetMinDeposit(func(ctx context.Context) (*big.Int, error) {
return nil, fmt.Errorf("get minimum deposit failed")
}),
)
ts, _, _, _ := newTestServer(t, testServerOptions{StakingContract: contractWithError})
jsonhttptest.Request(t, ts, http.MethodGet, "/stake", http.StatusInternalServerError,
jsonhttptest.WithExpectedJSONResponse(&jsonhttp.StatusResponse{Code: http.StatusInternalServerError, Message: "get minimum deposit failed"}))
})
}

func TestGetStakeWithdrawable(t *testing.T) {
Expand Down
Loading
Loading