github.com/bartle-stripe/trillian@v1.2.1/storage/cloudspanner/admin.go (about)

     1  // Copyright 2018 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package cloudspanner
    16  
    17  import (
    18  	"context"
    19  	"errors"
    20  	"fmt"
    21  	"sync"
    22  	"time"
    23  
    24  	"cloud.google.com/go/spanner"
    25  	"github.com/golang/glog"
    26  	"github.com/golang/protobuf/proto"
    27  	"github.com/golang/protobuf/ptypes"
    28  	"github.com/google/trillian"
    29  	"github.com/google/trillian/crypto/keyspb"
    30  	"github.com/google/trillian/crypto/sigpb"
    31  	"github.com/google/trillian/storage"
    32  	"github.com/google/trillian/storage/cloudspanner/spannerpb"
    33  	"google.golang.org/grpc/codes"
    34  	"google.golang.org/grpc/status"
    35  )
    36  
    37  var (
    38  	// NumUnseqBuckets is the length of the unsequenced time ring buffer.
    39  	NumUnseqBuckets = int64(4)
    40  	// NumMerkleBuckets is the number of individual buckets below each unsequenced ring buffer.
    41  	NumMerkleBuckets = int64(16)
    42  	// TimeNow is the function used to get the current time. Exposed so it may be mocked by tests.
    43  	TimeNow = time.Now
    44  
    45  	errRollback = errors.New("rollback")
    46  
    47  	treeStateMap = map[trillian.TreeState]spannerpb.TreeState{
    48  		trillian.TreeState_ACTIVE: spannerpb.TreeState_ACTIVE,
    49  		trillian.TreeState_FROZEN: spannerpb.TreeState_FROZEN,
    50  	}
    51  	treeTypeMap = map[trillian.TreeType]spannerpb.TreeType{
    52  		trillian.TreeType_LOG: spannerpb.TreeType_LOG,
    53  		trillian.TreeType_MAP: spannerpb.TreeType_MAP,
    54  	}
    55  	hashStrategyMap = map[trillian.HashStrategy]spannerpb.HashStrategy{
    56  		trillian.HashStrategy_RFC6962_SHA256:        spannerpb.HashStrategy_RFC_6962,
    57  		trillian.HashStrategy_TEST_MAP_HASHER:       spannerpb.HashStrategy_TEST_MAP_HASHER,
    58  		trillian.HashStrategy_OBJECT_RFC6962_SHA256: spannerpb.HashStrategy_OBJECT_RFC6962_SHA256,
    59  		trillian.HashStrategy_CONIKS_SHA512_256:     spannerpb.HashStrategy_CONIKS_SHA512_256,
    60  	}
    61  	hashAlgMap = map[sigpb.DigitallySigned_HashAlgorithm]spannerpb.HashAlgorithm{
    62  		sigpb.DigitallySigned_SHA256: spannerpb.HashAlgorithm_SHA256,
    63  	}
    64  	signatureAlgMap = map[sigpb.DigitallySigned_SignatureAlgorithm]spannerpb.SignatureAlgorithm{
    65  		sigpb.DigitallySigned_RSA:   spannerpb.SignatureAlgorithm_RSA,
    66  		sigpb.DigitallySigned_ECDSA: spannerpb.SignatureAlgorithm_ECDSA,
    67  	}
    68  
    69  	treeStateReverseMap    = reverseTreeStateMap(treeStateMap)
    70  	treeTypeReverseMap     = reverseTreeTypeMap(treeTypeMap)
    71  	hashStrategyReverseMap = reverseHashStrategyMap(hashStrategyMap)
    72  	hashAlgReverseMap      = reverseHashAlgMap(hashAlgMap)
    73  	signatureAlgReverseMap = reverseSignatureAlgMap(signatureAlgMap)
    74  )
    75  
    76  func reverseTreeStateMap(m map[trillian.TreeState]spannerpb.TreeState) map[spannerpb.TreeState]trillian.TreeState {
    77  	reverse := make(map[spannerpb.TreeState]trillian.TreeState)
    78  	for k, v := range m {
    79  		if x, ok := reverse[v]; ok {
    80  			glog.Fatalf("Duplicate values for key %v: %v and %v", v, x, k)
    81  		}
    82  		reverse[v] = k
    83  	}
    84  	return reverse
    85  }
    86  
    87  func reverseTreeTypeMap(m map[trillian.TreeType]spannerpb.TreeType) map[spannerpb.TreeType]trillian.TreeType {
    88  	reverse := make(map[spannerpb.TreeType]trillian.TreeType)
    89  	for k, v := range m {
    90  		if x, ok := reverse[v]; ok {
    91  			glog.Fatalf("Duplicate values for key %v: %v and %v", v, x, k)
    92  		}
    93  		reverse[v] = k
    94  	}
    95  	return reverse
    96  }
    97  
    98  func reverseHashStrategyMap(m map[trillian.HashStrategy]spannerpb.HashStrategy) map[spannerpb.HashStrategy]trillian.HashStrategy {
    99  	reverse := make(map[spannerpb.HashStrategy]trillian.HashStrategy)
   100  	for k, v := range m {
   101  		if x, ok := reverse[v]; ok {
   102  			glog.Fatalf("Duplicate values for key %v: %v and %v", v, x, k)
   103  		}
   104  		reverse[v] = k
   105  	}
   106  	return reverse
   107  }
   108  
   109  func reverseHashAlgMap(m map[sigpb.DigitallySigned_HashAlgorithm]spannerpb.HashAlgorithm) map[spannerpb.HashAlgorithm]sigpb.DigitallySigned_HashAlgorithm {
   110  	reverse := make(map[spannerpb.HashAlgorithm]sigpb.DigitallySigned_HashAlgorithm)
   111  	for k, v := range m {
   112  		if x, ok := reverse[v]; ok {
   113  			glog.Fatalf("Duplicate values for key %v: %v and %v", v, x, k)
   114  		}
   115  		reverse[v] = k
   116  	}
   117  	return reverse
   118  }
   119  
   120  func reverseSignatureAlgMap(m map[sigpb.DigitallySigned_SignatureAlgorithm]spannerpb.SignatureAlgorithm) map[spannerpb.SignatureAlgorithm]sigpb.DigitallySigned_SignatureAlgorithm {
   121  	reverse := make(map[spannerpb.SignatureAlgorithm]sigpb.DigitallySigned_SignatureAlgorithm)
   122  	for k, v := range m {
   123  		if x, ok := reverse[v]; ok {
   124  			glog.Fatalf("Duplicate values for key %v: %v and %v", v, x, k)
   125  		}
   126  		reverse[v] = k
   127  	}
   128  	return reverse
   129  }
   130  
   131  // adminTX implements both storage.ReadOnlyAdminTX and storage.AdminTX.
   132  type adminTX struct {
   133  	client *spanner.Client
   134  
   135  	// tx is either spanner.ReadOnlyTransaction or spanner.ReadWriteTransaction,
   136  	// according to the role adminTX is meant to fill.
   137  	// If tx is a snapshot transaction it'll be set to nil when adminTX is
   138  	// closed to avoid reuse.
   139  	tx spanRead
   140  
   141  	// mu guards closed, but it's only actively used for
   142  	// Commit/Rollback/Closed. In other scenarios we trust Spanner to blow up
   143  	// if you try to use a closed tx.
   144  	// Note that, if tx is a spanner.SnapshotTransaction, it'll be set to
   145  	// nil when adminTX is closed.
   146  	mu     sync.RWMutex
   147  	closed bool
   148  }
   149  
   150  // adminStorage implements storage.AdminStorage.
   151  type adminStorage struct {
   152  	client *spanner.Client
   153  }
   154  
   155  // NewAdminStorage returns a Spanner-based storage.AdminStorage implementation.
   156  func NewAdminStorage(client *spanner.Client) storage.AdminStorage {
   157  	return &adminStorage{client}
   158  }
   159  
   160  func (s *adminStorage) CheckDatabaseAccessible(ctx context.Context) error {
   161  	return checkDatabaseAccessible(ctx, s.client)
   162  }
   163  
   164  func (s *adminStorage) Snapshot(ctx context.Context) (storage.ReadOnlyAdminTX, error) {
   165  	tx := s.client.ReadOnlyTransaction()
   166  	return &adminTX{client: s.client, tx: tx}, nil
   167  }
   168  
   169  func (s *adminStorage) Begin(ctx context.Context) (storage.AdminTX, error) {
   170  	return nil, ErrNotImplemented
   171  }
   172  
   173  func (s *adminStorage) ReadWriteTransaction(ctx context.Context, f storage.AdminTXFunc) error {
   174  	_, err := s.client.ReadWriteTransaction(ctx, func(ctx context.Context, stx *spanner.ReadWriteTransaction) error {
   175  		tx := &adminTX{client: s.client, tx: stx}
   176  		return f(ctx, tx)
   177  	})
   178  	return err
   179  }
   180  
   181  func (t *adminTX) Commit() error {
   182  	return t.Close()
   183  }
   184  
   185  func (t *adminTX) Rollback() error {
   186  	if err := t.Close(); err != nil {
   187  		return nil
   188  	}
   189  	return errRollback
   190  }
   191  
   192  func (t *adminTX) IsClosed() bool {
   193  	t.mu.RLock()
   194  	defer t.mu.RUnlock()
   195  	return t.closed
   196  }
   197  
   198  func (t *adminTX) Close() error {
   199  	t.mu.Lock()
   200  	defer t.mu.Unlock()
   201  	if t.tx == nil {
   202  		return nil
   203  	}
   204  	// tx will be committed by ReadWriteTransaction(), so only close readonly tx here
   205  	if stx, ok := t.tx.(*spanner.ReadOnlyTransaction); ok {
   206  		glog.V(1).Infof("Closed admin %p", stx)
   207  		stx.Close()
   208  	}
   209  	t.tx = nil
   210  	return nil
   211  }
   212  
   213  func (t *adminTX) GetTree(ctx context.Context, treeID int64) (*trillian.Tree, error) {
   214  	info, err := t.getTreeInfo(ctx, treeID)
   215  	if err != nil {
   216  		return nil, err
   217  	}
   218  	return toTrillianTree(info)
   219  }
   220  
   221  func (t *adminTX) getTreeInfo(ctx context.Context, treeID int64) (*spannerpb.TreeInfo, error) {
   222  	cols := []string{
   223  		"TreeID",
   224  		"TreeState",
   225  		"TreeType",
   226  		"TreeInfo",
   227  		"Deleted",
   228  		"DeleteTimeMillis",
   229  	}
   230  
   231  	row, err := t.tx.ReadRow(ctx, "TreeRoots", spanner.Key{treeID}, cols)
   232  	switch {
   233  	case spanner.ErrCode(err) == codes.NotFound:
   234  		// Improve on the error message
   235  		return nil, status.Errorf(codes.NotFound, "tree %v not found", treeID)
   236  	case err != nil:
   237  		return nil, err
   238  	}
   239  
   240  	info := &spannerpb.TreeInfo{}
   241  	var infoBytes []byte
   242  	var tID, tState, tType int64
   243  	var deleted bool
   244  	var delMillis spanner.NullInt64
   245  	if err := row.Columns(
   246  		&tID,
   247  		&tState, //info.TreeState,
   248  		&tType,  //info.TreeType,
   249  		&infoBytes,
   250  		&deleted,
   251  		&delMillis,
   252  	); err != nil {
   253  		return nil, err
   254  	}
   255  
   256  	if infoBytes != nil {
   257  		if err := proto.Unmarshal(infoBytes, info); err != nil {
   258  			return nil, err
   259  		}
   260  	}
   261  	if tID != info.TreeId {
   262  		return nil, fmt.Errorf("inconsistency, treeIDs don't match: %d != %d", tID, info.TreeId)
   263  	}
   264  	if treeID != tID {
   265  		return nil, fmt.Errorf("inconsistency, got treeID %d, want %d", tID, treeID)
   266  	}
   267  	// TODO(al): check other denormalisations are consistent too.
   268  
   269  	// Sanity checks
   270  	switch tt := info.TreeType; tt {
   271  	case spannerpb.TreeType_LOG:
   272  		if info.GetLogStorageConfig() == nil {
   273  			return nil, status.Errorf(codes.Internal, "corrupt TreeInfo %#v: LogStorageConfig is nil", treeID)
   274  		}
   275  	case spannerpb.TreeType_MAP:
   276  		if info.GetMapStorageConfig() == nil {
   277  			return nil, status.Errorf(codes.Internal, "corrupt TreeInfo #%v: MapStorageConfig is nil", treeID)
   278  		}
   279  	default:
   280  		return nil, status.Errorf(codes.Internal, "corrupt TreeInfo %#v: unexpected TreeType = %s", treeID, tt)
   281  	}
   282  
   283  	return info, nil
   284  }
   285  
   286  func (t *adminTX) ListTreeIDs(ctx context.Context, includeDeleted bool) ([]int64, error) {
   287  	ids := []int64{}
   288  	err := t.readTrees(ctx, includeDeleted, true /* idOnly */, func(r *spanner.Row) error {
   289  		var id int64
   290  		if err := r.Columns(&id); err != nil {
   291  			return err
   292  		}
   293  		ids = append(ids, id)
   294  		return nil
   295  	})
   296  	return ids, err
   297  }
   298  
   299  func (t *adminTX) ListTrees(ctx context.Context, includeDeleted bool) ([]*trillian.Tree, error) {
   300  	trees := []*trillian.Tree{}
   301  	err := t.readTrees(ctx, includeDeleted, false /* idOnly */, func(r *spanner.Row) error {
   302  		info := &spannerpb.TreeInfo{}
   303  		if err := r.Columns(info); err != nil {
   304  			return err
   305  		}
   306  		tree, err := toTrillianTree(info)
   307  		if err != nil {
   308  			return err
   309  		}
   310  		trees = append(trees, tree)
   311  		return nil
   312  	})
   313  	return trees, err
   314  }
   315  
   316  func (t *adminTX) readTrees(ctx context.Context, includeDeleted, idOnly bool, f func(*spanner.Row) error) error {
   317  	var stmt spanner.Statement
   318  	if idOnly {
   319  		stmt = spanner.NewStatement("SELECT idx.TreeID FROM TreeRootsByDeleted idx")
   320  	} else {
   321  		stmt = spanner.NewStatement(
   322  			"SELECT t.TreeInfo FROM TreeRootsByDeleted idx" +
   323  				" INNER JOIN TreeRoots t ON idx.TreeID = t.TreeID")
   324  	}
   325  	if !includeDeleted {
   326  		stmt.SQL += " WHERE idx.Deleted = @deleted"
   327  		stmt.Params["deleted"] = false
   328  	}
   329  	rows := t.tx.Query(ctx, stmt)
   330  	return rows.Do(f)
   331  }
   332  
   333  func (t *adminTX) CreateTree(ctx context.Context, tree *trillian.Tree) (*trillian.Tree, error) {
   334  	if err := storage.ValidateTreeForCreation(ctx, tree); err != nil {
   335  		return nil, err
   336  	}
   337  
   338  	id, err := storage.NewTreeID()
   339  	if err != nil {
   340  		return nil, err
   341  	}
   342  
   343  	info, err := newTreeInfo(tree, id, TimeNow())
   344  	if err != nil {
   345  		return nil, err
   346  	}
   347  
   348  	infoBytes, err := proto.Marshal(info)
   349  	if err != nil {
   350  		return nil, err
   351  	}
   352  
   353  	m1 := spanner.Insert(
   354  		"TreeRoots",
   355  		[]string{
   356  			"TreeID",
   357  			"TreeState",
   358  			"TreeType",
   359  			"TreeInfo",
   360  			"Deleted",
   361  		},
   362  		[]interface{}{
   363  			info.TreeId,
   364  			int64(info.TreeState),
   365  			int64(info.TreeType),
   366  			infoBytes,
   367  			false,
   368  		})
   369  
   370  	stx, ok := t.tx.(*spanner.ReadWriteTransaction)
   371  	if !ok {
   372  		return nil, ErrWrongTXType
   373  	}
   374  	if err := stx.BufferWrite([]*spanner.Mutation{m1}); err != nil {
   375  		return nil, err
   376  	}
   377  	return toTrillianTree(info)
   378  }
   379  
   380  // newTreeInfo creates a new TreeInfo from a Tree. Meant to be used for new trees.
   381  func newTreeInfo(tree *trillian.Tree, treeID int64, now time.Time) (*spannerpb.TreeInfo, error) {
   382  	ts, ok := treeStateMap[tree.TreeState]
   383  	if !ok {
   384  		return nil, status.Errorf(codes.Internal, "unexpected TreeState: %s", tree.TreeState)
   385  	}
   386  
   387  	tt, ok := treeTypeMap[tree.TreeType]
   388  	if !ok {
   389  		return nil, status.Errorf(codes.Internal, "unexpected TreeType: %s", tree.TreeType)
   390  	}
   391  
   392  	hs, ok := hashStrategyMap[tree.HashStrategy]
   393  	if !ok {
   394  		return nil, status.Errorf(codes.Internal, "unexpected HashStrategy: %s", tree.HashStrategy)
   395  	}
   396  
   397  	ha, ok := hashAlgMap[tree.HashAlgorithm]
   398  	if !ok {
   399  		return nil, status.Errorf(codes.Internal, "unexpected HashAlgorithm: %s", tree.HashAlgorithm)
   400  	}
   401  
   402  	sa, ok := signatureAlgMap[tree.SignatureAlgorithm]
   403  	if !ok {
   404  		return nil, status.Errorf(codes.Internal, "unexpected SignatureAlgorithm: %s", tree.SignatureAlgorithm)
   405  	}
   406  
   407  	maxRootDuration, err := ptypes.Duration(tree.MaxRootDuration)
   408  	if err != nil {
   409  		return nil, status.Errorf(codes.InvalidArgument, "malformed MaxRootDuration: %v", err)
   410  	}
   411  
   412  	info := &spannerpb.TreeInfo{
   413  		TreeId:                treeID,
   414  		Name:                  tree.DisplayName,
   415  		Description:           tree.Description,
   416  		TreeState:             ts,
   417  		TreeType:              tt,
   418  		HashStrategy:          hs,
   419  		HashAlgorithm:         ha,
   420  		SignatureAlgorithm:    sa,
   421  		CreateTimeNanos:       now.UnixNano(),
   422  		UpdateTimeNanos:       now.UnixNano(),
   423  		PrivateKey:            tree.GetPrivateKey(),
   424  		PublicKeyDer:          tree.GetPublicKey().GetDer(),
   425  		MaxRootDurationMillis: int64(maxRootDuration / time.Millisecond),
   426  	}
   427  
   428  	switch tree.TreeType {
   429  	case trillian.TreeType_LOG:
   430  		config, err := logConfigOrDefault(tree)
   431  		if err != nil {
   432  			return nil, err
   433  		}
   434  		if err := validateLogStorageConfig(config); err != nil {
   435  			return nil, err
   436  		}
   437  		info.StorageConfig = &spannerpb.TreeInfo_LogStorageConfig{LogStorageConfig: config}
   438  	case trillian.TreeType_MAP:
   439  		config, err := mapConfigOrDefault(tree)
   440  		if err != nil {
   441  			return nil, err
   442  		}
   443  		// Nothing to validate on MapStorageConfig.
   444  		info.StorageConfig = &spannerpb.TreeInfo_MapStorageConfig{MapStorageConfig: config}
   445  	}
   446  
   447  	return info, nil
   448  }
   449  
   450  func logConfigOrDefault(tree *trillian.Tree) (*spannerpb.LogStorageConfig, error) {
   451  	settings, err := unmarshalSettings(tree)
   452  	if err != nil {
   453  		return nil, err
   454  	}
   455  	if settings == nil {
   456  		return &spannerpb.LogStorageConfig{
   457  			NumUnseqBuckets:  NumUnseqBuckets,
   458  			NumMerkleBuckets: NumMerkleBuckets,
   459  		}, nil
   460  	}
   461  	config, ok := settings.(*spannerpb.LogStorageConfig)
   462  	if !ok {
   463  		return nil, status.Errorf(codes.Internal, "unsupported config type for LOG tree: %T", settings)
   464  	}
   465  	return config, nil
   466  }
   467  
   468  func mapConfigOrDefault(tree *trillian.Tree) (*spannerpb.MapStorageConfig, error) {
   469  	settings, err := unmarshalSettings(tree)
   470  	if err != nil {
   471  		return nil, err
   472  	}
   473  	if settings == nil {
   474  		return &spannerpb.MapStorageConfig{}, nil
   475  	}
   476  	config, ok := settings.(*spannerpb.MapStorageConfig)
   477  	if !ok {
   478  		return nil, status.Errorf(codes.Internal, "unsupported config type for MAP tree: %T", settings)
   479  	}
   480  	return config, nil
   481  }
   482  
   483  func (t *adminTX) UpdateTree(ctx context.Context, treeID int64, updateFunc func(*trillian.Tree)) (*trillian.Tree, error) {
   484  	info, err := t.getTreeInfo(ctx, treeID)
   485  	if err != nil {
   486  		return nil, err
   487  	}
   488  
   489  	tree, err := toTrillianTree(info)
   490  	if err != nil {
   491  		return nil, err
   492  	}
   493  	beforeTree := *tree
   494  	updateFunc(tree)
   495  	if err = storage.ValidateTreeForUpdate(ctx, &beforeTree, tree); err != nil {
   496  		return nil, err
   497  	}
   498  	if !proto.Equal(beforeTree.StorageSettings, tree.StorageSettings) {
   499  		return nil, status.New(codes.InvalidArgument, "readonly field changed: storage_settings").Err()
   500  	}
   501  
   502  	ts, ok := treeStateMap[tree.TreeState]
   503  	if !ok {
   504  		return nil, status.Errorf(codes.Internal, "unexpected TreeState: %s", tree.TreeState)
   505  	}
   506  
   507  	maxRootDuration, err := ptypes.Duration(tree.MaxRootDuration)
   508  	if err != nil {
   509  		return nil, status.Errorf(codes.InvalidArgument, "malformed MaxRootDuration: %v", err)
   510  	}
   511  
   512  	// Update (just) the mutable fields in treeInfo.
   513  	now := TimeNow()
   514  	info.TreeState = ts
   515  	info.Name = tree.DisplayName
   516  	info.Description = tree.Description
   517  	info.UpdateTimeNanos = now.UnixNano()
   518  	info.MaxRootDurationMillis = int64(maxRootDuration / time.Millisecond)
   519  	info.PrivateKey = tree.PrivateKey
   520  
   521  	if err := t.updateTreeInfo(ctx, info); err != nil {
   522  		return nil, err
   523  	}
   524  
   525  	return toTrillianTree(info)
   526  }
   527  
   528  func (t *adminTX) updateTreeInfo(ctx context.Context, info *spannerpb.TreeInfo) error {
   529  	m1 := spanner.Update(
   530  		"TreeRoots",
   531  		[]string{
   532  			"TreeID",
   533  			"TreeState",
   534  			"TreeType",
   535  			"HashStrategy",
   536  			"HashAlgorithm",
   537  			"SignatureAlgorithm",
   538  			"DisplayName",
   539  			"Description",
   540  			"CreateTimeNanos",
   541  			"UpdateTimeNanos",
   542  			"MaxRootDurationMillis",
   543  			"PrivateKey",
   544  			"PublicKey",
   545  		},
   546  		[]interface{}{
   547  			info.TreeId,
   548  			info.TreeState,
   549  			info.TreeType,
   550  			info.HashStrategy,
   551  			info.HashAlgorithm,
   552  			info.SignatureAlgorithm,
   553  			info.Name,
   554  			info.Description,
   555  			info.CreateTimeNanos,
   556  			info.UpdateTimeNanos,
   557  			info.MaxRootDurationMillis,
   558  			info.PrivateKey,
   559  			info.PublicKeyDer,
   560  		})
   561  
   562  	stx, ok := t.tx.(*spanner.ReadWriteTransaction)
   563  	if !ok {
   564  		return ErrWrongTXType
   565  	}
   566  	return stx.BufferWrite([]*spanner.Mutation{m1})
   567  }
   568  
   569  func (t *adminTX) SoftDeleteTree(ctx context.Context, treeID int64) (*trillian.Tree, error) {
   570  	info, err := t.getTreeInfo(ctx, treeID)
   571  	if err != nil {
   572  		return nil, err
   573  	}
   574  	if info.Deleted {
   575  		return nil, status.Errorf(codes.FailedPrecondition, "tree %v already soft deleted", treeID)
   576  	}
   577  
   578  	info.Deleted = true
   579  	info.DeleteTimeNanos = TimeNow().UnixNano()
   580  	if err := t.updateTreeInfo(ctx, info); err != nil {
   581  		return nil, err
   582  	}
   583  
   584  	return toTrillianTree(info)
   585  }
   586  
   587  func (t *adminTX) HardDeleteTree(ctx context.Context, treeID int64) error {
   588  	info, err := t.getTreeInfo(ctx, treeID)
   589  	if err != nil {
   590  		return err
   591  	}
   592  	if !info.Deleted {
   593  		return status.Errorf(codes.FailedPrecondition, "tree %v is not soft deleted", treeID)
   594  	}
   595  
   596  	stx, ok := t.tx.(*spanner.ReadWriteTransaction)
   597  	if !ok {
   598  		return ErrWrongTXType
   599  	}
   600  
   601  	// Due to cloud spanner sizing recommendations, we don't interleave our tables
   602  	// which means no ON DELETE CASCADE goodies for us, so we have to
   603  	// transactionally delete related data from all tables.
   604  	return stx.BufferWrite([]*spanner.Mutation{
   605  		spanner.Delete("TreeRoots", spanner.Key{info.TreeId}),
   606  		spanner.Delete("TreeHeads", spanner.Key{info.TreeId}.AsPrefix()),
   607  		spanner.Delete("SubtreeData", spanner.Key{info.TreeId}.AsPrefix()),
   608  		spanner.Delete("LeafData", spanner.Key{info.TreeId}.AsPrefix()),
   609  		spanner.Delete("SequencedLeafData", spanner.Key{info.TreeId}.AsPrefix()),
   610  		spanner.Delete("Unsequenced", spanner.Key{info.TreeId}.AsPrefix()),
   611  		spanner.Delete("MapLeafData", spanner.Key{info.TreeId}.AsPrefix()),
   612  	})
   613  }
   614  
   615  func (t *adminTX) UndeleteTree(ctx context.Context, treeID int64) (*trillian.Tree, error) {
   616  	info, err := t.getTreeInfo(ctx, treeID)
   617  	if err != nil {
   618  		return nil, err
   619  	}
   620  	if !info.Deleted {
   621  		return nil, status.Errorf(codes.FailedPrecondition, "tree %v is not soft deleted", treeID)
   622  	}
   623  
   624  	info.Deleted = false
   625  	info.DeleteTimeNanos = 0
   626  	if err := t.updateTreeInfo(ctx, info); err != nil {
   627  		return nil, err
   628  	}
   629  
   630  	return toTrillianTree(info)
   631  }
   632  
   633  func toTrillianTree(info *spannerpb.TreeInfo) (*trillian.Tree, error) {
   634  	createdPB, err := ptypes.TimestampProto(time.Unix(0, info.CreateTimeNanos))
   635  	if err != nil {
   636  		return nil, status.Errorf(codes.Internal, "failed to convert creation time: %v", err)
   637  	}
   638  	updatedPB, err := ptypes.TimestampProto(time.Unix(0, info.UpdateTimeNanos))
   639  	if err != nil {
   640  		return nil, status.Errorf(codes.Internal, "failed to convert creation time: %v", err)
   641  	}
   642  	tree := &trillian.Tree{
   643  		TreeId:          info.TreeId,
   644  		DisplayName:     info.Name,
   645  		Description:     info.Description,
   646  		CreateTime:      createdPB,
   647  		UpdateTime:      updatedPB,
   648  		PrivateKey:      info.PrivateKey,
   649  		PublicKey:       &keyspb.PublicKey{Der: info.PublicKeyDer},
   650  		MaxRootDuration: ptypes.DurationProto(time.Duration(info.MaxRootDurationMillis) * time.Millisecond),
   651  	}
   652  
   653  	ts, ok := treeStateReverseMap[info.TreeState]
   654  	if !ok {
   655  		return nil, status.Errorf(codes.Internal, "unexpected TreeState: %s", info.TreeState)
   656  	}
   657  	tree.TreeState = ts
   658  
   659  	tt, ok := treeTypeReverseMap[info.TreeType]
   660  	if !ok {
   661  		return nil, status.Errorf(codes.Internal, "unexpected TreeType: %s", info.TreeType)
   662  	}
   663  	tree.TreeType = tt
   664  
   665  	hs, ok := hashStrategyReverseMap[info.HashStrategy]
   666  	if !ok {
   667  		return nil, status.Errorf(codes.Internal, "unexpected HashStrategy: %s", info.HashStrategy)
   668  	}
   669  	tree.HashStrategy = hs
   670  
   671  	ha, ok := hashAlgReverseMap[info.HashAlgorithm]
   672  	if !ok {
   673  		return nil, status.Errorf(codes.Internal, "unexpected HashAlgorithm: %s", info.HashAlgorithm)
   674  	}
   675  	tree.HashAlgorithm = ha
   676  
   677  	sa, ok := signatureAlgReverseMap[info.SignatureAlgorithm]
   678  	if !ok {
   679  		return nil, status.Errorf(codes.Internal, "unexpected SignatureAlgorithm: %s", info.SignatureAlgorithm)
   680  	}
   681  	tree.SignatureAlgorithm = sa
   682  
   683  	var config proto.Message
   684  	switch info.TreeType {
   685  	case spannerpb.TreeType_LOG:
   686  		config = info.GetLogStorageConfig()
   687  	case spannerpb.TreeType_MAP:
   688  		config = info.GetMapStorageConfig()
   689  	}
   690  	settings, err := ptypes.MarshalAny(config)
   691  	if err != nil {
   692  		return nil, err
   693  	}
   694  	tree.StorageSettings = settings
   695  
   696  	if info.Deleted {
   697  		tree.Deleted = info.Deleted
   698  	}
   699  	if info.DeleteTimeNanos > 0 {
   700  		var err error
   701  		tree.DeleteTime, err = ptypes.TimestampProto(time.Unix(0, info.DeleteTimeNanos))
   702  		if err != nil {
   703  			return nil, status.Errorf(codes.Internal, "failed to convert delete time: %v", err)
   704  		}
   705  	}
   706  
   707  	return tree, nil
   708  }
   709  
   710  // unmarshalSettings returns the message obtained from tree.StorageSettings.
   711  // If tree.StorageSettings is nil no unmarshaling will be attempted; instead the method will return
   712  // (nil, nil).
   713  func unmarshalSettings(tree *trillian.Tree) (proto.Message, error) {
   714  	settings := tree.GetStorageSettings()
   715  	if settings == nil {
   716  		return nil, nil
   717  	}
   718  	any := &ptypes.DynamicAny{}
   719  	if err := ptypes.UnmarshalAny(settings, any); err != nil {
   720  		return nil, err
   721  	}
   722  	return any.Message, nil
   723  }
   724  
   725  func validateLogStorageConfig(config *spannerpb.LogStorageConfig) error {
   726  	if config.NumUnseqBuckets < 1 {
   727  		return status.Errorf(codes.InvalidArgument, "NumUnseqBuckets = %v, want > 0", config.NumUnseqBuckets)
   728  	}
   729  	if config.NumMerkleBuckets < 1 || config.NumMerkleBuckets > 256 {
   730  		return status.Errorf(codes.InvalidArgument, "NumMerkleBuckets = %v, want a number in range [1, 256]", config.NumMerkleBuckets)
   731  	}
   732  	return nil
   733  }