Skip to content

Commit

Permalink
Scheme configuration system and unchained randomness (#816)
Browse files Browse the repository at this point in the history
* v1.2.0

* update go version to 1.17

* update go and protobuf version

* add unchained beacon feature on code

* update tests with new feature

* split test jobs to check both chained and unchained randomness

* regenerate proto files

* fix linter issues

* Scheme feature and unchained randomness refactoring (#31)

* create a scheme configuration system
* refactor unchain randomness feature to use scheme configuration, making it more easy to set up
* add a new CLI command to list scheme ids available

* regenerate protobuf files

* apply go mod tidy

* add docs to new added code

* apply minor changes

* set default scheme on client cfg

* fix lint issue

* change CI job names

Co-authored-by: Will Scott <will@cypherpunk.email>
Co-authored-by: Will Scott <will.scott@protocol.ai>
  • Loading branch information
3 people committed Oct 13, 2021
1 parent 6fe4c2f commit 7af9e77
Show file tree
Hide file tree
Showing 57 changed files with 1,403 additions and 616 deletions.
25 changes: 24 additions & 1 deletion .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
make drand-relay-gossip
make drand-relay-s3
test:
test_chained:
runs-on: ubuntu-latest
steps:
- name: Checkout
Expand All @@ -50,6 +50,29 @@ jobs:
- name: Integration tests
run: make test-integration

test_unchained:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
submodules: true
- uses: actions/setup-go@v2
with:
go-version: '^1.17'
- uses: actions/cache@v2
id: cache
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- run: go get -v -t -d ./...
- name: Unit tests
run: SCHEME_ID=pedersen-bls-unchained make test-unit
- name: Integration tests
run: SCHEME_ID=pedersen-bls-unchained make test-integration

coverage:
runs-on: ubuntu-latest
env:
Expand Down
34 changes: 0 additions & 34 deletions chain/beacon.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ import (
"fmt"

json "github.com/nikkolasg/hexjson"

"github.com/drand/drand/key"
"github.com/drand/kyber"
)

// Beacon holds the randomness as well as the info to verify it.
Expand Down Expand Up @@ -60,37 +57,6 @@ func (b *Beacon) String() string {
return fmt.Sprintf("{ round: %d, sig: %s, prevSig: %s }", b.Round, shortSigStr(b.Signature), shortSigStr(b.PreviousSig))
}

// VerifyBeacon returns an error if the given beacon does not verify given the
// public key. The public key "point" can be obtained from the
// `key.DistPublic.Key()` method. The distributed public is the one written in
// the configuration file of the network.
func VerifyBeacon(pubkey kyber.Point, b *Beacon) error {
prevSig := b.PreviousSig
round := b.Round
msg := Message(round, prevSig)
return key.Scheme.VerifyRecovered(pubkey, msg, b.Signature)
}

// Verify is similar to verify beacon but doesn't require to get the full beacon
// structure.
func Verify(pubkey kyber.Point, prevSig, signature []byte, round uint64) error {
return VerifyBeacon(pubkey, &Beacon{
Round: round,
PreviousSig: prevSig,
Signature: signature,
})
}

// Message returns a slice of bytes as the message to sign or to verify
// alongside a beacon signature.
// H ( prevSig || currRound)
func Message(currRound uint64, prevSig []byte) []byte {
h := sha256.New()
_, _ = h.Write(prevSig)
_, _ = h.Write(RoundToBytes(currRound))
return h.Sum(nil)
}

func shortSigStr(sig []byte) string {
max := 3
if len(sig) < max {
Expand Down
6 changes: 0 additions & 6 deletions chain/beacon/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"bytes"
"encoding/binary"

"github.com/drand/drand/chain"
"github.com/drand/drand/key"
"github.com/drand/drand/log"
"github.com/drand/drand/protobuf/drand"
Expand Down Expand Up @@ -141,11 +140,6 @@ func (r *roundCache) Len() int {
return len(r.sigs)
}

// Msg provides the chain for the current round
func (r *roundCache) Msg() []byte {
return chain.Message(r.round, r.prev)
}

// Partials provides all cached partial signatures
func (r *roundCache) Partials() [][]byte {
partials := make([][]byte, 0, len(r.sigs))
Expand Down
17 changes: 13 additions & 4 deletions chain/beacon/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"testing"

"github.com/drand/drand/chain"
"github.com/drand/drand/common/scheme"
"github.com/drand/drand/key"
"github.com/drand/drand/log"
"github.com/drand/drand/protobuf/drand"
Expand All @@ -14,11 +15,15 @@ import (
var fakeKey = key.NewKeyPair("127.0.0.1:8080")

func generatePartial(idx int, round uint64, prev []byte) *drand.PartialBeaconPacket {
sch := scheme.GetSchemeFromEnv()
verifier := chain.NewVerifier(sch)

sh := &share.PriShare{
I: idx,
V: fakeKey.Key,
}
msg := chain.Message(round, prev)

msg := verifier.DigestMessage(round, prev)
sig, _ := key.Scheme.Sign(sh, msg)
return &drand.PartialBeaconPacket{
Round: round,
Expand All @@ -29,16 +34,20 @@ func generatePartial(idx int, round uint64, prev []byte) *drand.PartialBeaconPac

func TestCacheRound(t *testing.T) {
id := "thisismyid"
var round uint64 = 64
round := uint64(64)
prev := []byte("yesterday was another day")
msg := chain.Message(round, prev)

sch := scheme.GetSchemeFromEnv()
verifier := chain.NewVerifier(sch)

msg := verifier.DigestMessage(round, prev)
partial := generatePartial(1, round, prev)
p2 := generatePartial(2, round, prev)
cache := newRoundCache(id, partial)
require.True(t, cache.append(partial))
require.False(t, cache.append(partial))
require.Equal(t, 1, cache.Len())
require.Equal(t, msg, cache.Msg())
require.Equal(t, msg, verifier.DigestMessage(cache.round, cache.prev))

require.True(t, cache.append(p2))
require.Equal(t, 2, cache.Len())
Expand Down
8 changes: 7 additions & 1 deletion chain/beacon/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type chainStore struct {
conf *Config
client net.ProtocolClient
sync Syncer
verifier *chain.Verifier
crypto *cryptoStore
ticker *ticker
done chan bool
Expand All @@ -46,12 +47,16 @@ func newChainStore(l log.Logger, cf *Config, cl net.ProtocolClient, c *cryptoSto
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)

cs := &chainStore{
CallbackStore: cbs,
l: l,
conf: cf,
client: cl,
sync: syncer,
verifier: verifier,
crypto: c,
ticker: t,
done: make(chan bool, 1),
Expand Down Expand Up @@ -133,7 +138,8 @@ func (c *chainStore) runAggregator() {
break
}

msg := roundCache.Msg()
msg := c.verifier.DigestMessage(roundCache.round, roundCache.prev)

finalSig, err := key.Scheme.Recover(c.crypto.GetPub(), msg, roundCache.Partials(), thr, n)
if err != nil {
c.l.Debugw("", "invalid_recovery", err, "round", pRound, "got", fmt.Sprintf("%d/%d", roundCache.Len(), n))
Expand Down
38 changes: 25 additions & 13 deletions chain/beacon/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ type Handler struct {
// keeps the cryptographic info (group share etc)
crypto *cryptoStore
// main logic that treats incoming packet / new beacons created
chain *chainStore
ticker *ticker
chain *chainStore
ticker *ticker
verifier *chain.Verifier

close chan bool
addr string
Expand Down Expand Up @@ -77,16 +78,19 @@ func NewHandler(c net.ProtocolClient, s chain.Store, conf *Config, l log.Logger,

ticker := newTicker(conf.Clock, conf.Group.Period, conf.Group.GenesisTime)
store := newChainStore(logger, conf, c, crypto, s, ticker)
verifier := chain.NewVerifier(conf.Group.Scheme)

handler := &Handler{
conf: conf,
client: c,
crypto: crypto,
chain: store,
ticker: ticker,
addr: addr,
close: make(chan bool),
l: logger,
version: version,
conf: conf,
client: c,
crypto: crypto,
chain: store,
verifier: verifier,
ticker: ticker,
addr: addr,
close: make(chan bool),
l: logger,
version: version,
}
return handler, nil
}
Expand All @@ -110,7 +114,8 @@ func (h *Handler) ProcessPartialBeacon(c context.Context, p *proto.PartialBeacon
return nil, fmt.Errorf("invalid round: %d instead of %d", p.GetRound(), currentRound)
}

msg := chain.Message(p.GetRound(), p.GetPreviousSig())
msg := h.verifier.DigestMessage(p.GetRound(), p.GetPreviousSig())

// XXX Remove that evaluation - find another way to show the current dist.
// key being used
shortPub := h.crypto.GetPub().Eval(1).V.String()[14:19]
Expand Down Expand Up @@ -351,7 +356,9 @@ func (h *Handler) broadcastNextPartial(current roundInfo, upon *chain.Beacon) {
previousSig = upon.PreviousSig
round = current.round
}
msg := chain.Message(round, previousSig)

msg := h.verifier.DigestMessage(round, previousSig)

currSig, err := h.crypto.SignPartial(msg)
if err != nil {
h.l.Fatal("beacon_round", "err creating signature", "err", err, "round", round)
Expand Down Expand Up @@ -427,6 +434,11 @@ func (h *Handler) RemoveCallback(id string) {
h.chain.RemoveCallback(id)
}

// GetConfg returns the conf used by the handler
func (h *Handler) GetConfg() *Config {
return h.conf
}

// SyncChain is a proxy method to sync a chain
func (h *Handler) SyncChain(req *proto.SyncRequest, stream proto.Protocol_SyncChainServer) error {
return h.chain.sync.SyncChain(req, stream)
Expand Down
42 changes: 31 additions & 11 deletions chain/beacon/node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"testing"
"time"

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

"github.com/drand/drand/chain"
"github.com/drand/drand/chain/boltdb"
"github.com/drand/drand/key"
Expand Down Expand Up @@ -127,14 +129,15 @@ type BeaconTest struct {
nodes map[int]*node
time clock.FakeClock
prefix string
scheme scheme.Scheme
}

func NewBeaconTest(t *testing.T, n, thr int, period time.Duration, genesisTime int64) *BeaconTest {
func NewBeaconTest(t *testing.T, n, thr int, period time.Duration, genesisTime int64, sch scheme.Scheme) *BeaconTest {
prefix, err := ioutil.TempDir(os.TempDir(), "beacon-test")
checkErr(err)
paths := createBoltStores(prefix, n)
shares, commits := dkgShares(t, n, thr)
privs, group := test.BatchIdentities(n)
privs, group := test.BatchIdentities(n, sch)
group.Threshold = thr
group.Period = period
group.GenesisTime = genesisTime
Expand All @@ -146,6 +149,7 @@ func NewBeaconTest(t *testing.T, n, thr int, period time.Duration, genesisTime i
privs: privs,
thr: thr,
period: period,
scheme: sch,
paths: paths,
shares: shares,
group: group,
Expand Down Expand Up @@ -390,16 +394,21 @@ func TestBeaconSync(t *testing.T) {
thr := n/2 + 1
period := 2 * time.Second

var genesisOffset = 2 * time.Second
genesisOffset := 2 * time.Second
genesisTime := clock.NewFakeClock().Now().Add(genesisOffset).Unix()
sch := scheme.GetSchemeFromEnv()

bt := NewBeaconTest(t, n, thr, period, genesisTime)
bt := NewBeaconTest(t, n, thr, period, genesisTime, sch)
defer bt.CleanUp()

verifier := chain.NewVerifier(sch)

var counter = &sync.WaitGroup{}
myCallBack := func(i int) func(*chain.Beacon) {
return func(b *chain.Beacon) {
require.NoError(t, chain.VerifyBeacon(bt.dpublic, b))
err := verifier.VerifyBeacon(*b, bt.dpublic)
require.NoError(t, err)

t.Logf("round %d done for %s\n", b.Round, bt.nodes[bt.searchNode(i)].private.Public.Address())
counter.Done()
}
Expand Down Expand Up @@ -459,21 +468,27 @@ func TestBeaconSync(t *testing.T) {

doRound(n+n-online, period)
}

func TestBeaconSimple(t *testing.T) {
n := 3
thr := n/2 + 1
period := 2 * time.Second

genesisTime := clock.NewFakeClock().Now().Unix() + 2
sch := scheme.GetSchemeFromEnv()

bt := NewBeaconTest(t, n, thr, period, genesisTime)
bt := NewBeaconTest(t, n, thr, period, genesisTime, sch)
defer bt.CleanUp()

verifier := chain.NewVerifier(sch)

var counter = &sync.WaitGroup{}
counter.Add(n)
myCallBack := func(b *chain.Beacon) {
// verify partial sig
require.NoError(t, chain.VerifyBeacon(bt.dpublic, b))
err := verifier.VerifyBeacon(*b, bt.dpublic)
require.NoError(t, err)

counter.Done()
}

Expand Down Expand Up @@ -521,18 +536,23 @@ func TestBeaconThreshold(t *testing.T) {

offsetGenesis := 2 * time.Second
genesisTime := clock.NewFakeClock().Now().Add(offsetGenesis).Unix()
sch := scheme.GetSchemeFromEnv()

bt := NewBeaconTest(t, n, thr, period, genesisTime)
bt := NewBeaconTest(t, n, thr, period, genesisTime, sch)
defer func() { go bt.CleanUp() }()
var currentRound uint64 = 0

verifier := chain.NewVerifier(sch)

currentRound := uint64(0)
var counter = &sync.WaitGroup{}
myCallBack := func(i int) func(*chain.Beacon) {
return func(b *chain.Beacon) {
fmt.Printf(" - test: callback called for node %d - round %d\n", i, b.Round)
// verify partial sig
msg := chain.Message(b.Round, b.PreviousSig)
err := key.Scheme.VerifyRecovered(bt.dpublic, msg, b.Signature)

err := verifier.VerifyBeacon(*b, bt.dpublic)
require.NoError(t, err)

// callbacks are called for syncing up as well so we only decrease
// waitgroup when it's the current round
if b.Round == currentRound {
Expand Down
Loading

0 comments on commit 7af9e77

Please sign in to comment.