Commit 88c0a195 authored by Peter Mrekaj's avatar Peter Mrekaj Committed by GitHub

feat: expose chain state through the debug API endpoint (#1756)

parent 2f677c40
...@@ -34,6 +34,28 @@ components: ...@@ -34,6 +34,28 @@ components:
pssPublicKey: pssPublicKey:
$ref: "#/components/schemas/PublicKey" $ref: "#/components/schemas/PublicKey"
ReserveState:
type: object
properties:
radius:
type: integer
available:
type: integer
outer:
type: integer
inner:
type: integer
ChainState:
type: object
properties:
block:
type: integer
totalAmount:
type: integer
currentPrice:
type: integer
Balance: Balance:
type: object type: object
properties: properties:
......
...@@ -18,7 +18,7 @@ servers: ...@@ -18,7 +18,7 @@ servers:
default: "localhost" default: "localhost"
description: Base address of the local bee node debug API description: Base address of the local bee node debug API
port: port:
default: 1635 default: "1635"
description: Service port provided in bee node config description: Service port provided in bee node config
paths: paths:
...@@ -249,6 +249,36 @@ paths: ...@@ -249,6 +249,36 @@ paths:
default: default:
description: Default response description: Default response
"/reservestate":
get:
summary: Get reserve state
tags:
- Status
responses:
"200":
description: Reserve State
content:
application/json:
schema:
$ref: "SwarmCommon.yaml#/components/schemas/ReserveState"
default:
description: Default response
"/chainstate":
get:
summary: Get chain state
tags:
- Status
responses:
"200":
description: Chain State
content:
application/json:
schema:
$ref: "SwarmCommon.yaml#/components/schemas/ChainState"
default:
description: Default response
"/health": "/health":
get: get:
summary: Get health of node summary: Get health of node
......
...@@ -10,6 +10,11 @@ import ( ...@@ -10,6 +10,11 @@ import (
"github.com/ethersphere/bee/pkg/jsonhttp" "github.com/ethersphere/bee/pkg/jsonhttp"
) )
func (s *Service) reserveStateHandler(w http.ResponseWriter, r *http.Request) { func (s *Service) reserveStateHandler(w http.ResponseWriter, _ *http.Request) {
jsonhttp.OK(w, s.batchStore.GetReserveState()) jsonhttp.OK(w, s.batchStore.GetReserveState())
} }
// chainStateHandler returns the current chain state.
func (s *Service) chainStateHandler(w http.ResponseWriter, _ *http.Request) {
jsonhttp.OK(w, s.batchStore.GetChainState())
}
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package debugapi_test package debugapi_test
import ( import (
"math/big"
"net/http" "net/http"
"testing" "testing"
...@@ -13,19 +14,50 @@ import ( ...@@ -13,19 +14,50 @@ import (
"github.com/ethersphere/bee/pkg/postage/batchstore/mock" "github.com/ethersphere/bee/pkg/postage/batchstore/mock"
) )
func TestReservestate(t *testing.T) { func TestReserveState(t *testing.T) {
ts := newTestServer(t, testServerOptions{
BatchStore: mock.New(mock.WithReserveState(&postage.Reservestate{
Radius: 5,
})),
})
t.Run("ok", func(t *testing.T) { t.Run("ok", func(t *testing.T) {
ts := newTestServer(t, testServerOptions{
BatchStore: mock.New(mock.WithReserveState(&postage.ReserveState{
Radius: 5,
})),
})
jsonhttptest.Request(t, ts.Client, http.MethodGet, "/reservestate", http.StatusOK, jsonhttptest.Request(t, ts.Client, http.MethodGet, "/reservestate", http.StatusOK,
jsonhttptest.WithExpectedJSONResponse(&postage.Reservestate{ jsonhttptest.WithExpectedJSONResponse(&postage.ReserveState{
Radius: 5, Radius: 5,
}), }),
) )
}) })
t.Run("empty", func(t *testing.T) {
ts := newTestServer(t, testServerOptions{
BatchStore: mock.New(),
})
jsonhttptest.Request(t, ts.Client, http.MethodGet, "/reservestate", http.StatusOK,
jsonhttptest.WithExpectedJSONResponse(&postage.ReserveState{}),
)
})
}
func TestChainState(t *testing.T) {
t.Run("ok", func(t *testing.T) {
cs := &postage.ChainState{
Block: 123456,
TotalAmount: big.NewInt(50),
CurrentPrice: big.NewInt(5),
}
ts := newTestServer(t, testServerOptions{
BatchStore: mock.New(mock.WithChainState(cs)),
})
jsonhttptest.Request(t, ts.Client, http.MethodGet, "/chainstate", http.StatusOK,
jsonhttptest.WithExpectedJSONResponse(cs),
)
})
t.Run("empty", func(t *testing.T) {
ts := newTestServer(t, testServerOptions{
BatchStore: mock.New(),
})
jsonhttptest.Request(t, ts.Client, http.MethodGet, "/chainstate", http.StatusOK,
jsonhttptest.WithExpectedJSONResponse(&postage.ChainState{}),
)
})
} }
...@@ -81,6 +81,10 @@ func (s *Service) newRouter() *mux.Router { ...@@ -81,6 +81,10 @@ func (s *Service) newRouter() *mux.Router {
"GET": http.HandlerFunc(s.reserveStateHandler), "GET": http.HandlerFunc(s.reserveStateHandler),
}) })
router.Handle("/chainstate", jsonhttp.MethodHandler{
"GET": http.HandlerFunc(s.chainStateHandler),
})
router.Handle("/connect/{multi-address:.+}", jsonhttp.MethodHandler{ router.Handle("/connect/{multi-address:.+}", jsonhttp.MethodHandler{
"POST": http.HandlerFunc(s.peerConnectHandler), "POST": http.HandlerFunc(s.peerConnectHandler),
}) })
......
...@@ -85,7 +85,7 @@ func (svc *batchService) UpdateDepth(id []byte, depth uint8, normalisedBalance * ...@@ -85,7 +85,7 @@ func (svc *batchService) UpdateDepth(id []byte, depth uint8, normalisedBalance *
// price from the chain in the service chain state. // price from the chain in the service chain state.
func (svc *batchService) UpdatePrice(price *big.Int) error { func (svc *batchService) UpdatePrice(price *big.Int) error {
cs := svc.storer.GetChainState() cs := svc.storer.GetChainState()
cs.Price = price cs.CurrentPrice = price
if err := svc.storer.PutChainState(cs); err != nil { if err := svc.storer.PutChainState(cs); err != nil {
return fmt.Errorf("put chain state: %w", err) return fmt.Errorf("put chain state: %w", err)
} }
...@@ -98,7 +98,7 @@ func (svc *batchService) UpdateBlockNumber(blockNumber uint64) error { ...@@ -98,7 +98,7 @@ func (svc *batchService) UpdateBlockNumber(blockNumber uint64) error {
cs := svc.storer.GetChainState() cs := svc.storer.GetChainState()
diff := big.NewInt(0).SetUint64(blockNumber - cs.Block) diff := big.NewInt(0).SetUint64(blockNumber - cs.Block)
cs.Total.Add(cs.Total, diff.Mul(diff, cs.Price)) cs.TotalAmount.Add(cs.TotalAmount, diff.Mul(diff, cs.CurrentPrice))
cs.Block = blockNumber cs.Block = blockNumber
if err := svc.storer.PutChainState(cs); err != nil { if err := svc.storer.PutChainState(cs); err != nil {
return fmt.Errorf("put chain state: %w", err) return fmt.Errorf("put chain state: %w", err)
......
...@@ -184,7 +184,7 @@ func TestBatchServiceUpdateDepth(t *testing.T) { ...@@ -184,7 +184,7 @@ func TestBatchServiceUpdateDepth(t *testing.T) {
func TestBatchServiceUpdatePrice(t *testing.T) { func TestBatchServiceUpdatePrice(t *testing.T) {
testChainState := postagetesting.NewChainState() testChainState := postagetesting.NewChainState()
testChainState.Price = big.NewInt(100000) testChainState.CurrentPrice = big.NewInt(100000)
testNewPrice := big.NewInt(20000000) testNewPrice := big.NewInt(20000000)
t.Run("expect put error", func(t *testing.T) { t.Run("expect put error", func(t *testing.T) {
...@@ -209,16 +209,16 @@ func TestBatchServiceUpdatePrice(t *testing.T) { ...@@ -209,16 +209,16 @@ func TestBatchServiceUpdatePrice(t *testing.T) {
} }
cs := batchStore.GetChainState() cs := batchStore.GetChainState()
if cs.Price.Cmp(testNewPrice) != 0 { if cs.CurrentPrice.Cmp(testNewPrice) != 0 {
t.Fatalf("bad price: want %v, got %v", cs.Price, testNewPrice) t.Fatalf("bad price: want %v, got %v", cs.CurrentPrice, testNewPrice)
} }
}) })
} }
func TestBatchServiceUpdateBlockNumber(t *testing.T) { func TestBatchServiceUpdateBlockNumber(t *testing.T) {
testChainState := &postage.ChainState{ testChainState := &postage.ChainState{
Block: 1, Block: 1,
Price: big.NewInt(100), CurrentPrice: big.NewInt(100),
Total: big.NewInt(100), TotalAmount: big.NewInt(100),
} }
svc, batchStore := newTestStoreAndService( svc, batchStore := newTestStoreAndService(
mock.WithChainState(testChainState), mock.WithChainState(testChainState),
...@@ -232,8 +232,8 @@ func TestBatchServiceUpdateBlockNumber(t *testing.T) { ...@@ -232,8 +232,8 @@ func TestBatchServiceUpdateBlockNumber(t *testing.T) {
} }
nn := big.NewInt(400) nn := big.NewInt(400)
cs := batchStore.GetChainState() cs := batchStore.GetChainState()
if cs.Total.Cmp(nn) != 0 { if cs.TotalAmount.Cmp(nn) != 0 {
t.Fatalf("bad price: want %v, got %v", nn, cs.Total) t.Fatalf("bad price: want %v, got %v", nn, cs.TotalAmount)
} }
} }
......
...@@ -16,7 +16,7 @@ var _ postage.Storer = (*BatchStore)(nil) ...@@ -16,7 +16,7 @@ var _ postage.Storer = (*BatchStore)(nil)
// BatchStore is a mock BatchStorer // BatchStore is a mock BatchStorer
type BatchStore struct { type BatchStore struct {
rs *postage.Reservestate rs *postage.ReserveState
cs *postage.ChainState cs *postage.ChainState
id []byte id []byte
batch *postage.Batch batch *postage.Batch
...@@ -41,7 +41,7 @@ func New(opts ...Option) *BatchStore { ...@@ -41,7 +41,7 @@ func New(opts ...Option) *BatchStore {
} }
// WithChainState will set the initial chainstate in the ChainStore mock. // WithChainState will set the initial chainstate in the ChainStore mock.
func WithReserveState(rs *postage.Reservestate) Option { func WithReserveState(rs *postage.ReserveState) Option {
return func(bs *BatchStore) { return func(bs *BatchStore) {
bs.rs = rs bs.rs = rs
} }
...@@ -120,8 +120,15 @@ func (bs *BatchStore) PutChainState(cs *postage.ChainState) error { ...@@ -120,8 +120,15 @@ func (bs *BatchStore) PutChainState(cs *postage.ChainState) error {
return nil return nil
} }
func (bs *BatchStore) GetReserveState() *postage.Reservestate { func (bs *BatchStore) GetReserveState() *postage.ReserveState {
return bs.rs rs := new(postage.ReserveState)
if bs.rs != nil {
rs.Radius = bs.rs.Radius
rs.Available = bs.rs.Available
rs.Outer = bs.rs.Outer
rs.Inner = bs.rs.Inner
}
return rs
} }
func (bs *BatchStore) SetRadiusSetter(r postage.RadiusSetter) { func (bs *BatchStore) SetRadiusSetter(r postage.RadiusSetter) {
......
...@@ -75,9 +75,9 @@ func (s *store) evictExpired() error { ...@@ -75,9 +75,9 @@ func (s *store) evictExpired() error {
until := new(big.Int) until := new(big.Int)
// if inner > 0 && total >= inner // if inner > 0 && total >= inner
if s.rs.Inner.Cmp(big.NewInt(0)) > 0 && s.cs.Total.Cmp(s.rs.Inner) >= 0 { if s.rs.Inner.Cmp(big.NewInt(0)) > 0 && s.cs.TotalAmount.Cmp(s.rs.Inner) >= 0 {
// collect until total+1 // collect until total+1
until.Add(s.cs.Total, big1) until.Add(s.cs.TotalAmount, big1)
} else { } else {
// collect until inner (collect all outer ones) // collect until inner (collect all outer ones)
until.Set(s.rs.Inner) until.Set(s.rs.Inner)
...@@ -120,7 +120,7 @@ func (s *store) evictExpired() error { ...@@ -120,7 +120,7 @@ func (s *store) evictExpired() error {
s.rs.Available += multiplier * exp2(b.Radius-s.rs.Radius-1) s.rs.Available += multiplier * exp2(b.Radius-s.rs.Radius-1)
// if batch has no value then delete it // if batch has no value then delete it
if b.Value.Cmp(s.cs.Total) <= 0 { if b.Value.Cmp(s.cs.TotalAmount) <= 0 {
toDelete = append(toDelete, b.ID) toDelete = append(toDelete, b.ID)
} }
return false, nil return false, nil
......
...@@ -219,9 +219,9 @@ func setupBatchStore(t *testing.T) (postage.Storer, map[string]uint8) { ...@@ -219,9 +219,9 @@ func setupBatchStore(t *testing.T) (postage.Storer, map[string]uint8) {
// initialise chainstate // initialise chainstate
err = bStore.PutChainState(&postage.ChainState{ err = bStore.PutChainState(&postage.ChainState{
Block: 0, Block: 0,
Total: big.NewInt(0), TotalAmount: big.NewInt(0),
Price: big.NewInt(1), CurrentPrice: big.NewInt(1),
}) })
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
...@@ -234,10 +234,10 @@ func nextChainState(bStore postage.Storer) (*postage.ChainState, error) { ...@@ -234,10 +234,10 @@ func nextChainState(bStore postage.Storer) (*postage.ChainState, error) {
// random advance on the blockchain // random advance on the blockchain
advance := newBlockAdvance() advance := newBlockAdvance()
cs = &postage.ChainState{ cs = &postage.ChainState{
Block: advance + cs.Block, Block: advance + cs.Block,
Price: cs.Price, CurrentPrice: cs.CurrentPrice,
// settle although no price change // settle although no price change
Total: cs.Total.Add(cs.Total, new(big.Int).Mul(cs.Price, big.NewInt(int64(advance)))), TotalAmount: cs.TotalAmount.Add(cs.TotalAmount, new(big.Int).Mul(cs.CurrentPrice, big.NewInt(int64(advance)))),
} }
return cs, bStore.PutChainState(cs) return cs, bStore.PutChainState(cs)
} }
...@@ -246,23 +246,23 @@ func nextChainState(bStore postage.Storer) (*postage.ChainState, error) { ...@@ -246,23 +246,23 @@ func nextChainState(bStore postage.Storer) (*postage.ChainState, error) {
func createBatch(bStore postage.Storer, cs *postage.ChainState, depth uint8) (*postage.Batch, error) { func createBatch(bStore postage.Storer, cs *postage.ChainState, depth uint8) (*postage.Batch, error) {
b := postagetest.MustNewBatch() b := postagetest.MustNewBatch()
b.Depth = newBatchDepth(depth) b.Depth = newBatchDepth(depth)
value := newValue(cs.Price, cs.Total) value := newValue(cs.CurrentPrice, cs.TotalAmount)
b.Value = big.NewInt(0) b.Value = big.NewInt(0)
return b, bStore.Put(b, value, b.Depth) return b, bStore.Put(b, value, b.Depth)
} }
// tops up a batch with random amount // tops up a batch with random amount
func topUp(bStore postage.Storer, cs *postage.ChainState, b *postage.Batch) error { func topUp(bStore postage.Storer, cs *postage.ChainState, b *postage.Batch) error {
value := newValue(cs.Price, b.Value) value := newValue(cs.CurrentPrice, b.Value)
return bStore.Put(b, value, b.Depth) return bStore.Put(b, value, b.Depth)
} }
// dilutes the batch with random factor // dilutes the batch with random factor
func increaseDepth(bStore postage.Storer, cs *postage.ChainState, b *postage.Batch) error { func increaseDepth(bStore postage.Storer, cs *postage.ChainState, b *postage.Batch) error {
diff := newDilutionFactor() diff := newDilutionFactor()
value := new(big.Int).Sub(b.Value, cs.Total) value := new(big.Int).Sub(b.Value, cs.TotalAmount)
value.Div(value, big.NewInt(int64(1<<diff))) value.Div(value, big.NewInt(int64(1<<diff)))
value.Add(value, cs.Total) value.Add(value, cs.TotalAmount)
return bStore.Put(b, value, b.Depth+uint8(diff)) return bStore.Put(b, value, b.Depth+uint8(diff))
} }
...@@ -824,7 +824,7 @@ func TestBatchStore_EvictExpired(t *testing.T) { ...@@ -824,7 +824,7 @@ func TestBatchStore_EvictExpired(t *testing.T) {
cs := store.GetChainState() cs := store.GetChainState()
cs.Block = 4 cs.Block = 4
cs.Total = big.NewInt(4) cs.TotalAmount = big.NewInt(4)
err := store.PutChainState(cs) err := store.PutChainState(cs)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
......
...@@ -44,9 +44,9 @@ func New(st storage.StateStorer, unreserveFunc unreserveFn) (postage.Storer, err ...@@ -44,9 +44,9 @@ func New(st storage.StateStorer, unreserveFunc unreserveFn) (postage.Storer, err
return nil, err return nil, err
} }
cs = &postage.ChainState{ cs = &postage.ChainState{
Block: 0, Block: 0,
Total: big.NewInt(0), TotalAmount: big.NewInt(0),
Price: big.NewInt(0), CurrentPrice: big.NewInt(0),
} }
} }
rs := &reserveState{} rs := &reserveState{}
...@@ -73,8 +73,8 @@ func New(st storage.StateStorer, unreserveFunc unreserveFn) (postage.Storer, err ...@@ -73,8 +73,8 @@ func New(st storage.StateStorer, unreserveFunc unreserveFn) (postage.Storer, err
return s, nil return s, nil
} }
func (s *store) GetReserveState() *postage.Reservestate { func (s *store) GetReserveState() *postage.ReserveState {
return &postage.Reservestate{ return &postage.ReserveState{
Radius: s.rs.Radius, Radius: s.rs.Radius,
Available: s.rs.Available, Available: s.rs.Available,
Outer: new(big.Int).Set(s.rs.Outer), Outer: new(big.Int).Set(s.rs.Outer),
......
...@@ -8,7 +8,7 @@ import "math/big" ...@@ -8,7 +8,7 @@ import "math/big"
// ChainState contains data the batch service reads from the chain. // ChainState contains data the batch service reads from the chain.
type ChainState struct { type ChainState struct {
Block uint64 `json:"block"` // The block number of the last postage event. Block uint64 `json:"block"` // The block number of the last postage event.
Total *big.Int `json:"total"` // Cumulative amount paid per stamp. TotalAmount *big.Int `json:"totalAmount"` // Cumulative amount paid per stamp.
Price *big.Int `json:"price"` // Bzz/chunk/block normalised price. CurrentPrice *big.Int `json:"currentPrice"` // Bzz/chunk/block normalised price.
} }
...@@ -27,7 +27,7 @@ type Storer interface { ...@@ -27,7 +27,7 @@ type Storer interface {
Put(*Batch, *big.Int, uint8) error Put(*Batch, *big.Int, uint8) error
PutChainState(*ChainState) error PutChainState(*ChainState) error
GetChainState() *ChainState GetChainState() *ChainState
GetReserveState() *Reservestate GetReserveState() *ReserveState
SetRadiusSetter(RadiusSetter) SetRadiusSetter(RadiusSetter)
} }
......
...@@ -6,7 +6,7 @@ package postage ...@@ -6,7 +6,7 @@ package postage
import "math/big" import "math/big"
type Reservestate struct { type ReserveState struct {
Radius uint8 `json:"radius"` Radius uint8 `json:"radius"`
Available int64 `json:"available"` Available int64 `json:"available"`
Outer *big.Int `json:"outer"` // lower value limit for outer layer = the further half of chunks Outer *big.Int `json:"outer"` // lower value limit for outer layer = the further half of chunks
......
...@@ -14,9 +14,9 @@ import ( ...@@ -14,9 +14,9 @@ import (
// NewChainState will create a new ChainState with random values. // NewChainState will create a new ChainState with random values.
func NewChainState() *postage.ChainState { func NewChainState() *postage.ChainState {
return &postage.ChainState{ return &postage.ChainState{
Block: rand.Uint64(), // skipcq: GSC-G404 Block: rand.Uint64(), // skipcq: GSC-G404
Price: NewBigInt(), CurrentPrice: NewBigInt(),
Total: NewBigInt(), TotalAmount: NewBigInt(),
} }
} }
...@@ -29,10 +29,10 @@ func CompareChainState(t *testing.T, want, got *postage.ChainState) { ...@@ -29,10 +29,10 @@ func CompareChainState(t *testing.T, want, got *postage.ChainState) {
if want.Block != got.Block { if want.Block != got.Block {
t.Fatalf("block: want %v, got %v", want.Block, got.Block) t.Fatalf("block: want %v, got %v", want.Block, got.Block)
} }
if want.Price.Cmp(got.Price) != 0 { if want.CurrentPrice.Cmp(got.CurrentPrice) != 0 {
t.Fatalf("price: want %v, got %v", want.Price, got.Price) t.Fatalf("price: want %v, got %v", want.CurrentPrice, got.CurrentPrice)
} }
if want.Total.Cmp(got.Total) != 0 { if want.TotalAmount.Cmp(got.TotalAmount) != 0 {
t.Fatalf("total: want %v, got %v", want.Total, got.Total) t.Fatalf("total: want %v, got %v", want.TotalAmount, got.TotalAmount)
} }
} }
...@@ -92,6 +92,7 @@ func (ts *Tags) Get(uid uint32) (*Tag, error) { ...@@ -92,6 +92,7 @@ func (ts *Tags) Get(uid uint32) (*Tag, error) {
// if yes, load it in to the memory // if yes, load it in to the memory
ta, err := ts.getTagFromStore(uid) ta, err := ts.getTagFromStore(uid)
if err != nil { if err != nil {
ts.logger.Debugf("tags: Get: %d not found: %v", uid, err)
return nil, ErrNotFound return nil, ErrNotFound
} }
ts.tags.LoadOrStore(ta.Uid, ta) ts.tags.LoadOrStore(ta.Uid, ta)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment