@@ -22,7 +22,7 @@ import qualified Data.List.NonEmpty as NE
22import Data.Time.Clock
22import Data.Time.Clock
23import qualified Data.Text as T
23import qualified Data.Text as T
24import GHC.Generics
24import GHC.Generics
25−import Data.Binary
25+import Data.Binary as B
26import Data.Either (lefts, rights, isRight)
26import Data.Either (lefts, rights, isRight)
27#if __GLASGOW_HASKELL__ < 804
27#if __GLASGOW_HASKELL__ < 804
28import Data.Monoid
28import Data.Monoid
@@ -30,6 +30,10 @@ import Data.Monoid
30import Control.Arrow
30import Control.Arrow
31import Data.Maybe
31import Data.Maybe
32import Data.UUID.V4
32import Data.UUID.V4
33+import qualified Data.ByteString as BS
34+import qualified Data.ByteString.Lazy as BL
35+import ProjectM36.DatabaseContext as DBC
36+import Crypto.Hash.SHA256
34-- | Record a lookup for a specific transaction in the graph.
38-- | Record a lookup for a specific transaction in the graph.
35data TransactionIdLookup = TransactionIdLookup TransactionId |
39data TransactionIdLookup = TransactionIdLookup TransactionId |
@@ -67,7 +71,8 @@ bootstrapTransactionGraph stamp' freshId context = TransactionGraph bootstrapHea
67 bootstrapHeads = M.singleton "master" freshTransaction
71 bootstrapHeads = M.singleton "master" freshTransaction
68 newSchemas = Schemas context M.empty
72 newSchemas = Schemas context M.empty
69 freshTransaction = fresh freshId stamp' newSchemas
73 freshTransaction = fresh freshId stamp' newSchemas
74+ hashedTransaction = Transaction freshId ((transactionInfo freshTransaction) { merkleHash = calculateMerkleHash freshTransaction emptyTransactionGraph }) newSchemas
70− bootstrapTransactions = S.singleton freshTransaction
75+ bootstrapTransactions = S.singleton hashedTransaction
72-- | Create a transaction graph from a context.
77-- | Create a transaction graph from a context.
73freshTransactionGraph :: DatabaseContext -> IO (TransactionGraph, TransactionId)
78freshTransactionGraph :: DatabaseContext -> IO (TransactionGraph, TransactionId)
@@ -125,10 +130,13 @@ addBranch stamp' newId newBranchName branchPointId graph = do
126--adds a disconnected transaction to a transaction graph at some head
131--adds a disconnected transaction to a transaction graph at some head
127addDisconnectedTransaction :: UTCTime -> TransactionId -> HeadName -> DisconnectedTransaction -> TransactionGraph -> Either RelationalError (Transaction, TransactionGraph)
132addDisconnectedTransaction :: UTCTime -> TransactionId -> HeadName -> DisconnectedTransaction -> TransactionGraph -> Either RelationalError (Transaction, TransactionGraph)
128−addDisconnectedTransaction stamp' newId headName (DisconnectedTransaction parentId schemas' _) = addTransactionToGraph headName newTrans
133+addDisconnectedTransaction stamp' newId headName (DisconnectedTransaction parentId schemas' _) graph = addTransactionToGraph headName newTrans' graph
135+ newTrans = Transaction newId newTInfo schemas'
130− newTrans = Transaction newId (TI.singleParent parentId stamp') schemas'
136+ newTInfo = TI.singleParent parentId stamp'
131−
137+ newTrans' = Transaction newId (newTInfo { merkleHash = newHash }) schemas'
138+ newHash = calculateMerkleHash newTrans graph--lookup parents in graph to calculate
132addTransactionToGraph :: HeadName -> Transaction -> TransactionGraph -> Either RelationalError (Transaction, TransactionGraph)
140addTransactionToGraph :: HeadName -> Transaction -> TransactionGraph -> Either RelationalError (Transaction, TransactionGraph)
133addTransactionToGraph headName newTrans graph = do
141addTransactionToGraph headName newTrans graph = do
134 let parentIds' = parentIds newTrans
142 let parentIds' = parentIds newTrans
@@ -295,6 +303,7 @@ graphAsRelation (DisconnectedTransaction parentId _ _) graph@(TransactionGraph _
295 mkRelationFromList attrs tupleMatrix
303 mkRelationFromList attrs tupleMatrix
297 attrs = A.attributesFromList [Attribute "id" TextAtomType,
305 attrs = A.attributesFromList [Attribute "id" TextAtomType,
306+ Attribute "hash" ByteStringAtomType,
298 Attribute "stamp" DateTimeAtomType,
307 Attribute "stamp" DateTimeAtomType,
299 Attribute "parents" (RelationAtomType parentAttributes),
308 Attribute "parents" (RelationAtomType parentAttributes),
300 Attribute "current" BoolAtomType,
309 Attribute "current" BoolAtomType,
@@ -304,6 +313,7 @@ graphAsRelation (DisconnectedTransaction parentId _ _) graph@(TransactionGraph _
304 tupleGenerator transaction = case transactionParentsRelation transaction graph of
313 tupleGenerator transaction = case transactionParentsRelation transaction graph of
305 Left err -> Left err
314 Left err -> Left err
306 Right parentTransRel -> Right [TextAtom $ T.pack $ show (transactionId transaction),
315 Right parentTransRel -> Right [TextAtom $ T.pack $ show (transactionId transaction),
316+ ByteStringAtom $ merkleHash (transactionInfo transaction),
307 DateTimeAtom (timestamp transaction),
317 DateTimeAtom (timestamp transaction),
308 RelationAtom parentTransRel,
318 RelationAtom parentTransRel,
309 BoolAtom $ parentId == transactionId transaction,
319 BoolAtom $ parentId == transactionId transaction,
@@ -338,7 +348,8 @@ createMergeTransaction stamp' newId (SelectedBranchMergeStrategy selectedBranch)
338 pure $ Transaction newId (TransactionInfo {
348 pure $ Transaction newId (TransactionInfo {
339 parents = NE.fromList [transactionId trans1,
349 parents = NE.fromList [transactionId trans1,
340 transactionId trans2],
350 transactionId trans2],
341− stamp = stamp' }) (schemas selectedTrans)
352+ merkleHash = unimplemented }) (schemas selectedTrans)
343-- merge functions, relvars, individually
354-- merge functions, relvars, individually
344createMergeTransaction stamp' newId strat@UnionMergeStrategy t2 =
355createMergeTransaction stamp' newId strat@UnionMergeStrategy t2 =
@@ -496,7 +507,8 @@ createUnionMergeTransaction stamp' newId strategy (t1,t2) = do
496 pure (Transaction newId (TransactionInfo {
507 pure (Transaction newId (TransactionInfo {
497 parents = NE.fromList [transactionId t1,
508 parents = NE.fromList [transactionId t1,
498 transactionId t2],
509 transactionId t2],
499− stamp = stamp'}) newSchemas)
510+ stamp = stamp',
511+ merkleHash = unimplemented }) newSchemas)
501lookupTransaction :: TransactionGraph -> TransactionIdLookup -> Either RelationalError Transaction
513lookupTransaction :: TransactionGraph -> TransactionIdLookup -> Either RelationalError Transaction
502lookupTransaction graph (TransactionIdLookup tid) = transactionForId tid graph
514lookupTransaction graph (TransactionIdLookup tid) = transactionForId tid graph
@@ -572,3 +584,21 @@ autoMergeToHead stamp' (tempBranchTransId, tempCommitTransId, mergeTransId) disc
572 pure (discon''''', graph''''')
584 pure (discon''''', graph''''')
587+-- the new hash includes the parents' ids, the current id, and the hash of the context, and the merkle hashes of the parent transactions
588+calculateMerkleHash :: Transaction -> TransactionGraph -> BS.ByteString
589+calculateMerkleHash trans graph = hashlazy (mconcat ([transIds,
592+ parentMerkleHashes]))
594+ parentMerkleHashes = BL.fromChunks $ map getMerkleHash parentTranses
596+ case transactionsForIds (parentIds trans) graph of
597+ Left RootTransactionTraversalError -> []
598+ Left e -> error ("failed to find transaction in Merkle hash construction: " ++ show e)
599+ Right t -> S.toList t
600+ getMerkleHash t = merkleHash (transactionInfo t)
601+ transIds = B.encode (transactionId trans : S.toList (parentIds trans))
602+ dbcBytes = DBC.hashBytes (concreteDatabaseContext trans)
603+ schemasBytes = B.encode (subschemas trans)