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 }