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

Stress tests #54

Merged
merged 12 commits into from
Aug 4, 2016
43 changes: 43 additions & 0 deletions crypto/util_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package crypto

import (
"bytes"
"crypto/rand"
"errors"
"testing"
)

func TestDigest(t *testing.T) {
msg := []byte("test message")
d := Digest(msg)
if len(d) != HashSizeByte {
t.Fatal("Computation of Hash failed.")
}
if bytes.Equal(d, make([]byte, HashSizeByte)) {
t.Fatal("Hash is all zeros.")
}
}

type testErrorRandReader struct{}

func (er testErrorRandReader) Read([]byte) (int, error) {
return 0, errors.New("Not enough entropy!")
}

func TestMakeRand(t *testing.T) {
r, err := MakeRand()
if err != nil {
t.Fatal(err)
}
// check if hashed the random output:
if len(r) != HashSizeByte {
t.Fatal("Looks like Digest wasn't called correctly.")
}
orig := rand.Reader
rand.Reader = testErrorRandReader{}
r, err = MakeRand()
if err == nil {
t.Fatal("No error returned")
}
rand.Reader = orig
}
191 changes: 191 additions & 0 deletions merkletree/pad_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import (
"bytes"
"testing"

"crypto/rand"
"errors"
"fmt"
"github.com/coniks-sys/coniks-go/crypto/sign"
"io"
)

var signKey sign.PrivateKey
Expand Down Expand Up @@ -235,3 +239,190 @@ func TestPoliciesChange(t *testing.T) {
t.Error(key3, "value mismatch")
}
}

func TestTB(t *testing.T) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should TestTB() be moved to tb_test.go?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, why not. It makes sense (04cf99a)

key1 := "key"
val1 := []byte("value")

pad, err := NewPAD(NewPolicies(3, vrfPrivKey1), signKey, 3)
if err != nil {
t.Fatal(err)
}
tb, err := pad.TB(key1, val1)
if err != nil {
t.Fatal(err)
}

pk, ok := pad.signKey.Public()
if !ok {
t.Fatal("Couldn't retrieve public-key.")
}
tbb := tb.Serialize(pad.latestSTR.Signature)
if !pk.Verify(tbb, tb.Signature) {
t.Fatal("Couldn't validate signature")
}
// create next epoch and see if the TB is inserted as promised:
pad.Update(nil)

ap, err := pad.Lookup(key1)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also verify the proof of inclusion here, just to make this test as a sample for complete TB verification?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like in 91a5725?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, I suggest to make some small changes, though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your feedback :-) 95650ab

if !bytes.Equal(ap.LookupIndex, tb.Index) || !bytes.Equal(ap.Leaf.Value(), tb.Value) {
t.Error("Value wasn't inserted as promised")
}
}

func TestNewPADMissingPolicies(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Fatal("Expected NewPAD to panic if policies are missing.")
}
}()
if _, err := NewPAD(nil, signKey, 10); err != nil {
t.Fatal("Expected NewPAD to panic but got error.")
}
}

// TODO move the following to some (internal?) testutils package
type testErrorRandReader struct{}

func (er testErrorRandReader) Read([]byte) (int, error) {
return 0, errors.New("Not enough entropy!")
}

func mockRandReadWithErroringReader() (orig io.Reader) {
orig = rand.Reader
rand.Reader = testErrorRandReader{}
return
}

func unMockRandReader(orig io.Reader) {
rand.Reader = orig
}

func TestNewPADErrorWhileCreatingTree(t *testing.T) {
origRand := mockRandReadWithErroringReader()
defer unMockRandReader(origRand)

pad, err := NewPAD(NewPolicies(3, vrfPrivKey1), signKey, 3)
if err == nil || pad != nil {
t.Fatal("NewPad should return an error in case the tree creation failed")
}
}

func BenchmarkCreateLargePAD(b *testing.B) {
snapLen := uint64(10)
keyPrefix := "key"
valuePrefix := []byte("value")

// total number of entries in tree:
NumEntries := uint64(1000000)
// tree.Clone and update STR every:
noUpdate := uint64(NumEntries + 1)

b.ResetTimer()
// benchmark creating a large tree (don't Update tree)
for n := 0; n < b.N; n++ {
_, err := createPad(NumEntries, keyPrefix, valuePrefix, snapLen,
noUpdate)
if err != nil {
b.Fatal(err)
}
}
}

//
// Benchmarks which can be used produce data similar to Figure 7. in Section 5.
//
func BenchmarkPADUpdate100K(b *testing.B) { benchPADUpdate(b, 100000) }
func BenchmarkPADUpdate500K(b *testing.B) { benchPADUpdate(b, 500000) }

// make sure you have enough memory/cpu power if you want to run the benchmarks
// below; also give the benchmarks enough time to finish using the -timeout flag
func BenchmarkPADUpdate1M(b *testing.B) { benchPADUpdate(b, 1000000) }
func BenchmarkPADUpdate2_5M(b *testing.B) { benchPADUpdate(b, 2500000) }
func BenchmarkPADUpdate5M(b *testing.B) { benchPADUpdate(b, 5000000) }
func BenchmarkPADUpdate7_5M(b *testing.B) { benchPADUpdate(b, 7500000) }
func BenchmarkPADUpdate10M(b *testing.B) { benchPADUpdate(b, 10000000) }

func benchPADUpdate(b *testing.B, entries uint64) {
keyPrefix := "key"
valuePrefix := []byte("value")
snapLen := uint64(10)
noUpdate := uint64(entries + 1)
pad, err := createPad(uint64(entries), keyPrefix, valuePrefix, snapLen, noUpdate)
if err != nil {
b.Fatal(err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
pad.Update(nil)
}
}

//
// END Benchmarks for Figure 7. in Section 5
//

func BenchmarkPADLookUpFrom10K(b *testing.B) { benchPADLookup(b, 10000) }
func BenchmarkPADLookUpFrom50K(b *testing.B) { benchPADLookup(b, 50000) }
func BenchmarkPADLookUpFrom100K(b *testing.B) { benchPADLookup(b, 100000) }
func BenchmarkPADLookUpFrom500K(b *testing.B) { benchPADLookup(b, 500000) }
func BenchmarkPADLookUpFrom1M(b *testing.B) { benchPADLookup(b, 1000000) }
func BenchmarkPADLookUpFrom5M(b *testing.B) { benchPADLookup(b, 5000000) }
func BenchmarkPADLookUpFrom10M(b *testing.B) { benchPADLookup(b, 10000000) }

func benchPADLookup(b *testing.B, entries uint64) {
snapLen := uint64(10)
keyPrefix := "key"
valuePrefix := []byte("value")
updateOnce := uint64(entries - 1)
pad, err := createPad(entries, keyPrefix, valuePrefix, snapLen,
updateOnce)
if err != nil {
b.Fatal(err)
}
// ignore the tree creation:
b.ResetTimer()
//fmt.Println("Done creating large pad/tree.")

// measure LookUps in large tree (with NumEntries leafs)
for n := 0; n < b.N; n++ {
b.StopTimer()
var key string
if n < int(entries) {
key = keyPrefix + string(n)
} else {
key = keyPrefix + string(n%int(entries))
}
b.StartTimer()
_, err := pad.Lookup(key)
if err != nil {
b.Fatalf("Coudldn't lookup key=%s", key)
}
}
}

// creates a PAD containing a tree with N entries (+ potential emptyLeafNodes)
// each key value pair has the form (keyPrefix+string(i), valuePrefix+string(i))
// for i = 0,...,N
// The STR will get updated every epoch defined by every multiple of
// `updateEvery`. If `updateEvery > N` createPAD won't update the STR.
func createPad(N uint64, keyPrefix string, valuePrefix []byte, snapLen uint64,
updateEvery uint64) (*PAD, error) {
pad, err := NewPAD(NewPolicies(3, vrfPrivKey1), signKey, snapLen)
if err != nil {
return nil, err
}

for i := uint64(0); i < N; i++ {
key := keyPrefix + string(i)
value := append(valuePrefix, byte(i))
if err := pad.Set(key, value); err != nil {
return nil, fmt.Errorf("Couldn't set key=%s and value=%s. Error: %v",
key, value, err)
}
if i != 0 && (i%updateEvery == 0) {
pad.Update(nil)
}
}
return pad, nil
}
8 changes: 8 additions & 0 deletions merkletree/proof_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,11 @@ func TestVerifyProofSamePrefix(t *testing.T) {
t.Error("Proof of absence verification failed.")
}
}

func TestEmptyNodeCommitment(t *testing.T) {
n := node{parent: nil, level: 1}
e := emptyNode{node: n, index: []byte("some index")}
if c := e.Commitment(); c != nil {
t.Fatal("Commitment of emptyNode should be nil")
}
}
9 changes: 0 additions & 9 deletions merkletree/str.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
package merkletree

import (
"errors"

"github.com/coniks-sys/coniks-go/crypto/sign"
"github.com/coniks-sys/coniks-go/utils"
)

var (
ErrorBadEpoch = errors.New("[merkletree] Bad STR Epoch. STR's epoch must be nonzero")
)

// SignedTreeRoot represents a signed tree root, which is generated
// at the beginning of every epoch.
// Signed tree roots contain the current root node,
Expand All @@ -27,9 +21,6 @@ type SignedTreeRoot struct {
}

func NewSTR(key sign.PrivateKey, policies Policies, m *MerkleTree, epoch uint64, prevHash []byte) *SignedTreeRoot {
if epoch < 0 {
panic(ErrorBadEpoch)
}
prevEpoch := epoch - 1
if epoch == 0 {
prevEpoch = 0
Expand Down
35 changes: 35 additions & 0 deletions utils/util_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package util

import (
"encoding/binary"
"math/rand"
"testing"
"time"
Expand All @@ -27,3 +28,37 @@ func TestBitsBytesConvert(t *testing.T) {
}
}
}

func TestIntToBytes(t *testing.T) {
numInt := 42
b := IntToBytes(numInt)
if int(binary.LittleEndian.Uint32(b)) != numInt {
t.Fatal("Conversion to bytes looks wrong!")
}
numInt = -42
b = IntToBytes(numInt)
if int32(binary.LittleEndian.Uint32(b)) != int32(numInt) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can it be simplified to int32(binary.LittleEndian.Uint32(b)) != numInt?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, pushed a second ago.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, I changed another line; but here it's needed or not? int32(binary.LittleEndian.Uint32(b)) != numInt won't work because the type of numInt is an int and the type of int32(binary.LittleEndian.Uint32(b)) is an int32

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. Sorry for the noise.

t.Fatal("Conversion to bytes looks wrong!")
}
}

func TestULongToBytes(t *testing.T) {
numInt := uint64(42)
b := ULongToBytes(numInt)
if binary.LittleEndian.Uint64(b) != numInt {
t.Fatal("Conversion to bytes looks wrong!")
}
}

func TestLongToBytes(t *testing.T) {
numInt := int64(42)
b := LongToBytes(numInt)
if int64(binary.LittleEndian.Uint64(b)) != numInt {
t.Fatal("Conversion to bytes looks wrong!")
}
numInt = int64(-42)
b = LongToBytes(numInt)
if int64(binary.LittleEndian.Uint64(b)) != numInt {
t.Fatal("Conversion to bytes looks wrong!")
}
}