diff options
Diffstat (limited to 'pkg/db')
| -rw-r--r-- | pkg/db/client.go | 17 | ||||
| -rw-r--r-- | pkg/db/mocks/client.go | 111 | ||||
| -rw-r--r-- | pkg/db/mocks/trillian.go | 317 | ||||
| -rw-r--r-- | pkg/db/trillian.go | 193 | ||||
| -rw-r--r-- | pkg/db/trillian_test.go | 530 | 
5 files changed, 1168 insertions, 0 deletions
| diff --git a/pkg/db/client.go b/pkg/db/client.go new file mode 100644 index 0000000..090dfff --- /dev/null +++ b/pkg/db/client.go @@ -0,0 +1,17 @@ +package db + +import ( +	"context" + +	"git.sigsum.org/sigsum-lib-go/pkg/requests" +	"git.sigsum.org/sigsum-lib-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 new file mode 100644 index 0000000..182869f --- /dev/null +++ b/pkg/db/mocks/client.go @@ -0,0 +1,111 @@ +// 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-lib-go/pkg/requests" +	types "git.sigsum.org/sigsum-lib-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 new file mode 100644 index 0000000..8aa3a58 --- /dev/null +++ b/pkg/db/mocks/trillian.go @@ -0,0 +1,317 @@ +// 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 new file mode 100644 index 0000000..ab57db6 --- /dev/null +++ b/pkg/db/trillian.go @@ -0,0 +1,193 @@ +package db + +import ( +	"context" +	"fmt" + +	"git.sigsum.org/sigsum-lib-go/pkg/requests" +	"git.sigsum.org/sigsum-lib-go/pkg/types" +	"github.com/golang/glog" +	"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:  req.Checksum, +		}, +		Signature: req.Signature, +		KeyHash:   *types.HashFn(req.VerificationKey[:]), +	} +	serialized := leaf.ToBinary() + +	glog.V(3).Infof("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(lr.TimestampNanos / 1000 / 1000 / 1000), +		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 new file mode 100644 index 0000000..a33458f --- /dev/null +++ b/pkg/db/trillian_test.go @@ -0,0 +1,530 @@ +package db + +import ( +	"context" +	"fmt" +	"reflect" +	"testing" + +	"git.sigsum.org/sigsum-lib-go/pkg/requests" +	"git.sigsum.org/sigsum-lib-go/pkg/types" +	"git.sigsum.org/sigsum-log-go/pkg/db/mocks" +	"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{ +		Statement: types.Statement{ +			ShardHint: 0, +			Checksum:  types.Hash{}, +		}, +		Signature:       types.Signature{}, +		VerificationKey: 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 +			} +			if got, want := th, table.wantTh; !reflect.DeepEqual(got, want) { +				t.Errorf("got tree head\n\t%v\nbut wanted\n\t%v\nin 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) +			} +		}() +	} +} | 
