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

     1  // Copyright 2016 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 mysql
    16  
    17  import (
    18  	"context"
    19  	"database/sql"
    20  
    21  	"github.com/google/trillian"
    22  	"github.com/google/trillian/merkle/hashers"
    23  	"github.com/google/trillian/storage"
    24  	"github.com/google/trillian/storage/cache"
    25  	"github.com/google/trillian/types"
    26  
    27  	"github.com/golang/glog"
    28  	"github.com/golang/protobuf/proto"
    29  )
    30  
    31  const (
    32  	insertMapHeadSQL = `INSERT INTO MapHead(TreeId, MapHeadTimestamp, RootHash, MapRevision, RootSignature, MapperData)
    33  	VALUES(?, ?, ?, ?, ?, ?)`
    34  	selectLatestSignedMapRootSQL = `SELECT MapHeadTimestamp, RootHash, MapRevision, RootSignature, MapperData
    35  		 FROM MapHead WHERE TreeId=?
    36  		 ORDER BY MapHeadTimestamp DESC LIMIT 1`
    37  	selectGetSignedMapRootSQL = `SELECT MapHeadTimestamp, RootHash, MapRevision, RootSignature, MapperData
    38  		 FROM MapHead WHERE TreeId=? AND MapRevision=?`
    39  	insertMapLeafSQL = `INSERT INTO MapLeaf(TreeId, KeyHash, MapRevision, LeafValue) VALUES (?, ?, ?, ?)`
    40  	selectMapLeafSQL = `
    41   SELECT t1.KeyHash, t1.MapRevision, t1.LeafValue
    42   FROM MapLeaf t1
    43   INNER JOIN
    44   (
    45  	SELECT TreeId, KeyHash, MAX(MapRevision) as maxrev
    46  	FROM MapLeaf t0
    47  	WHERE t0.KeyHash IN (` + placeholderSQL + `) AND
    48  	      t0.TreeId = ? AND t0.MapRevision <= ?
    49  	GROUP BY t0.TreeId, t0.KeyHash
    50   ) t2
    51   ON t1.TreeId=t2.TreeId
    52   AND t1.KeyHash=t2.KeyHash
    53   AND t1.MapRevision=t2.maxrev`
    54  )
    55  
    56  var defaultMapStrata = []int{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 176}
    57  
    58  type mySQLMapStorage struct {
    59  	*mySQLTreeStorage
    60  	admin storage.AdminStorage
    61  }
    62  
    63  // NewMapStorage creates a storage.MapStorage instance for the specified MySQL URL.
    64  // It assumes storage.AdminStorage is backed by the same MySQL database as well.
    65  func NewMapStorage(db *sql.DB) storage.MapStorage {
    66  	return &mySQLMapStorage{
    67  		admin:            NewAdminStorage(db),
    68  		mySQLTreeStorage: newTreeStorage(db),
    69  	}
    70  }
    71  
    72  func (m *mySQLMapStorage) CheckDatabaseAccessible(ctx context.Context) error {
    73  	return m.db.PingContext(ctx)
    74  }
    75  
    76  type readOnlyMapTX struct {
    77  	*sql.Tx
    78  }
    79  
    80  func (m *mySQLMapStorage) Snapshot(ctx context.Context) (storage.ReadOnlyMapTX, error) {
    81  	tx, err := m.db.BeginTx(ctx, nil /* opts */)
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  	return &readOnlyMapTX{tx}, nil
    86  }
    87  
    88  func (t *readOnlyMapTX) Close() error {
    89  	if err := t.Rollback(); err != nil && err != sql.ErrTxDone {
    90  		glog.Warningf("Rollback error on Close(): %v", err)
    91  		return err
    92  	}
    93  	return nil
    94  }
    95  
    96  func (m *mySQLMapStorage) begin(ctx context.Context, tree *trillian.Tree, readonly bool) (storage.MapTreeTX, error) {
    97  	hasher, err := hashers.NewMapHasher(tree.HashStrategy)
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  
   102  	stCache := cache.NewMapSubtreeCache(defaultMapStrata, tree.TreeId, hasher)
   103  	ttx, err := m.beginTreeTx(ctx, tree, hasher.Size(), stCache)
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  
   108  	mtx := &mapTreeTX{
   109  		treeTX:       ttx,
   110  		ms:           m,
   111  		readRevision: -1,
   112  	}
   113  
   114  	if readonly {
   115  		// readRevision will be set later, by the first
   116  		// GetSignedMapRoot/LatestSignedMapRoot operation.
   117  		return mtx, nil
   118  	}
   119  
   120  	// A read-write transaction needs to know the current revision
   121  	// so it can write at revision+1.
   122  	root, err := mtx.LatestSignedMapRoot(ctx)
   123  	if err != nil && err != storage.ErrTreeNeedsInit {
   124  		return nil, err
   125  	}
   126  	if err == storage.ErrTreeNeedsInit {
   127  		return mtx, err
   128  	}
   129  
   130  	var mr types.MapRootV1
   131  	if err := mr.UnmarshalBinary(root.MapRoot); err != nil {
   132  		return nil, err
   133  	}
   134  
   135  	mtx.readRevision = int64(mr.Revision)
   136  	mtx.treeTX.writeRevision = int64(mr.Revision) + 1
   137  	return mtx, nil
   138  }
   139  
   140  func (m *mySQLMapStorage) SnapshotForTree(ctx context.Context, tree *trillian.Tree) (storage.ReadOnlyMapTreeTX, error) {
   141  	return m.begin(ctx, tree, true /* readonly */)
   142  }
   143  
   144  func (m *mySQLMapStorage) ReadWriteTransaction(ctx context.Context, tree *trillian.Tree, f storage.MapTXFunc) error {
   145  	tx, err := m.begin(ctx, tree, false /* readonly */)
   146  	if tx != nil {
   147  		defer tx.Close()
   148  	}
   149  	if err != nil && err != storage.ErrTreeNeedsInit {
   150  		return err
   151  	}
   152  	if err := f(ctx, tx); err != nil {
   153  		return err
   154  	}
   155  	return tx.Commit()
   156  }
   157  
   158  type mapTreeTX struct {
   159  	treeTX
   160  	ms           *mySQLMapStorage
   161  	readRevision int64
   162  }
   163  
   164  func (m *mapTreeTX) ReadRevision() int64 {
   165  	return int64(m.readRevision)
   166  }
   167  
   168  func (m *mapTreeTX) WriteRevision() int64 {
   169  	return m.treeTX.writeRevision
   170  }
   171  
   172  func (m *mapTreeTX) Set(ctx context.Context, keyHash []byte, value trillian.MapLeaf) error {
   173  	// TODO(al): consider storing some sort of value which represents the group of keys being set in this Tx.
   174  	//           That way, if this attempt partially fails (i.e. because some subset of the in-the-future Merkle
   175  	//           nodes do get written), we can enforce that future map update attempts are a complete replay of
   176  	//           the failed set.
   177  	flatValue, err := proto.Marshal(&value)
   178  	if err != nil {
   179  		return nil
   180  	}
   181  
   182  	stmt, err := m.tx.PrepareContext(ctx, insertMapLeafSQL)
   183  	if err != nil {
   184  		return err
   185  	}
   186  	defer stmt.Close()
   187  
   188  	_, err = stmt.ExecContext(ctx, m.treeID, keyHash, m.writeRevision, flatValue)
   189  	return err
   190  }
   191  
   192  // Get returns a list of map leaves indicated by indexes.
   193  // If an index is not found, no corresponding entry is returned.
   194  // Each MapLeaf.Index is overwritten with the index the leaf was found at.
   195  func (m *mapTreeTX) Get(ctx context.Context, revision int64, indexes [][]byte) ([]trillian.MapLeaf, error) {
   196  	// If no indexes are requested, return an empty set.
   197  	if len(indexes) == 0 {
   198  		return []trillian.MapLeaf{}, nil
   199  	}
   200  	stmt, err := m.ms.getStmt(ctx, selectMapLeafSQL, len(indexes), "?", "?")
   201  	if err != nil {
   202  		return nil, err
   203  	}
   204  	stx := m.tx.StmtContext(ctx, stmt)
   205  	defer stx.Close()
   206  
   207  	args := make([]interface{}, 0, len(indexes)+2)
   208  	for _, index := range indexes {
   209  		args = append(args, index)
   210  	}
   211  	args = append(args, m.treeID)
   212  	args = append(args, revision)
   213  
   214  	rows, err := stx.QueryContext(ctx, args...)
   215  	// It's possible there are no values for any of these keys yet
   216  	if err == sql.ErrNoRows {
   217  		return nil, nil
   218  	} else if err != nil {
   219  		return nil, err
   220  	}
   221  	defer rows.Close()
   222  
   223  	ret := make([]trillian.MapLeaf, 0, len(indexes))
   224  	nr := 0
   225  	er := 0
   226  	for rows.Next() {
   227  		var mapKeyHash []byte
   228  		var mapRevision int64
   229  		var flatData []byte
   230  		err = rows.Scan(&mapKeyHash, &mapRevision, &flatData)
   231  		if err != nil {
   232  			return nil, err
   233  		}
   234  		if len(flatData) == 0 {
   235  			er++
   236  			continue
   237  		}
   238  		var mapLeaf trillian.MapLeaf
   239  		err = proto.Unmarshal(flatData, &mapLeaf)
   240  		if err != nil {
   241  			return nil, err
   242  		}
   243  		mapLeaf.Index = mapKeyHash
   244  		ret = append(ret, mapLeaf)
   245  		nr++
   246  	}
   247  	return ret, nil
   248  }
   249  
   250  func (m *mapTreeTX) GetSignedMapRoot(ctx context.Context, revision int64) (trillian.SignedMapRoot, error) {
   251  	var timestamp, mapRevision int64
   252  	var rootHash, rootSignatureBytes []byte
   253  	var mapperMetaBytes []byte
   254  
   255  	stmt, err := m.tx.PrepareContext(ctx, selectGetSignedMapRootSQL)
   256  	if err != nil {
   257  		return trillian.SignedMapRoot{}, err
   258  	}
   259  	defer stmt.Close()
   260  
   261  	err = stmt.QueryRowContext(ctx, m.treeID, revision).Scan(
   262  		&timestamp, &rootHash, &mapRevision, &rootSignatureBytes, &mapperMetaBytes)
   263  	if err != nil {
   264  		if revision == 0 {
   265  			return trillian.SignedMapRoot{}, storage.ErrTreeNeedsInit
   266  		}
   267  		return trillian.SignedMapRoot{}, err
   268  	}
   269  	m.readRevision = mapRevision
   270  	return m.signedMapRoot(timestamp, mapRevision, rootHash, rootSignatureBytes, mapperMetaBytes)
   271  }
   272  
   273  func (m *mapTreeTX) LatestSignedMapRoot(ctx context.Context) (trillian.SignedMapRoot, error) {
   274  	var timestamp, mapRevision int64
   275  	var rootHash, rootSignatureBytes []byte
   276  	var mapperMetaBytes []byte
   277  
   278  	stmt, err := m.tx.PrepareContext(ctx, selectLatestSignedMapRootSQL)
   279  	if err != nil {
   280  		return trillian.SignedMapRoot{}, err
   281  	}
   282  	defer stmt.Close()
   283  
   284  	err = stmt.QueryRowContext(ctx, m.treeID).Scan(
   285  		&timestamp, &rootHash, &mapRevision, &rootSignatureBytes, &mapperMetaBytes)
   286  
   287  	// It's possible there are no roots for this tree yet
   288  	if err == sql.ErrNoRows {
   289  		return trillian.SignedMapRoot{}, storage.ErrTreeNeedsInit
   290  	} else if err != nil {
   291  		return trillian.SignedMapRoot{}, err
   292  	}
   293  	m.readRevision = mapRevision
   294  	return m.signedMapRoot(timestamp, mapRevision, rootHash, rootSignatureBytes, mapperMetaBytes)
   295  }
   296  
   297  func (m *mapTreeTX) signedMapRoot(timestamp, mapRevision int64, rootHash, rootSignature, mapperMeta []byte) (trillian.SignedMapRoot, error) {
   298  	mapRoot, err := (&types.MapRootV1{
   299  		RootHash:       rootHash,
   300  		TimestampNanos: uint64(timestamp),
   301  		Revision:       uint64(mapRevision),
   302  		Metadata:       mapperMeta,
   303  	}).MarshalBinary()
   304  	if err != nil {
   305  		return trillian.SignedMapRoot{}, err
   306  	}
   307  
   308  	return trillian.SignedMapRoot{
   309  		MapRoot:   mapRoot,
   310  		Signature: rootSignature,
   311  	}, nil
   312  }
   313  
   314  func (m *mapTreeTX) StoreSignedMapRoot(ctx context.Context, root trillian.SignedMapRoot) error {
   315  	var r types.MapRootV1
   316  	if err := r.UnmarshalBinary(root.MapRoot); err != nil {
   317  		return err
   318  	}
   319  
   320  	stmt, err := m.tx.PrepareContext(ctx, insertMapHeadSQL)
   321  	if err != nil {
   322  		return err
   323  	}
   324  	defer stmt.Close()
   325  
   326  	// TODO(al): store transactionLogHead too
   327  	res, err := stmt.ExecContext(ctx, m.treeID, r.TimestampNanos, r.RootHash, r.Revision, root.Signature, r.Metadata)
   328  
   329  	if err != nil {
   330  		glog.Warningf("Failed to store signed map root: %s", err)
   331  	}
   332  
   333  	return checkResultOkAndRowCountIs(res, err, 1)
   334  }