Skip to content

Commit

Permalink
Save prev signature on db only if chained mode is enabled (#895)
Browse files Browse the repository at this point in the history
* save prev signature on db only if chained mode is enabled
* add extra layer to split functionality in two stores
* add test to check scheme store behaviour
* add scheme store on follow chain command
  • Loading branch information
emmanuelm41 committed Jan 11, 2022
1 parent ef80ae4 commit dd45899
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 3 deletions.
11 changes: 10 additions & 1 deletion chain/beacon/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,19 @@ type chainStore struct {
func newChainStore(l log.Logger, cf *Config, cl net.ProtocolClient, c *cryptoStore, store chain.Store, t *ticker) *chainStore {
// we make sure the chain is increasing monotically
as := newAppendStore(store)

// we add an store to run some checks depending on scheme-related config
ss := NewSchemeStore(as, cf.Group.Scheme)

// we write some stats about the timing when new beacon is saved
ds := newDiscrepancyStore(as, l, c.GetGroup())
ds := newDiscrepancyStore(ss, l, c.GetGroup())

// we can register callbacks on it
cbs := NewCallbackStore(ds)

// we give the final append store to the syncer
syncer := NewSyncer(l, cbs, c.chain, cl)

//
verifier := chain.NewVerifier(cf.Group.Scheme)

Expand Down Expand Up @@ -152,11 +159,13 @@ func (c *chainStore) runAggregator() {
break
}
cache.FlushRounds(partial.p.GetRound())

newBeacon := &chain.Beacon{
Round: roundCache.round,
PreviousSig: roundCache.prev,
Signature: finalSig,
}

c.l.Infow("", "beacon_id", beaconID, "aggregated_beacon", newBeacon.Round)
if c.tryAppend(lastBeacon, newBeacon) {
lastBeacon = newBeacon
Expand Down
39 changes: 38 additions & 1 deletion chain/beacon/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"sync"
"time"

"github.com/drand/drand/common/scheme"

"github.com/drand/drand/chain"
"github.com/drand/drand/key"
"github.com/drand/drand/log"
Expand Down Expand Up @@ -43,12 +45,47 @@ func (a *appendStore) Put(b *chain.Beacon) error {
if b.Round != a.last.Round+1 {
return fmt.Errorf("invalid round inserted: last %d, new %d", a.last.Round, b.Round)
}
if !bytes.Equal(a.last.Signature, b.PreviousSig) {
if err := a.Store.Put(b); err != nil {
return err
}
a.last = b
return nil
}

// schemeStore is a store that run different checks depending on what scheme is being used.
type schemeStore struct {
chain.Store
sch scheme.Scheme
last *chain.Beacon
sync.Mutex
}

func NewSchemeStore(s chain.Store, sch scheme.Scheme) chain.Store {
last, _ := s.Last()
return &schemeStore{
Store: s,
last: last,
sch: sch,
}
}

func (a *schemeStore) Put(b *chain.Beacon) error {
a.Lock()
defer a.Unlock()

// If the scheme is unchained, previous signature is set to nil. In that case,
// relationship between signature in the previous beacon and previous signature
// on the actual beacon is not necessary. Otherwise, it will be checked.
if a.sch.DecouplePrevSig {
b.PreviousSig = nil
} else if !bytes.Equal(a.last.Signature, b.PreviousSig) {
return fmt.Errorf("invalid previous signature")
}

if err := a.Store.Put(b); err != nil {
return err
}

a.last = b
return nil
}
Expand Down
65 changes: 65 additions & 0 deletions chain/beacon/store_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package beacon

import (
"bytes"
"os"
"testing"

"github.com/drand/drand/common/scheme"

"github.com/drand/drand/chain"
"github.com/drand/drand/chain/boltdb"
"github.com/stretchr/testify/require"
)

func TestSchemeStore(t *testing.T) {
sch, _ := scheme.ReadSchemeByEnv()

dir, err := os.MkdirTemp("", "*")
require.NoError(t, err)
defer os.RemoveAll(dir)

bstore, err := boltdb.NewBoltStore(dir, nil)
require.NoError(t, err)

genesisBeacon := chain.GenesisBeacon(&chain.Info{GroupHash: []byte("genesis_signature")})
err = bstore.Put(genesisBeacon)
require.NoError(t, err)

ss := NewSchemeStore(bstore, sch)

newBeacon := &chain.Beacon{
Round: 1,
Signature: []byte("signature_1"),
PreviousSig: []byte("genesis_signature"),
}
err = ss.Put(newBeacon)
require.NoError(t, err)

beaconSaved, err := ss.Last()
require.NoError(t, err)

// test if store sets to nil prev signature depending on scheme
// with chained scheme, it should keep the consistency between prev signature and signature
if sch.DecouplePrevSig && beaconSaved.PreviousSig != nil {
t.Errorf("previous signature should be nil")
} else if !sch.DecouplePrevSig && !bytes.Equal(beaconSaved.PreviousSig, genesisBeacon.Signature) {
t.Errorf("previous signature on last beacon [%s] should be equal to previous beacon signature [%s]",
beaconSaved.PreviousSig, genesisBeacon.PreviousSig)
}

newBeacon = &chain.Beacon{
Round: 2,
Signature: []byte("signature_2"),
PreviousSig: nil,
}

err = ss.Put(newBeacon)

// test if store checks consistency between signature and prev signature depending on the scheme
if sch.DecouplePrevSig && err != nil {
t.Errorf("new beacon should be allow to be put on store")
} else if !sch.DecouplePrevSig && err == nil {
t.Errorf("new beacon should not be allow to be put on store")
}
}
6 changes: 5 additions & 1 deletion core/drand_beacon_control.go
Original file line number Diff line number Diff line change
Expand Up @@ -1077,8 +1077,12 @@ func (bp *BeaconProcess) StartFollowChain(req *drand.StartFollowRequest, stream
store.Close()
return fmt.Errorf("unable to insert genesis block: %s", err)
}

// add scheme store to handle scheme configuration on beacon storing process correctly
ss := beacon.NewSchemeStore(store, info.Scheme)

// register callback to notify client of progress
cbStore := beacon.NewCallbackStore(store)
cbStore := beacon.NewCallbackStore(ss)
defer cbStore.Close()

syncer := beacon.NewSyncer(bp.log, cbStore, info, bp.privGateway)
Expand Down

0 comments on commit dd45899

Please sign in to comment.