aboutsummaryrefslogtreecommitdiff
path: root/pkg/state
diff options
context:
space:
mode:
authorRasmus Dahlberg <rasmus.dahlberg@kau.se>2021-09-13 19:53:17 +0200
committerRasmus Dahlberg <rasmus.dahlberg@kau.se>2021-09-13 19:53:17 +0200
commit26b786d9857db21fdf110eaf9cb6d1d6e4e68ef9 (patch)
tree92606f47a91ebcc9e3cb4258af7a9472fc6f2e07 /pkg/state
parentf34351da0731a11316e2266d2aadd62554a5b867 (diff)
updated (co)signed tree head structuresv0.2.0
- Added key_hash in tree head, see motivation in api.md - Added separate types for (co)signed tree heads - Refactored tree head HTTP APIs to be current, see api.md
Diffstat (limited to 'pkg/state')
-rw-r--r--pkg/state/state_manager.go32
-rw-r--r--pkg/state/state_manager_test.go148
2 files changed, 98 insertions, 82 deletions
diff --git a/pkg/state/state_manager.go b/pkg/state/state_manager.go
index 7ddf986..a08fd84 100644
--- a/pkg/state/state_manager.go
+++ b/pkg/state/state_manager.go
@@ -3,6 +3,7 @@ package state
import (
"context"
"crypto"
+ "crypto/ed25519"
"fmt"
"reflect"
"sync"
@@ -18,7 +19,7 @@ import (
type StateManager interface {
Latest(context.Context) (*types.SignedTreeHead, error)
ToSign(context.Context) (*types.SignedTreeHead, error)
- Cosigned(context.Context) (*types.SignedTreeHead, error)
+ Cosigned(context.Context) (*types.CosignedTreeHead, error)
AddCosignature(context.Context, *[types.VerificationKeySize]byte, *[types.SignatureSize]byte) error
Run(context.Context)
}
@@ -33,7 +34,7 @@ type StateManagerSingle struct {
sync.RWMutex
// cosigned is the current cosigned tree head that is being served
- cosigned types.SignedTreeHead
+ cosigned types.CosignedTreeHead
// tosign is the current tree head that is being cosigned by witnesses
tosign types.SignedTreeHead
@@ -56,18 +57,19 @@ func NewStateManagerSingle(client trillian.Client, signer crypto.Signer, interva
return nil, fmt.Errorf("Latest: %v", err)
}
- sm.cosigned = *sth
- sm.tosign = *sth
- sm.cosignature = map[[types.HashSize]byte]*types.SigIdent{
- *sth.SigIdent[0].KeyHash: sth.SigIdent[0], // log signature
+ sm.cosigned = types.CosignedTreeHead{
+ SignedTreeHead: *sth,
+ SigIdent: []*types.SigIdent{},
}
+ sm.tosign = *sth
+ sm.cosignature = map[[types.HashSize]byte]*types.SigIdent{}
return sm, nil
}
func (sm *StateManagerSingle) Run(ctx context.Context) {
schedule.Every(ctx, sm.interval, func(ctx context.Context) {
ictx, _ := context.WithTimeout(ctx, sm.deadline)
- nextTreeHead, err := sm.Latest(ictx)
+ nextSTH, err := sm.Latest(ictx)
if err != nil {
glog.Warningf("rotate failed: Latest: %v", err)
return
@@ -75,7 +77,7 @@ func (sm *StateManagerSingle) Run(ctx context.Context) {
sm.Lock()
defer sm.Unlock()
- sm.rotate(nextTreeHead)
+ sm.rotate(nextSTH)
})
}
@@ -84,6 +86,7 @@ func (sm *StateManagerSingle) Latest(ctx context.Context) (*types.SignedTreeHead
if err != nil {
return nil, fmt.Errorf("LatestTreeHead: %v", err)
}
+ th.KeyHash = types.Hash(sm.signer.Public().(ed25519.PublicKey)[:])
sth, err := th.Sign(sm.signer)
if err != nil {
return nil, fmt.Errorf("sign: %v", err)
@@ -97,9 +100,12 @@ func (sm *StateManagerSingle) ToSign(_ context.Context) (*types.SignedTreeHead,
return &sm.tosign, nil
}
-func (sm *StateManagerSingle) Cosigned(_ context.Context) (*types.SignedTreeHead, error) {
+func (sm *StateManagerSingle) Cosigned(_ context.Context) (*types.CosignedTreeHead, error) {
sm.RLock()
defer sm.RUnlock()
+ if len(sm.cosigned.SigIdent) == 0 {
+ return nil, fmt.Errorf("no witness cosignatures available")
+ }
return &sm.cosigned, nil
}
@@ -126,7 +132,7 @@ func (sm *StateManagerSingle) AddCosignature(_ context.Context, vk *[types.Verif
// rotate rotates the log's cosigned and stable STH. The caller must aquire the
// source's read-write lock if there are concurrent reads and/or writes.
func (sm *StateManagerSingle) rotate(next *types.SignedTreeHead) {
- if reflect.DeepEqual(sm.cosigned.TreeHead, sm.tosign.TreeHead) {
+ if reflect.DeepEqual(sm.cosigned.SignedTreeHead, sm.tosign) {
// cosigned and tosign are the same. So, we need to merge all
// cosignatures that we already had with the new collected ones.
for _, sigident := range sm.cosigned.SigIdent {
@@ -142,13 +148,11 @@ func (sm *StateManagerSingle) rotate(next *types.SignedTreeHead) {
}
// Update cosigned tree head
- sm.cosigned.TreeHead = sm.tosign.TreeHead
+ sm.cosigned.SignedTreeHead = sm.tosign
sm.cosigned.SigIdent = cosignatures
// Update to-sign tree head
sm.tosign = *next
- sm.cosignature = map[[types.HashSize]byte]*types.SigIdent{
- *next.SigIdent[0].KeyHash: next.SigIdent[0], // log signature
- }
+ sm.cosignature = map[[types.HashSize]byte]*types.SigIdent{} // TODO: on repeat we might want to not zero this
glog.V(3).Infof("rotated tree heads")
}
diff --git a/pkg/state/state_manager_test.go b/pkg/state/state_manager_test.go
index acc2319..6650544 100644
--- a/pkg/state/state_manager_test.go
+++ b/pkg/state/state_manager_test.go
@@ -23,14 +23,21 @@ var (
Timestamp: 0,
TreeSize: 0,
RootHash: types.Hash(nil),
+ KeyHash: types.Hash(testPub[:]),
}
testSigIdent = &types.SigIdent{
Signature: testSig,
KeyHash: types.Hash(testPub[:]),
}
testSTH = &types.SignedTreeHead{
- TreeHead: *testTH,
- SigIdent: []*types.SigIdent{testSigIdent},
+ TreeHead: *testTH,
+ Signature: testSig,
+ }
+ testCTH = &types.CosignedTreeHead{
+ SignedTreeHead: *testSTH,
+ SigIdent: []*types.SigIdent{
+ testSigIdent,
+ },
}
testSignerOK = &mocks.TestSigner{testPub, testSig, nil}
testSignerErr = &mocks.TestSigner{testPub, testSig, fmt.Errorf("something went wrong")}
@@ -72,14 +79,14 @@ func TestNewStateManagerSingle(t *testing.T) {
if err != nil {
return
}
- if got, want := &sm.cosigned, table.wantSth; !reflect.DeepEqual(got, want) {
+ if got, want := &sm.cosigned.SignedTreeHead, table.wantSth; !reflect.DeepEqual(got, want) {
t.Errorf("got cosigned tree head\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
}
if got, want := &sm.tosign, table.wantSth; !reflect.DeepEqual(got, want) {
t.Errorf("got tosign tree head\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
}
// we only have log signature on startup
- if got, want := len(sm.cosignature), 1; got != want {
+ if got, want := len(sm.cosignature), 0; got != want {
t.Errorf("got %d cosignatures but wanted %d in test %q", got, want, table.description)
}
}()
@@ -157,16 +164,23 @@ func TestToSign(t *testing.T) {
func TestCosigned(t *testing.T) {
description := "valid"
sm := StateManagerSingle{
- cosigned: *testSTH,
+ cosigned: *testCTH,
}
- sth, err := sm.Cosigned(context.Background())
+ cth, err := sm.Cosigned(context.Background())
if err != nil {
t.Errorf("Cosigned should not fail with error: %v", err)
return
}
- if got, want := sth, testSTH; !reflect.DeepEqual(got, want) {
+ if got, want := cth, testCTH; !reflect.DeepEqual(got, want) {
t.Errorf("got signed tree head\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, description)
}
+
+ sm.cosigned.SigIdent = make([]*types.SigIdent, 0)
+ cth, err = sm.Cosigned(context.Background())
+ if err == nil {
+ t.Errorf("Cosigned should fail without witness cosignatures")
+ return
+ }
}
func TestAddCosignature(t *testing.T) {
@@ -202,15 +216,15 @@ func TestAddCosignature(t *testing.T) {
},
} {
sth, _ := table.th.Sign(testSignerOK)
- logKeyHash := sth.SigIdent[0].KeyHash
- logSigIdent := sth.SigIdent[0]
+ cth := &types.CosignedTreeHead{
+ SignedTreeHead: *sth,
+ SigIdent: []*types.SigIdent{},
+ }
sm := &StateManagerSingle{
- signer: testSignerOK,
- cosigned: *sth,
- tosign: *sth,
- cosignature: map[[types.HashSize]byte]*types.SigIdent{
- *logKeyHash: logSigIdent,
- },
+ signer: testSignerOK,
+ cosigned: *cth,
+ tosign: *sth,
+ cosignature: map[[types.HashSize]byte]*types.SigIdent{},
}
// Prepare witness signature
@@ -218,11 +232,13 @@ func TestAddCosignature(t *testing.T) {
if err != nil {
t.Fatalf("Sign: %v", err)
}
- witnessKeyHash := sth.SigIdent[0].KeyHash
- witnessSigIdent := sth.SigIdent[0]
+ si := &types.SigIdent{
+ KeyHash: types.Hash(table.signer.Public().(ed25519.PublicKey)[:]),
+ Signature: sth.Signature,
+ }
// Add witness signature
- err = sm.AddCosignature(context.Background(), table.vk, witnessSigIdent.Signature)
+ err = sm.AddCosignature(context.Background(), table.vk, si.Signature)
if got, want := err != nil, table.wantErr; got != want {
t.Errorf("got error %v but wanted %v in test %q: %v", got, want, table.description, err)
}
@@ -230,33 +246,24 @@ func TestAddCosignature(t *testing.T) {
continue
}
- // We should have two signatures (log + witness)
- if got, want := len(sm.cosignature), 2; got != want {
+ // We should have one witness signature
+ if got, want := len(sm.cosignature), 1; got != want {
t.Errorf("got %d cosignatures but wanted %v in test %q", got, want, table.description)
continue
}
- // check that log signature is there
- sigident, ok := sm.cosignature[*logKeyHash]
- if !ok {
- t.Errorf("log signature is missing")
- continue
- }
- if got, want := sigident, logSigIdent; !reflect.DeepEqual(got, want) {
- t.Errorf("got log sigident\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
- }
// check that witness signature is there
- sigident, ok = sm.cosignature[*witnessKeyHash]
+ sigident, ok := sm.cosignature[*si.KeyHash]
if !ok {
t.Errorf("witness signature is missing")
continue
}
- if got, want := sigident, witnessSigIdent; !reflect.DeepEqual(got, want) {
+ if got, want := si, sigident; !reflect.DeepEqual(got, want) {
t.Errorf("got witness sigident\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
continue
}
// Adding a duplicate signature should give an error
- if err := sm.AddCosignature(context.Background(), table.vk, witnessSigIdent.Signature); err == nil {
+ if err := sm.AddCosignature(context.Background(), table.vk, si.Signature); err == nil {
t.Errorf("duplicate witness signature accepted as valid")
}
}
@@ -292,81 +299,86 @@ func TestRotate(t *testing.T) {
{
description: "tosign tree head repated, but got one new witnes signature",
before: &StateManagerSingle{
- cosigned: types.SignedTreeHead{
- TreeHead: *th0,
- SigIdent: []*types.SigIdent{log, wit1},
+ cosigned: types.CosignedTreeHead{
+ SignedTreeHead: types.SignedTreeHead{
+ TreeHead: *th0,
+ Signature: log.Signature,
+ },
+ SigIdent: []*types.SigIdent{wit1},
},
tosign: types.SignedTreeHead{
- TreeHead: *th0,
- SigIdent: []*types.SigIdent{log},
+ TreeHead: *th0,
+ Signature: log.Signature,
},
cosignature: map[[types.HashSize]byte]*types.SigIdent{
- *log.KeyHash: log,
*wit2.KeyHash: wit2, // the new witness signature
},
},
next: &types.SignedTreeHead{
- TreeHead: *th1,
- SigIdent: []*types.SigIdent{log},
+ TreeHead: *th1,
+ Signature: log.Signature,
},
after: &StateManagerSingle{
- cosigned: types.SignedTreeHead{
- TreeHead: *th0,
- SigIdent: []*types.SigIdent{log, wit1, wit2},
+ cosigned: types.CosignedTreeHead{
+ SignedTreeHead: types.SignedTreeHead{
+ TreeHead: *th0,
+ Signature: log.Signature,
+ },
+ SigIdent: []*types.SigIdent{wit1, wit2},
},
tosign: types.SignedTreeHead{
- TreeHead: *th1,
- SigIdent: []*types.SigIdent{log},
- },
- cosignature: map[[types.HashSize]byte]*types.SigIdent{
- *log.KeyHash: log, // after rotate we always have log sig
+ TreeHead: *th1,
+ Signature: log.Signature,
},
+ cosignature: map[[types.HashSize]byte]*types.SigIdent{},
},
},
{
description: "tosign tree head did not repeat, it got one witness signature",
before: &StateManagerSingle{
- cosigned: types.SignedTreeHead{
- TreeHead: *th0,
- SigIdent: []*types.SigIdent{log, wit1},
+ cosigned: types.CosignedTreeHead{
+ SignedTreeHead: types.SignedTreeHead{
+ TreeHead: *th0,
+ Signature: log.Signature,
+ },
+ SigIdent: []*types.SigIdent{wit1},
},
tosign: types.SignedTreeHead{
- TreeHead: *th1,
- SigIdent: []*types.SigIdent{log},
+ TreeHead: *th1,
+ Signature: log.Signature,
},
cosignature: map[[types.HashSize]byte]*types.SigIdent{
- *log.KeyHash: log,
- *wit2.KeyHash: wit2, // the only witness that signed tosign
+ *log.KeyHash: wit2,
},
},
next: &types.SignedTreeHead{
- TreeHead: *th2,
- SigIdent: []*types.SigIdent{log},
+ TreeHead: *th2,
+ Signature: log.Signature,
},
after: &StateManagerSingle{
- cosigned: types.SignedTreeHead{
- TreeHead: *th1,
- SigIdent: []*types.SigIdent{log, wit2},
+ cosigned: types.CosignedTreeHead{
+ SignedTreeHead: types.SignedTreeHead{
+ TreeHead: *th1,
+ Signature: log.Signature,
+ },
+ SigIdent: []*types.SigIdent{wit2},
},
tosign: types.SignedTreeHead{
- TreeHead: *th2,
- SigIdent: []*types.SigIdent{log},
- },
- cosignature: map[[types.HashSize]byte]*types.SigIdent{
- *log.KeyHash: log, // after rotate we always have log sig
+ TreeHead: *th2,
+ Signature: log.Signature,
},
+ cosignature: map[[types.HashSize]byte]*types.SigIdent{},
},
},
} {
table.before.rotate(table.next)
- if got, want := table.before.cosigned.TreeHead, table.after.cosigned.TreeHead; !reflect.DeepEqual(got, want) {
+ if got, want := table.before.cosigned.SignedTreeHead, table.after.cosigned.SignedTreeHead; !reflect.DeepEqual(got, want) {
t.Errorf("got cosigned tree head\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
}
checkWitnessList(t, table.description, table.before.cosigned.SigIdent, table.after.cosigned.SigIdent)
- if got, want := table.before.tosign.TreeHead, table.after.tosign.TreeHead; !reflect.DeepEqual(got, want) {
+ if got, want := table.before.tosign, table.after.tosign; !reflect.DeepEqual(got, want) {
t.Errorf("got tosign tree head\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
}
- checkWitnessList(t, table.description, table.before.tosign.SigIdent, table.after.tosign.SigIdent)
if got, want := table.before.cosignature, table.after.cosignature; !reflect.DeepEqual(got, want) {
t.Errorf("got cosignature map\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
}