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:
pssPublicKey:
$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:
type: object
properties:
......
......@@ -18,7 +18,7 @@ servers:
default: "localhost"
description: Base address of the local bee node debug API
port:
default: 1635
default: "1635"
description: Service port provided in bee node config
paths:
......@@ -249,6 +249,36 @@ paths:
default:
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":
get:
summary: Get health of node
......
......@@ -10,6 +10,11 @@ import (
"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())
}
// 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 @@
package debugapi_test
import (
"math/big"
"net/http"
"testing"
......@@ -13,19 +14,50 @@ import (
"github.com/ethersphere/bee/pkg/postage/batchstore/mock"
)
func TestReservestate(t *testing.T) {
func TestReserveState(t *testing.T) {
t.Run("ok", func(t *testing.T) {
ts := newTestServer(t, testServerOptions{
BatchStore: mock.New(mock.WithReserveState(&postage.Reservestate{
BatchStore: mock.New(mock.WithReserveState(&postage.ReserveState{
Radius: 5,
})),
})
t.Run("ok", func(t *testing.T) {
jsonhttptest.Request(t, ts.Client, http.MethodGet, "/reservestate", http.StatusOK,
jsonhttptest.WithExpectedJSONResponse(&postage.Reservestate{
jsonhttptest.WithExpectedJSONResponse(&postage.ReserveState{
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 {
"GET": http.HandlerFunc(s.reserveStateHandler),
})
router.Handle("/chainstate", jsonhttp.MethodHandler{
"GET": http.HandlerFunc(s.chainStateHandler),
})
router.Handle("/connect/{multi-address:.+}", jsonhttp.MethodHandler{
"POST": http.HandlerFunc(s.peerConnectHandler),
})
......
......@@ -85,7 +85,7 @@ func (svc *batchService) UpdateDepth(id []byte, depth uint8, normalisedBalance *
// price from the chain in the service chain state.
func (svc *batchService) UpdatePrice(price *big.Int) error {
cs := svc.storer.GetChainState()
cs.Price = price
cs.CurrentPrice = price
if err := svc.storer.PutChainState(cs); err != nil {
return fmt.Errorf("put chain state: %w", err)
}
......@@ -98,7 +98,7 @@ func (svc *batchService) UpdateBlockNumber(blockNumber uint64) error {
cs := svc.storer.GetChainState()
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
if err := svc.storer.PutChainState(cs); err != nil {
return fmt.Errorf("put chain state: %w", err)
......
......@@ -184,7 +184,7 @@ func TestBatchServiceUpdateDepth(t *testing.T) {
func TestBatchServiceUpdatePrice(t *testing.T) {
testChainState := postagetesting.NewChainState()
testChainState.Price = big.NewInt(100000)
testChainState.CurrentPrice = big.NewInt(100000)
testNewPrice := big.NewInt(20000000)
t.Run("expect put error", func(t *testing.T) {
......@@ -209,16 +209,16 @@ func TestBatchServiceUpdatePrice(t *testing.T) {
}
cs := batchStore.GetChainState()
if cs.Price.Cmp(testNewPrice) != 0 {
t.Fatalf("bad price: want %v, got %v", cs.Price, testNewPrice)
if cs.CurrentPrice.Cmp(testNewPrice) != 0 {
t.Fatalf("bad price: want %v, got %v", cs.CurrentPrice, testNewPrice)
}
})
}
func TestBatchServiceUpdateBlockNumber(t *testing.T) {
testChainState := &postage.ChainState{
Block: 1,
Price: big.NewInt(100),
Total: big.NewInt(100),
CurrentPrice: big.NewInt(100),
TotalAmount: big.NewInt(100),
}
svc, batchStore := newTestStoreAndService(
mock.WithChainState(testChainState),
......@@ -232,8 +232,8 @@ func TestBatchServiceUpdateBlockNumber(t *testing.T) {
}
nn := big.NewInt(400)
cs := batchStore.GetChainState()
if cs.Total.Cmp(nn) != 0 {
t.Fatalf("bad price: want %v, got %v", nn, cs.Total)
if cs.TotalAmount.Cmp(nn) != 0 {
t.Fatalf("bad price: want %v, got %v", nn, cs.TotalAmount)
}
}
......
......@@ -16,7 +16,7 @@ var _ postage.Storer = (*BatchStore)(nil)
// BatchStore is a mock BatchStorer
type BatchStore struct {
rs *postage.Reservestate
rs *postage.ReserveState
cs *postage.ChainState
id []byte
batch *postage.Batch
......@@ -41,7 +41,7 @@ func New(opts ...Option) *BatchStore {
}
// 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) {
bs.rs = rs
}
......@@ -120,8 +120,15 @@ func (bs *BatchStore) PutChainState(cs *postage.ChainState) error {
return nil
}
func (bs *BatchStore) GetReserveState() *postage.Reservestate {
return bs.rs
func (bs *BatchStore) GetReserveState() *postage.ReserveState {
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) {
......
......@@ -75,9 +75,9 @@ func (s *store) evictExpired() error {
until := new(big.Int)
// 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
until.Add(s.cs.Total, big1)
until.Add(s.cs.TotalAmount, big1)
} else {
// collect until inner (collect all outer ones)
until.Set(s.rs.Inner)
......@@ -120,7 +120,7 @@ func (s *store) evictExpired() error {
s.rs.Available += multiplier * exp2(b.Radius-s.rs.Radius-1)
// 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)
}
return false, nil
......
......@@ -220,8 +220,8 @@ func setupBatchStore(t *testing.T) (postage.Storer, map[string]uint8) {
// initialise chainstate
err = bStore.PutChainState(&postage.ChainState{
Block: 0,
Total: big.NewInt(0),
Price: big.NewInt(1),
TotalAmount: big.NewInt(0),
CurrentPrice: big.NewInt(1),
})
if err != nil {
t.Fatal(err)
......@@ -235,9 +235,9 @@ func nextChainState(bStore postage.Storer) (*postage.ChainState, error) {
advance := newBlockAdvance()
cs = &postage.ChainState{
Block: advance + cs.Block,
Price: cs.Price,
CurrentPrice: cs.CurrentPrice,
// 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)
}
......@@ -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) {
b := postagetest.MustNewBatch()
b.Depth = newBatchDepth(depth)
value := newValue(cs.Price, cs.Total)
value := newValue(cs.CurrentPrice, cs.TotalAmount)
b.Value = big.NewInt(0)
return b, bStore.Put(b, value, b.Depth)
}
// tops up a batch with random amount
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)
}
// dilutes the batch with random factor
func increaseDepth(bStore postage.Storer, cs *postage.ChainState, b *postage.Batch) error {
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.Add(value, cs.Total)
value.Add(value, cs.TotalAmount)
return bStore.Put(b, value, b.Depth+uint8(diff))
}
......@@ -824,7 +824,7 @@ func TestBatchStore_EvictExpired(t *testing.T) {
cs := store.GetChainState()
cs.Block = 4
cs.Total = big.NewInt(4)
cs.TotalAmount = big.NewInt(4)
err := store.PutChainState(cs)
if err != nil {
t.Fatal(err)
......
......@@ -45,8 +45,8 @@ func New(st storage.StateStorer, unreserveFunc unreserveFn) (postage.Storer, err
}
cs = &postage.ChainState{
Block: 0,
Total: big.NewInt(0),
Price: big.NewInt(0),
TotalAmount: big.NewInt(0),
CurrentPrice: big.NewInt(0),
}
}
rs := &reserveState{}
......@@ -73,8 +73,8 @@ func New(st storage.StateStorer, unreserveFunc unreserveFn) (postage.Storer, err
return s, nil
}
func (s *store) GetReserveState() *postage.Reservestate {
return &postage.Reservestate{
func (s *store) GetReserveState() *postage.ReserveState {
return &postage.ReserveState{
Radius: s.rs.Radius,
Available: s.rs.Available,
Outer: new(big.Int).Set(s.rs.Outer),
......
......@@ -9,6 +9,6 @@ import "math/big"
// ChainState contains data the batch service reads from the chain.
type ChainState struct {
Block uint64 `json:"block"` // The block number of the last postage event.
Total *big.Int `json:"total"` // Cumulative amount paid per stamp.
Price *big.Int `json:"price"` // Bzz/chunk/block normalised price.
TotalAmount *big.Int `json:"totalAmount"` // Cumulative amount paid per stamp.
CurrentPrice *big.Int `json:"currentPrice"` // Bzz/chunk/block normalised price.
}
......@@ -27,7 +27,7 @@ type Storer interface {
Put(*Batch, *big.Int, uint8) error
PutChainState(*ChainState) error
GetChainState() *ChainState
GetReserveState() *Reservestate
GetReserveState() *ReserveState
SetRadiusSetter(RadiusSetter)
}
......
......@@ -6,7 +6,7 @@ package postage
import "math/big"
type Reservestate struct {
type ReserveState struct {
Radius uint8 `json:"radius"`
Available int64 `json:"available"`
Outer *big.Int `json:"outer"` // lower value limit for outer layer = the further half of chunks
......
......@@ -15,8 +15,8 @@ import (
func NewChainState() *postage.ChainState {
return &postage.ChainState{
Block: rand.Uint64(), // skipcq: GSC-G404
Price: NewBigInt(),
Total: NewBigInt(),
CurrentPrice: NewBigInt(),
TotalAmount: NewBigInt(),
}
}
......@@ -29,10 +29,10 @@ func CompareChainState(t *testing.T, want, got *postage.ChainState) {
if want.Block != got.Block {
t.Fatalf("block: want %v, got %v", want.Block, got.Block)
}
if want.Price.Cmp(got.Price) != 0 {
t.Fatalf("price: want %v, got %v", want.Price, got.Price)
if want.CurrentPrice.Cmp(got.CurrentPrice) != 0 {
t.Fatalf("price: want %v, got %v", want.CurrentPrice, got.CurrentPrice)
}
if want.Total.Cmp(got.Total) != 0 {
t.Fatalf("total: want %v, got %v", want.Total, got.Total)
if want.TotalAmount.Cmp(got.TotalAmount) != 0 {
t.Fatalf("total: want %v, got %v", want.TotalAmount, got.TotalAmount)
}
}
......@@ -92,6 +92,7 @@ func (ts *Tags) Get(uid uint32) (*Tag, error) {
// if yes, load it in to the memory
ta, err := ts.getTagFromStore(uid)
if err != nil {
ts.logger.Debugf("tags: Get: %d not found: %v", uid, err)
return nil, ErrNotFound
}
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