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 ×tamp, &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 ×tamp, &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 }