aboutsummaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
authorLinus Nordberg <linus@nordberg.se>2022-05-24 23:33:38 +0200
committerRasmus Dahlberg <rasmus@mullvad.net>2022-06-23 11:33:17 +0200
commit559bccccd40d028e412d9f11709ded0250ba6dcd (patch)
tree50f3193dbe70fec21357963c11e5f663013f4b4c /pkg
parent4b20ef0c1732bcef633c0ed7104501898aa84e2c (diff)
implement primary and secondary role, for replicationv0.5.0
Diffstat (limited to 'pkg')
-rw-r--r--pkg/db/client.go17
-rw-r--r--pkg/db/mocks/client.go111
-rw-r--r--pkg/db/mocks/trillian.go317
-rw-r--r--pkg/db/trillian.go194
-rw-r--r--pkg/db/trillian_test.go540
-rw-r--r--pkg/instance/experimental.go85
-rw-r--r--pkg/instance/handler.go184
-rw-r--r--pkg/instance/handler_test.go688
-rw-r--r--pkg/instance/instance.go135
-rw-r--r--pkg/instance/instance_test.go23
-rw-r--r--pkg/instance/metric.go19
-rw-r--r--pkg/state/mocks/signer.go23
-rw-r--r--pkg/state/mocks/state_manager.go92
-rw-r--r--pkg/state/single.go165
-rw-r--r--pkg/state/single_test.go217
-rw-r--r--pkg/state/state_manager.go29
16 files changed, 0 insertions, 2839 deletions
diff --git a/pkg/db/client.go b/pkg/db/client.go
deleted file mode 100644
index 09b8bfb..0000000
--- a/pkg/db/client.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package db
-
-import (
- "context"
-
- "git.sigsum.org/sigsum-go/pkg/requests"
- "git.sigsum.org/sigsum-go/pkg/types"
-)
-
-// Client is an interface that interacts with a log's database backend
-type Client interface {
- AddLeaf(context.Context, *requests.Leaf) error
- GetTreeHead(context.Context) (*types.TreeHead, error)
- GetConsistencyProof(context.Context, *requests.ConsistencyProof) (*types.ConsistencyProof, error)
- GetInclusionProof(context.Context, *requests.InclusionProof) (*types.InclusionProof, error)
- GetLeaves(context.Context, *requests.Leaves) (*types.Leaves, error)
-}
diff --git a/pkg/db/mocks/client.go b/pkg/db/mocks/client.go
deleted file mode 100644
index 0313bfb..0000000
--- a/pkg/db/mocks/client.go
+++ /dev/null
@@ -1,111 +0,0 @@
-// Code generated by MockGen. DO NOT EDIT.
-// Source: git.sigsum.org/sigsum-log-go/pkg/db (interfaces: Client)
-
-// Package mocks is a generated GoMock package.
-package mocks
-
-import (
- context "context"
- reflect "reflect"
-
- requests "git.sigsum.org/sigsum-go/pkg/requests"
- types "git.sigsum.org/sigsum-go/pkg/types"
- gomock "github.com/golang/mock/gomock"
-)
-
-// MockClient is a mock of Client interface.
-type MockClient struct {
- ctrl *gomock.Controller
- recorder *MockClientMockRecorder
-}
-
-// MockClientMockRecorder is the mock recorder for MockClient.
-type MockClientMockRecorder struct {
- mock *MockClient
-}
-
-// NewMockClient creates a new mock instance.
-func NewMockClient(ctrl *gomock.Controller) *MockClient {
- mock := &MockClient{ctrl: ctrl}
- mock.recorder = &MockClientMockRecorder{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockClient) EXPECT() *MockClientMockRecorder {
- return m.recorder
-}
-
-// AddLeaf mocks base method.
-func (m *MockClient) AddLeaf(arg0 context.Context, arg1 *requests.Leaf) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "AddLeaf", arg0, arg1)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// AddLeaf indicates an expected call of AddLeaf.
-func (mr *MockClientMockRecorder) AddLeaf(arg0, arg1 interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddLeaf", reflect.TypeOf((*MockClient)(nil).AddLeaf), arg0, arg1)
-}
-
-// GetConsistencyProof mocks base method.
-func (m *MockClient) GetConsistencyProof(arg0 context.Context, arg1 *requests.ConsistencyProof) (*types.ConsistencyProof, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetConsistencyProof", arg0, arg1)
- ret0, _ := ret[0].(*types.ConsistencyProof)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// GetConsistencyProof indicates an expected call of GetConsistencyProof.
-func (mr *MockClientMockRecorder) GetConsistencyProof(arg0, arg1 interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetConsistencyProof", reflect.TypeOf((*MockClient)(nil).GetConsistencyProof), arg0, arg1)
-}
-
-// GetInclusionProof mocks base method.
-func (m *MockClient) GetInclusionProof(arg0 context.Context, arg1 *requests.InclusionProof) (*types.InclusionProof, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetInclusionProof", arg0, arg1)
- ret0, _ := ret[0].(*types.InclusionProof)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// GetInclusionProof indicates an expected call of GetInclusionProof.
-func (mr *MockClientMockRecorder) GetInclusionProof(arg0, arg1 interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInclusionProof", reflect.TypeOf((*MockClient)(nil).GetInclusionProof), arg0, arg1)
-}
-
-// GetLeaves mocks base method.
-func (m *MockClient) GetLeaves(arg0 context.Context, arg1 *requests.Leaves) (*types.Leaves, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetLeaves", arg0, arg1)
- ret0, _ := ret[0].(*types.Leaves)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// GetLeaves indicates an expected call of GetLeaves.
-func (mr *MockClientMockRecorder) GetLeaves(arg0, arg1 interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLeaves", reflect.TypeOf((*MockClient)(nil).GetLeaves), arg0, arg1)
-}
-
-// GetTreeHead mocks base method.
-func (m *MockClient) GetTreeHead(arg0 context.Context) (*types.TreeHead, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetTreeHead", arg0)
- ret0, _ := ret[0].(*types.TreeHead)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// GetTreeHead indicates an expected call of GetTreeHead.
-func (mr *MockClientMockRecorder) GetTreeHead(arg0 interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTreeHead", reflect.TypeOf((*MockClient)(nil).GetTreeHead), arg0)
-}
diff --git a/pkg/db/mocks/trillian.go b/pkg/db/mocks/trillian.go
deleted file mode 100644
index 8aa3a58..0000000
--- a/pkg/db/mocks/trillian.go
+++ /dev/null
@@ -1,317 +0,0 @@
-// Code generated by MockGen. DO NOT EDIT.
-// Source: github.com/google/trillian (interfaces: TrillianLogClient)
-
-// Package mocks is a generated GoMock package.
-package mocks
-
-import (
- context "context"
- reflect "reflect"
-
- gomock "github.com/golang/mock/gomock"
- trillian "github.com/google/trillian"
- grpc "google.golang.org/grpc"
-)
-
-// MockTrillianLogClient is a mock of TrillianLogClient interface.
-type MockTrillianLogClient struct {
- ctrl *gomock.Controller
- recorder *MockTrillianLogClientMockRecorder
-}
-
-// MockTrillianLogClientMockRecorder is the mock recorder for MockTrillianLogClient.
-type MockTrillianLogClientMockRecorder struct {
- mock *MockTrillianLogClient
-}
-
-// NewMockTrillianLogClient creates a new mock instance.
-func NewMockTrillianLogClient(ctrl *gomock.Controller) *MockTrillianLogClient {
- mock := &MockTrillianLogClient{ctrl: ctrl}
- mock.recorder = &MockTrillianLogClientMockRecorder{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockTrillianLogClient) EXPECT() *MockTrillianLogClientMockRecorder {
- return m.recorder
-}
-
-// AddSequencedLeaf mocks base method.
-func (m *MockTrillianLogClient) AddSequencedLeaf(arg0 context.Context, arg1 *trillian.AddSequencedLeafRequest, arg2 ...grpc.CallOption) (*trillian.AddSequencedLeafResponse, error) {
- m.ctrl.T.Helper()
- varargs := []interface{}{arg0, arg1}
- for _, a := range arg2 {
- varargs = append(varargs, a)
- }
- ret := m.ctrl.Call(m, "AddSequencedLeaf", varargs...)
- ret0, _ := ret[0].(*trillian.AddSequencedLeafResponse)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// AddSequencedLeaf indicates an expected call of AddSequencedLeaf.
-func (mr *MockTrillianLogClientMockRecorder) AddSequencedLeaf(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- varargs := append([]interface{}{arg0, arg1}, arg2...)
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddSequencedLeaf", reflect.TypeOf((*MockTrillianLogClient)(nil).AddSequencedLeaf), varargs...)
-}
-
-// AddSequencedLeaves mocks base method.
-func (m *MockTrillianLogClient) AddSequencedLeaves(arg0 context.Context, arg1 *trillian.AddSequencedLeavesRequest, arg2 ...grpc.CallOption) (*trillian.AddSequencedLeavesResponse, error) {
- m.ctrl.T.Helper()
- varargs := []interface{}{arg0, arg1}
- for _, a := range arg2 {
- varargs = append(varargs, a)
- }
- ret := m.ctrl.Call(m, "AddSequencedLeaves", varargs...)
- ret0, _ := ret[0].(*trillian.AddSequencedLeavesResponse)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// AddSequencedLeaves indicates an expected call of AddSequencedLeaves.
-func (mr *MockTrillianLogClientMockRecorder) AddSequencedLeaves(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- varargs := append([]interface{}{arg0, arg1}, arg2...)
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddSequencedLeaves", reflect.TypeOf((*MockTrillianLogClient)(nil).AddSequencedLeaves), varargs...)
-}
-
-// GetConsistencyProof mocks base method.
-func (m *MockTrillianLogClient) GetConsistencyProof(arg0 context.Context, arg1 *trillian.GetConsistencyProofRequest, arg2 ...grpc.CallOption) (*trillian.GetConsistencyProofResponse, error) {
- m.ctrl.T.Helper()
- varargs := []interface{}{arg0, arg1}
- for _, a := range arg2 {
- varargs = append(varargs, a)
- }
- ret := m.ctrl.Call(m, "GetConsistencyProof", varargs...)
- ret0, _ := ret[0].(*trillian.GetConsistencyProofResponse)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// GetConsistencyProof indicates an expected call of GetConsistencyProof.
-func (mr *MockTrillianLogClientMockRecorder) GetConsistencyProof(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- varargs := append([]interface{}{arg0, arg1}, arg2...)
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetConsistencyProof", reflect.TypeOf((*MockTrillianLogClient)(nil).GetConsistencyProof), varargs...)
-}
-
-// GetEntryAndProof mocks base method.
-func (m *MockTrillianLogClient) GetEntryAndProof(arg0 context.Context, arg1 *trillian.GetEntryAndProofRequest, arg2 ...grpc.CallOption) (*trillian.GetEntryAndProofResponse, error) {
- m.ctrl.T.Helper()
- varargs := []interface{}{arg0, arg1}
- for _, a := range arg2 {
- varargs = append(varargs, a)
- }
- ret := m.ctrl.Call(m, "GetEntryAndProof", varargs...)
- ret0, _ := ret[0].(*trillian.GetEntryAndProofResponse)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// GetEntryAndProof indicates an expected call of GetEntryAndProof.
-func (mr *MockTrillianLogClientMockRecorder) GetEntryAndProof(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- varargs := append([]interface{}{arg0, arg1}, arg2...)
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEntryAndProof", reflect.TypeOf((*MockTrillianLogClient)(nil).GetEntryAndProof), varargs...)
-}
-
-// GetInclusionProof mocks base method.
-func (m *MockTrillianLogClient) GetInclusionProof(arg0 context.Context, arg1 *trillian.GetInclusionProofRequest, arg2 ...grpc.CallOption) (*trillian.GetInclusionProofResponse, error) {
- m.ctrl.T.Helper()
- varargs := []interface{}{arg0, arg1}
- for _, a := range arg2 {
- varargs = append(varargs, a)
- }
- ret := m.ctrl.Call(m, "GetInclusionProof", varargs...)
- ret0, _ := ret[0].(*trillian.GetInclusionProofResponse)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// GetInclusionProof indicates an expected call of GetInclusionProof.
-func (mr *MockTrillianLogClientMockRecorder) GetInclusionProof(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- varargs := append([]interface{}{arg0, arg1}, arg2...)
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInclusionProof", reflect.TypeOf((*MockTrillianLogClient)(nil).GetInclusionProof), varargs...)
-}
-
-// GetInclusionProofByHash mocks base method.
-func (m *MockTrillianLogClient) GetInclusionProofByHash(arg0 context.Context, arg1 *trillian.GetInclusionProofByHashRequest, arg2 ...grpc.CallOption) (*trillian.GetInclusionProofByHashResponse, error) {
- m.ctrl.T.Helper()
- varargs := []interface{}{arg0, arg1}
- for _, a := range arg2 {
- varargs = append(varargs, a)
- }
- ret := m.ctrl.Call(m, "GetInclusionProofByHash", varargs...)
- ret0, _ := ret[0].(*trillian.GetInclusionProofByHashResponse)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// GetInclusionProofByHash indicates an expected call of GetInclusionProofByHash.
-func (mr *MockTrillianLogClientMockRecorder) GetInclusionProofByHash(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- varargs := append([]interface{}{arg0, arg1}, arg2...)
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInclusionProofByHash", reflect.TypeOf((*MockTrillianLogClient)(nil).GetInclusionProofByHash), varargs...)
-}
-
-// GetLatestSignedLogRoot mocks base method.
-func (m *MockTrillianLogClient) GetLatestSignedLogRoot(arg0 context.Context, arg1 *trillian.GetLatestSignedLogRootRequest, arg2 ...grpc.CallOption) (*trillian.GetLatestSignedLogRootResponse, error) {
- m.ctrl.T.Helper()
- varargs := []interface{}{arg0, arg1}
- for _, a := range arg2 {
- varargs = append(varargs, a)
- }
- ret := m.ctrl.Call(m, "GetLatestSignedLogRoot", varargs...)
- ret0, _ := ret[0].(*trillian.GetLatestSignedLogRootResponse)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// GetLatestSignedLogRoot indicates an expected call of GetLatestSignedLogRoot.
-func (mr *MockTrillianLogClientMockRecorder) GetLatestSignedLogRoot(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- varargs := append([]interface{}{arg0, arg1}, arg2...)
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLatestSignedLogRoot", reflect.TypeOf((*MockTrillianLogClient)(nil).GetLatestSignedLogRoot), varargs...)
-}
-
-// GetLeavesByHash mocks base method.
-func (m *MockTrillianLogClient) GetLeavesByHash(arg0 context.Context, arg1 *trillian.GetLeavesByHashRequest, arg2 ...grpc.CallOption) (*trillian.GetLeavesByHashResponse, error) {
- m.ctrl.T.Helper()
- varargs := []interface{}{arg0, arg1}
- for _, a := range arg2 {
- varargs = append(varargs, a)
- }
- ret := m.ctrl.Call(m, "GetLeavesByHash", varargs...)
- ret0, _ := ret[0].(*trillian.GetLeavesByHashResponse)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// GetLeavesByHash indicates an expected call of GetLeavesByHash.
-func (mr *MockTrillianLogClientMockRecorder) GetLeavesByHash(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- varargs := append([]interface{}{arg0, arg1}, arg2...)
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLeavesByHash", reflect.TypeOf((*MockTrillianLogClient)(nil).GetLeavesByHash), varargs...)
-}
-
-// GetLeavesByIndex mocks base method.
-func (m *MockTrillianLogClient) GetLeavesByIndex(arg0 context.Context, arg1 *trillian.GetLeavesByIndexRequest, arg2 ...grpc.CallOption) (*trillian.GetLeavesByIndexResponse, error) {
- m.ctrl.T.Helper()
- varargs := []interface{}{arg0, arg1}
- for _, a := range arg2 {
- varargs = append(varargs, a)
- }
- ret := m.ctrl.Call(m, "GetLeavesByIndex", varargs...)
- ret0, _ := ret[0].(*trillian.GetLeavesByIndexResponse)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// GetLeavesByIndex indicates an expected call of GetLeavesByIndex.
-func (mr *MockTrillianLogClientMockRecorder) GetLeavesByIndex(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- varargs := append([]interface{}{arg0, arg1}, arg2...)
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLeavesByIndex", reflect.TypeOf((*MockTrillianLogClient)(nil).GetLeavesByIndex), varargs...)
-}
-
-// GetLeavesByRange mocks base method.
-func (m *MockTrillianLogClient) GetLeavesByRange(arg0 context.Context, arg1 *trillian.GetLeavesByRangeRequest, arg2 ...grpc.CallOption) (*trillian.GetLeavesByRangeResponse, error) {
- m.ctrl.T.Helper()
- varargs := []interface{}{arg0, arg1}
- for _, a := range arg2 {
- varargs = append(varargs, a)
- }
- ret := m.ctrl.Call(m, "GetLeavesByRange", varargs...)
- ret0, _ := ret[0].(*trillian.GetLeavesByRangeResponse)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// GetLeavesByRange indicates an expected call of GetLeavesByRange.
-func (mr *MockTrillianLogClientMockRecorder) GetLeavesByRange(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- varargs := append([]interface{}{arg0, arg1}, arg2...)
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLeavesByRange", reflect.TypeOf((*MockTrillianLogClient)(nil).GetLeavesByRange), varargs...)
-}
-
-// GetSequencedLeafCount mocks base method.
-func (m *MockTrillianLogClient) GetSequencedLeafCount(arg0 context.Context, arg1 *trillian.GetSequencedLeafCountRequest, arg2 ...grpc.CallOption) (*trillian.GetSequencedLeafCountResponse, error) {
- m.ctrl.T.Helper()
- varargs := []interface{}{arg0, arg1}
- for _, a := range arg2 {
- varargs = append(varargs, a)
- }
- ret := m.ctrl.Call(m, "GetSequencedLeafCount", varargs...)
- ret0, _ := ret[0].(*trillian.GetSequencedLeafCountResponse)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// GetSequencedLeafCount indicates an expected call of GetSequencedLeafCount.
-func (mr *MockTrillianLogClientMockRecorder) GetSequencedLeafCount(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- varargs := append([]interface{}{arg0, arg1}, arg2...)
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSequencedLeafCount", reflect.TypeOf((*MockTrillianLogClient)(nil).GetSequencedLeafCount), varargs...)
-}
-
-// InitLog mocks base method.
-func (m *MockTrillianLogClient) InitLog(arg0 context.Context, arg1 *trillian.InitLogRequest, arg2 ...grpc.CallOption) (*trillian.InitLogResponse, error) {
- m.ctrl.T.Helper()
- varargs := []interface{}{arg0, arg1}
- for _, a := range arg2 {
- varargs = append(varargs, a)
- }
- ret := m.ctrl.Call(m, "InitLog", varargs...)
- ret0, _ := ret[0].(*trillian.InitLogResponse)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// InitLog indicates an expected call of InitLog.
-func (mr *MockTrillianLogClientMockRecorder) InitLog(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- varargs := append([]interface{}{arg0, arg1}, arg2...)
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InitLog", reflect.TypeOf((*MockTrillianLogClient)(nil).InitLog), varargs...)
-}
-
-// QueueLeaf mocks base method.
-func (m *MockTrillianLogClient) QueueLeaf(arg0 context.Context, arg1 *trillian.QueueLeafRequest, arg2 ...grpc.CallOption) (*trillian.QueueLeafResponse, error) {
- m.ctrl.T.Helper()
- varargs := []interface{}{arg0, arg1}
- for _, a := range arg2 {
- varargs = append(varargs, a)
- }
- ret := m.ctrl.Call(m, "QueueLeaf", varargs...)
- ret0, _ := ret[0].(*trillian.QueueLeafResponse)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// QueueLeaf indicates an expected call of QueueLeaf.
-func (mr *MockTrillianLogClientMockRecorder) QueueLeaf(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- varargs := append([]interface{}{arg0, arg1}, arg2...)
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueueLeaf", reflect.TypeOf((*MockTrillianLogClient)(nil).QueueLeaf), varargs...)
-}
-
-// QueueLeaves mocks base method.
-func (m *MockTrillianLogClient) QueueLeaves(arg0 context.Context, arg1 *trillian.QueueLeavesRequest, arg2 ...grpc.CallOption) (*trillian.QueueLeavesResponse, error) {
- m.ctrl.T.Helper()
- varargs := []interface{}{arg0, arg1}
- for _, a := range arg2 {
- varargs = append(varargs, a)
- }
- ret := m.ctrl.Call(m, "QueueLeaves", varargs...)
- ret0, _ := ret[0].(*trillian.QueueLeavesResponse)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// QueueLeaves indicates an expected call of QueueLeaves.
-func (mr *MockTrillianLogClientMockRecorder) QueueLeaves(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- varargs := append([]interface{}{arg0, arg1}, arg2...)
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueueLeaves", reflect.TypeOf((*MockTrillianLogClient)(nil).QueueLeaves), varargs...)
-}
diff --git a/pkg/db/trillian.go b/pkg/db/trillian.go
deleted file mode 100644
index 3147c8d..0000000
--- a/pkg/db/trillian.go
+++ /dev/null
@@ -1,194 +0,0 @@
-package db
-
-import (
- "context"
- "fmt"
- "time"
-
- "git.sigsum.org/sigsum-go/pkg/log"
- "git.sigsum.org/sigsum-go/pkg/requests"
- "git.sigsum.org/sigsum-go/pkg/types"
- "github.com/google/trillian"
- trillianTypes "github.com/google/trillian/types"
- "google.golang.org/grpc/codes"
-)
-
-// TrillianClient implements the Client interface for Trillian's gRPC backend
-type TrillianClient struct {
- // TreeID is a Merkle tree identifier that Trillian uses
- TreeID int64
-
- // GRPC is a Trillian gRPC client
- GRPC trillian.TrillianLogClient
-}
-
-func (c *TrillianClient) AddLeaf(ctx context.Context, req *requests.Leaf) error {
- leaf := types.Leaf{
- Statement: types.Statement{
- ShardHint: req.ShardHint,
- Checksum: *types.HashFn(req.Message[:]),
- },
- Signature: req.Signature,
- KeyHash: *types.HashFn(req.PublicKey[:]),
- }
- serialized := leaf.ToBinary()
-
- log.Debug("queueing leaf request: %x", types.LeafHash(serialized))
- rsp, err := c.GRPC.QueueLeaf(ctx, &trillian.QueueLeafRequest{
- LogId: c.TreeID,
- Leaf: &trillian.LogLeaf{
- LeafValue: serialized,
- },
- })
- if err != nil {
- return fmt.Errorf("backend failure: %v", err)
- }
- if rsp == nil {
- return fmt.Errorf("no response")
- }
- if rsp.QueuedLeaf == nil {
- return fmt.Errorf("no queued leaf")
- }
- if codes.Code(rsp.QueuedLeaf.GetStatus().GetCode()) == codes.AlreadyExists {
- return fmt.Errorf("leaf is already queued or included")
- }
- return nil
-}
-
-func (c *TrillianClient) GetTreeHead(ctx context.Context) (*types.TreeHead, error) {
- rsp, err := c.GRPC.GetLatestSignedLogRoot(ctx, &trillian.GetLatestSignedLogRootRequest{
- LogId: c.TreeID,
- })
- if err != nil {
- return nil, fmt.Errorf("backend failure: %v", err)
- }
- if rsp == nil {
- return nil, fmt.Errorf("no response")
- }
- if rsp.SignedLogRoot == nil {
- return nil, fmt.Errorf("no signed log root")
- }
- if rsp.SignedLogRoot.LogRoot == nil {
- return nil, fmt.Errorf("no log root")
- }
- var r trillianTypes.LogRootV1
- if err := r.UnmarshalBinary(rsp.SignedLogRoot.LogRoot); err != nil {
- return nil, fmt.Errorf("no log root: unmarshal failed: %v", err)
- }
- if len(r.RootHash) != types.HashSize {
- return nil, fmt.Errorf("unexpected hash length: %d", len(r.RootHash))
- }
- return treeHeadFromLogRoot(&r), nil
-}
-
-func (c *TrillianClient) GetConsistencyProof(ctx context.Context, req *requests.ConsistencyProof) (*types.ConsistencyProof, error) {
- rsp, err := c.GRPC.GetConsistencyProof(ctx, &trillian.GetConsistencyProofRequest{
- LogId: c.TreeID,
- FirstTreeSize: int64(req.OldSize),
- SecondTreeSize: int64(req.NewSize),
- })
- if err != nil {
- return nil, fmt.Errorf("backend failure: %v", err)
- }
- if rsp == nil {
- return nil, fmt.Errorf("no response")
- }
- if rsp.Proof == nil {
- return nil, fmt.Errorf("no consistency proof")
- }
- if len(rsp.Proof.Hashes) == 0 {
- return nil, fmt.Errorf("not a consistency proof: empty")
- }
- path, err := nodePathFromHashes(rsp.Proof.Hashes)
- if err != nil {
- return nil, fmt.Errorf("not a consistency proof: %v", err)
- }
- return &types.ConsistencyProof{
- OldSize: req.OldSize,
- NewSize: req.NewSize,
- Path: path,
- }, nil
-}
-
-func (c *TrillianClient) GetInclusionProof(ctx context.Context, req *requests.InclusionProof) (*types.InclusionProof, error) {
- rsp, err := c.GRPC.GetInclusionProofByHash(ctx, &trillian.GetInclusionProofByHashRequest{
- LogId: c.TreeID,
- LeafHash: req.LeafHash[:],
- TreeSize: int64(req.TreeSize),
- OrderBySequence: true,
- })
- if err != nil {
- return nil, fmt.Errorf("backend failure: %v", err)
- }
- if rsp == nil {
- return nil, fmt.Errorf("no response")
- }
- if len(rsp.Proof) != 1 {
- return nil, fmt.Errorf("bad proof count: %d", len(rsp.Proof))
- }
- proof := rsp.Proof[0]
- if len(proof.Hashes) == 0 {
- return nil, fmt.Errorf("not an inclusion proof: empty")
- }
- path, err := nodePathFromHashes(proof.Hashes)
- if err != nil {
- return nil, fmt.Errorf("not an inclusion proof: %v", err)
- }
- return &types.InclusionProof{
- TreeSize: req.TreeSize,
- LeafIndex: uint64(proof.LeafIndex),
- Path: path,
- }, nil
-}
-
-func (c *TrillianClient) GetLeaves(ctx context.Context, req *requests.Leaves) (*types.Leaves, error) {
- rsp, err := c.GRPC.GetLeavesByRange(ctx, &trillian.GetLeavesByRangeRequest{
- LogId: c.TreeID,
- StartIndex: int64(req.StartSize),
- Count: int64(req.EndSize-req.StartSize) + 1,
- })
- if err != nil {
- return nil, fmt.Errorf("backend failure: %v", err)
- }
- if rsp == nil {
- return nil, fmt.Errorf("no response")
- }
- if got, want := len(rsp.Leaves), int(req.EndSize-req.StartSize+1); got != want {
- return nil, fmt.Errorf("unexpected number of leaves: %d", got)
- }
- var list types.Leaves = make([]types.Leaf, 0, len(rsp.Leaves))
- for i, leaf := range rsp.Leaves {
- leafIndex := int64(req.StartSize + uint64(i))
- if leafIndex != leaf.LeafIndex {
- return nil, fmt.Errorf("unexpected leaf(%d): got index %d", leafIndex, leaf.LeafIndex)
- }
-
- var l types.Leaf
- if err := l.FromBinary(leaf.LeafValue); err != nil {
- return nil, fmt.Errorf("unexpected leaf(%d): %v", leafIndex, err)
- }
- list = append(list[:], l)
- }
- return &list, nil
-}
-
-func treeHeadFromLogRoot(lr *trillianTypes.LogRootV1) *types.TreeHead {
- th := types.TreeHead{
- Timestamp: uint64(time.Now().Unix()),
- TreeSize: uint64(lr.TreeSize),
- }
- copy(th.RootHash[:], lr.RootHash)
- return &th
-}
-
-func nodePathFromHashes(hashes [][]byte) ([]types.Hash, error) {
- path := make([]types.Hash, len(hashes))
- for i := 0; i < len(hashes); i++ {
- if len(hashes[i]) != types.HashSize {
- return nil, fmt.Errorf("unexpected hash length: %v", len(hashes[i]))
- }
-
- copy(path[i][:], hashes[i])
- }
- return path, nil
-}
diff --git a/pkg/db/trillian_test.go b/pkg/db/trillian_test.go
deleted file mode 100644
index 2b19096..0000000
--- a/pkg/db/trillian_test.go
+++ /dev/null
@@ -1,540 +0,0 @@
-package db
-
-import (
- "bytes"
- "context"
- "fmt"
- "reflect"
- "testing"
- "time"
-
- "git.sigsum.org/log-go/pkg/db/mocks"
- "git.sigsum.org/sigsum-go/pkg/requests"
- "git.sigsum.org/sigsum-go/pkg/types"
- "github.com/golang/mock/gomock"
- "github.com/google/trillian"
- ttypes "github.com/google/trillian/types"
- "google.golang.org/grpc/codes"
- "google.golang.org/grpc/status"
-)
-
-func TestAddLeaf(t *testing.T) {
- req := &requests.Leaf{
- ShardHint: 0,
- Message: types.Hash{},
- Signature: types.Signature{},
- PublicKey: types.PublicKey{},
- DomainHint: "example.com",
- }
- for _, table := range []struct {
- description string
- req *requests.Leaf
- rsp *trillian.QueueLeafResponse
- err error
- wantErr bool
- }{
- {
- description: "invalid: backend failure",
- req: req,
- err: fmt.Errorf("something went wrong"),
- wantErr: true,
- },
- {
- description: "invalid: no response",
- req: req,
- wantErr: true,
- },
- {
- description: "invalid: no queued leaf",
- req: req,
- rsp: &trillian.QueueLeafResponse{},
- wantErr: true,
- },
- {
- description: "invalid: leaf is already queued or included",
- req: req,
- rsp: &trillian.QueueLeafResponse{
- QueuedLeaf: &trillian.QueuedLogLeaf{
- Leaf: &trillian.LogLeaf{
- LeafValue: []byte{0}, // does not matter for test
- },
- Status: status.New(codes.AlreadyExists, "duplicate").Proto(),
- },
- },
- wantErr: true,
- },
- {
- description: "valid",
- req: req,
- rsp: &trillian.QueueLeafResponse{
- QueuedLeaf: &trillian.QueuedLogLeaf{
- Leaf: &trillian.LogLeaf{
- LeafValue: []byte{0}, // does not matter for test
- },
- Status: status.New(codes.OK, "ok").Proto(),
- },
- },
- },
- } {
- // Run deferred functions at the end of each iteration
- func() {
- ctrl := gomock.NewController(t)
- defer ctrl.Finish()
- grpc := mocks.NewMockTrillianLogClient(ctrl)
- grpc.EXPECT().QueueLeaf(gomock.Any(), gomock.Any()).Return(table.rsp, table.err)
- client := TrillianClient{GRPC: grpc}
-
- err := client.AddLeaf(context.Background(), table.req)
- 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)
- }
- }()
- }
-}
-
-func TestGetTreeHead(t *testing.T) {
- // valid root
- root := &ttypes.LogRootV1{
- TreeSize: 0,
- RootHash: make([]byte, types.HashSize),
- TimestampNanos: 1622585623133599429,
- }
- buf, err := root.MarshalBinary()
- if err != nil {
- t.Fatalf("must marshal log root: %v", err)
- }
- // invalid root
- root.RootHash = make([]byte, types.HashSize+1)
- bufBadHash, err := root.MarshalBinary()
- if err != nil {
- t.Fatalf("must marshal log root: %v", err)
- }
-
- for _, table := range []struct {
- description string
- rsp *trillian.GetLatestSignedLogRootResponse
- err error
- wantErr bool
- wantTh *types.TreeHead
- }{
- {
- description: "invalid: backend failure",
- err: fmt.Errorf("something went wrong"),
- wantErr: true,
- },
- {
- description: "invalid: no response",
- wantErr: true,
- },
- {
- description: "invalid: no signed log root",
- rsp: &trillian.GetLatestSignedLogRootResponse{},
- wantErr: true,
- },
- {
- description: "invalid: no log root",
- rsp: &trillian.GetLatestSignedLogRootResponse{
- SignedLogRoot: &trillian.SignedLogRoot{},
- },
- wantErr: true,
- },
- {
- description: "invalid: no log root: unmarshal failed",
- rsp: &trillian.GetLatestSignedLogRootResponse{
- SignedLogRoot: &trillian.SignedLogRoot{
- LogRoot: buf[1:],
- },
- },
- wantErr: true,
- },
- {
- description: "invalid: unexpected hash length",
- rsp: &trillian.GetLatestSignedLogRootResponse{
- SignedLogRoot: &trillian.SignedLogRoot{
- LogRoot: bufBadHash,
- },
- },
- wantErr: true,
- },
- {
- description: "valid",
- rsp: &trillian.GetLatestSignedLogRootResponse{
- SignedLogRoot: &trillian.SignedLogRoot{
- LogRoot: buf,
- },
- },
- wantTh: &types.TreeHead{
- Timestamp: 1622585623,
- TreeSize: 0,
- RootHash: types.Hash{},
- },
- },
- } {
- // Run deferred functions at the end of each iteration
- func() {
- ctrl := gomock.NewController(t)
- defer ctrl.Finish()
- grpc := mocks.NewMockTrillianLogClient(ctrl)
- grpc.EXPECT().GetLatestSignedLogRoot(gomock.Any(), gomock.Any()).Return(table.rsp, table.err)
- client := TrillianClient{GRPC: grpc}
-
- th, err := client.GetTreeHead(context.Background())
- 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)
- }
- if err != nil {
- return
- }
-
- // we would need a clock that can be mocked to make a nicer test
- now := uint64(time.Now().Unix())
- if got, wantLow, wantHigh := th.Timestamp, now-5, now+5; got < wantLow || got > wantHigh {
- t.Errorf("got tree head with timestamp %d but wanted between [%d, %d] in test %q",
- got, wantLow, wantHigh, table.description)
- }
- if got, want := th.TreeSize, table.wantTh.TreeSize; got != want {
- t.Errorf("got tree head with tree size %d but wanted %d in test %q", got, want, table.description)
- }
- if got, want := th.RootHash[:], table.wantTh.RootHash[:]; !bytes.Equal(got, want) {
- t.Errorf("got root hash %x but wanted %x in test %q", got, want, table.description)
- }
- }()
- }
-}
-
-func TestGetConsistencyProof(t *testing.T) {
- req := &requests.ConsistencyProof{
- OldSize: 1,
- NewSize: 3,
- }
- for _, table := range []struct {
- description string
- req *requests.ConsistencyProof
- rsp *trillian.GetConsistencyProofResponse
- err error
- wantErr bool
- wantProof *types.ConsistencyProof
- }{
- {
- description: "invalid: backend failure",
- req: req,
- err: fmt.Errorf("something went wrong"),
- wantErr: true,
- },
- {
- description: "invalid: no response",
- req: req,
- wantErr: true,
- },
- {
- description: "invalid: no consistency proof",
- req: req,
- rsp: &trillian.GetConsistencyProofResponse{},
- wantErr: true,
- },
- {
- description: "invalid: not a consistency proof (1/2)",
- req: req,
- rsp: &trillian.GetConsistencyProofResponse{
- Proof: &trillian.Proof{
- Hashes: [][]byte{},
- },
- },
- wantErr: true,
- },
- {
- description: "invalid: not a consistency proof (2/2)",
- req: req,
- rsp: &trillian.GetConsistencyProofResponse{
- Proof: &trillian.Proof{
- Hashes: [][]byte{
- make([]byte, types.HashSize),
- make([]byte, types.HashSize+1),
- },
- },
- },
- wantErr: true,
- },
- {
- description: "valid",
- req: req,
- rsp: &trillian.GetConsistencyProofResponse{
- Proof: &trillian.Proof{
- Hashes: [][]byte{
- make([]byte, types.HashSize),
- make([]byte, types.HashSize),
- },
- },
- },
- wantProof: &types.ConsistencyProof{
- OldSize: 1,
- NewSize: 3,
- Path: []types.Hash{
- types.Hash{},
- types.Hash{},
- },
- },
- },
- } {
- // Run deferred functions at the end of each iteration
- func() {
- ctrl := gomock.NewController(t)
- defer ctrl.Finish()
- grpc := mocks.NewMockTrillianLogClient(ctrl)
- grpc.EXPECT().GetConsistencyProof(gomock.Any(), gomock.Any()).Return(table.rsp, table.err)
- client := TrillianClient{GRPC: grpc}
-
- proof, err := client.GetConsistencyProof(context.Background(), table.req)
- 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)
- }
- if err != nil {
- return
- }
- if got, want := proof, table.wantProof; !reflect.DeepEqual(got, want) {
- t.Errorf("got proof\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
- }
- }()
- }
-}
-
-func TestGetInclusionProof(t *testing.T) {
- req := &requests.InclusionProof{
- TreeSize: 4,
- LeafHash: types.Hash{},
- }
- for _, table := range []struct {
- description string
- req *requests.InclusionProof
- rsp *trillian.GetInclusionProofByHashResponse
- err error
- wantErr bool
- wantProof *types.InclusionProof
- }{
- {
- description: "invalid: backend failure",
- req: req,
- err: fmt.Errorf("something went wrong"),
- wantErr: true,
- },
- {
- description: "invalid: no response",
- req: req,
- wantErr: true,
- },
- {
- description: "invalid: bad proof count",
- req: req,
- rsp: &trillian.GetInclusionProofByHashResponse{
- Proof: []*trillian.Proof{
- &trillian.Proof{},
- &trillian.Proof{},
- },
- },
- wantErr: true,
- },
- {
- description: "invalid: not an inclusion proof (1/2)",
- req: req,
- rsp: &trillian.GetInclusionProofByHashResponse{
- Proof: []*trillian.Proof{
- &trillian.Proof{
- LeafIndex: 1,
- Hashes: [][]byte{},
- },
- },
- },
- wantErr: true,
- },
- {
- description: "invalid: not an inclusion proof (2/2)",
- req: req,
- rsp: &trillian.GetInclusionProofByHashResponse{
- Proof: []*trillian.Proof{
- &trillian.Proof{
- LeafIndex: 1,
- Hashes: [][]byte{
- make([]byte, types.HashSize),
- make([]byte, types.HashSize+1),
- },
- },
- },
- },
- wantErr: true,
- },
- {
- description: "valid",
- req: req,
- rsp: &trillian.GetInclusionProofByHashResponse{
- Proof: []*trillian.Proof{
- &trillian.Proof{
- LeafIndex: 1,
- Hashes: [][]byte{
- make([]byte, types.HashSize),
- make([]byte, types.HashSize),
- },
- },
- },
- },
- wantProof: &types.InclusionProof{
- TreeSize: 4,
- LeafIndex: 1,
- Path: []types.Hash{
- types.Hash{},
- types.Hash{},
- },
- },
- },
- } {
- // Run deferred functions at the end of each iteration
- func() {
- ctrl := gomock.NewController(t)
- defer ctrl.Finish()
- grpc := mocks.NewMockTrillianLogClient(ctrl)
- grpc.EXPECT().GetInclusionProofByHash(gomock.Any(), gomock.Any()).Return(table.rsp, table.err)
- client := TrillianClient{GRPC: grpc}
-
- proof, err := client.GetInclusionProof(context.Background(), table.req)
- 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)
- }
- if err != nil {
- return
- }
- if got, want := proof, table.wantProof; !reflect.DeepEqual(got, want) {
- t.Errorf("got proof\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
- }
- }()
- }
-}
-
-func TestGetLeaves(t *testing.T) {
- req := &requests.Leaves{
- StartSize: 1,
- EndSize: 2,
- }
- firstLeaf := &types.Leaf{
- Statement: types.Statement{
- ShardHint: 0,
- Checksum: types.Hash{},
- },
- Signature: types.Signature{},
- KeyHash: types.Hash{},
- }
- secondLeaf := &types.Leaf{
- Statement: types.Statement{
- ShardHint: 0,
- Checksum: types.Hash{},
- },
- Signature: types.Signature{},
- KeyHash: types.Hash{},
- }
-
- for _, table := range []struct {
- description string
- req *requests.Leaves
- rsp *trillian.GetLeavesByRangeResponse
- err error
- wantErr bool
- wantLeaves *types.Leaves
- }{
- {
- description: "invalid: backend failure",
- req: req,
- err: fmt.Errorf("something went wrong"),
- wantErr: true,
- },
- {
- description: "invalid: no response",
- req: req,
- wantErr: true,
- },
- {
- description: "invalid: unexpected number of leaves",
- req: req,
- rsp: &trillian.GetLeavesByRangeResponse{
- Leaves: []*trillian.LogLeaf{
- &trillian.LogLeaf{
- LeafValue: firstLeaf.ToBinary(),
- LeafIndex: 1,
- },
- },
- },
- wantErr: true,
- },
- {
- description: "invalid: unexpected leaf (1/2)",
- req: req,
- rsp: &trillian.GetLeavesByRangeResponse{
- Leaves: []*trillian.LogLeaf{
- &trillian.LogLeaf{
- LeafValue: firstLeaf.ToBinary(),
- LeafIndex: 1,
- },
- &trillian.LogLeaf{
- LeafValue: secondLeaf.ToBinary(),
- LeafIndex: 3,
- },
- },
- },
- wantErr: true,
- },
- {
- description: "invalid: unexpected leaf (2/2)",
- req: req,
- rsp: &trillian.GetLeavesByRangeResponse{
- Leaves: []*trillian.LogLeaf{
- &trillian.LogLeaf{
- LeafValue: firstLeaf.ToBinary(),
- LeafIndex: 1,
- },
- &trillian.LogLeaf{
- LeafValue: secondLeaf.ToBinary()[1:],
- LeafIndex: 2,
- },
- },
- },
- wantErr: true,
- },
- {
- description: "valid",
- req: req,
- rsp: &trillian.GetLeavesByRangeResponse{
- Leaves: []*trillian.LogLeaf{
- &trillian.LogLeaf{
- LeafValue: firstLeaf.ToBinary(),
- LeafIndex: 1,
- },
- &trillian.LogLeaf{
- LeafValue: secondLeaf.ToBinary(),
- LeafIndex: 2,
- },
- },
- },
- wantLeaves: &types.Leaves{
- *firstLeaf,
- *secondLeaf,
- },
- },
- } {
- // Run deferred functions at the end of each iteration
- func() {
- ctrl := gomock.NewController(t)
- defer ctrl.Finish()
- grpc := mocks.NewMockTrillianLogClient(ctrl)
- grpc.EXPECT().GetLeavesByRange(gomock.Any(), gomock.Any()).Return(table.rsp, table.err)
- client := TrillianClient{GRPC: grpc}
-
- leaves, err := client.GetLeaves(context.Background(), table.req)
- 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)
- }
- if err != nil {
- return
- }
- if got, want := leaves, table.wantLeaves; !reflect.DeepEqual(got, want) {
- t.Errorf("got leaves\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
- }
- }()
- }
-}
diff --git a/pkg/instance/experimental.go b/pkg/instance/experimental.go
deleted file mode 100644
index 24feeaf..0000000
--- a/pkg/instance/experimental.go
+++ /dev/null
@@ -1,85 +0,0 @@
-package instance
-
-import (
- "bytes"
- "context"
- "crypto"
- "crypto/ed25519"
- "crypto/sha256"
- "encoding/base64"
- "encoding/binary"
- "fmt"
- "net/http"
-
- "git.sigsum.org/sigsum-go/pkg/log"
- "git.sigsum.org/sigsum-go/pkg/types"
-)
-
-// algEd25519 identifies a checkpoint signature algorithm
-const algEd25519 byte = 1
-
-// getCheckpoint is an experimental endpoint that is not part of the official
-// Sigsum API. Documentation can be found in the transparency-dev repo.
-func getCheckpoint(ctx context.Context, i *Instance, w http.ResponseWriter, r *http.Request) (int, error) {
- log.Debug("handling get-checkpoint request")
- sth, err := i.Stateman.ToCosignTreeHead(ctx)
- if err != nil {
- return http.StatusInternalServerError, err
- }
- if err := i.signWriteNote(w, sth); err != nil {
- return http.StatusInternalServerError, err
- }
- return http.StatusOK, nil
-}
-
-// signWriteNote signs and writes a checkpoint which uses "sigsum.org:<prefix>"
-// as origin string. Origin string is also used as ID in the note signature.
-// This means that a sigsum log's prefix (say, "glass-frog"), must be unique.
-func (i *Instance) signWriteNote(w http.ResponseWriter, sth *types.SignedTreeHead) error {
- origin := fmt.Sprintf("sigsum.org:%s", i.Prefix)
- msg := fmt.Sprintf("%s\n%d\n%s\n",
- origin,
- sth.TreeSize,
- base64.StdEncoding.EncodeToString(sth.RootHash[:]),
- )
- sig, err := noteSign(i.Signer, origin, msg)
- if err != nil {
- return err
- }
-
- fmt.Fprintf(w, "%s\n\u2014 %s %s\n", msg, origin, sig)
- return nil
-}
-
-// noteSign returns a note signature for the provided origin and message
-func noteSign(signer crypto.Signer, origin, msg string) (string, error) {
- sig, err := signer.Sign(nil, []byte(msg), crypto.Hash(0))
- if err != nil {
- return "", err
- }
-
- var hbuf [4]byte
- binary.BigEndian.PutUint32(hbuf[:], noteKeyHash(origin, notePubKeyEd25519(signer)))
- sig = append(hbuf[:], sig...)
- return base64.StdEncoding.EncodeToString(sig), nil
-}
-
-// See:
-// https://cs.opensource.google/go/x/mod/+/refs/tags/v0.5.1:sumdb/note/note.go;l=336
-func notePubKeyEd25519(signer crypto.Signer) []byte {
- return bytes.Join([][]byte{
- []byte{algEd25519},
- signer.Public().(ed25519.PublicKey),
- }, nil)
-}
-
-// Source:
-// https://cs.opensource.google/go/x/mod/+/refs/tags/v0.5.1:sumdb/note/note.go;l=222
-func noteKeyHash(name string, key []byte) uint32 {
- h := sha256.New()
- h.Write([]byte(name))
- h.Write([]byte("\n"))
- h.Write(key)
- sum := h.Sum(nil)
- return binary.BigEndian.Uint32(sum)
-}
diff --git a/pkg/instance/handler.go b/pkg/instance/handler.go
deleted file mode 100644
index fa465ee..0000000
--- a/pkg/instance/handler.go
+++ /dev/null
@@ -1,184 +0,0 @@
-package instance
-
-import (
- "context"
- "fmt"
- "net/http"
- "time"
-
- "git.sigsum.org/sigsum-go/pkg/log"
- "git.sigsum.org/sigsum-go/pkg/types"
-)
-
-// Handler implements the http.Handler interface, and contains a reference
-// to a sigsum server instance as well as a function that uses it.
-type Handler struct {
- Instance *Instance
- Endpoint types.Endpoint
- Method string
- Handler func(context.Context, *Instance, http.ResponseWriter, *http.Request) (int, error)
-}
-
-// Path returns a path that should be configured for this handler
-func (h Handler) Path() string {
- if len(h.Instance.Prefix) == 0 {
- return h.Endpoint.Path("", "sigsum", "v0")
- }
- return h.Endpoint.Path("", h.Instance.Prefix, "sigsum", "v0")
-}
-
-// ServeHTTP is part of the http.Handler interface
-func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- start := time.Now()
- code := 0
- defer func() {
- end := time.Now().Sub(start).Seconds()
- sc := fmt.Sprintf("%d", code)
-
- rspcnt.Inc(h.Instance.LogID, string(h.Endpoint), sc)
- latency.Observe(end, h.Instance.LogID, string(h.Endpoint), sc)
- }()
- reqcnt.Inc(h.Instance.LogID, string(h.Endpoint))
-
- code = h.verifyMethod(w, r)
- if code != 0 {
- return
- }
- code = h.handle(w, r)
-}
-
-// verifyMethod checks that an appropriate HTTP method is used. Error handling
-// is based on RFC 7231, see Sections 6.5.5 (Status 405) and 6.5.1 (Status 400).
-func (h *Handler) verifyMethod(w http.ResponseWriter, r *http.Request) int {
- if h.Method == r.Method {
- return 0
- }
-
- code := http.StatusBadRequest
- if ok := h.Instance.checkHTTPMethod(r.Method); ok {
- w.Header().Set("Allow", h.Method)
- code = http.StatusMethodNotAllowed
- }
-
- http.Error(w, fmt.Sprintf("error=%s", http.StatusText(code)), code)
- return code
-}
-
-// handle handles an HTTP request for which the HTTP method is already verified
-func (h Handler) handle(w http.ResponseWriter, r *http.Request) int {
- deadline := time.Now().Add(h.Instance.Deadline)
- ctx, cancel := context.WithDeadline(r.Context(), deadline)
- defer cancel()
-
- code, err := h.Handler(ctx, h.Instance, w, r)
- if err != nil {
- log.Debug("%s/%s: %v", h.Instance.Prefix, h.Endpoint, err)
- http.Error(w, fmt.Sprintf("error=%s", err.Error()), code)
- }
- return code
-}
-
-func addLeaf(ctx context.Context, i *Instance, w http.ResponseWriter, r *http.Request) (int, error) {
- log.Debug("handling add-leaf request")
- req, err := i.leafRequestFromHTTP(ctx, r)
- if err != nil {
- return http.StatusBadRequest, err
- }
- if err := i.Client.AddLeaf(ctx, req); err != nil {
- return http.StatusInternalServerError, err
- }
- return http.StatusOK, nil
-}
-
-func addCosignature(ctx context.Context, i *Instance, w http.ResponseWriter, r *http.Request) (int, error) {
- log.Debug("handling add-cosignature request")
- req, err := i.cosignatureRequestFromHTTP(r)
- if err != nil {
- return http.StatusBadRequest, err
- }
- vk := i.Witnesses[req.KeyHash]
- if err := i.Stateman.AddCosignature(ctx, &vk, &req.Cosignature); err != nil {
- return http.StatusBadRequest, err
- }
- return http.StatusOK, nil
-}
-
-func getTreeHeadToCosign(ctx context.Context, i *Instance, w http.ResponseWriter, _ *http.Request) (int, error) {
- log.Debug("handling get-tree-head-to-cosign request")
- sth, err := i.Stateman.ToCosignTreeHead(ctx)
- if err != nil {
- return http.StatusInternalServerError, err
- }
- if err := sth.ToASCII(w); err != nil {
- return http.StatusInternalServerError, err
- }
- return http.StatusOK, nil
-}
-
-func getTreeHeadCosigned(ctx context.Context, i *Instance, w http.ResponseWriter, _ *http.Request) (int, error) {
- log.Debug("handling get-tree-head-cosigned request")
- cth, err := i.Stateman.CosignedTreeHead(ctx)
- if err != nil {
- return http.StatusInternalServerError, err
- }
- if err := cth.ToASCII(w); err != nil {
- return http.StatusInternalServerError, err
- }
- return http.StatusOK, nil
-}
-
-func getConsistencyProof(ctx context.Context, i *Instance, w http.ResponseWriter, r *http.Request) (int, error) {
- log.Debug("handling get-consistency-proof request")
- req, err := i.consistencyProofRequestFromHTTP(r)
- if err != nil {
- return http.StatusBadRequest, err
- }
- // XXX: check tree size of latest thing we signed?
-
- proof, err := i.Client.GetConsistencyProof(ctx, req)
- if err != nil {
- return http.StatusInternalServerError, err
- }
- if err := proof.ToASCII(w); err != nil {
- return http.StatusInternalServerError, err
- }
- return http.StatusOK, nil
-}
-
-func getInclusionProof(ctx context.Context, i *Instance, w http.ResponseWriter, r *http.Request) (int, error) {
- log.Debug("handling get-inclusion-proof request")
- req, err := i.inclusionProofRequestFromHTTP(r)
- if err != nil {
- return http.StatusBadRequest, err
- }
- // XXX: check tree size of latest thing we signed?
-
- proof, err := i.Client.GetInclusionProof(ctx, req)
- if err != nil {
- return http.StatusInternalServerError, err
- }
- if err := proof.ToASCII(w); err != nil {
- return http.StatusInternalServerError, err
- }
- return http.StatusOK, nil
-}
-
-func getLeaves(ctx context.Context, i *Instance, w http.ResponseWriter, r *http.Request) (int, error) {
- log.Debug("handling get-leaves request")
- req, err := i.leavesRequestFromHTTP(r)
- if err != nil {
- return http.StatusBadRequest, err
- }
- // XXX: check tree size of latest thing we signed?
-
- leaves, err := i.Client.GetLeaves(ctx, req)
- if err != nil {
- return http.StatusInternalServerError, err
- }
- for _, leaf := range *leaves {
- if err := leaf.ToASCII(w); err != nil {
- return http.StatusInternalServerError, err
- }
- }
- return http.StatusOK, nil
-}
diff --git a/pkg/instance/handler_test.go b/pkg/instance/handler_test.go
deleted file mode 100644
index 50bd3a4..0000000
--- a/pkg/instance/handler_test.go
+++ /dev/null
@@ -1,688 +0,0 @@
-package instance
-
-import (
- "bytes"
- "crypto/ed25519"
- "crypto/rand"
- "fmt"
- "io"
- "net/http"
- "net/http/httptest"
- "reflect"
- "testing"
- "time"
-
- mocksDB "git.sigsum.org/log-go/pkg/db/mocks"
- mocksDNS "git.sigsum.org/log-go/internal/mocks/dns"
- mocksState "git.sigsum.org/log-go/pkg/state/mocks"
- "git.sigsum.org/sigsum-go/pkg/types"
- "github.com/golang/mock/gomock"
-)
-
-var (
- testWitVK = types.PublicKey{}
- testConfig = Config{
- LogID: fmt.Sprintf("%x", types.HashFn([]byte("logid"))[:]),
- TreeID: 0,
- Prefix: "testonly",
- MaxRange: 3,
- Deadline: 10,
- Interval: 10,
- ShardStart: 10,
- Witnesses: map[types.Hash]types.PublicKey{
- *types.HashFn(testWitVK[:]): testWitVK,
- },
- }
- testSTH = &types.SignedTreeHead{
- TreeHead: types.TreeHead{
- Timestamp: 0,
- TreeSize: 0,
- RootHash: *types.HashFn([]byte("root hash")),
- },
- Signature: types.Signature{},
- }
- testCTH = &types.CosignedTreeHead{
- SignedTreeHead: *testSTH,
- Cosignature: []types.Signature{types.Signature{}},
- KeyHash: []types.Hash{types.Hash{}},
- }
-)
-
-// TestHandlers check that the expected handlers are configured
-func TestHandlers(t *testing.T) {
- endpoints := map[types.Endpoint]bool{
- types.EndpointAddLeaf: false,
- types.EndpointAddCosignature: false,
- types.EndpointGetTreeHeadToCosign: false,
- types.EndpointGetTreeHeadCosigned: false,
- types.EndpointGetConsistencyProof: false,
- types.EndpointGetInclusionProof: false,
- types.EndpointGetLeaves: false,
- types.Endpoint("get-checkpoint"): false,
- }
- i := &Instance{
- Config: testConfig,
- }
- for _, handler := range i.Handlers() {
- if _, ok := endpoints[handler.Endpoint]; !ok {
- t.Errorf("got unexpected endpoint: %s", handler.Endpoint)
- }
- endpoints[handler.Endpoint] = true
- }
- for endpoint, ok := range endpoints {
- if !ok {
- t.Errorf("endpoint %s is not configured", endpoint)
- }
- }
-}
-
-func TestVerifyMethod(t *testing.T) {
- badMethod := http.MethodHead
- instance := Instance{Config: testConfig}
- for _, handler := range instance.Handlers() {
- for _, method := range []string{
- http.MethodGet,
- http.MethodPost,
- badMethod,
- } {
- url := handler.Endpoint.Path("http://log.example.com", instance.Prefix)
- req, err := http.NewRequest(method, url, nil)
- if err != nil {
- t.Fatalf("must create HTTP request: %v", err)
- }
-
- w := httptest.NewRecorder()
- code := handler.verifyMethod(w, req)
- if got, want := code == 0, handler.Method == method; got != want {
- t.Errorf("%s %s: got %v but wanted %v: %v", method, url, got, want, err)
- continue
- }
- if code == 0 {
- continue
- }
-
- if method == badMethod {
- if got, want := code, http.StatusBadRequest; got != want {
- t.Errorf("%s %s: got status %d, wanted %d", method, url, got, want)
- }
- if _, ok := w.Header()["Allow"]; ok {
- t.Errorf("%s %s: got Allow header, wanted none", method, url)
- }
- continue
- }
-
- if got, want := code, http.StatusMethodNotAllowed; got != want {
- t.Errorf("%s %s: got status %d, wanted %d", method, url, got, want)
- } else if methods, ok := w.Header()["Allow"]; !ok {
- t.Errorf("%s %s: got no allow header, expected one", method, url)
- } else if got, want := len(methods), 1; got != want {
- t.Errorf("%s %s: got %d allowed method(s), wanted %d", method, url, got, want)
- } else if got, want := methods[0], handler.Method; got != want {
- t.Errorf("%s %s: got allowed method %s, wanted %s", method, url, got, want)
- }
- }
- }
-}
-
-// TestPath checks that Path works for an endpoint (add-leaf)
-func TestPath(t *testing.T) {
- for _, table := range []struct {
- description string
- prefix string
- want string
- }{
- {
- description: "no prefix",
- want: "/sigsum/v0/add-leaf",
- },
- {
- description: "a prefix",
- prefix: "test-prefix",
- want: "/test-prefix/sigsum/v0/add-leaf",
- },
- } {
- instance := &Instance{
- Config: Config{
- Prefix: table.prefix,
- },
- }
- handler := Handler{
- Instance: instance,
- Handler: addLeaf,
- Endpoint: types.EndpointAddLeaf,
- Method: http.MethodPost,
- }
- if got, want := handler.Path(), table.want; got != want {
- t.Errorf("got path %v but wanted %v", got, want)
- }
- }
-}
-
-func TestAddLeaf(t *testing.T) {
- for _, table := range []struct {
- description string
- ascii io.Reader // buffer used to populate HTTP request
- expectTrillian bool // expect Trillian client code path
- errTrillian error // error from Trillian client
- expectDNS bool // expect DNS verifier code path
- errDNS error // error from DNS verifier
- wantCode int // HTTP status ok
- }{
- {
- description: "invalid: bad request (parser error)",
- ascii: bytes.NewBufferString("key=value\n"),
- wantCode: http.StatusBadRequest,
- },
- {
- description: "invalid: bad request (signature error)",
- ascii: mustLeafBuffer(t, 10, types.Hash{}, false),
- wantCode: http.StatusBadRequest,
- },
- {
- description: "invalid: bad request (shard hint is before shard start)",
- ascii: mustLeafBuffer(t, 9, types.Hash{}, true),
- wantCode: http.StatusBadRequest,
- },
- {
- description: "invalid: bad request (shard hint is after shard end)",
- ascii: mustLeafBuffer(t, uint64(time.Now().Unix())+1024, types.Hash{}, true),
- wantCode: http.StatusBadRequest,
- },
- {
- description: "invalid: failed verifying domain hint",
- ascii: mustLeafBuffer(t, 10, types.Hash{}, true),
- expectDNS: true,
- errDNS: fmt.Errorf("something went wrong"),
- wantCode: http.StatusBadRequest,
- },
- {
- description: "invalid: backend failure",
- ascii: mustLeafBuffer(t, 10, types.Hash{}, true),
- expectDNS: true,
- expectTrillian: true,
- errTrillian: fmt.Errorf("something went wrong"),
- wantCode: http.StatusInternalServerError,
- },
- {
- description: "valid",
- ascii: mustLeafBuffer(t, 10, types.Hash{}, true),
- expectDNS: true,
- expectTrillian: true,
- wantCode: http.StatusOK,
- },
- } {
- // Run deferred functions at the end of each iteration
- func() {
- ctrl := gomock.NewController(t)
- defer ctrl.Finish()
- dns := mocksDNS.NewMockVerifier(ctrl)
- if table.expectDNS {
- dns.EXPECT().Verify(gomock.Any(), gomock.Any(), gomock.Any()).Return(table.errDNS)
- }
- client := mocksDB.NewMockClient(ctrl)
- if table.expectTrillian {
- client.EXPECT().AddLeaf(gomock.Any(), gomock.Any()).Return(table.errTrillian)
- }
- i := Instance{
- Config: testConfig,
- Client: client,
- DNS: dns,
- }
-
- // Create HTTP request
- url := types.EndpointAddLeaf.Path("http://example.com", i.Prefix)
- req, err := http.NewRequest("POST", url, table.ascii)
- if err != nil {
- t.Fatalf("must create http request: %v", err)
- }
-
- // Run HTTP request
- w := httptest.NewRecorder()
- mustHandle(t, i, types.EndpointAddLeaf).ServeHTTP(w, req)
- if got, want := w.Code, table.wantCode; got != want {
- t.Errorf("got HTTP status code %v but wanted %v in test %q", got, want, table.description)
- }
- }()
- }
-}
-
-func TestAddCosignature(t *testing.T) {
- buf := func() io.Reader {
- return bytes.NewBufferString(fmt.Sprintf("%s=%x\n%s=%x\n",
- "cosignature", types.Signature{},
- "key_hash", *types.HashFn(testWitVK[:]),
- ))
- }
- for _, table := range []struct {
- description string
- ascii io.Reader // buffer used to populate HTTP request
- expect bool // set if a mock answer is expected
- err error // error from Trillian client
- wantCode int // HTTP status ok
- }{
- {
- description: "invalid: bad request (parser error)",
- ascii: bytes.NewBufferString("key=value\n"),
- wantCode: http.StatusBadRequest,
- },
- {
- description: "invalid: bad request (unknown witness)",
- ascii: bytes.NewBufferString(fmt.Sprintf("%s=%x\n%s=%x\n",
- "cosignature", types.Signature{},
- "key_hash", *types.HashFn(testWitVK[1:]),
- )),
- wantCode: http.StatusBadRequest,
- },
- {
- description: "invalid: backend failure",
- ascii: buf(),
- expect: true,
- err: fmt.Errorf("something went wrong"),
- wantCode: http.StatusBadRequest,
- },
- {
- description: "valid",
- ascii: buf(),
- expect: true,
- wantCode: http.StatusOK,
- },
- } {
- // Run deferred functions at the end of each iteration
- func() {
- ctrl := gomock.NewController(t)
- defer ctrl.Finish()
- stateman := mocksState.NewMockStateManager(ctrl)
- if table.expect {
- stateman.EXPECT().AddCosignature(gomock.Any(), gomock.Any(), gomock.Any()).Return(table.err)
- }
- i := Instance{
- Config: testConfig,
- Stateman: stateman,
- }
-
- // Create HTTP request
- url := types.EndpointAddCosignature.Path("http://example.com", i.Prefix)
- req, err := http.NewRequest("POST", url, table.ascii)
- if err != nil {
- t.Fatalf("must create http request: %v", err)
- }
-
- // Run HTTP request
- w := httptest.NewRecorder()
- mustHandle(t, i, types.EndpointAddCosignature).ServeHTTP(w, req)
- if got, want := w.Code, table.wantCode; got != want {
- t.Errorf("got HTTP status code %v but wanted %v in test %q", got, want, table.description)
- }
- }()
- }
-}
-
-func TestGetTreeToSign(t *testing.T) {
- for _, table := range []struct {
- description string
- expect bool // set if a mock answer is expected
- rsp *types.SignedTreeHead // signed tree head from Trillian client
- err error // error from Trillian client
- wantCode int // HTTP status ok
- }{
- {
- description: "invalid: backend failure",
- expect: true,
- err: fmt.Errorf("something went wrong"),
- wantCode: http.StatusInternalServerError,
- },
- {
- description: "valid",
- expect: true,
- rsp: testSTH,
- wantCode: http.StatusOK,
- },
- } {
- // Run deferred functions at the end of each iteration
- func() {
- ctrl := gomock.NewController(t)
- defer ctrl.Finish()
- stateman := mocksState.NewMockStateManager(ctrl)
- if table.expect {
- stateman.EXPECT().ToCosignTreeHead(gomock.Any()).Return(table.rsp, table.err)
- }
- i := Instance{
- Config: testConfig,
- Stateman: stateman,
- }
-
- // Create HTTP request
- url := types.EndpointGetTreeHeadToCosign.Path("http://example.com", i.Prefix)
- req, err := http.NewRequest("GET", url, nil)
- if err != nil {
- t.Fatalf("must create http request: %v", err)
- }
-
- // Run HTTP request
- w := httptest.NewRecorder()
- mustHandle(t, i, types.EndpointGetTreeHeadToCosign).ServeHTTP(w, req)
- if got, want := w.Code, table.wantCode; got != want {
- t.Errorf("got HTTP status code %v but wanted %v in test %q", got, want, table.description)
- }
- }()
- }
-}
-
-func TestGetTreeCosigned(t *testing.T) {
- for _, table := range []struct {
- description string
- expect bool // set if a mock answer is expected
- rsp *types.CosignedTreeHead // cosigned tree head from Trillian client
- err error // error from Trillian client
- wantCode int // HTTP status ok
- }{
- {
- description: "invalid: backend failure",
- expect: true,
- err: fmt.Errorf("something went wrong"),
- wantCode: http.StatusInternalServerError,
- },
- {
- description: "valid",
- expect: true,
- rsp: testCTH,
- wantCode: http.StatusOK,
- },
- } {
- // Run deferred functions at the end of each iteration
- func() {
- ctrl := gomock.NewController(t)
- defer ctrl.Finish()
- stateman := mocksState.NewMockStateManager(ctrl)
- if table.expect {
- stateman.EXPECT().CosignedTreeHead(gomock.Any()).Return(table.rsp, table.err)
- }
- i := Instance{
- Config: testConfig,
- Stateman: stateman,
- }
-
- // Create HTTP request
- url := types.EndpointGetTreeHeadCosigned.Path("http://example.com", i.Prefix)
- req, err := http.NewRequest("GET", url, nil)
- if err != nil {
- t.Fatalf("must create http request: %v", err)
- }
-
- // Run HTTP request
- w := httptest.NewRecorder()
- mustHandle(t, i, types.EndpointGetTreeHeadCosigned).ServeHTTP(w, req)
- if got, want := w.Code, table.wantCode; got != want {
- t.Errorf("got HTTP status code %v but wanted %v in test %q", got, want, table.description)
- }
- }()
- }
-}
-
-func TestGetConsistencyProof(t *testing.T) {
- for _, table := range []struct {
- description string
- params string // params is the query's url params
- expect bool // set if a mock answer is expected
- rsp *types.ConsistencyProof // consistency proof from Trillian client
- err error // error from Trillian client
- wantCode int // HTTP status ok
- }{
- {
- description: "invalid: bad request (parser error)",
- params: "a/1",
- wantCode: http.StatusBadRequest,
- },
- {
- description: "invalid: bad request (OldSize is zero)",
- params: "0/1",
- wantCode: http.StatusBadRequest,
- },
- {
- description: "invalid: bad request (OldSize > NewSize)",
- params: "2/1",
- wantCode: http.StatusBadRequest,
- },
- {
- description: "invalid: backend failure",
- params: "1/2",
- expect: true,
- err: fmt.Errorf("something went wrong"),
- wantCode: http.StatusInternalServerError,
- },
- {
- description: "valid",
- params: "1/2",
- expect: true,
- rsp: &types.ConsistencyProof{
- OldSize: 1,
- NewSize: 2,
- Path: []types.Hash{
- *types.HashFn([]byte{}),
- },
- },
- wantCode: http.StatusOK,
- },
- } {
- // Run deferred functions at the end of each iteration
- func() {
- ctrl := gomock.NewController(t)
- defer ctrl.Finish()
- client := mocksDB.NewMockClient(ctrl)
- if table.expect {
- client.EXPECT().GetConsistencyProof(gomock.Any(), gomock.Any()).Return(table.rsp, table.err)
- }
- i := Instance{
- Config: testConfig,
- Client: client,
- }
-
- // Create HTTP request
- url := types.EndpointGetConsistencyProof.Path("http://example.com", i.Prefix)
- req, err := http.NewRequest(http.MethodGet, url+table.params, nil)
- if err != nil {
- t.Fatalf("must create http request: %v", err)
- }
-
- // Run HTTP request
- w := httptest.NewRecorder()
- mustHandle(t, i, types.EndpointGetConsistencyProof).ServeHTTP(w, req)
- if got, want := w.Code, table.wantCode; got != want {
- t.Errorf("got HTTP status code %v but wanted %v in test %q", got, want, table.description)
- }
- }()
- }
-}
-
-func TestGetInclusionProof(t *testing.T) {
- for _, table := range []struct {
- description string
- params string // params is the query's url params
- expect bool // set if a mock answer is expected
- rsp *types.InclusionProof // inclusion proof from Trillian client
- err error // error from Trillian client
- wantCode int // HTTP status ok
- }{
- {
- description: "invalid: bad request (parser error)",
- params: "a/0000000000000000000000000000000000000000000000000000000000000000",
- wantCode: http.StatusBadRequest,
- },
- {
- description: "invalid: bad request (no proof for tree size)",
- params: "1/0000000000000000000000000000000000000000000000000000000000000000",
- wantCode: http.StatusBadRequest,
- },
- {
- description: "invalid: backend failure",
- params: "2/0000000000000000000000000000000000000000000000000000000000000000",
- expect: true,
- err: fmt.Errorf("something went wrong"),
- wantCode: http.StatusInternalServerError,
- },
- {
- description: "valid",
- params: "2/0000000000000000000000000000000000000000000000000000000000000000",
- expect: true,
- rsp: &types.InclusionProof{
- TreeSize: 2,
- LeafIndex: 0,
- Path: []types.Hash{
- *types.HashFn([]byte{}),
- },
- },
- wantCode: http.StatusOK,
- },
- } {
- // Run deferred functions at the end of each iteration
- func() {
- ctrl := gomock.NewController(t)
- defer ctrl.Finish()
- client := mocksDB.NewMockClient(ctrl)
- if table.expect {
- client.EXPECT().GetInclusionProof(gomock.Any(), gomock.Any()).Return(table.rsp, table.err)
- }
- i := Instance{
- Config: testConfig,
- Client: client,
- }
-
- // Create HTTP request
- url := types.EndpointGetInclusionProof.Path("http://example.com", i.Prefix)
- req, err := http.NewRequest(http.MethodGet, url+table.params, nil)
- if err != nil {
- t.Fatalf("must create http request: %v", err)
- }
-
- // Run HTTP request
- w := httptest.NewRecorder()
- mustHandle(t, i, types.EndpointGetInclusionProof).ServeHTTP(w, req)
- if got, want := w.Code, table.wantCode; got != want {
- t.Errorf("got HTTP status code %v but wanted %v in test %q", got, want, table.description)
- }
- }()
- }
-}
-
-func TestGetLeaves(t *testing.T) {
- for _, table := range []struct {
- description string
- params string // params is the query's url params
- expect bool // set if a mock answer is expected
- rsp *types.Leaves // list of leaves from Trillian client
- err error // error from Trillian client
- wantCode int // HTTP status ok
- }{
- {
- description: "invalid: bad request (parser error)",
- params: "a/1",
- wantCode: http.StatusBadRequest,
- },
- {
- description: "invalid: bad request (StartSize > EndSize)",
- params: "1/0",
- wantCode: http.StatusBadRequest,
- },
- {
- description: "invalid: backend failure",
- params: "0/0",
- expect: true,
- err: fmt.Errorf("something went wrong"),
- wantCode: http.StatusInternalServerError,
- },
- {
- description: "valid: one more entry than the configured MaxRange",
- params: fmt.Sprintf("%d/%d", 0, testConfig.MaxRange), // query will be pruned
- expect: true,
- rsp: func() *types.Leaves {
- var list types.Leaves
- for i := int64(0); i < testConfig.MaxRange; i++ {
- list = append(list[:], types.Leaf{
- Statement: types.Statement{
- ShardHint: 0,
- Checksum: types.Hash{},
- },
- Signature: types.Signature{},
- KeyHash: types.Hash{},
- })
- }
- return &list
- }(),
- wantCode: http.StatusOK,
- },
- } {
- // Run deferred functions at the end of each iteration
- func() {
- ctrl := gomock.NewController(t)
- defer ctrl.Finish()
- client := mocksDB.NewMockClient(ctrl)
- if table.expect {
- client.EXPECT().GetLeaves(gomock.Any(), gomock.Any()).Return(table.rsp, table.err)
- }
- i := Instance{
- Config: testConfig,
- Client: client,
- }
-
- // Create HTTP request
- url := types.EndpointGetLeaves.Path("http://example.com", i.Prefix)
- req, err := http.NewRequest(http.MethodGet, url+table.params, nil)
- if err != nil {
- t.Fatalf("must create http request: %v", err)
- }
-
- // Run HTTP request
- w := httptest.NewRecorder()
- mustHandle(t, i, types.EndpointGetLeaves).ServeHTTP(w, req)
- if got, want := w.Code, table.wantCode; got != want {
- t.Errorf("got HTTP status code %v but wanted %v in test %q", got, want, table.description)
- }
- if w.Code != http.StatusOK {
- return
- }
-
- list := types.Leaves{}
- if err := list.FromASCII(w.Body); err != nil {
- t.Fatalf("must unmarshal leaf list: %v", err)
- }
- if got, want := &list, table.rsp; !reflect.DeepEqual(got, want) {
- t.Errorf("got leaf list\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
- }
- }()
- }
-}
-
-func mustHandle(t *testing.T, i Instance, e types.Endpoint) Handler {
- for _, handler := range i.Handlers() {
- if handler.Endpoint == e {
- return handler
- }
- }
- t.Fatalf("must handle endpoint: %v", e)
- return Handler{}
-}
-
-func mustLeafBuffer(t *testing.T, shardHint uint64, message types.Hash, wantSig bool) io.Reader {
- t.Helper()
-
- vk, sk, err := ed25519.GenerateKey(rand.Reader)
- if err != nil {
- t.Fatalf("must generate ed25519 keys: %v", err)
- }
- msg := types.Statement{
- ShardHint: shardHint,
- Checksum: *types.HashFn(message[:]),
- }
- sig := ed25519.Sign(sk, msg.ToBinary())
- if !wantSig {
- sig[0] += 1
- }
- return bytes.NewBufferString(fmt.Sprintf(
- "%s=%d\n"+"%s=%x\n"+"%s=%x\n"+"%s=%x\n"+"%s=%s\n",
- "shard_hint", shardHint,
- "message", message[:],
- "signature", sig,
- "public_key", vk,
- "domain_hint", "example.com",
- ))
-}
diff --git a/pkg/instance/instance.go b/pkg/instance/instance.go
deleted file mode 100644
index f4c0089..0000000
--- a/pkg/instance/instance.go
+++ /dev/null
@@ -1,135 +0,0 @@
-package instance
-
-import (
- "context"
- "crypto"
- "fmt"
- "net/http"
- "time"
-
- "git.sigsum.org/log-go/pkg/db"
- "git.sigsum.org/log-go/pkg/state"
- "git.sigsum.org/sigsum-go/pkg/dns"
- "git.sigsum.org/sigsum-go/pkg/requests"
- "git.sigsum.org/sigsum-go/pkg/types"
-)
-
-// Config is a collection of log parameters
-type Config struct {
- LogID string // H(public key), then hex-encoded
- TreeID int64 // Merkle tree identifier used by Trillian
- Prefix string // The portion between base URL and st/v0 (may be "")
- MaxRange int64 // Maximum number of leaves per get-leaves request
- Deadline time.Duration // Deadline used for gRPC requests
- Interval time.Duration // Cosigning frequency
- ShardStart uint64 // Shard interval start (num seconds since UNIX epoch)
-
- // Witnesses map trusted witness identifiers to public keys
- Witnesses map[types.Hash]types.PublicKey
-}
-
-// Instance is an instance of the log's front-end
-type Instance struct {
- Config // configuration parameters
- Client db.Client // provides access to the Trillian backend
- Signer crypto.Signer // provides access to Ed25519 private key
- Stateman state.StateManager // coordinates access to (co)signed tree heads
- DNS dns.Verifier // checks if domain name knows a public key
-}
-
-// Handlers returns a list of sigsum handlers
-func (i *Instance) Handlers() []Handler {
- return []Handler{
- Handler{Instance: i, Handler: addLeaf, Endpoint: types.EndpointAddLeaf, Method: http.MethodPost},
- Handler{Instance: i, Handler: addCosignature, Endpoint: types.EndpointAddCosignature, Method: http.MethodPost},
- Handler{Instance: i, Handler: getTreeHeadToCosign, Endpoint: types.EndpointGetTreeHeadToCosign, Method: http.MethodGet},
- Handler{Instance: i, Handler: getTreeHeadCosigned, Endpoint: types.EndpointGetTreeHeadCosigned, Method: http.MethodGet},
- Handler{Instance: i, Handler: getCheckpoint, Endpoint: types.Endpoint("get-checkpoint"), Method: http.MethodGet},
- Handler{Instance: i, Handler: getConsistencyProof, Endpoint: types.EndpointGetConsistencyProof, Method: http.MethodGet},
- Handler{Instance: i, Handler: getInclusionProof, Endpoint: types.EndpointGetInclusionProof, Method: http.MethodGet},
- Handler{Instance: i, Handler: getLeaves, Endpoint: types.EndpointGetLeaves, Method: http.MethodGet},
- }
-}
-
-// checkHTTPMethod checks if an HTTP method is supported
-func (i *Instance) checkHTTPMethod(m string) bool {
- return m == http.MethodGet || m == http.MethodPost
-}
-
-func (i *Instance) leafRequestFromHTTP(ctx context.Context, r *http.Request) (*requests.Leaf, error) {
- var req requests.Leaf
- if err := req.FromASCII(r.Body); err != nil {
- return nil, fmt.Errorf("FromASCII: %v", err)
- }
- stmt := types.Statement{
- ShardHint: req.ShardHint,
- Checksum: *types.HashFn(req.Message[:]),
- }
- if !stmt.Verify(&req.PublicKey, &req.Signature) {
- return nil, fmt.Errorf("invalid signature")
- }
- shardEnd := uint64(time.Now().Unix())
- if req.ShardHint < i.ShardStart {
- return nil, fmt.Errorf("invalid shard hint: %d not in [%d, %d]", req.ShardHint, i.ShardStart, shardEnd)
- }
- if req.ShardHint > shardEnd {
- return nil, fmt.Errorf("invalid shard hint: %d not in [%d, %d]", req.ShardHint, i.ShardStart, shardEnd)
- }
- if err := i.DNS.Verify(ctx, req.DomainHint, &req.PublicKey); err != nil {
- return nil, fmt.Errorf("invalid domain hint: %v", err)
- }
- return &req, nil
-}
-
-func (i *Instance) cosignatureRequestFromHTTP(r *http.Request) (*requests.Cosignature, error) {
- var req requests.Cosignature
- if err := req.FromASCII(r.Body); err != nil {
- return nil, fmt.Errorf("FromASCII: %v", err)
- }
- if _, ok := i.Witnesses[req.KeyHash]; !ok {
- return nil, fmt.Errorf("Unknown witness: %x", req.KeyHash)
- }
- return &req, nil
-}
-
-func (i *Instance) consistencyProofRequestFromHTTP(r *http.Request) (*requests.ConsistencyProof, error) {
- var req requests.ConsistencyProof
- if err := req.FromURL(r.URL.Path); err != nil {
- return nil, fmt.Errorf("FromASCII: %v", err)
- }
- if req.OldSize < 1 {
- return nil, fmt.Errorf("OldSize(%d) must be larger than zero", req.OldSize)
- }
- if req.NewSize <= req.OldSize {
- return nil, fmt.Errorf("NewSize(%d) must be larger than OldSize(%d)", req.NewSize, req.OldSize)
- }
- return &req, nil
-}
-
-func (i *Instance) inclusionProofRequestFromHTTP(r *http.Request) (*requests.InclusionProof, error) {
- var req requests.InclusionProof
- if err := req.FromURL(r.URL.Path); err != nil {
- return nil, fmt.Errorf("FromASCII: %v", err)
- }
- if req.TreeSize < 2 {
- // TreeSize:0 => not possible to prove inclusion of anything
- // TreeSize:1 => you don't need an inclusion proof (it is always empty)
- return nil, fmt.Errorf("TreeSize(%d) must be larger than one", req.TreeSize)
- }
- return &req, nil
-}
-
-func (i *Instance) leavesRequestFromHTTP(r *http.Request) (*requests.Leaves, error) {
- var req requests.Leaves
- if err := req.FromURL(r.URL.Path); err != nil {
- return nil, fmt.Errorf("FromASCII: %v", err)
- }
-
- if req.StartSize > req.EndSize {
- return nil, fmt.Errorf("StartSize(%d) must be less than or equal to EndSize(%d)", req.StartSize, req.EndSize)
- }
- if req.EndSize-req.StartSize+1 > uint64(i.MaxRange) {
- req.EndSize = req.StartSize + uint64(i.MaxRange) - 1
- }
- return &req, nil
-}
diff --git a/pkg/instance/instance_test.go b/pkg/instance/instance_test.go
deleted file mode 100644
index 00d996d..0000000
--- a/pkg/instance/instance_test.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package instance
-
-import (
- "net/http"
- "testing"
-)
-
-func CheckHTTPMethod(t *testing.T) {
- var instance Instance
- for _, table := range []struct {
- method string
- wantOK bool
- }{
- {wantOK: false, method: http.MethodHead},
- {wantOK: true, method: http.MethodPost},
- {wantOK: true, method: http.MethodGet},
- } {
- ok := instance.checkHTTPMethod(table.method)
- if got, want := ok, table.wantOK; got != want {
- t.Errorf("%s: got %v but wanted %v", table.method, got, want)
- }
- }
-}
diff --git a/pkg/instance/metric.go b/pkg/instance/metric.go
deleted file mode 100644
index cbd0223..0000000
--- a/pkg/instance/metric.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package instance
-
-import (
- "github.com/google/trillian/monitoring"
- "github.com/google/trillian/monitoring/prometheus"
-)
-
-var (
- reqcnt monitoring.Counter // number of incoming http requests
- rspcnt monitoring.Counter // number of valid http responses
- latency monitoring.Histogram // request-response latency
-)
-
-func init() {
- mf := prometheus.MetricFactory{}
- reqcnt = mf.NewCounter("http_req", "number of http requests", "logid", "endpoint")
- rspcnt = mf.NewCounter("http_rsp", "number of http requests", "logid", "endpoint", "status")
- latency = mf.NewHistogram("http_latency", "http request-response latency", "logid", "endpoint", "status")
-}
diff --git a/pkg/state/mocks/signer.go b/pkg/state/mocks/signer.go
deleted file mode 100644
index 7c699dd..0000000
--- a/pkg/state/mocks/signer.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package mocks
-
-import (
- "crypto"
- "crypto/ed25519"
- "io"
-)
-
-// TestSign implements the signer interface. It can be used to mock an Ed25519
-// signer that always return the same public key, signature, and error.
-type TestSigner struct {
- PublicKey [ed25519.PublicKeySize]byte
- Signature [ed25519.SignatureSize]byte
- Error error
-}
-
-func (ts *TestSigner) Public() crypto.PublicKey {
- return ed25519.PublicKey(ts.PublicKey[:])
-}
-
-func (ts *TestSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
- return ts.Signature[:], ts.Error
-}
diff --git a/pkg/state/mocks/state_manager.go b/pkg/state/mocks/state_manager.go
deleted file mode 100644
index 05831c6..0000000
--- a/pkg/state/mocks/state_manager.go
+++ /dev/null
@@ -1,92 +0,0 @@
-// Code generated by MockGen. DO NOT EDIT.
-// Source: git.sigsum.org/sigsum-log-go/pkg/state (interfaces: StateManager)
-
-// Package mocks is a generated GoMock package.
-package mocks
-
-import (
- context "context"
- reflect "reflect"
-
- types "git.sigsum.org/sigsum-go/pkg/types"
- gomock "github.com/golang/mock/gomock"
-)
-
-// MockStateManager is a mock of StateManager interface.
-type MockStateManager struct {
- ctrl *gomock.Controller
- recorder *MockStateManagerMockRecorder
-}
-
-// MockStateManagerMockRecorder is the mock recorder for MockStateManager.
-type MockStateManagerMockRecorder struct {
- mock *MockStateManager
-}
-
-// NewMockStateManager creates a new mock instance.
-func NewMockStateManager(ctrl *gomock.Controller) *MockStateManager {
- mock := &MockStateManager{ctrl: ctrl}
- mock.recorder = &MockStateManagerMockRecorder{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockStateManager) EXPECT() *MockStateManagerMockRecorder {
- return m.recorder
-}
-
-// AddCosignature mocks base method.
-func (m *MockStateManager) AddCosignature(arg0 context.Context, arg1 *types.PublicKey, arg2 *types.Signature) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "AddCosignature", arg0, arg1, arg2)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// AddCosignature indicates an expected call of AddCosignature.
-func (mr *MockStateManagerMockRecorder) AddCosignature(arg0, arg1, arg2 interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddCosignature", reflect.TypeOf((*MockStateManager)(nil).AddCosignature), arg0, arg1, arg2)
-}
-
-// CosignedTreeHead mocks base method.
-func (m *MockStateManager) CosignedTreeHead(arg0 context.Context) (*types.CosignedTreeHead, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "CosignedTreeHead", arg0)
- ret0, _ := ret[0].(*types.CosignedTreeHead)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// CosignedTreeHead indicates an expected call of CosignedTreeHead.
-func (mr *MockStateManagerMockRecorder) CosignedTreeHead(arg0 interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CosignedTreeHead", reflect.TypeOf((*MockStateManager)(nil).CosignedTreeHead), arg0)
-}
-
-// Run mocks base method.
-func (m *MockStateManager) Run(arg0 context.Context) {
- m.ctrl.T.Helper()
- m.ctrl.Call(m, "Run", arg0)
-}
-
-// Run indicates an expected call of Run.
-func (mr *MockStateManagerMockRecorder) Run(arg0 interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Run", reflect.TypeOf((*MockStateManager)(nil).Run), arg0)
-}
-
-// ToCosignTreeHead mocks base method.
-func (m *MockStateManager) ToCosignTreeHead(arg0 context.Context) (*types.SignedTreeHead, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "ToCosignTreeHead", arg0)
- ret0, _ := ret[0].(*types.SignedTreeHead)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// ToCosignTreeHead indicates an expected call of ToCosignTreeHead.
-func (mr *MockStateManagerMockRecorder) ToCosignTreeHead(arg0 interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ToCosignTreeHead", reflect.TypeOf((*MockStateManager)(nil).ToCosignTreeHead), arg0)
-}
diff --git a/pkg/state/single.go b/pkg/state/single.go
deleted file mode 100644
index 695f0e3..0000000
--- a/pkg/state/single.go
+++ /dev/null
@@ -1,165 +0,0 @@
-package state
-
-import (
- "context"
- "crypto"
- "crypto/ed25519"
- "fmt"
- "sync"
- "time"
-
- "git.sigsum.org/log-go/pkg/db"
- "git.sigsum.org/sigsum-go/pkg/log"
- "git.sigsum.org/sigsum-go/pkg/types"
-)
-
-// StateManagerSingle implements a single-instance StateManager
-type StateManagerSingle struct {
- client db.Client
- signer crypto.Signer
- namespace types.Hash
- interval time.Duration
- deadline time.Duration
-
- // Lock-protected access to pointers. A write lock is only obtained once
- // per interval when doing pointer rotation. All endpoints are readers.
- sync.RWMutex
- signedTreeHead *types.SignedTreeHead
- cosignedTreeHead *types.CosignedTreeHead
-
- // Syncronized and deduplicated witness cosignatures for signedTreeHead
- events chan *event
- cosignatures map[types.Hash]*types.Signature
-}
-
-func NewStateManagerSingle(client db.Client, signer crypto.Signer, interval, deadline time.Duration) (*StateManagerSingle, error) {
- sm := &StateManagerSingle{
- client: client,
- signer: signer,
- namespace: *types.HashFn(signer.Public().(ed25519.PublicKey)),
- interval: interval,
- deadline: deadline,
- }
- sth, err := sm.latestSTH(context.Background())
- sm.setCosignedTreeHead()
- sm.setToCosignTreeHead(sth)
- return sm, err
-}
-
-func (sm *StateManagerSingle) Run(ctx context.Context) {
- rotation := func() {
- nextSTH, err := sm.latestSTH(ctx)
- if err != nil {
- log.Warning("cannot rotate without tree head: %v", err)
- return
- }
- sm.rotate(nextSTH)
- }
- sm.events = make(chan *event, 4096)
- defer close(sm.events)
- ticker := time.NewTicker(sm.interval)
- defer ticker.Stop()
-
- rotation()
- for {
- select {
- case <-ticker.C:
- rotation()
- case ev := <-sm.events:
- sm.handleEvent(ev)
- case <-ctx.Done():
- return
- }
- }
-}
-
-func (sm *StateManagerSingle) ToCosignTreeHead(_ context.Context) (*types.SignedTreeHead, error) {
- sm.RLock()
- defer sm.RUnlock()
- return sm.signedTreeHead, nil
-}
-
-func (sm *StateManagerSingle) CosignedTreeHead(_ context.Context) (*types.CosignedTreeHead, error) {
- sm.RLock()
- defer sm.RUnlock()
- if sm.cosignedTreeHead == nil {
- return nil, fmt.Errorf("no cosignatures available")
- }
- return sm.cosignedTreeHead, nil
-}
-
-func (sm *StateManagerSingle) AddCosignature(ctx context.Context, pub *types.PublicKey, sig *types.Signature) error {
- sm.RLock()
- defer sm.RUnlock()
-
- msg := sm.signedTreeHead.TreeHead.ToBinary(&sm.namespace)
- if !ed25519.Verify(ed25519.PublicKey(pub[:]), msg, sig[:]) {
- return fmt.Errorf("invalid cosignature")
- }
- select {
- case sm.events <- &event{types.HashFn(pub[:]), sig}:
- return nil
- case <-ctx.Done():
- return fmt.Errorf("request timeout")
- }
-}
-
-func (sm *StateManagerSingle) rotate(nextSTH *types.SignedTreeHead) {
- sm.Lock()
- defer sm.Unlock()
-
- log.Debug("rotating tree heads")
- sm.handleEvents()
- sm.setCosignedTreeHead()
- sm.setToCosignTreeHead(nextSTH)
-}
-
-func (sm *StateManagerSingle) handleEvents() {
- log.Debug("handling any outstanding events")
- for i, n := 0, len(sm.events); i < n; i++ {
- sm.handleEvent(<-sm.events)
- }
-}
-
-func (sm *StateManagerSingle) handleEvent(ev *event) {
- log.Debug("handling event from witness %x", ev.keyHash[:])
- sm.cosignatures[*ev.keyHash] = ev.cosignature
-}
-
-func (sm *StateManagerSingle) setCosignedTreeHead() {
- n := len(sm.cosignatures)
- if n == 0 {
- sm.cosignedTreeHead = nil
- return
- }
-
- var cth types.CosignedTreeHead
- cth.SignedTreeHead = *sm.signedTreeHead
- cth.Cosignature = make([]types.Signature, 0, n)
- cth.KeyHash = make([]types.Hash, 0, n)
- for keyHash, cosignature := range sm.cosignatures {
- cth.KeyHash = append(cth.KeyHash, keyHash)
- cth.Cosignature = append(cth.Cosignature, *cosignature)
- }
- sm.cosignedTreeHead = &cth
-}
-
-func (sm *StateManagerSingle) setToCosignTreeHead(nextSTH *types.SignedTreeHead) {
- sm.cosignatures = make(map[types.Hash]*types.Signature)
- sm.signedTreeHead = nextSTH
-}
-
-func (sm *StateManagerSingle) latestSTH(ctx context.Context) (*types.SignedTreeHead, error) {
- ictx, cancel := context.WithTimeout(ctx, sm.deadline)
- defer cancel()
-
- th, err := sm.client.GetTreeHead(ictx)
- if err != nil {
- return nil, fmt.Errorf("failed fetching tree head: %v", err)
- }
- sth, err := th.Sign(sm.signer, &sm.namespace)
- if err != nil {
- return nil, fmt.Errorf("failed signing tree head: %v", err)
- }
- return sth, nil
-}
diff --git a/pkg/state/single_test.go b/pkg/state/single_test.go
deleted file mode 100644
index 8e89020..0000000
--- a/pkg/state/single_test.go
+++ /dev/null
@@ -1,217 +0,0 @@
-package state
-
-import (
- "context"
- "crypto"
- "crypto/ed25519"
- "crypto/rand"
- "fmt"
- "reflect"
- "testing"
- "time"
-
- db "git.sigsum.org/log-go/pkg/db/mocks"
- "git.sigsum.org/log-go/pkg/state/mocks"
- "git.sigsum.org/sigsum-go/pkg/types"
- "github.com/golang/mock/gomock"
-)
-
-func TestNewStateManagerSingle(t *testing.T) {
- signerOk := &mocks.TestSigner{types.PublicKey{}, types.Signature{}, nil}
- signerErr := &mocks.TestSigner{types.PublicKey{}, types.Signature{}, fmt.Errorf("err")}
- for _, table := range []struct {
- description string
- signer crypto.Signer
- rsp types.TreeHead
- err error
- wantErr bool
- wantSth types.SignedTreeHead
- }{
- {
- description: "invalid: backend failure",
- signer: signerOk,
- err: fmt.Errorf("something went wrong"),
- wantErr: true,
- },
- {
- description: "invalid: signer failure",
- signer: signerErr,
- rsp: types.TreeHead{},
- wantErr: true,
- },
- {
- description: "valid",
- signer: signerOk,
- rsp: types.TreeHead{},
- wantSth: types.SignedTreeHead{},
- },
- } {
- func() {
- ctrl := gomock.NewController(t)
- defer ctrl.Finish()
- client := db.NewMockClient(ctrl)
- client.EXPECT().GetTreeHead(gomock.Any()).Return(&table.rsp, table.err)
-
- sm, err := NewStateManagerSingle(client, table.signer, time.Duration(0), time.Duration(0))
- 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)
- }
- if err != nil {
- return
- }
- if got, want := sm.signedTreeHead, &table.wantSth; !reflect.DeepEqual(got, want) {
- t.Errorf("got to-cosign tree head\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
- }
- if got := sm.cosignedTreeHead; got != nil {
- t.Errorf("got cosigned tree head but should have none in test %q", table.description)
- }
- }()
- }
-}
-
-func TestToCosignTreeHead(t *testing.T) {
- want := &types.SignedTreeHead{}
- sm := StateManagerSingle{
- signedTreeHead: want,
- }
- sth, err := sm.ToCosignTreeHead(context.Background())
- if err != nil {
- t.Errorf("should not fail with error: %v", err)
- return
- }
- if got := sth; !reflect.DeepEqual(got, want) {
- t.Errorf("got signed tree head\n\t%v\nbut wanted\n\t%v", got, want)
- }
-}
-
-func TestCosignedTreeHead(t *testing.T) {
- want := &types.CosignedTreeHead{
- Cosignature: make([]types.Signature, 1),
- KeyHash: make([]types.Hash, 1),
- }
- sm := StateManagerSingle{
- cosignedTreeHead: want,
- }
- cth, err := sm.CosignedTreeHead(context.Background())
- if err != nil {
- t.Errorf("should not fail with error: %v", err)
- return
- }
- if got := cth; !reflect.DeepEqual(got, want) {
- t.Errorf("got cosigned tree head\n\t%v\nbut wanted\n\t%v", got, want)
- }
-
- sm.cosignedTreeHead = nil
- cth, err = sm.CosignedTreeHead(context.Background())
- if err == nil {
- t.Errorf("should fail without a cosigned tree head")
- return
- }
-}
-
-func TestAddCosignature(t *testing.T) {
- secret, public := mustKeyPair(t)
- for _, table := range []struct {
- desc string
- signer crypto.Signer
- vk types.PublicKey
- wantErr bool
- }{
- {
- desc: "invalid: wrong public key",
- signer: secret,
- vk: types.PublicKey{},
- wantErr: true,
- },
- {
- desc: "valid",
- signer: secret,
- vk: public,
- },
- } {
- sm := &StateManagerSingle{
- namespace: *types.HashFn(nil),
- signedTreeHead: &types.SignedTreeHead{},
- events: make(chan *event, 1),
- }
- defer close(sm.events)
-
- sth := mustSign(t, table.signer, &sm.signedTreeHead.TreeHead, &sm.namespace)
- ctx := context.Background()
- err := sm.AddCosignature(ctx, &table.vk, &sth.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.desc, err)
- }
- if err != nil {
- continue
- }
-
- ctx, cancel := context.WithTimeout(ctx, 50*time.Millisecond)
- defer cancel()
- if err := sm.AddCosignature(ctx, &table.vk, &sth.Signature); err == nil {
- t.Errorf("expected full channel in test %q", table.desc)
- }
- if got, want := len(sm.events), 1; got != want {
- t.Errorf("wanted %d cosignatures but got %d in test %q", want, got, table.desc)
- }
- }
-}
-
-func TestRotate(t *testing.T) {
- sth := &types.SignedTreeHead{}
- nextSTH := &types.SignedTreeHead{TreeHead: types.TreeHead{Timestamp: 1}}
- ev := &event{
- keyHash: &types.Hash{},
- cosignature: &types.Signature{},
- }
- wantCTH := &types.CosignedTreeHead{
- SignedTreeHead: *sth,
- KeyHash: []types.Hash{*ev.keyHash},
- Cosignature: []types.Signature{*ev.cosignature},
- }
- sm := &StateManagerSingle{
- signedTreeHead: sth,
- cosignatures: make(map[types.Hash]*types.Signature),
- events: make(chan *event, 1),
- }
- defer close(sm.events)
-
- sm.events <- ev
- sm.rotate(nextSTH)
- if got, want := sm.signedTreeHead, nextSTH; !reflect.DeepEqual(got, want) {
- t.Errorf("got to-cosign tree head\n\t%v\nbut wanted\n\t%v", got, want)
- }
- if got, want := sm.cosignedTreeHead, wantCTH; !reflect.DeepEqual(got, want) {
- t.Errorf("got cosigned tree head\n\t%v\nbut wanted\n\t%v", got, want)
- }
-
- sth = nextSTH
- nextSTH = &types.SignedTreeHead{TreeHead: types.TreeHead{Timestamp: 2}}
- sm.rotate(nextSTH)
- if got, want := sm.signedTreeHead, nextSTH; !reflect.DeepEqual(got, want) {
- t.Errorf("got to-cosign tree head\n\t%v\nbut wanted\n\t%v", got, want)
- }
- if got := sm.cosignedTreeHead; got != nil {
- t.Errorf("expected no cosignatures to be available")
- }
-}
-
-func mustKeyPair(t *testing.T) (crypto.Signer, types.PublicKey) {
- t.Helper()
- vk, sk, err := ed25519.GenerateKey(rand.Reader)
- if err != nil {
- t.Fatal(err)
- }
- var pub types.PublicKey
- copy(pub[:], vk[:])
- return sk, pub
-}
-
-func mustSign(t *testing.T, s crypto.Signer, th *types.TreeHead, kh *types.Hash) *types.SignedTreeHead {
- t.Helper()
- sth, err := th.Sign(s, kh)
- if err != nil {
- t.Fatal(err)
- }
- return sth
-}
diff --git a/pkg/state/state_manager.go b/pkg/state/state_manager.go
deleted file mode 100644
index 9533479..0000000
--- a/pkg/state/state_manager.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package state
-
-import (
- "context"
-
- "git.sigsum.org/sigsum-go/pkg/types"
-)
-
-// StateManager coordinates access to a log's tree heads and (co)signatures.
-type StateManager interface {
- // ToCosignTreeHead returns the log's to-cosign tree head
- ToCosignTreeHead(context.Context) (*types.SignedTreeHead, error)
-
- // CosignedTreeHead returns the log's cosigned tree head
- CosignedTreeHead(context.Context) (*types.CosignedTreeHead, error)
-
- // AddCosignature verifies that a cosignature is valid for the to-cosign
- // tree head before adding it
- AddCosignature(context.Context, *types.PublicKey, *types.Signature) error
-
- // Run peridically rotates the log's to-cosign and cosigned tree heads
- Run(context.Context)
-}
-
-// event is a verified cosignature request
-type event struct {
- keyHash *types.Hash
- cosignature *types.Signature
-}