Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Postage BatchStore and BatchService #1070

Merged
merged 17 commits into from
Jan 13, 2021
43 changes: 43 additions & 0 deletions pkg/postage/batch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright 2020 The Swarm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package postage

import (
"encoding/binary"
"math/big"
)

// Batch represents a postage batch, a payment on the blockchain.
type Batch struct {
ID []byte // batch ID
Value *big.Int // overall balance of the batch
Start uint64 // block number the batch was created
Owner []byte // owner's ethereum address
Depth uint8 // batch depth, i.e., size = 2^{depth}
}

// MarshalBinary implements BinaryMarshaller. It will attempt to serialize the
// postage batch to a byte slice.
func (b *Batch) MarshalBinary() ([]byte, error) {
out := make([]byte, 93)
copy(out, b.ID)
value := b.Value.Bytes()
copy(out[64-len(value):], value)
binary.BigEndian.PutUint64(out[64:72], b.Start)
copy(out[72:], b.Owner)
out[92] = b.Depth
return out, nil
}

// UnmarshalBinary implements BinaryUnmarshaller. It will attempt deserialize
// the given byte slice into the batch.
func (b *Batch) UnmarshalBinary(buf []byte) error {
b.ID = buf[:32]
b.Value = big.NewInt(0).SetBytes(buf[32:64])
b.Start = binary.BigEndian.Uint64(buf[64:72])
b.Owner = buf[72:92]
b.Depth = buf[92]
return nil
}
46 changes: 46 additions & 0 deletions pkg/postage/batch_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright 2020 The Swarm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package postage_test

import (
"bytes"
"testing"

"github.com/ethersphere/bee/pkg/postage"
postagetesting "github.com/ethersphere/bee/pkg/postage/testing"
)

// TestBatchMarshalling tests the idempotence of binary marshal/unmarshal for a
// Batch.
func TestBatchMarshalling(t *testing.T) {
a := postagetesting.MustNewBatch()

buf, err := a.MarshalBinary()
if err != nil {
t.Fatal(err)
}
if len(buf) != 93 {
t.Fatalf("invalid length for serialised batch. expected 93, got %d", len(buf))
}
b := &postage.Batch{}
if err := b.UnmarshalBinary(buf); err != nil {
t.Fatalf("unexpected error unmarshalling batch: %v", err)
}
if !bytes.Equal(b.ID, a.ID) {
t.Fatalf("id mismatch, expected %x, got %x", a.ID, b.ID)
}
if !bytes.Equal(b.Owner, a.Owner) {
t.Fatalf("owner mismatch, expected %x, got %x", a.Owner, b.Owner)
}
if a.Value.Uint64() != b.Value.Uint64() {
t.Fatalf("value mismatch, expected %d, got %d", a.Value.Uint64(), b.Value.Uint64())
}
if a.Start != b.Start {
t.Fatalf("start mismatch, expected %d, got %d", a.Start, b.Start)
}
if a.Depth != b.Depth {
t.Fatalf("depth mismatch, expected %d, got %d", a.Depth, b.Depth)
}
}
107 changes: 107 additions & 0 deletions pkg/postage/batchservice/batchservice.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright 2020 The Swarm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package batchservice

import (
"encoding/hex"
"fmt"
"math/big"

"github.com/ethersphere/bee/pkg/logging"
"github.com/ethersphere/bee/pkg/postage"
)

type batchService struct {
cs *postage.ChainState
storer postage.Storer
logger logging.Logger
}

// New will create a new BatchService.
func New(storer postage.Storer, logger logging.Logger) (postage.EventUpdater, error) {
b := &batchService{
storer: storer,
logger: logger,
}

cs, err := storer.GetChainState()
if err != nil {
return nil, fmt.Errorf("get chain state: %w", err)
}
b.cs = cs

return b, nil
}

// Create will create a new batch with the given ID, owner value and depth and
// stores it in the BatchStore.
func (svc *batchService) Create(id, owner []byte, value *big.Int, depth uint8) error {
b := &postage.Batch{
ID: id,
Owner: owner,
Value: value,
Start: svc.cs.Block,
Depth: depth,
}

err := svc.storer.Put(b)
if err != nil {
return fmt.Errorf("put: %w", err)
}

svc.logger.Debugf("created batch id %s", hex.EncodeToString(b.ID))
return nil
}

// TopUp implements the EventUpdater interface. It tops ups a batch with the
// given ID with the given amount.
func (svc *batchService) TopUp(id []byte, amount *big.Int) error {
b, err := svc.storer.Get(id)
if err != nil {
return fmt.Errorf("get: %w", err)
}

b.Value.Add(b.Value, amount)

err = svc.storer.Put(b)
if err != nil {
return fmt.Errorf("put: %w", err)
}

svc.logger.Debugf("topped up batch id %s with %v", hex.EncodeToString(b.ID), b.Value)
return nil
}

// UpdateDepth implements the EventUpdater inteface. It sets the new depth of a
// batch with the given ID.
func (svc *batchService) UpdateDepth(id []byte, depth uint8) error {
b, err := svc.storer.Get(id)
if err != nil {
return fmt.Errorf("get: %w", err)
}

b.Depth = depth

err = svc.storer.Put(b)
if err != nil {
return fmt.Errorf("put: %w", err)
}

svc.logger.Debugf("updated depth of batch id %s to %d", hex.EncodeToString(b.ID), b.Depth)
return nil
}

// UpdatePrice implements the EventUpdater interface. It sets the current
// price from the chain in the service chain state.
func (svc *batchService) UpdatePrice(price *big.Int) error {
svc.cs.Price = price

if err := svc.storer.PutChainState(svc.cs); err != nil {
return fmt.Errorf("put chain state: %w", err)
}

svc.logger.Debugf("updated chain price to %s", svc.cs.Price)
return nil
}
Loading