Skip to content

Commit d833b8e

Browse files
committed
randomness: make testable, mockable and add tests
Refactor the package so it has a settable provider. Convert the existing init function to the real drand-based provided. Add another mock provider that returns a fixed value instead of a proper random one. Override the drand provided with the mock provider in tests. This way tests can run and pass when e.g. the service is dead or when I'm on the plane again with no internet access. Move it to utils because this is where all the goodies that are helpful but not really related to the trusted setup itself are going to live. Signed-off-by: Wojciech Zmuda <[email protected]>
1 parent 5524a33 commit d833b8e

File tree

7 files changed

+143
-63
lines changed

7 files changed

+143
-63
lines changed

cmd/init.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
"github.com/urfave/cli/v3"
99

1010
"github.com/reilabs/trusted-setup/phase2"
11-
"github.com/reilabs/trusted-setup/randomness"
11+
"github.com/reilabs/trusted-setup/utils/randomness"
1212
)
1313

1414
func Phase2Init(_ context.Context, cmd *cli.Command) error {

randomness/randomness.go

Lines changed: 0 additions & 61 deletions
This file was deleted.

test/offline_ceremony_test.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/reilabs/trusted-setup/phase1"
1515
"github.com/reilabs/trusted-setup/phase2"
1616
"github.com/reilabs/trusted-setup/r1cs"
17+
"github.com/reilabs/trusted-setup/utils/randomness"
1718
)
1819

1920
// testOfflineCeremony verifies the trusted setup ceremony in the offline mode.
@@ -42,7 +43,7 @@ const r1csFileName = "test.r1cs"
4243
const pkFileName = "test.pk"
4344
const vkFileName = "test.vk"
4445

45-
var beacon = bytes.Repeat([]byte{0x42}, 32)
46+
var beacon []byte
4647

4748
func setup() {
4849
ccs, err := buildCcs()
@@ -53,6 +54,10 @@ func setup() {
5354
if err != nil {
5455
panic(err)
5556
}
57+
58+
mockBeacon := bytes.Repeat([]byte{0x42}, 32)
59+
randomness.SetProvider(&randomness.MockProvider{Beacon: mockBeacon})
60+
beacon = randomness.GetBeacon()
5661
}
5762

5863
func teardown() {

utils/randomness/drand_provider.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package randomness
2+
3+
import (
4+
"context"
5+
"encoding/hex"
6+
"fmt"
7+
8+
"github.com/drand/go-clients/client"
9+
"github.com/drand/go-clients/client/http"
10+
"github.com/drand/go-clients/drand"
11+
)
12+
13+
// drandProvider implements BeaconProvider by retrieving randomness from drand network.
14+
type drandProvider struct {
15+
client drand.Client
16+
}
17+
18+
// newDrandProvider initializes the randomness module. It connects to the drand network, so random value can be
19+
// obtained with GetBeacon.
20+
func newDrandProvider() (*drandProvider, error) {
21+
httpClient, err := http.NewSimpleClient(apiHost, chainHash)
22+
if err != nil {
23+
panic(err)
24+
}
25+
chb, err := hex.DecodeString(chainHash)
26+
if err != nil {
27+
panic(err)
28+
}
29+
30+
p := drandProvider{}
31+
p.client, err = client.New(
32+
client.From(httpClient),
33+
client.WithChainHash(chb),
34+
)
35+
if err != nil {
36+
panic(err)
37+
}
38+
39+
return &p, nil
40+
}
41+
42+
// GetBeacon returns the 32 bytes of randomness.
43+
func (d *drandProvider) GetBeacon() []byte {
44+
const mostRecentKnownRound = 0
45+
r, err := d.client.Get(context.Background(), mostRecentKnownRound)
46+
if err != nil {
47+
panic(err)
48+
}
49+
50+
beacon := r.GetRandomness()
51+
if len(beacon) != 32 {
52+
panic(fmt.Errorf("randomness: expected 32 bytes, got %d", len(beacon)))
53+
}
54+
if beacon == nil {
55+
panic(fmt.Errorf("randomness: drand did not return randomness"))
56+
}
57+
58+
return beacon
59+
}

utils/randomness/mock_provider.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package randomness
2+
3+
// MockProvider implements BeaconProvider and returns a fixed 32-byte beacon.
4+
type MockProvider struct {
5+
Beacon []byte
6+
}
7+
8+
// GetBeacon returns the 32 bytes of randomness acquired at the module initialization.
9+
func (m *MockProvider) GetBeacon() []byte {
10+
return m.Beacon
11+
}

utils/randomness/randomness.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Package randomness provides "publicly verifiable, unbiased and unpredictable random values" as guaranteed
2+
// by the drand organization (https://github.com/drand/drand).
3+
package randomness
4+
5+
// Default network chain hash as per the drand project documentation.
6+
const chainHash = "8990e7a9aaed2ffed73dbd7092123d6f289930540d7651336225dc172e51b2ce"
7+
8+
// API returning the randomness, as per the drand project documentation.
9+
const apiHost = "https://api.drand.sh/"
10+
11+
// BeaconProvider defines an interface to get 32 bytes of randomness (beacon).
12+
type BeaconProvider interface {
13+
GetBeacon() []byte
14+
}
15+
16+
// provider is a handler for randomness provider.
17+
var provider BeaconProvider
18+
19+
// init is called at package initialization and sets the default provider.
20+
func init() {
21+
p, err := newDrandProvider()
22+
if err != nil {
23+
panic(err)
24+
}
25+
provider = p
26+
}
27+
28+
// SetProvider sets the BeaconProvider used by the package. This allows overriding randomness source (e.g. for tests).
29+
func SetProvider(p BeaconProvider) {
30+
provider = p
31+
}
32+
33+
// GetBeacon returns the 32 bytes of randomness acquired at the module initialization or from the currently set provider.
34+
func GetBeacon() []byte {
35+
return provider.GetBeacon()
36+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package randomness_test
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
9+
"github.com/reilabs/trusted-setup/utils/randomness"
10+
)
11+
12+
func TestGetBeaconWithMock(t *testing.T) {
13+
mockValue := bytes.Repeat([]byte{0x42}, 32) // 32 bytes of value 0x42
14+
mock := &randomness.MockProvider{Beacon: mockValue}
15+
16+
// Override the default provider with the mock.
17+
randomness.SetProvider(mock)
18+
19+
got := randomness.GetBeacon()
20+
if !bytes.Equal(got, mockValue) {
21+
t.Fatalf("GetBeacon() = %x; want %x", got, mockValue)
22+
}
23+
}
24+
25+
func TestGetBeaconWithDrand(t *testing.T) {
26+
got := randomness.GetBeacon()
27+
28+
assert.NotEmpty(t, got)
29+
assert.Equal(t, 32, len(got))
30+
}

0 commit comments

Comments
 (0)