aboutsummaryrefslogtreecommitdiff
path: root/pkg/db
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/db')
-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.go193
-rw-r--r--pkg/db/trillian_test.go530
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)
+ }
+ }()
+ }
+}