Commit 1e58ecfc authored by aloknerurkar's avatar aloknerurkar Committed by GitHub

feat(postage): stampissuer usability (#2063)

parent d73dfe30
21c21
< blockThreshold = 10
---
> blockThreshold = 0
...@@ -43,6 +43,7 @@ jobs: ...@@ -43,6 +43,7 @@ jobs:
patch pkg/postage/batchstore/reserve.go .github/patches/postagereserve.patch patch pkg/postage/batchstore/reserve.go .github/patches/postagereserve.patch
patch pkg/postage/postagecontract/contract.go .github/patches/postagecontract.patch patch pkg/postage/postagecontract/contract.go .github/patches/postagecontract.patch
patch pkg/postage/listener/listener.go .github/patches/listener.patch patch pkg/postage/listener/listener.go .github/patches/listener.patch
patch pkg/postage/service.go .github/patches/postageservice.patch
- name: Prepare local cluster - name: Prepare local cluster
run: | run: |
printf ${{ secrets.CR_PAT }} | docker login ghcr.io -u bee-worker --password-stdin printf ${{ secrets.CR_PAT }} | docker login ghcr.io -u bee-worker --password-stdin
......
...@@ -319,6 +319,8 @@ components: ...@@ -319,6 +319,8 @@ components:
$ref: "#/components/schemas/BatchID" $ref: "#/components/schemas/BatchID"
utilization: utilization:
type: integer type: integer
usable:
type: boolean
Settlement: Settlement:
type: object type: object
......
...@@ -267,7 +267,7 @@ func TestPostageHeaderError(t *testing.T) { ...@@ -267,7 +267,7 @@ func TestPostageHeaderError(t *testing.T) {
mockStorer = mock.NewStorer() mockStorer = mock.NewStorer()
mockStatestore = statestore.NewStateStore() mockStatestore = statestore.NewStateStore()
logger = logging.New(ioutil.Discard, 5) logger = logging.New(ioutil.Discard, 5)
mp = mockpost.New(mockpost.WithIssuer(postage.NewStampIssuer("", "", batchOk, 11, 10))) mp = mockpost.New(mockpost.WithIssuer(postage.NewStampIssuer("", "", batchOk, 11, 10, 1000)))
client, _, _ = newTestServer(t, testServerOptions{ client, _, _ = newTestServer(t, testServerOptions{
Storer: mockStorer, Storer: mockStorer,
Tags: tags.NewTags(mockStatestore, logger), Tags: tags.NewTags(mockStatestore, logger),
......
...@@ -57,7 +57,14 @@ func (s *server) bzzUploadHandler(w http.ResponseWriter, r *http.Request) { ...@@ -57,7 +57,14 @@ func (s *server) bzzUploadHandler(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
logger.Debugf("bzz upload: putter: %v", err) logger.Debugf("bzz upload: putter: %v", err)
logger.Error("bzz upload: putter") logger.Error("bzz upload: putter")
jsonhttp.BadRequest(w, nil) switch {
case errors.Is(err, postage.ErrNotFound):
jsonhttp.BadRequest(w, "batch not found")
case errors.Is(err, postage.ErrNotUsable):
jsonhttp.BadRequest(w, "batch not usable yet")
default:
jsonhttp.BadRequest(w, nil)
}
return return
} }
......
...@@ -19,6 +19,7 @@ import ( ...@@ -19,6 +19,7 @@ import (
"github.com/ethersphere/bee/pkg/file/loadsave" "github.com/ethersphere/bee/pkg/file/loadsave"
"github.com/ethersphere/bee/pkg/jsonhttp" "github.com/ethersphere/bee/pkg/jsonhttp"
"github.com/ethersphere/bee/pkg/manifest" "github.com/ethersphere/bee/pkg/manifest"
"github.com/ethersphere/bee/pkg/postage"
"github.com/ethersphere/bee/pkg/soc" "github.com/ethersphere/bee/pkg/soc"
"github.com/ethersphere/bee/pkg/swarm" "github.com/ethersphere/bee/pkg/swarm"
"github.com/gorilla/mux" "github.com/gorilla/mux"
...@@ -152,7 +153,14 @@ func (s *server) feedPostHandler(w http.ResponseWriter, r *http.Request) { ...@@ -152,7 +153,14 @@ func (s *server) feedPostHandler(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
s.logger.Debugf("feed put: putter: %v", err) s.logger.Debugf("feed put: putter: %v", err)
s.logger.Error("feed put: putter") s.logger.Error("feed put: putter")
jsonhttp.BadRequest(w, nil) switch {
case errors.Is(err, postage.ErrNotFound):
jsonhttp.BadRequest(w, "batch not found")
case errors.Is(err, postage.ErrNotUsable):
jsonhttp.BadRequest(w, "batch not usable yet")
default:
jsonhttp.BadRequest(w, nil)
}
return return
} }
...@@ -185,7 +193,12 @@ func (s *server) feedPostHandler(w http.ResponseWriter, r *http.Request) { ...@@ -185,7 +193,12 @@ func (s *server) feedPostHandler(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
s.logger.Debugf("feed post: store manifest: %v", err) s.logger.Debugf("feed post: store manifest: %v", err)
s.logger.Error("feed post: store manifest") s.logger.Error("feed post: store manifest")
jsonhttp.InternalServerError(w, nil) switch {
case errors.Is(err, postage.ErrBucketFull):
jsonhttp.PaymentRequired(w, "batch is overissued")
default:
jsonhttp.InternalServerError(w, nil)
}
return return
} }
......
...@@ -154,7 +154,7 @@ func TestFeed_Post(t *testing.T) { ...@@ -154,7 +154,7 @@ func TestFeed_Post(t *testing.T) {
logger = logging.New(ioutil.Discard, 0) logger = logging.New(ioutil.Discard, 0)
tag = tags.NewTags(mockStatestore, logger) tag = tags.NewTags(mockStatestore, logger)
topic = "aabbcc" topic = "aabbcc"
mp = mockpost.New(mockpost.WithIssuer(postage.NewStampIssuer("", "", batchOk, 11, 10))) mp = mockpost.New(mockpost.WithIssuer(postage.NewStampIssuer("", "", batchOk, 11, 10, 1000)))
mockStorer = mock.NewStorer() mockStorer = mock.NewStorer()
client, _, _ = newTestServer(t, testServerOptions{ client, _, _ = newTestServer(t, testServerOptions{
Storer: mockStorer, Storer: mockStorer,
......
...@@ -99,6 +99,7 @@ func (s *server) postageCreateHandler(w http.ResponseWriter, r *http.Request) { ...@@ -99,6 +99,7 @@ func (s *server) postageCreateHandler(w http.ResponseWriter, r *http.Request) {
type postageStampResponse struct { type postageStampResponse struct {
BatchID batchID `json:"batchID"` BatchID batchID `json:"batchID"`
Utilization uint32 `json:"utilization"` Utilization uint32 `json:"utilization"`
Usable bool `json:"usable"`
} }
type postageStampsResponse struct { type postageStampsResponse struct {
...@@ -109,7 +110,11 @@ func (s *server) postageGetStampsHandler(w http.ResponseWriter, r *http.Request) ...@@ -109,7 +110,11 @@ func (s *server) postageGetStampsHandler(w http.ResponseWriter, r *http.Request)
issuers := s.post.StampIssuers() issuers := s.post.StampIssuers()
resp := postageStampsResponse{} resp := postageStampsResponse{}
for _, v := range issuers { for _, v := range issuers {
issuer := postageStampResponse{BatchID: v.ID(), Utilization: v.Utilization()} issuer := postageStampResponse{
BatchID: v.ID(),
Utilization: v.Utilization(),
Usable: s.post.IssuerUsable(v),
}
resp.Stamps = append(resp.Stamps, issuer) resp.Stamps = append(resp.Stamps, issuer)
} }
jsonhttp.OK(w, resp) jsonhttp.OK(w, resp)
...@@ -140,6 +145,7 @@ func (s *server) postageGetStampHandler(w http.ResponseWriter, r *http.Request) ...@@ -140,6 +145,7 @@ func (s *server) postageGetStampHandler(w http.ResponseWriter, r *http.Request)
resp := postageStampResponse{ resp := postageStampResponse{
BatchID: id, BatchID: id,
Utilization: issuer.Utilization(), Utilization: issuer.Utilization(),
Usable: s.post.IssuerUsable(issuer),
} }
jsonhttp.OK(w, &resp) jsonhttp.OK(w, &resp)
} }
...@@ -192,7 +192,7 @@ func TestPostageCreateStamp(t *testing.T) { ...@@ -192,7 +192,7 @@ func TestPostageCreateStamp(t *testing.T) {
} }
func TestPostageGetStamps(t *testing.T) { func TestPostageGetStamps(t *testing.T) {
mp := mockpost.New(mockpost.WithIssuer(postage.NewStampIssuer("", "", batchOk, 11, 10))) mp := mockpost.New(mockpost.WithIssuer(postage.NewStampIssuer("", "", batchOk, 11, 10, 1000)))
client, _, _ := newTestServer(t, testServerOptions{Post: mp}) client, _, _ := newTestServer(t, testServerOptions{Post: mp})
jsonhttptest.Request(t, client, http.MethodGet, "/stamps", http.StatusOK, jsonhttptest.Request(t, client, http.MethodGet, "/stamps", http.StatusOK,
...@@ -201,6 +201,7 @@ func TestPostageGetStamps(t *testing.T) { ...@@ -201,6 +201,7 @@ func TestPostageGetStamps(t *testing.T) {
{ {
BatchID: batchOk, BatchID: batchOk,
Utilization: 0, Utilization: 0,
Usable: true,
}, },
}, },
}), }),
...@@ -208,7 +209,7 @@ func TestPostageGetStamps(t *testing.T) { ...@@ -208,7 +209,7 @@ func TestPostageGetStamps(t *testing.T) {
} }
func TestPostageGetStamp(t *testing.T) { func TestPostageGetStamp(t *testing.T) {
mp := mockpost.New(mockpost.WithIssuer(postage.NewStampIssuer("", "", batchOk, 11, 10))) mp := mockpost.New(mockpost.WithIssuer(postage.NewStampIssuer("", "", batchOk, 11, 10, 1000)))
client, _, _ := newTestServer(t, testServerOptions{Post: mp}) client, _, _ := newTestServer(t, testServerOptions{Post: mp})
t.Run("ok", func(t *testing.T) { t.Run("ok", func(t *testing.T) {
...@@ -216,6 +217,7 @@ func TestPostageGetStamp(t *testing.T) { ...@@ -216,6 +217,7 @@ func TestPostageGetStamp(t *testing.T) {
jsonhttptest.WithExpectedJSONResponse(&api.PostageStampResponse{ jsonhttptest.WithExpectedJSONResponse(&api.PostageStampResponse{
BatchID: batchOk, BatchID: batchOk,
Utilization: 0, Utilization: 0,
Usable: true,
}), }),
) )
}) })
......
...@@ -88,7 +88,14 @@ func (s *server) pssPostHandler(w http.ResponseWriter, r *http.Request) { ...@@ -88,7 +88,14 @@ func (s *server) pssPostHandler(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
s.logger.Debugf("pss: postage batch issuer: %v", err) s.logger.Debugf("pss: postage batch issuer: %v", err)
s.logger.Error("pss: postage batch issue") s.logger.Error("pss: postage batch issue")
jsonhttp.BadRequest(w, "postage stamp issuer") switch {
case errors.Is(err, postage.ErrNotFound):
jsonhttp.BadRequest(w, "batch not found")
case errors.Is(err, postage.ErrNotUsable):
jsonhttp.BadRequest(w, "batch not usable yet")
default:
jsonhttp.BadRequest(w, "postage stamp issuer")
}
return return
} }
stamper := postage.NewStamper(i, s.signer) stamper := postage.NewStamper(i, s.signer)
......
...@@ -185,7 +185,7 @@ func TestPssSend(t *testing.T) { ...@@ -185,7 +185,7 @@ func TestPssSend(t *testing.T) {
mtx.Unlock() mtx.Unlock()
return err return err
} }
mp = mockpost.New(mockpost.WithIssuer(postage.NewStampIssuer("", "", batchOk, 11, 10))) mp = mockpost.New(mockpost.WithIssuer(postage.NewStampIssuer("", "", batchOk, 11, 10, 1000)))
p = newMockPss(sendFn) p = newMockPss(sendFn)
client, _, _ = newTestServer(t, testServerOptions{ client, _, _ = newTestServer(t, testServerOptions{
Pss: p, Pss: p,
......
...@@ -139,7 +139,14 @@ func (s *server) socUploadHandler(w http.ResponseWriter, r *http.Request) { ...@@ -139,7 +139,14 @@ func (s *server) socUploadHandler(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
s.logger.Debugf("soc upload: postage batch issuer: %v", err) s.logger.Debugf("soc upload: postage batch issuer: %v", err)
s.logger.Error("soc upload: postage batch issue") s.logger.Error("soc upload: postage batch issue")
jsonhttp.BadRequest(w, "postage stamp issuer") switch {
case errors.Is(err, postage.ErrNotFound):
jsonhttp.BadRequest(w, "batch not found")
case errors.Is(err, postage.ErrNotUsable):
jsonhttp.BadRequest(w, "batch not usable yet")
default:
jsonhttp.BadRequest(w, "postage stamp issuer")
}
return return
} }
stamper := postage.NewStamper(i, s.signer) stamper := postage.NewStamper(i, s.signer)
...@@ -147,7 +154,12 @@ func (s *server) socUploadHandler(w http.ResponseWriter, r *http.Request) { ...@@ -147,7 +154,12 @@ func (s *server) socUploadHandler(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
s.logger.Debugf("soc upload: stamp: %v", err) s.logger.Debugf("soc upload: stamp: %v", err)
s.logger.Error("soc upload: stamp error") s.logger.Error("soc upload: stamp error")
jsonhttp.InternalServerError(w, "stamp error") switch {
case errors.Is(err, postage.ErrBucketFull):
jsonhttp.PaymentRequired(w, "batch is overissued")
default:
jsonhttp.InternalServerError(w, "stamp error")
}
return return
} }
sch = sch.WithStamp(stamp) sch = sch.WithStamp(stamp)
......
...@@ -32,7 +32,7 @@ func TestSOC(t *testing.T) { ...@@ -32,7 +32,7 @@ func TestSOC(t *testing.T) {
mockStatestore = statestore.NewStateStore() mockStatestore = statestore.NewStateStore()
logger = logging.New(ioutil.Discard, 0) logger = logging.New(ioutil.Discard, 0)
tag = tags.NewTags(mockStatestore, logger) tag = tags.NewTags(mockStatestore, logger)
mp = mockpost.New(mockpost.WithIssuer(postage.NewStampIssuer("", "", batchOk, 11, 10))) mp = mockpost.New(mockpost.WithIssuer(postage.NewStampIssuer("", "", batchOk, 11, 10, 1000)))
mockStorer = mock.NewStorer() mockStorer = mock.NewStorer()
client, _, _ = newTestServer(t, testServerOptions{ client, _, _ = newTestServer(t, testServerOptions{
Storer: mockStorer, Storer: mockStorer,
......
...@@ -357,7 +357,7 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey, ...@@ -357,7 +357,7 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey,
return nil, fmt.Errorf("batchstore: %w", err) return nil, fmt.Errorf("batchstore: %w", err)
} }
validStamp := postage.ValidStamp(batchStore) validStamp := postage.ValidStamp(batchStore)
post, err := postage.NewService(stateStore, chainID) post, err := postage.NewService(stateStore, batchStore, chainID)
if err != nil { if err != nil {
return nil, fmt.Errorf("postage service load: %w", err) return nil, fmt.Errorf("postage service load: %w", err)
} }
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package postage package postage
var ( var (
IndexToBytes = indexToBytes IndexToBytes = indexToBytes
BytesToIndex = bytesToIndex BytesToIndex = bytesToIndex
BlockThreshold = blockThreshold
) )
...@@ -54,7 +54,7 @@ func (m *mockPostage) StampIssuers() []*postage.StampIssuer { ...@@ -54,7 +54,7 @@ func (m *mockPostage) StampIssuers() []*postage.StampIssuer {
func (m *mockPostage) GetStampIssuer(id []byte) (*postage.StampIssuer, error) { func (m *mockPostage) GetStampIssuer(id []byte) (*postage.StampIssuer, error) {
if m.acceptAll { if m.acceptAll {
return postage.NewStampIssuer("test fallback", "test identity", id, 24, 6), nil return postage.NewStampIssuer("test fallback", "test identity", id, 24, 6, 1000), nil
} }
if m.i != nil { if m.i != nil {
...@@ -64,6 +64,10 @@ func (m *mockPostage) GetStampIssuer(id []byte) (*postage.StampIssuer, error) { ...@@ -64,6 +64,10 @@ func (m *mockPostage) GetStampIssuer(id []byte) (*postage.StampIssuer, error) {
return nil, errors.New("stampissuer not found") return nil, errors.New("stampissuer not found")
} }
func (m *mockPostage) IssuerUsable(_ *postage.StampIssuer) bool {
return true
}
func (m *mockPostage) Close() error { func (m *mockPostage) Close() error {
return nil return nil
} }
...@@ -192,6 +192,7 @@ func (c *postageContract) CreateBatch(ctx context.Context, initialBalance *big.I ...@@ -192,6 +192,7 @@ func (c *postageContract) CreateBatch(ctx context.Context, initialBalance *big.I
batchID, batchID,
depth, depth,
createdEvent.BucketDepth, createdEvent.BucketDepth,
ev.BlockNumber,
)) ))
return createdEvent.BatchId[:], nil return createdEvent.BatchId[:], nil
......
...@@ -16,11 +16,16 @@ import ( ...@@ -16,11 +16,16 @@ import (
const ( const (
postagePrefix = "postage" postagePrefix = "postage"
// blockThreshold is used to allow threshold no of blocks to be synced before a
// batch is usable.
blockThreshold = 10
) )
var ( var (
// ErrNotFound is the error returned when issuer with given batch ID does not exist. // ErrNotFound is the error returned when issuer with given batch ID does not exist.
ErrNotFound = errors.New("not found") ErrNotFound = errors.New("not found")
// ErrNotUsable is the error returned when issuer with given batch ID is not usable.
ErrNotUsable = errors.New("not usable")
) )
// Service is the postage service interface. // Service is the postage service interface.
...@@ -28,23 +33,26 @@ type Service interface { ...@@ -28,23 +33,26 @@ type Service interface {
Add(*StampIssuer) Add(*StampIssuer)
StampIssuers() []*StampIssuer StampIssuers() []*StampIssuer
GetStampIssuer([]byte) (*StampIssuer, error) GetStampIssuer([]byte) (*StampIssuer, error)
IssuerUsable(*StampIssuer) bool
io.Closer io.Closer
} }
// service handles postage batches // service handles postage batches
// stores the active batches. // stores the active batches.
type service struct { type service struct {
lock sync.Mutex lock sync.Mutex
store storage.StateStorer store storage.StateStorer
chainID int64 postageStore Storer
issuers []*StampIssuer chainID int64
issuers []*StampIssuer
} }
// NewService constructs a new Service. // NewService constructs a new Service.
func NewService(store storage.StateStorer, chainID int64) (Service, error) { func NewService(store storage.StateStorer, postageStore Storer, chainID int64) (Service, error) {
s := &service{ s := &service{
store: store, store: store,
chainID: chainID, postageStore: postageStore,
chainID: chainID,
} }
n := 0 n := 0
...@@ -79,12 +87,28 @@ func (ps *service) StampIssuers() []*StampIssuer { ...@@ -79,12 +87,28 @@ func (ps *service) StampIssuers() []*StampIssuer {
return ps.issuers return ps.issuers
} }
func (ps *service) IssuerUsable(st *StampIssuer) bool {
cs := ps.postageStore.GetChainState()
// this checks atleast threshold blocks are seen on the blockchain after
// the batch creation, before we start using a stamp issuer. The threshold
// is meant to allow enough time for upstream peers to see the batch and
// hence validate the stamps issued
if cs.Block < st.blockNumber || (cs.Block-st.blockNumber) < blockThreshold {
return false
}
return true
}
// GetStampIssuer finds a stamp issuer by batch ID. // GetStampIssuer finds a stamp issuer by batch ID.
func (ps *service) GetStampIssuer(batchID []byte) (*StampIssuer, error) { func (ps *service) GetStampIssuer(batchID []byte) (*StampIssuer, error) {
ps.lock.Lock() ps.lock.Lock()
defer ps.lock.Unlock() defer ps.lock.Unlock()
for _, st := range ps.issuers { for _, st := range ps.issuers {
if bytes.Equal(batchID, st.batchID) { if bytes.Equal(batchID, st.batchID) {
if !ps.IssuerUsable(st) {
return nil, ErrNotUsable
}
return st, nil return st, nil
} }
} }
......
...@@ -11,6 +11,8 @@ import ( ...@@ -11,6 +11,8 @@ import (
"testing" "testing"
"github.com/ethersphere/bee/pkg/postage" "github.com/ethersphere/bee/pkg/postage"
pstoremock "github.com/ethersphere/bee/pkg/postage/batchstore/mock"
postagetesting "github.com/ethersphere/bee/pkg/postage/testing"
storemock "github.com/ethersphere/bee/pkg/statestore/mock" storemock "github.com/ethersphere/bee/pkg/statestore/mock"
) )
...@@ -18,13 +20,14 @@ import ( ...@@ -18,13 +20,14 @@ import (
// with all the active stamp issuers. // with all the active stamp issuers.
func TestSaveLoad(t *testing.T) { func TestSaveLoad(t *testing.T) {
store := storemock.NewStateStore() store := storemock.NewStateStore()
pstore := pstoremock.New()
saved := func(id int64) postage.Service { saved := func(id int64) postage.Service {
ps, err := postage.NewService(store, id) ps, err := postage.NewService(store, pstore, id)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
for i := 0; i < 16; i++ { for i := 0; i < 16; i++ {
ps.Add(newTestStampIssuer(t)) ps.Add(newTestStampIssuer(t, 1000))
} }
if err := ps.Close(); err != nil { if err := ps.Close(); err != nil {
t.Fatal(err) t.Fatal(err)
...@@ -32,7 +35,7 @@ func TestSaveLoad(t *testing.T) { ...@@ -32,7 +35,7 @@ func TestSaveLoad(t *testing.T) {
return ps return ps
} }
loaded := func(id int64) postage.Service { loaded := func(id int64) postage.Service {
ps, err := postage.NewService(store, id) ps, err := postage.NewService(store, pstore, id)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
...@@ -51,7 +54,13 @@ func TestSaveLoad(t *testing.T) { ...@@ -51,7 +54,13 @@ func TestSaveLoad(t *testing.T) {
func TestGetStampIssuer(t *testing.T) { func TestGetStampIssuer(t *testing.T) {
store := storemock.NewStateStore() store := storemock.NewStateStore()
ps, err := postage.NewService(store, int64(0)) testChainState := postagetesting.NewChainState()
if testChainState.Block < uint64(postage.BlockThreshold) {
testChainState.Block += uint64(postage.BlockThreshold + 1)
}
validBlockNumber := testChainState.Block - uint64(postage.BlockThreshold+1)
pstore := pstoremock.New(pstoremock.WithChainState(testChainState))
ps, err := postage.NewService(store, pstore, int64(0))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
...@@ -66,10 +75,14 @@ func TestGetStampIssuer(t *testing.T) { ...@@ -66,10 +75,14 @@ func TestGetStampIssuer(t *testing.T) {
if i == 0 { if i == 0 {
continue continue
} }
ps.Add(postage.NewStampIssuer(string(id), "", id, 16, 8)) if i < 4 {
ps.Add(postage.NewStampIssuer(string(id), "", id, 16, 8, validBlockNumber))
} else {
ps.Add(postage.NewStampIssuer(string(id), "", id, 16, 8, validBlockNumber+uint64(i)))
}
} }
t.Run("found", func(t *testing.T) { t.Run("found", func(t *testing.T) {
for _, id := range ids[1:] { for _, id := range ids[1:4] {
st, err := ps.GetStampIssuer(id) st, err := ps.GetStampIssuer(id)
if err != nil { if err != nil {
t.Fatalf("expected no error, got %v", err) t.Fatalf("expected no error, got %v", err)
...@@ -85,4 +98,12 @@ func TestGetStampIssuer(t *testing.T) { ...@@ -85,4 +98,12 @@ func TestGetStampIssuer(t *testing.T) {
t.Fatalf("expected ErrNotFound, got %v", err) t.Fatalf("expected ErrNotFound, got %v", err)
} }
}) })
t.Run("not usable", func(t *testing.T) {
for _, id := range ids[4:] {
_, err := ps.GetStampIssuer(id)
if err != postage.ErrNotUsable {
t.Fatalf("expected ErrNotUsable, got %v", err)
}
}
})
} }
...@@ -74,7 +74,7 @@ func TestValidStamp(t *testing.T) { ...@@ -74,7 +74,7 @@ func TestValidStamp(t *testing.T) {
b := postagetesting.MustNewBatch(postagetesting.WithOwner(owner)) b := postagetesting.MustNewBatch(postagetesting.WithOwner(owner))
bs := mock.New(mock.WithBatch(b)) bs := mock.New(mock.WithBatch(b))
signer := crypto.NewDefaultSigner(privKey) signer := crypto.NewDefaultSigner(privKey)
issuer := postage.NewStampIssuer("label", "keyID", b.ID, b.Depth, b.BucketDepth) issuer := postage.NewStampIssuer("label", "keyID", b.ID, b.Depth, b.BucketDepth, 1000)
stamper := postage.NewStamper(issuer, signer) stamper := postage.NewStamper(issuer, signer)
// this creates a chunk with a mocked stamp. ValidStamp will override this // this creates a chunk with a mocked stamp. ValidStamp will override this
......
...@@ -44,7 +44,7 @@ func TestStamperStamping(t *testing.T) { ...@@ -44,7 +44,7 @@ func TestStamperStamping(t *testing.T) {
// tests a valid stamp // tests a valid stamp
t.Run("valid stamp", func(t *testing.T) { t.Run("valid stamp", func(t *testing.T) {
st := newTestStampIssuer(t) st := newTestStampIssuer(t, 1000)
stamper := postage.NewStamper(st, signer) stamper := postage.NewStamper(st, signer)
chunkAddr, stamp := createStamp(t, stamper) chunkAddr, stamp := createStamp(t, stamper)
if err := stamp.Valid(chunkAddr, owner, 12, 8, true); err != nil { if err := stamp.Valid(chunkAddr, owner, 12, 8, true); err != nil {
...@@ -54,7 +54,7 @@ func TestStamperStamping(t *testing.T) { ...@@ -54,7 +54,7 @@ func TestStamperStamping(t *testing.T) {
// tests that Stamps returns with postage.ErrBucketMismatch // tests that Stamps returns with postage.ErrBucketMismatch
t.Run("bucket mismatch", func(t *testing.T) { t.Run("bucket mismatch", func(t *testing.T) {
st := newTestStampIssuer(t) st := newTestStampIssuer(t, 1000)
stamper := postage.NewStamper(st, signer) stamper := postage.NewStamper(st, signer)
chunkAddr, stamp := createStamp(t, stamper) chunkAddr, stamp := createStamp(t, stamper)
a := chunkAddr.Bytes() a := chunkAddr.Bytes()
...@@ -66,7 +66,7 @@ func TestStamperStamping(t *testing.T) { ...@@ -66,7 +66,7 @@ func TestStamperStamping(t *testing.T) {
// tests that Stamps returns with postage.ErrInvalidIndex // tests that Stamps returns with postage.ErrInvalidIndex
t.Run("invalid index", func(t *testing.T) { t.Run("invalid index", func(t *testing.T) {
st := newTestStampIssuer(t) st := newTestStampIssuer(t, 1000)
stamper := postage.NewStamper(st, signer) stamper := postage.NewStamper(st, signer)
// issue 1 stamp // issue 1 stamp
chunkAddr, _ := createStamp(t, stamper) chunkAddr, _ := createStamp(t, stamper)
...@@ -90,8 +90,8 @@ func TestStamperStamping(t *testing.T) { ...@@ -90,8 +90,8 @@ func TestStamperStamping(t *testing.T) {
// tests that Stamps returns with postage.ErrBucketFull iff // tests that Stamps returns with postage.ErrBucketFull iff
// issuer has the corresponding collision bucket filled] // issuer has the corresponding collision bucket filled]
t.Run("bucket full", func(t *testing.T) { t.Run("bucket full", func(t *testing.T) {
st := newTestStampIssuer(t) st := newTestStampIssuer(t, 1000)
st = postage.NewStampIssuer("", "", st.ID(), 12, 8) st = postage.NewStampIssuer("", "", st.ID(), 12, 8, 1000)
stamper := postage.NewStamper(st, signer) stamper := postage.NewStamper(st, signer)
// issue 1 stamp // issue 1 stamp
chunkAddr, _ := createStamp(t, stamper) chunkAddr, _ := createStamp(t, stamper)
...@@ -112,7 +112,7 @@ func TestStamperStamping(t *testing.T) { ...@@ -112,7 +112,7 @@ func TestStamperStamping(t *testing.T) {
// tests return with ErrOwnerMismatch // tests return with ErrOwnerMismatch
t.Run("owner mismatch", func(t *testing.T) { t.Run("owner mismatch", func(t *testing.T) {
owner[0] ^= 0xff // bitflip the owner first byte, this case must come last! owner[0] ^= 0xff // bitflip the owner first byte, this case must come last!
st := newTestStampIssuer(t) st := newTestStampIssuer(t, 1000)
stamper := postage.NewStamper(st, signer) stamper := postage.NewStamper(st, signer)
chunkAddr, stamp := createStamp(t, stamper) chunkAddr, stamp := createStamp(t, stamper)
if err := stamp.Valid(chunkAddr, owner, 12, 8, true); !errors.Is(err, postage.ErrOwnerMismatch) { if err := stamp.Valid(chunkAddr, owner, 12, 8, true); !errors.Is(err, postage.ErrOwnerMismatch) {
......
...@@ -23,13 +23,14 @@ type StampIssuer struct { ...@@ -23,13 +23,14 @@ type StampIssuer struct {
mu sync.Mutex // Mutex for buckets. mu sync.Mutex // Mutex for buckets.
buckets []uint32 // Collision buckets: counts per neighbourhoods (limited to 2^{batchdepth-bucketdepth}). buckets []uint32 // Collision buckets: counts per neighbourhoods (limited to 2^{batchdepth-bucketdepth}).
maxBucketCount uint32 // the count of the fullest bucket maxBucketCount uint32 // the count of the fullest bucket
blockNumber uint64 // blockNumber when this batch was created
} }
// NewStampIssuer constructs a StampIssuer as an extension of a batch for local // NewStampIssuer constructs a StampIssuer as an extension of a batch for local
// upload. // upload.
// //
// bucketDepth must always be smaller than batchDepth otherwise inc() panics. // bucketDepth must always be smaller than batchDepth otherwise inc() panics.
func NewStampIssuer(label, keyID string, batchID []byte, batchDepth, bucketDepth uint8) *StampIssuer { func NewStampIssuer(label, keyID string, batchID []byte, batchDepth, bucketDepth uint8, blockNumber uint64) *StampIssuer {
return &StampIssuer{ return &StampIssuer{
label: label, label: label,
keyID: keyID, keyID: keyID,
...@@ -37,6 +38,7 @@ func NewStampIssuer(label, keyID string, batchID []byte, batchDepth, bucketDepth ...@@ -37,6 +38,7 @@ func NewStampIssuer(label, keyID string, batchID []byte, batchDepth, bucketDepth
batchDepth: batchDepth, batchDepth: batchDepth,
bucketDepth: bucketDepth, bucketDepth: bucketDepth,
buckets: make([]uint32, 1<<bucketDepth), buckets: make([]uint32, 1<<bucketDepth),
blockNumber: blockNumber,
} }
} }
...@@ -87,9 +89,9 @@ func (st *StampIssuer) Label() string { ...@@ -87,9 +89,9 @@ func (st *StampIssuer) Label() string {
} }
// MarshalBinary gives the byte slice serialisation of a StampIssuer: // MarshalBinary gives the byte slice serialisation of a StampIssuer:
// = label[32]|keyID[32]|batchID[32]|batchDepth[1]|bucketDepth[1]|size_0[4]|size_1[4]|.... // = label[32]|keyID[32]|batchID[32]|batchDepth[1]|bucketDepth[1]|blockNumber[8]|size_0[4]|size_1[4]|....
func (st *StampIssuer) MarshalBinary() ([]byte, error) { func (st *StampIssuer) MarshalBinary() ([]byte, error) {
buf := make([]byte, 32+32+32+1+1+4*(1<<st.bucketDepth)) buf := make([]byte, 32+32+32+1+1+8+4*(1<<st.bucketDepth))
label := []byte(st.label) label := []byte(st.label)
copy(buf[32-len(label):32], label) copy(buf[32-len(label):32], label)
keyID := []byte(st.keyID) keyID := []byte(st.keyID)
...@@ -97,10 +99,11 @@ func (st *StampIssuer) MarshalBinary() ([]byte, error) { ...@@ -97,10 +99,11 @@ func (st *StampIssuer) MarshalBinary() ([]byte, error) {
copy(buf[64:96], st.batchID) copy(buf[64:96], st.batchID)
buf[96] = st.batchDepth buf[96] = st.batchDepth
buf[97] = st.bucketDepth buf[97] = st.bucketDepth
binary.BigEndian.PutUint64(buf[98:106], st.blockNumber)
st.mu.Lock() st.mu.Lock()
defer st.mu.Unlock() defer st.mu.Unlock()
for i, addr := range st.buckets { for i, addr := range st.buckets {
offset := 98 + i*4 offset := 106 + i*4
binary.BigEndian.PutUint32(buf[offset:offset+4], addr) binary.BigEndian.PutUint32(buf[offset:offset+4], addr)
} }
return buf, nil return buf, nil
...@@ -113,10 +116,11 @@ func (st *StampIssuer) UnmarshalBinary(buf []byte) error { ...@@ -113,10 +116,11 @@ func (st *StampIssuer) UnmarshalBinary(buf []byte) error {
st.batchID = buf[64:96] st.batchID = buf[64:96]
st.batchDepth = buf[96] st.batchDepth = buf[96]
st.bucketDepth = buf[97] st.bucketDepth = buf[97]
st.blockNumber = binary.BigEndian.Uint64(buf[98:106])
st.buckets = make([]uint32, 1<<st.bucketDepth) st.buckets = make([]uint32, 1<<st.bucketDepth)
// not using lock as unmarshal is init // not using lock as unmarshal is init
for i := range st.buckets { for i := range st.buckets {
offset := 98 + i*4 offset := 106 + i*4
st.buckets[i] = binary.BigEndian.Uint32(buf[offset : offset+4]) st.buckets[i] = binary.BigEndian.Uint32(buf[offset : offset+4])
} }
return nil return nil
......
...@@ -15,7 +15,7 @@ import ( ...@@ -15,7 +15,7 @@ import (
// TestStampIssuerMarshalling tests the idempotence of binary marshal/unmarshal. // TestStampIssuerMarshalling tests the idempotence of binary marshal/unmarshal.
func TestStampIssuerMarshalling(t *testing.T) { func TestStampIssuerMarshalling(t *testing.T) {
st := newTestStampIssuer(t) st := newTestStampIssuer(t, 1000)
buf, err := st.MarshalBinary() buf, err := st.MarshalBinary()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
...@@ -30,12 +30,12 @@ func TestStampIssuerMarshalling(t *testing.T) { ...@@ -30,12 +30,12 @@ func TestStampIssuerMarshalling(t *testing.T) {
} }
} }
func newTestStampIssuer(t *testing.T) *postage.StampIssuer { func newTestStampIssuer(t *testing.T, block uint64) *postage.StampIssuer {
t.Helper() t.Helper()
id := make([]byte, 32) id := make([]byte, 32)
_, err := io.ReadFull(crand.Reader, id) _, err := io.ReadFull(crand.Reader, id)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
return postage.NewStampIssuer("label", "keyID", id, 12, 8) return postage.NewStampIssuer("label", "keyID", id, 12, 8, block)
} }
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