mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-03-30 15:08:33 +00:00

* [NOD-828] Reimplement FFLDB (#663) * [NOD-828] Create the database2 package that will some day replace the database package. * [NOD-828] Implement a "bucket" key mechanism. * [NOD-828] Move bucket.go into the ffldb2 package. * [NOD-828] Delete the un-interfaced ffldb package from database2, since we aren't going to be using it anyway. * [NOD-828] Copy over + fixup flat file structs from the old ffldb. * [NOD-828] Implement flatFilePath. * [NOD-828] Implement flatFileStore.write(). * [NOD-828] Implement flatFileStore.read(). * [NOD-828] Implement flatFileStore.rollback(). * [NOD-828] Sync the file to disk at the end of write(). * [NOD-828] Extract crc32ByteOrder to a separate variable. * [NOD-828] Add a sanity test. * [NOD-828] Remove context-unrelated methods from the Database interface. * [NOD-828] Create an ffldb object. Simply work against a context. * [NOD-828] Open the new database on start. * [NOD-828] Create the leveldb package. * [NOD-828] Implement opening/closing leveldb. * [NOD-828] Implement get/put out of/into leveldb. * [NOD-828] Implement transactions and make them implement a generic database interface. * [NOD-828] Write sanity tests for leveldb with and without transactions. * [NOD-828] Add another case to the transaction sanity test. * [NOD-828] Implement AppendBlock/RetrieveBlock. * [NOD-828] Refactor so that concepts such as "block" and "metadata" don't leak into the database package. * [NOD-828] Add RollbackFlatData to the database interface. * [NOD-828] Remove anything from dbaccess that I'm not planning to implement as part of this ticket. * [NOD-828] Implement StoreBlock. * [NOD-828] Implement FetchBlock. * [NOD-828] Implement HasBlock. * [NOD-828] Write a sanity test for block insertion. * [NOD-828] Implement CurrentFlatDataLocation. * [NOD-828] Implement storing the current block location. * [NOD-828] Implement initializing/syncing the flat file block store and the "metadata". * [NOD-828] Add InitBlockStore to TestBlockStoreSanity. * [NOD-828] Fix rename errors. * [NOD-828] Fix lint errors in the root database package. * [NOD-828] Fix lint errors in the ffldb.go. * [NOD-828] Fix lint errors in the flatfile/db.go. * [NOD-828] Rename packages in such a way to make the linter happy. * [NOD-828] Finish satisfying the linter. * [NOD-828] Fix doc.go. * [NOD-828] Fix comments in block.go. * [NOD-828] Move dbaccess out of the database package. * [NOD-828] Move opening/closing the database to dbaccess. * [NOD-828] Move the Database interface to the root database package, since it's meant to be generic. * [NOD-828] Make ffldb generic to later support additional databases. * [NOD-828] Make ffldb.Open return DatabaseHandle, since ffldb is no longer exported. * [NOD-828] Fix comments. * [NOD-828] Rename AppendFlatData to AppendToStore and RetrieveFlatData to RetrieveFromStore. * [NOD-828] Make buckets nicer to use. * [NOD-828] Implement cursors that iterate over some bucket. * [NOD-828] Generalize flat-file repairing and move block database repairing into ffldb. * [NOD-828] Write a test making sure that flat file repair works. * [NOD-828] Properly close the database in TestRepairFlatFiles. * [NOD-828] Add a comment warning against putting and getting the same data within the same transaction. * [NOD-828] Fix the flatFilesBucket description. * [NOD-828] Remove superfluous whitespace. * [NOD-828] Fix the ffldb description. * [NOD-828] Rename Database to DataAccessor and rename Handle to Database. * [NOD-828] Make Get return nil if the value doesn't exist. * [NOD-828] Attempt to close leveldb even if closing ffdb failed. * [NOD-828] Fix a bug where the wrong location would be written to the current store location bucket. * [NOD-828] Fix not updating the store location in ffldb transactions. * [NOD-828] Make scanFlatFiles return an error if os.Stat fails for any reason other than file-not-found. * [NOD-828] Update the README and doc.go. * [NOD-828] Simplify Bucket.Path(). * [NOD-828] Since LevelDBCursor satisfied the database2.Cursor interface, use it directly. * [NOD-828] Combine two lines into one. * [NOD-828] Combine another two lines into one. * [NOD-828] Move a misplaced comment. * [NOD-828] Use Wrapf instead of Errorf where appropriate. * [NOD-828] Fix a comment. * [NOD-828] Fix a comment. * [NOD-828] Fix Wrapf calls. * [NOD-828] Fix comments. * [NOD-828] Remove superfluous whitespace. * [NOD-828] Add a comment explaining the use of batches and snapshots. * [NOD-828] Implement RollbackUnlessClosed(). * [NOD-828] Return both errors in StoreBlock rollback. * [NOD-828] Move rollback-on-error logic into ffldb. Remove CurrentStoreLocation and RollbackStore from DataAccessor. * [NOD-828] Make bucket a type alias instead of a struct. * [NOD-828] Fix a typo. * [NOD-828] Use copy instead of append in Bucket. * [NOD-828] Extract flatFileLocationSerializedSize to a const. * [NOD-828] Debugf -> Warnf in rollback.go. * [NOD-828] Fix a comment. * [NOD-828] Fix a comment. * [NOD-828] Remove data length from flat file data format. * [NOD-828] Rearrange TestLevelDBTransactionSanity a bit. * [NOD-828] Add stack traces to all errors that come out of library functions. * [NOD-828] Return errors from rollback(). * [NOD-828] Remove an irrelevant comment. * [NOD-828] Remove redundant whitespace. * [NOD-828] Handle nil in FetchBlock. * [NOD-828] Move the explanation about batches and snapshots to the LevelDBTransaction struct. * Revert "[NOD-828] Make bucket a type alias instead of a struct." This reverts commit 1fd39652 * [NOD-828] Fix revert errors. * Revert "[NOD-828] Remove data length from flat file data format." This reverts commit ef408e32 * [NOD-862] Move Cursor() into the DataAccessor interface. * [NOD-828] Add Delete to DataAccessor. * [NOD-828] Fix a couple of places that erroneously referenced blocks. * [NOD-828] Add a comment on top of flatFileLocationSerializedSize. * [NOD-828] Add Seek to Cursor. * [NOD-828] Add First to Cursor. * [NOD-828] Rename db to accessor in Context. * [NOD-828] Make Get/Fetch calls return a boolean to indicate whether the requested item was found. * [NOD-828] Name the output parameters of all Get functions. * [NOD-828] Make RetrieveFromStore return whether the data was found. * [NOD-887] Add a couple of QoL features to Cursor (#674) * [NOD-887] Changed First to not return an error. * [NOD-887] Fix merge error. * [NOD-887] Make Cursor.Key not return the entire key path. * [NOD-888] Add RollbackUnlessClosed to Context (#676) * [NOD-888] Add RollbackUnlessClosed to Context. * [NOD-888] Fix copy+paste error. * [NOD-889] Instead of returning a boolean for not-found, return an error (#677) * [NOD-889] Instead of returning a boolean for not-found, return an error. * [NOD-889] Wrapped ErrNotFound for Get calls with nicer error messages. * [NOD-889] Fix format. * [NOD-889] Fix double space in a comment. * [NOD-889] Add IsNotFoundError to dbaccess. * [NOD-862] Replace calls to Tx.StoreBlock, Tx.HasBlock, Tx.FetchBlock with appropriate calls in dbaccess (#672) * [NOD-828] Create the database2 package that will some day replace the database package. * [NOD-828] Implement a "bucket" key mechanism. * [NOD-828] Move bucket.go into the ffldb2 package. * [NOD-828] Delete the un-interfaced ffldb package from database2, since we aren't going to be using it anyway. * [NOD-828] Copy over + fixup flat file structs from the old ffldb. * [NOD-828] Implement flatFilePath. * [NOD-828] Implement flatFileStore.write(). * [NOD-828] Implement flatFileStore.read(). * [NOD-828] Implement flatFileStore.rollback(). * [NOD-828] Sync the file to disk at the end of write(). * [NOD-828] Extract crc32ByteOrder to a separate variable. * [NOD-828] Add a sanity test. * [NOD-828] Remove context-unrelated methods from the Database interface. * [NOD-828] Create an ffldb object. Simply work against a context. * [NOD-828] Open the new database on start. * [NOD-828] Create the leveldb package. * [NOD-828] Implement opening/closing leveldb. * [NOD-828] Implement get/put out of/into leveldb. * [NOD-828] Implement transactions and make them implement a generic database interface. * [NOD-828] Write sanity tests for leveldb with and without transactions. * [NOD-828] Add another case to the transaction sanity test. * [NOD-828] Implement AppendBlock/RetrieveBlock. * [NOD-828] Refactor so that concepts such as "block" and "metadata" don't leak into the database package. * [NOD-828] Add RollbackFlatData to the database interface. * [NOD-828] Remove anything from dbaccess that I'm not planning to implement as part of this ticket. * [NOD-828] Implement StoreBlock. * [NOD-828] Implement FetchBlock. * [NOD-828] Implement HasBlock. * [NOD-828] Write a sanity test for block insertion. * [NOD-828] Implement CurrentFlatDataLocation. * [NOD-828] Implement storing the current block location. * [NOD-828] Implement initializing/syncing the flat file block store and the "metadata". * [NOD-828] Add InitBlockStore to TestBlockStoreSanity. * [NOD-828] Fix rename errors. * [NOD-828] Fix lint errors in the root database package. * [NOD-828] Fix lint errors in the ffldb.go. * [NOD-828] Fix lint errors in the flatfile/db.go. * [NOD-828] Rename packages in such a way to make the linter happy. * [NOD-828] Finish satisfying the linter. * [NOD-828] Fix doc.go. * [NOD-828] Fix comments in block.go. * [NOD-828] Move dbaccess out of the database package. * [NOD-828] Move opening/closing the database to dbaccess. * [NOD-828] Move the Database interface to the root database package, since it's meant to be generic. * [NOD-828] Make ffldb generic to later support additional databases. * [NOD-828] Make ffldb.Open return DatabaseHandle, since ffldb is no longer exported. * [NOD-828] Fix comments. * [NOD-828] Rename AppendFlatData to AppendToStore and RetrieveFlatData to RetrieveFromStore. * [NOD-828] Make buckets nicer to use. * [NOD-828] Implement cursors that iterate over some bucket. * [NOD-828] Generalize flat-file repairing and move block database repairing into ffldb. * [NOD-828] Write a test making sure that flat file repair works. * [NOD-828] Properly close the database in TestRepairFlatFiles. * [NOD-828] Add a comment warning against putting and getting the same data within the same transaction. * [NOD-862] Use dbaccess.HasBlock instead of Tx.HasBlock in initDAGState. * [NOD-862] Use dbaccess.StoreBlock instead of dbStoreBlock. * [NOD-862] Use dbaccess.FetchBlock instead of various block fetching mechanisms. * [NOD-828] Fix the flatFilesBucket description. * [NOD-828] Remove superfluous whitespace. * [NOD-828] Fix the ffldb description. * [NOD-828] Rename Database to DataAccessor and rename Handle to Database. * [NOD-828] Make Get return nil if the value doesn't exist. * [NOD-828] Attempt to close leveldb even if closing ffdb failed. * [NOD-828] Fix a bug where the wrong location would be written to the current store location bucket. * [NOD-828] Fix not updating the store location in ffldb transactions. * [NOD-828] Make scanFlatFiles return an error if os.Stat fails for any reason other than file-not-found. * [NOD-828] Update the README and doc.go. * [NOD-828] Simplify Bucket.Path(). * [NOD-828] Since LevelDBCursor satisfied the database2.Cursor interface, use it directly. * [NOD-828] Combine two lines into one. * [NOD-828] Combine another two lines into one. * [NOD-828] Move a misplaced comment. * [NOD-828] Use Wrapf instead of Errorf where appropriate. * [NOD-828] Fix a comment. * [NOD-828] Fix a comment. * [NOD-828] Fix Wrapf calls. * [NOD-828] Fix comments. * [NOD-828] Remove superfluous whitespace. * [NOD-828] Add a comment explaining the use of batches and snapshots. * [NOD-828] Implement RollbackUnlessClosed(). * [NOD-828] Return both errors in StoreBlock rollback. * [NOD-828] Move rollback-on-error logic into ffldb. Remove CurrentStoreLocation and RollbackStore from DataAccessor. * [NOD-828] Make bucket a type alias instead of a struct. * [NOD-828] Fix a typo. * [NOD-828] Use copy instead of append in Bucket. * [NOD-828] Extract flatFileLocationSerializedSize to a const. * [NOD-828] Debugf -> Warnf in rollback.go. * [NOD-828] Fix a comment. * [NOD-828] Fix a comment. * [NOD-828] Remove data length from flat file data format. * [NOD-828] Rearrange TestLevelDBTransactionSanity a bit. * [NOD-828] Add stack traces to all errors that come out of library functions. * [NOD-828] Return errors from rollback(). * [NOD-828] Remove an irrelevant comment. * [NOD-828] Remove redundant whitespace. * [NOD-828] Handle nil in FetchBlock. * [NOD-828] Implement a dbaccess.BlockNode struct. * [NOD-828] Move the explanation about batches and snapshots to the LevelDBTransaction struct. * [NOD-828] Implement toDBBlockNode and fromDBBlockNode. * Revert "[NOD-828] Make bucket a type alias instead of a struct." This reverts commit 1fd39652 * [NOD-828] Fix revert errors. * Revert "[NOD-828] Remove data length from flat file data format." This reverts commit ef408e32 * [NOD-862] Implement storing index blocks. * [NOD-862] Use database transactions where appropriate. * [NOD-862] Fix tests failing on DAGSetup. * [NOD-862] Fix bad make call. * [NOD-862] Fix remaining database opening problems in tests. * [NOD-862] Move Cursor() into the DataAccessor interface. * [NOD-862] Move Cursor() into the DataAccessor interface. * [NOD-862] Iterate over the new block index in dagio. * [NOD-862] Fix block index key. * [NOD-828] Add Delete to DataAccessor. * [NOD-862] Pass byte slices to dbaccess instead of objects. * [NOD-862] Fix errors. * [NOD-862] Fix maybeAcceptBlock not checking block existence. * [NOD-862] Fix TestAcceptanceIndexRecover. * [NOD-862] Add comments to StoreBlockIndex and BlockIndexCursor. * [NOD-828] Fix a couple of places that erroneously referenced blocks. * [NOD-828] Add a comment on top of flatFileLocationSerializedSize. * [NOD-828] Add Seek to Cursor. * [NOD-828] Add First to Cursor. * [NOD-828] Rename db to accessor in Context. * [NOD-828] Make Get/Fetch calls return a boolean to indicate whether the requested item was found. * [NOD-828] Name the output parameters of all Get functions. * [NOD-828] Make RetrieveFromStore return whether the data was found. * [NOD-862] Fix merge errors. * [NOD-862] Fix DAGSetup using bad temp directories. * [NOD-862] Fix TestProcessDelayedBlocks not closing the database properly. * [NOD-862] Fix merge errors. * [NOD-862] Merge flushToDBWithContext and flushToDB. * [NOD-862] Remove TODO. * [NOD-862] Add prefix to the temp dir in DAGSetup. * [NOD-862] Bring back dbFetchBlockByHash. * [NOD-862] Use BlockDAG.BlockByHash in p2p and rpc. * [NOD-862] Use daghash.Hash in dbaccess. * [NOD-862] Add defer to RollbackUnlessClosed after NewTx(). * [NOD-862] Extract dbStoreBlock to a separate function. * [NOD-862] Fix grammar in comment. * [NOD-862] Fix merge errors. * [NOD-867] Migrate database logic in blockdag/dagio.go to dbaccess (#675) * [NOD-828] Create the database2 package that will some day replace the database package. * [NOD-828] Implement a "bucket" key mechanism. * [NOD-828] Move bucket.go into the ffldb2 package. * [NOD-828] Delete the un-interfaced ffldb package from database2, since we aren't going to be using it anyway. * [NOD-828] Copy over + fixup flat file structs from the old ffldb. * [NOD-828] Implement flatFilePath. * [NOD-828] Implement flatFileStore.write(). * [NOD-828] Implement flatFileStore.read(). * [NOD-828] Implement flatFileStore.rollback(). * [NOD-828] Sync the file to disk at the end of write(). * [NOD-828] Extract crc32ByteOrder to a separate variable. * [NOD-828] Add a sanity test. * [NOD-828] Remove context-unrelated methods from the Database interface. * [NOD-828] Create an ffldb object. Simply work against a context. * [NOD-828] Open the new database on start. * [NOD-828] Create the leveldb package. * [NOD-828] Implement opening/closing leveldb. * [NOD-828] Implement get/put out of/into leveldb. * [NOD-828] Implement transactions and make them implement a generic database interface. * [NOD-828] Write sanity tests for leveldb with and without transactions. * [NOD-828] Add another case to the transaction sanity test. * [NOD-828] Implement AppendBlock/RetrieveBlock. * [NOD-828] Refactor so that concepts such as "block" and "metadata" don't leak into the database package. * [NOD-828] Add RollbackFlatData to the database interface. * [NOD-828] Remove anything from dbaccess that I'm not planning to implement as part of this ticket. * [NOD-828] Implement StoreBlock. * [NOD-828] Implement FetchBlock. * [NOD-828] Implement HasBlock. * [NOD-828] Write a sanity test for block insertion. * [NOD-828] Implement CurrentFlatDataLocation. * [NOD-828] Implement storing the current block location. * [NOD-828] Implement initializing/syncing the flat file block store and the "metadata". * [NOD-828] Add InitBlockStore to TestBlockStoreSanity. * [NOD-828] Fix rename errors. * [NOD-828] Fix lint errors in the root database package. * [NOD-828] Fix lint errors in the ffldb.go. * [NOD-828] Fix lint errors in the flatfile/db.go. * [NOD-828] Rename packages in such a way to make the linter happy. * [NOD-828] Finish satisfying the linter. * [NOD-828] Fix doc.go. * [NOD-828] Fix comments in block.go. * [NOD-828] Move dbaccess out of the database package. * [NOD-828] Move opening/closing the database to dbaccess. * [NOD-828] Move the Database interface to the root database package, since it's meant to be generic. * [NOD-828] Make ffldb generic to later support additional databases. * [NOD-828] Make ffldb.Open return DatabaseHandle, since ffldb is no longer exported. * [NOD-828] Fix comments. * [NOD-828] Rename AppendFlatData to AppendToStore and RetrieveFlatData to RetrieveFromStore. * [NOD-828] Make buckets nicer to use. * [NOD-828] Implement cursors that iterate over some bucket. * [NOD-828] Generalize flat-file repairing and move block database repairing into ffldb. * [NOD-828] Write a test making sure that flat file repair works. * [NOD-828] Properly close the database in TestRepairFlatFiles. * [NOD-828] Add a comment warning against putting and getting the same data within the same transaction. * [NOD-862] Use dbaccess.HasBlock instead of Tx.HasBlock in initDAGState. * [NOD-862] Use dbaccess.StoreBlock instead of dbStoreBlock. * [NOD-862] Use dbaccess.FetchBlock instead of various block fetching mechanisms. * [NOD-828] Fix the flatFilesBucket description. * [NOD-828] Remove superfluous whitespace. * [NOD-828] Fix the ffldb description. * [NOD-828] Rename Database to DataAccessor and rename Handle to Database. * [NOD-828] Make Get return nil if the value doesn't exist. * [NOD-828] Attempt to close leveldb even if closing ffdb failed. * [NOD-828] Fix a bug where the wrong location would be written to the current store location bucket. * [NOD-828] Fix not updating the store location in ffldb transactions. * [NOD-828] Make scanFlatFiles return an error if os.Stat fails for any reason other than file-not-found. * [NOD-828] Update the README and doc.go. * [NOD-828] Simplify Bucket.Path(). * [NOD-828] Since LevelDBCursor satisfied the database2.Cursor interface, use it directly. * [NOD-828] Combine two lines into one. * [NOD-828] Combine another two lines into one. * [NOD-828] Move a misplaced comment. * [NOD-828] Use Wrapf instead of Errorf where appropriate. * [NOD-828] Fix a comment. * [NOD-828] Fix a comment. * [NOD-828] Fix Wrapf calls. * [NOD-828] Fix comments. * [NOD-828] Remove superfluous whitespace. * [NOD-828] Add a comment explaining the use of batches and snapshots. * [NOD-828] Implement RollbackUnlessClosed(). * [NOD-828] Return both errors in StoreBlock rollback. * [NOD-828] Move rollback-on-error logic into ffldb. Remove CurrentStoreLocation and RollbackStore from DataAccessor. * [NOD-828] Make bucket a type alias instead of a struct. * [NOD-828] Fix a typo. * [NOD-828] Use copy instead of append in Bucket. * [NOD-828] Extract flatFileLocationSerializedSize to a const. * [NOD-828] Debugf -> Warnf in rollback.go. * [NOD-828] Fix a comment. * [NOD-828] Fix a comment. * [NOD-828] Remove data length from flat file data format. * [NOD-828] Rearrange TestLevelDBTransactionSanity a bit. * [NOD-828] Add stack traces to all errors that come out of library functions. * [NOD-828] Return errors from rollback(). * [NOD-828] Remove an irrelevant comment. * [NOD-828] Remove redundant whitespace. * [NOD-828] Handle nil in FetchBlock. * [NOD-828] Implement a dbaccess.BlockNode struct. * [NOD-828] Move the explanation about batches and snapshots to the LevelDBTransaction struct. * [NOD-828] Implement toDBBlockNode and fromDBBlockNode. * Revert "[NOD-828] Make bucket a type alias instead of a struct." This reverts commit 1fd39652 * [NOD-828] Fix revert errors. * Revert "[NOD-828] Remove data length from flat file data format." This reverts commit ef408e32 * [NOD-862] Implement storing index blocks. * [NOD-862] Use database transactions where appropriate. * [NOD-862] Fix tests failing on DAGSetup. * [NOD-862] Fix bad make call. * [NOD-862] Fix remaining database opening problems in tests. * [NOD-862] Move Cursor() into the DataAccessor interface. * [NOD-862] Move Cursor() into the DataAccessor interface. * [NOD-862] Iterate over the new block index in dagio. * [NOD-862] Fix block index key. * [NOD-828] Add Delete to DataAccessor. * [NOD-862] Pass byte slices to dbaccess instead of objects. * [NOD-862] Fix errors. * [NOD-862] Fix maybeAcceptBlock not checking block existence. * [NOD-862] Fix TestAcceptanceIndexRecover. * [NOD-862] Add comments to StoreBlockIndex and BlockIndexCursor. * [NOD-828] Fix a couple of places that erroneously referenced blocks. * [NOD-828] Add a comment on top of flatFileLocationSerializedSize. * [NOD-828] Add Seek to Cursor. * [NOD-828] Add First to Cursor. * [NOD-828] Rename db to accessor in Context. * [NOD-828] Make Get/Fetch calls return a boolean to indicate whether the requested item was found. * [NOD-828] Name the output parameters of all Get functions. * [NOD-828] Make RetrieveFromStore return whether the data was found. * [NOD-862] Fix merge errors. * [NOD-862] Fix DAGSetup using bad temp directories. * [NOD-862] Fix TestProcessDelayedBlocks not closing the database properly. * [NOD-867] Remove blockIndexBucket from dagio. * [NOD-867] Fix wrong key in StoreIndexBucket. * [NOD-867] Migrate DAG state to dbaccess. * [NOD-867] Remove utxoSetVersionKeyName. * [NOD-862] Fix merge errors. * [NOD-867] Move localSubnetworkID into dagState. * [NOD-867] Fix a comment. * [NOD-867] Remove an unused function. * [NOD-867] Migrate the database's UTXO set to dbaccess. * [NOD-867] Add missing error check. * [NOD-867] Changed First to not return an error. * [NOD-867] Make Cursor.Key not return the entire key path. * [NOD-887] Fix the comment above BlockIndexCursorFrom. * [NOD-862] Merge flushToDBWithContext and flushToDB. * [NOD-862] Remove TODO. * [NOD-862] Add prefix to the temp dir in DAGSetup. * [NOD-862] Bring back dbFetchBlockByHash. * [NOD-862] Use BlockDAG.BlockByHash in p2p and rpc. * [NOD-862] Use daghash.Hash in dbaccess. * [NOD-862] Add defer to RollbackUnlessClosed after NewTx(). * [NOD-862] Extract dbStoreBlock to a separate function. * [NOD-867] Remove TODOs. * [NOD-867] Fix merge errors. * [NOD-867] Fix comments and errors. * [NOD-867] Unexport blockIndexKey. * [NOD-867] Fix merge errors. * [NOD-867] Move a misplaced comment. * [NOD-867] Fix an error message. * [NOD-867] Remove preallocation in initDAGState. * [NOD-866] Migrate database logic in blockdag/indexers package to dbaccess (#682) * [NOD-865] Delete blockidhash.go. * [NOD-865] Remove a lot of no-longer relevant logic from indexers. * [NOD-865] Pass TxContext to ConnectBlock. * [NOD-865] Migrate the acceptance index to dbaccess. * [NOD-865] Fix a block not being sent to ConnectBlock. * [NOD-865] Pass the block's hash instead of the whole block. * [NOD-865] Add forgotten Commit call. * [NOD-865] Add comments. * [NOD-866] Fix a comment. * [NOD-866] Fix a comment. * [NOD-866] Remove pointless indirection in acceptanceindex. * [NOD-866] Fix comment over ForEachHash. * [NOD-866] Rename ClearAcceptanceIndex to DropAcceptanceIndex. * [NOD-866] Explain collecting keys before deleting them. * [NOD-865] Move misc db logic to db access (#681) * [NOD-828] Create the database2 package that will some day replace the database package. * [NOD-828] Implement a "bucket" key mechanism. * [NOD-828] Move bucket.go into the ffldb2 package. * [NOD-828] Delete the un-interfaced ffldb package from database2, since we aren't going to be using it anyway. * [NOD-828] Copy over + fixup flat file structs from the old ffldb. * [NOD-828] Implement flatFilePath. * [NOD-828] Implement flatFileStore.write(). * [NOD-828] Implement flatFileStore.read(). * [NOD-828] Implement flatFileStore.rollback(). * [NOD-828] Sync the file to disk at the end of write(). * [NOD-828] Extract crc32ByteOrder to a separate variable. * [NOD-828] Add a sanity test. * [NOD-828] Remove context-unrelated methods from the Database interface. * [NOD-828] Create an ffldb object. Simply work against a context. * [NOD-828] Open the new database on start. * [NOD-828] Create the leveldb package. * [NOD-828] Implement opening/closing leveldb. * [NOD-828] Implement get/put out of/into leveldb. * [NOD-828] Implement transactions and make them implement a generic database interface. * [NOD-828] Write sanity tests for leveldb with and without transactions. * [NOD-828] Add another case to the transaction sanity test. * [NOD-828] Implement AppendBlock/RetrieveBlock. * [NOD-828] Refactor so that concepts such as "block" and "metadata" don't leak into the database package. * [NOD-828] Add RollbackFlatData to the database interface. * [NOD-828] Remove anything from dbaccess that I'm not planning to implement as part of this ticket. * [NOD-828] Implement StoreBlock. * [NOD-828] Implement FetchBlock. * [NOD-828] Implement HasBlock. * [NOD-828] Write a sanity test for block insertion. * [NOD-828] Implement CurrentFlatDataLocation. * [NOD-828] Implement storing the current block location. * [NOD-828] Implement initializing/syncing the flat file block store and the "metadata". * [NOD-828] Add InitBlockStore to TestBlockStoreSanity. * [NOD-828] Fix rename errors. * [NOD-828] Fix lint errors in the root database package. * [NOD-828] Fix lint errors in the ffldb.go. * [NOD-828] Fix lint errors in the flatfile/db.go. * [NOD-828] Rename packages in such a way to make the linter happy. * [NOD-828] Finish satisfying the linter. * [NOD-828] Fix doc.go. * [NOD-828] Fix comments in block.go. * [NOD-828] Move dbaccess out of the database package. * [NOD-828] Move opening/closing the database to dbaccess. * [NOD-828] Move the Database interface to the root database package, since it's meant to be generic. * [NOD-828] Make ffldb generic to later support additional databases. * [NOD-828] Make ffldb.Open return DatabaseHandle, since ffldb is no longer exported. * [NOD-828] Fix comments. * [NOD-828] Rename AppendFlatData to AppendToStore and RetrieveFlatData to RetrieveFromStore. * [NOD-828] Make buckets nicer to use. * [NOD-828] Implement cursors that iterate over some bucket. * [NOD-828] Generalize flat-file repairing and move block database repairing into ffldb. * [NOD-828] Write a test making sure that flat file repair works. * [NOD-828] Properly close the database in TestRepairFlatFiles. * [NOD-828] Add a comment warning against putting and getting the same data within the same transaction. * [NOD-828] Fix the flatFilesBucket description. * [NOD-828] Remove superfluous whitespace. * [NOD-828] Fix the ffldb description. * [NOD-828] Rename Database to DataAccessor and rename Handle to Database. * [NOD-828] Make Get return nil if the value doesn't exist. * [NOD-828] Attempt to close leveldb even if closing ffdb failed. * [NOD-828] Fix a bug where the wrong location would be written to the current store location bucket. * [NOD-828] Fix not updating the store location in ffldb transactions. * [NOD-828] Make scanFlatFiles return an error if os.Stat fails for any reason other than file-not-found. * [NOD-828] Update the README and doc.go. * [NOD-828] Simplify Bucket.Path(). * [NOD-828] Since LevelDBCursor satisfied the database2.Cursor interface, use it directly. * [NOD-828] Combine two lines into one. * [NOD-828] Combine another two lines into one. * [NOD-828] Move a misplaced comment. * [NOD-828] Use Wrapf instead of Errorf where appropriate. * [NOD-828] Fix a comment. * [NOD-828] Fix a comment. * [NOD-828] Fix Wrapf calls. * [NOD-828] Fix comments. * [NOD-828] Remove superfluous whitespace. * [NOD-828] Add a comment explaining the use of batches and snapshots. * [NOD-828] Implement RollbackUnlessClosed(). * [NOD-828] Return both errors in StoreBlock rollback. * [NOD-828] Move rollback-on-error logic into ffldb. Remove CurrentStoreLocation and RollbackStore from DataAccessor. * [NOD-828] Make bucket a type alias instead of a struct. * [NOD-828] Fix a typo. * [NOD-828] Use copy instead of append in Bucket. * [NOD-828] Extract flatFileLocationSerializedSize to a const. * [NOD-828] Debugf -> Warnf in rollback.go. * [NOD-828] Fix a comment. * [NOD-828] Fix a comment. * [NOD-828] Remove data length from flat file data format. * [NOD-828] Rearrange TestLevelDBTransactionSanity a bit. * [NOD-828] Add stack traces to all errors that come out of library functions. * [NOD-828] Return errors from rollback(). * [NOD-828] Remove an irrelevant comment. * [NOD-828] Remove redundant whitespace. * [NOD-828] Handle nil in FetchBlock. * [NOD-828] Move the explanation about batches and snapshots to the LevelDBTransaction struct. * Revert "[NOD-828] Make bucket a type alias instead of a struct." This reverts commit 1fd39652 * [NOD-828] Fix revert errors. * Revert "[NOD-828] Remove data length from flat file data format." This reverts commit ef408e32 * [NOD-862] Move Cursor() into the DataAccessor interface. * [NOD-828] Add Delete to DataAccessor. * [NOD-865] Move fee data db operations to dbaccess * [NOD-865] Move reachability data db operations to dbaccess * [NOD-865] Move UTXO diff data db operations to dbaccess * [NOD-865] Move subnetwork data db operations to dbaccess * [NOD-865] Fix createDAGState * [NOD-865] Remove old Get signature with "exists" * [NOD-865] Move multiset db operations to dbaccess * [NOD-865] Use dbaccess transactions where possible * [NOD-865] Remove old Get signature with "exists" * [NOD-881] Recover TestGHOSTDAGErrors * [NOD-865] Create function for db keys * [NOD-865] Change Exists to Has, and use accessor.Has where possible * [NOD-865] Make ClearReachabilityData transactive * [NOD-865] Don't iterate cursors while changing db data * [NOD-865] Rename RegisterSubnetwork -> StoreSubnetwork * [NOD-865] Change bucket from utxodiffs to utxo-diffs * [NOD-865] Rename SubnetworkExists->HasSubnetwork * [NOD-865] Change a comment * [NOD-865] Fix tests * [NOD-865] Fix comment * [NOD-865] Remove the prefix "db" from some functions * [NOD-865] Remove redundant comments * [NOD-865] Make clearBucket function * [NOD-865] Make clear functions get a dbTx as an arg * [NOD-865] Remove erroneous tx commit Co-authored-by: stasatdaglabs <stas@daglabs.com> * [NOD-868] Delete the old database package (#683) * [NOD-828] Create the database2 package that will some day replace the database package. * [NOD-828] Implement a "bucket" key mechanism. * [NOD-828] Move bucket.go into the ffldb2 package. * [NOD-828] Delete the un-interfaced ffldb package from database2, since we aren't going to be using it anyway. * [NOD-828] Copy over + fixup flat file structs from the old ffldb. * [NOD-828] Implement flatFilePath. * [NOD-828] Implement flatFileStore.write(). * [NOD-828] Implement flatFileStore.read(). * [NOD-828] Implement flatFileStore.rollback(). * [NOD-828] Sync the file to disk at the end of write(). * [NOD-828] Extract crc32ByteOrder to a separate variable. * [NOD-828] Add a sanity test. * [NOD-828] Remove context-unrelated methods from the Database interface. * [NOD-828] Create an ffldb object. Simply work against a context. * [NOD-828] Open the new database on start. * [NOD-828] Create the leveldb package. * [NOD-828] Implement opening/closing leveldb. * [NOD-828] Implement get/put out of/into leveldb. * [NOD-828] Implement transactions and make them implement a generic database interface. * [NOD-828] Write sanity tests for leveldb with and without transactions. * [NOD-828] Add another case to the transaction sanity test. * [NOD-828] Implement AppendBlock/RetrieveBlock. * [NOD-828] Refactor so that concepts such as "block" and "metadata" don't leak into the database package. * [NOD-828] Add RollbackFlatData to the database interface. * [NOD-828] Remove anything from dbaccess that I'm not planning to implement as part of this ticket. * [NOD-828] Implement StoreBlock. * [NOD-828] Implement FetchBlock. * [NOD-828] Implement HasBlock. * [NOD-828] Write a sanity test for block insertion. * [NOD-828] Implement CurrentFlatDataLocation. * [NOD-828] Implement storing the current block location. * [NOD-828] Implement initializing/syncing the flat file block store and the "metadata". * [NOD-828] Add InitBlockStore to TestBlockStoreSanity. * [NOD-828] Fix rename errors. * [NOD-828] Fix lint errors in the root database package. * [NOD-828] Fix lint errors in the ffldb.go. * [NOD-828] Fix lint errors in the flatfile/db.go. * [NOD-828] Rename packages in such a way to make the linter happy. * [NOD-828] Finish satisfying the linter. * [NOD-828] Fix doc.go. * [NOD-828] Fix comments in block.go. * [NOD-828] Move dbaccess out of the database package. * [NOD-828] Move opening/closing the database to dbaccess. * [NOD-828] Move the Database interface to the root database package, since it's meant to be generic. * [NOD-828] Make ffldb generic to later support additional databases. * [NOD-828] Make ffldb.Open return DatabaseHandle, since ffldb is no longer exported. * [NOD-828] Fix comments. * [NOD-828] Rename AppendFlatData to AppendToStore and RetrieveFlatData to RetrieveFromStore. * [NOD-828] Make buckets nicer to use. * [NOD-828] Implement cursors that iterate over some bucket. * [NOD-828] Generalize flat-file repairing and move block database repairing into ffldb. * [NOD-828] Write a test making sure that flat file repair works. * [NOD-828] Properly close the database in TestRepairFlatFiles. * [NOD-828] Add a comment warning against putting and getting the same data within the same transaction. * [NOD-828] Fix the flatFilesBucket description. * [NOD-828] Remove superfluous whitespace. * [NOD-828] Fix the ffldb description. * [NOD-828] Rename Database to DataAccessor and rename Handle to Database. * [NOD-828] Make Get return nil if the value doesn't exist. * [NOD-828] Attempt to close leveldb even if closing ffdb failed. * [NOD-828] Fix a bug where the wrong location would be written to the current store location bucket. * [NOD-828] Fix not updating the store location in ffldb transactions. * [NOD-828] Make scanFlatFiles return an error if os.Stat fails for any reason other than file-not-found. * [NOD-828] Update the README and doc.go. * [NOD-828] Simplify Bucket.Path(). * [NOD-828] Since LevelDBCursor satisfied the database2.Cursor interface, use it directly. * [NOD-828] Combine two lines into one. * [NOD-828] Combine another two lines into one. * [NOD-828] Move a misplaced comment. * [NOD-828] Use Wrapf instead of Errorf where appropriate. * [NOD-828] Fix a comment. * [NOD-828] Fix a comment. * [NOD-828] Fix Wrapf calls. * [NOD-828] Fix comments. * [NOD-828] Remove superfluous whitespace. * [NOD-828] Add a comment explaining the use of batches and snapshots. * [NOD-828] Implement RollbackUnlessClosed(). * [NOD-828] Return both errors in StoreBlock rollback. * [NOD-828] Move rollback-on-error logic into ffldb. Remove CurrentStoreLocation and RollbackStore from DataAccessor. * [NOD-828] Make bucket a type alias instead of a struct. * [NOD-828] Fix a typo. * [NOD-828] Use copy instead of append in Bucket. * [NOD-828] Extract flatFileLocationSerializedSize to a const. * [NOD-828] Debugf -> Warnf in rollback.go. * [NOD-828] Fix a comment. * [NOD-828] Fix a comment. * [NOD-828] Remove data length from flat file data format. * [NOD-828] Rearrange TestLevelDBTransactionSanity a bit. * [NOD-828] Add stack traces to all errors that come out of library functions. * [NOD-828] Return errors from rollback(). * [NOD-828] Remove an irrelevant comment. * [NOD-828] Remove redundant whitespace. * [NOD-828] Handle nil in FetchBlock. * [NOD-828] Move the explanation about batches and snapshots to the LevelDBTransaction struct. * Revert "[NOD-828] Make bucket a type alias instead of a struct." This reverts commit 1fd39652 * [NOD-828] Fix revert errors. * Revert "[NOD-828] Remove data length from flat file data format." This reverts commit ef408e32 * [NOD-862] Move Cursor() into the DataAccessor interface. * [NOD-828] Add Delete to DataAccessor. * [NOD-865] Move fee data db operations to dbaccess * [NOD-865] Move reachability data db operations to dbaccess * [NOD-865] Move UTXO diff data db operations to dbaccess * [NOD-865] Move subnetwork data db operations to dbaccess * [NOD-865] Fix createDAGState * [NOD-865] Remove old Get signature with "exists" * [NOD-865] Move multiset db operations to dbaccess * [NOD-865] Use dbaccess transactions where possible * [NOD-865] Remove old Get signature with "exists" * [NOD-881] Recover TestGHOSTDAGErrors * [NOD-865] Create function for db keys * [NOD-865] Change Exists to Has, and use accessor.Has where possible * [NOD-865] Make ClearReachabilityData transactive * [NOD-865] Don't iterate cursors while changing db data * [NOD-865] Rename RegisterSubnetwork -> StoreSubnetwork * [NOD-865] Change bucket from utxodiffs to utxo-diffs * [NOD-865] Rename SubnetworkExists->HasSubnetwork * [NOD-865] Change a comment * [NOD-868] Remove all tests from old database. * [NOD-868] Remove all unused methods from the old database's interfaces. * [NOD-865] Fix tests * [NOD-868] Remove references to DB. * [NOD-865] Fix comment * [NOD-868] Remove the old ffldb besides the interface and errors.go. * [NOD-868] Remove errors.go. * [NOD-868] Remove the old database package. * [NOD-868] Add openDB to DAGSetup to emulate the old dbpath in dag.config. * [NOD-868] Rename database2 to database. * [NOD-868] Use NewTx instead of NoTx where required. * [NOD-868] Fix merge errors. * [NOD-868] Rename dbXXX functions to just xxx. * [NOD-868] Rename putDAGState to saveDAGState. * [NOD-868] Replace comments in initDAGState with logs. * [NOD-868] Explain the openDB parameter in DAGSetup. * [NOD-868] Fixup doc.go and README.md. * [NOD-868] Remove pointless transactions. Co-authored-by: Ori Newman <orinewman1@gmail.com> * [NOD-805] Fix merge errors. * [NOD-805] Fix a comment. * [NOD-805] Don't return virtualTxsAcceptanceData from applyDAGChanges. * [NOD-805] Add missing error handling in TestAcceptanceDataIndexRecover. * [NOD-805] Rename blockDAG to dag in indexers/manager.go. * [NOD-805] Defer cursor.Close() everywhere. * [NOD-805] Rename scanFlatFiles to findCurrentLocation. * [NOD-805] Extract crc32ChecksumLength and dataLengthLength to constants. * [NOD-805] Handle open files properly in rollback.go. * [NOD-805] Remove unnecessary func wrapper. * [NOD-805] Remove unnecessary trimming in initialize. * [NOD-805] Made StoreBlock accept only TxContext. * [NOD-805] Changed the log level of an error message to Error. * [NOD-805] Add a note about holding mutexes over deleteFile. * [NOD-805] Remove a false comment. * [NOD-805] Fix a comment. * [NOD-805] Rename blk to block. * [NOD-805] Extract utxoKey to a separate function. * [NOD-805] Move dbaccess.xxxKey functions to the tops of their respective files. * [NOD-805] Fix grammar in dbaccess/db.go. * [NOD-805] Wrap a failed database corruption recovery error. * [NOD-805] Split lines with WithStack in them. * [NOD-805] Fix the comment over initialize. * [NOD-805] Rename ffdb to flatFileDB and ldb to levelDB. * [NOD-805] Fix a comment. * [NOD-805] Fix a comment. * [NOD-805] Use s.writeCursor instead of cursor. * [NOD-805] Embed file in lockableFile. * [NOD-805] the the -> the * [NOD-805] openDB -> db * [NOD-805] Use TxContext in all flushToDB functions. * [NOD-805] Rename context -> dbContext. * [NOD-805] Reword the comment at the beginning on initDAGState. * [NOD-805] Explain cursor key trimming. * [NOD-805] Remove Error from Cursor. * [NOD-805] Return ErrNotFound from done Cursor Key and Value. * [NOD-805] Add missing error handling. * [NOD-805] Fix a comment. * [NOD-805] Fix a variable name. * [NOD-805] Remove pointless underscore. * [NOD-805] Fix a comment. * [NOD-805] Fix a variable name. Co-authored-by: Mike Zak <feanorr@gmail.com> Co-authored-by: Ori Newman <orinewman1@gmail.com>
621 lines
17 KiB
Go
621 lines
17 KiB
Go
// Copyright (c) 2015-2017 The btcsuite developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package blockdag
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"sync"
|
|
|
|
"github.com/kaspanet/kaspad/dagconfig"
|
|
"github.com/kaspanet/kaspad/dbaccess"
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/kaspanet/kaspad/util"
|
|
"github.com/kaspanet/kaspad/util/binaryserializer"
|
|
"github.com/kaspanet/kaspad/util/buffers"
|
|
"github.com/kaspanet/kaspad/util/daghash"
|
|
"github.com/kaspanet/kaspad/util/subnetworkid"
|
|
"github.com/kaspanet/kaspad/wire"
|
|
)
|
|
|
|
var (
|
|
// byteOrder is the preferred byte order used for serializing numeric
|
|
// fields for storage in the database.
|
|
byteOrder = binary.LittleEndian
|
|
)
|
|
|
|
// errNotInDAG signifies that a block hash or height that is not in the
|
|
// DAG was requested.
|
|
type errNotInDAG string
|
|
|
|
// Error implements the error interface.
|
|
func (e errNotInDAG) Error() string {
|
|
return string(e)
|
|
}
|
|
|
|
// isNotInDAGErr returns whether or not the passed error is an
|
|
// errNotInDAG error.
|
|
func isNotInDAGErr(err error) bool {
|
|
var notInDAGErr errNotInDAG
|
|
return errors.As(err, ¬InDAGErr)
|
|
}
|
|
|
|
// outpointKeyPool defines a concurrent safe free list of byte buffers used to
|
|
// provide temporary buffers for outpoint database keys.
|
|
var outpointKeyPool = sync.Pool{
|
|
New: func() interface{} {
|
|
return &bytes.Buffer{} // Pointer to a buffer to avoid boxing alloc.
|
|
},
|
|
}
|
|
|
|
// outpointIndexByteOrder is the byte order for serializing the outpoint index.
|
|
// It uses big endian to ensure that when outpoint is used as database key, the
|
|
// keys will be iterated in an ascending order by the outpoint index.
|
|
var outpointIndexByteOrder = binary.BigEndian
|
|
|
|
func serializeOutpoint(w io.Writer, outpoint *wire.Outpoint) error {
|
|
_, err := w.Write(outpoint.TxID[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return binaryserializer.PutUint32(w, outpointIndexByteOrder, outpoint.Index)
|
|
}
|
|
|
|
var outpointSerializeSize = daghash.TxIDSize + 4
|
|
|
|
// deserializeOutpoint decodes an outpoint from the passed serialized byte
|
|
// slice into a new wire.Outpoint using a format that is suitable for long-
|
|
// term storage. This format is described in detail above.
|
|
func deserializeOutpoint(r io.Reader) (*wire.Outpoint, error) {
|
|
outpoint := &wire.Outpoint{}
|
|
_, err := r.Read(outpoint.TxID[:])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
outpoint.Index, err = binaryserializer.Uint32(r, outpointIndexByteOrder)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return outpoint, nil
|
|
}
|
|
|
|
// updateUTXOSet updates the UTXO set in the database based on the provided
|
|
// UTXO diff.
|
|
func updateUTXOSet(dbContext dbaccess.Context, virtualUTXODiff *UTXODiff) error {
|
|
for outpoint := range virtualUTXODiff.toRemove {
|
|
w := outpointKeyPool.Get().(*bytes.Buffer)
|
|
w.Reset()
|
|
err := serializeOutpoint(w, &outpoint)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
key := w.Bytes()
|
|
err = dbaccess.RemoveFromUTXOSet(dbContext, key)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
outpointKeyPool.Put(w)
|
|
}
|
|
|
|
// We are preallocating for P2PKH entries because they are the most common ones.
|
|
// If we have entries with a compressed script bigger than P2PKH's, the buffer will grow.
|
|
bytesToPreallocate := (p2pkhUTXOEntrySerializeSize + outpointSerializeSize) * len(virtualUTXODiff.toAdd)
|
|
buff := bytes.NewBuffer(make([]byte, bytesToPreallocate))
|
|
for outpoint, entry := range virtualUTXODiff.toAdd {
|
|
// Serialize and store the UTXO entry.
|
|
sBuff := buffers.NewSubBuffer(buff)
|
|
err := serializeUTXOEntry(sBuff, entry)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
serializedEntry := sBuff.Bytes()
|
|
|
|
sBuff = buffers.NewSubBuffer(buff)
|
|
err = serializeOutpoint(sBuff, &outpoint)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
key := sBuff.Bytes()
|
|
err = dbaccess.AddToUTXOSet(dbContext, key, serializedEntry)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type dagState struct {
|
|
TipHashes []*daghash.Hash
|
|
LastFinalityPoint *daghash.Hash
|
|
LocalSubnetworkID *subnetworkid.SubnetworkID
|
|
}
|
|
|
|
// serializeDAGState returns the serialization of the DAG state.
|
|
// This is data to be stored in the DAG state bucket.
|
|
func serializeDAGState(state *dagState) ([]byte, error) {
|
|
return json.Marshal(state)
|
|
}
|
|
|
|
// deserializeDAGState deserializes the passed serialized DAG state.
|
|
// This is data stored in the DAG state bucket and is updated after
|
|
// every block is connected to the DAG.
|
|
func deserializeDAGState(serializedData []byte) (*dagState, error) {
|
|
var state *dagState
|
|
err := json.Unmarshal(serializedData, &state)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return state, nil
|
|
}
|
|
|
|
// saveDAGState uses an existing database context to store the latest
|
|
// tip hashes of the DAG.
|
|
func saveDAGState(dbContext dbaccess.Context, state *dagState) error {
|
|
serializedDAGState, err := serializeDAGState(state)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return dbaccess.StoreDAGState(dbContext, serializedDAGState)
|
|
}
|
|
|
|
// createDAGState initializes the DAG state to the
|
|
// genesis block and the node's local subnetwork id.
|
|
func (dag *BlockDAG) createDAGState(localSubnetworkID *subnetworkid.SubnetworkID) error {
|
|
return saveDAGState(dbaccess.NoTx(), &dagState{
|
|
TipHashes: []*daghash.Hash{dag.dagParams.GenesisHash},
|
|
LastFinalityPoint: dag.dagParams.GenesisHash,
|
|
LocalSubnetworkID: localSubnetworkID,
|
|
})
|
|
}
|
|
|
|
// initDAGState attempts to load and initialize the DAG state from the
|
|
// database. When the db does not yet contain any DAG state, both it and the
|
|
// DAG state are initialized to the genesis block.
|
|
func (dag *BlockDAG) initDAGState() error {
|
|
// Fetch the stored DAG state from the database. If it doesn't exist,
|
|
// it means that kaspad is running for the first time.
|
|
serializedDAGState, err := dbaccess.FetchDAGState(dbaccess.NoTx())
|
|
if dbaccess.IsNotFoundError(err) {
|
|
// Initialize the database and the DAG state to the genesis block.
|
|
return dag.createDAGState(dag.subnetworkID)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
dagState, err := deserializeDAGState(serializedDAGState)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !dagState.LocalSubnetworkID.IsEqual(dag.subnetworkID) {
|
|
return errors.Errorf("Cannot start kaspad with subnetwork ID %s because"+
|
|
" its database is already built with subnetwork ID %s. If you"+
|
|
" want to switch to a new database, please reset the"+
|
|
" database by starting kaspad with --reset-db flag", dag.subnetworkID, dagState.LocalSubnetworkID)
|
|
}
|
|
|
|
log.Debugf("Loading block index...")
|
|
var unprocessedBlockNodes []*blockNode
|
|
blockIndexCursor, err := dbaccess.BlockIndexCursor(dbaccess.NoTx())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer blockIndexCursor.Close()
|
|
for blockIndexCursor.Next() {
|
|
serializedDBNode, err := blockIndexCursor.Value()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
node, err := dag.deserializeBlockNode(serializedDBNode)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Check to see if this node had been stored in the the block DB
|
|
// but not yet accepted. If so, add it to a slice to be processed later.
|
|
if node.status == statusDataStored {
|
|
unprocessedBlockNodes = append(unprocessedBlockNodes, node)
|
|
continue
|
|
}
|
|
|
|
// If the node is known to be invalid add it as-is to the block
|
|
// index and continue.
|
|
if node.status.KnownInvalid() {
|
|
dag.index.addNode(node)
|
|
continue
|
|
}
|
|
|
|
if dag.blockCount == 0 {
|
|
if !node.hash.IsEqual(dag.dagParams.GenesisHash) {
|
|
return AssertError(fmt.Sprintf("initDAGState: Expected "+
|
|
"first entry in block index to be genesis block, "+
|
|
"found %s", node.hash))
|
|
}
|
|
} else {
|
|
if len(node.parents) == 0 {
|
|
return AssertError(fmt.Sprintf("initDAGState: block %s "+
|
|
"has no parents but it's not the genesis block", node.hash))
|
|
}
|
|
}
|
|
|
|
// Add the node to its parents children, connect it,
|
|
// and add it to the block index.
|
|
node.updateParentsChildren()
|
|
dag.index.addNode(node)
|
|
|
|
dag.blockCount++
|
|
}
|
|
|
|
log.Debugf("Loading UTXO set...")
|
|
fullUTXOCollection := make(utxoCollection)
|
|
cursor, err := dbaccess.UTXOSetCursor(dbaccess.NoTx())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer cursor.Close()
|
|
|
|
for cursor.Next() {
|
|
// Deserialize the outpoint
|
|
key, err := cursor.Key()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
outpoint, err := deserializeOutpoint(bytes.NewReader(key))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Deserialize the utxo entry
|
|
value, err := cursor.Value()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
entry, err := deserializeUTXOEntry(bytes.NewReader(value))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fullUTXOCollection[*outpoint] = entry
|
|
}
|
|
|
|
log.Debugf("Loading reachability data...")
|
|
err = dag.reachabilityStore.init(dbaccess.NoTx())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Debugf("Loading multiset data...")
|
|
err = dag.multisetStore.init(dbaccess.NoTx())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Debugf("Applying the loaded utxoCollection to the virtual block...")
|
|
dag.virtual.utxoSet, err = newFullUTXOSetFromUTXOCollection(fullUTXOCollection)
|
|
if err != nil {
|
|
return AssertError(fmt.Sprintf("Error loading UTXOSet: %s", err))
|
|
}
|
|
|
|
log.Debugf("Applying the stored tips to the virtual block...")
|
|
tips := newBlockSet()
|
|
for _, tipHash := range dagState.TipHashes {
|
|
tip := dag.index.LookupNode(tipHash)
|
|
if tip == nil {
|
|
return AssertError(fmt.Sprintf("initDAGState: cannot find "+
|
|
"DAG tip %s in block index", dagState.TipHashes))
|
|
}
|
|
tips.add(tip)
|
|
}
|
|
dag.virtual.SetTips(tips)
|
|
|
|
log.Debugf("Setting the last finality point...")
|
|
dag.lastFinalityPoint = dag.index.LookupNode(dagState.LastFinalityPoint)
|
|
dag.finalizeNodesBelowFinalityPoint(false)
|
|
|
|
log.Debugf("Processing unprocessed blockNodes...")
|
|
for _, node := range unprocessedBlockNodes {
|
|
// Check to see if the block exists in the block DB. If it
|
|
// doesn't, the database has certainly been corrupted.
|
|
blockExists, err := dbaccess.HasBlock(dbaccess.NoTx(), node.hash)
|
|
if err != nil {
|
|
return AssertError(fmt.Sprintf("initDAGState: HasBlock "+
|
|
"for block %s failed: %s", node.hash, err))
|
|
}
|
|
if !blockExists {
|
|
return AssertError(fmt.Sprintf("initDAGState: block %s "+
|
|
"exists in block index but not in block db", node.hash))
|
|
}
|
|
|
|
// Attempt to accept the block.
|
|
block, err := fetchBlockByHash(dbaccess.NoTx(), node.hash)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
isOrphan, isDelayed, err := dag.ProcessBlock(block, BFWasStored)
|
|
if err != nil {
|
|
log.Warnf("Block %s, which was not previously processed, "+
|
|
"failed to be accepted to the DAG: %s", node.hash, err)
|
|
continue
|
|
}
|
|
|
|
// If the block is an orphan or is delayed then it couldn't have
|
|
// possibly been written to the block index in the first place.
|
|
if isOrphan {
|
|
return AssertError(fmt.Sprintf("Block %s, which was not "+
|
|
"previously processed, turned out to be an orphan, which is "+
|
|
"impossible.", node.hash))
|
|
}
|
|
if isDelayed {
|
|
return AssertError(fmt.Sprintf("Block %s, which was not "+
|
|
"previously processed, turned out to be delayed, which is "+
|
|
"impossible.", node.hash))
|
|
}
|
|
}
|
|
|
|
log.Infof("DAG state initialized.")
|
|
|
|
return nil
|
|
}
|
|
|
|
// deserializeBlockNode parses a value in the block index bucket and returns a block node.
|
|
func (dag *BlockDAG) deserializeBlockNode(blockRow []byte) (*blockNode, error) {
|
|
buffer := bytes.NewReader(blockRow)
|
|
|
|
var header wire.BlockHeader
|
|
err := header.Deserialize(buffer)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
node := &blockNode{
|
|
hash: header.BlockHash(),
|
|
version: header.Version,
|
|
bits: header.Bits,
|
|
nonce: header.Nonce,
|
|
timestamp: header.Timestamp.Unix(),
|
|
hashMerkleRoot: header.HashMerkleRoot,
|
|
acceptedIDMerkleRoot: header.AcceptedIDMerkleRoot,
|
|
utxoCommitment: header.UTXOCommitment,
|
|
}
|
|
|
|
node.children = newBlockSet()
|
|
node.parents = newBlockSet()
|
|
|
|
for _, hash := range header.ParentHashes {
|
|
parent := dag.index.LookupNode(hash)
|
|
if parent == nil {
|
|
return nil, AssertError(fmt.Sprintf("deserializeBlockNode: Could "+
|
|
"not find parent %s for block %s", hash, header.BlockHash()))
|
|
}
|
|
node.parents.add(parent)
|
|
}
|
|
|
|
statusByte, err := buffer.ReadByte()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
node.status = blockStatus(statusByte)
|
|
|
|
selectedParentHash := &daghash.Hash{}
|
|
if _, err := io.ReadFull(buffer, selectedParentHash[:]); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Because genesis doesn't have selected parent, it's serialized as zero hash
|
|
if !selectedParentHash.IsEqual(&daghash.ZeroHash) {
|
|
node.selectedParent = dag.index.LookupNode(selectedParentHash)
|
|
}
|
|
|
|
node.blueScore, err = binaryserializer.Uint64(buffer, byteOrder)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
bluesCount, err := wire.ReadVarInt(buffer)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
node.blues = make([]*blockNode, bluesCount)
|
|
for i := uint64(0); i < bluesCount; i++ {
|
|
hash := &daghash.Hash{}
|
|
if _, err := io.ReadFull(buffer, hash[:]); err != nil {
|
|
return nil, err
|
|
}
|
|
node.blues[i] = dag.index.LookupNode(hash)
|
|
}
|
|
|
|
bluesAnticoneSizesLen, err := wire.ReadVarInt(buffer)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
node.bluesAnticoneSizes = make(map[*blockNode]dagconfig.KType)
|
|
for i := uint64(0); i < bluesAnticoneSizesLen; i++ {
|
|
hash := &daghash.Hash{}
|
|
if _, err := io.ReadFull(buffer, hash[:]); err != nil {
|
|
return nil, err
|
|
}
|
|
bluesAnticoneSize, err := binaryserializer.Uint8(buffer)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
blue := dag.index.LookupNode(hash)
|
|
if blue == nil {
|
|
return nil, errors.Errorf("couldn't find block with hash %s", hash)
|
|
}
|
|
node.bluesAnticoneSizes[blue] = dagconfig.KType(bluesAnticoneSize)
|
|
}
|
|
|
|
return node, nil
|
|
}
|
|
|
|
// fetchBlockByHash retrieves the raw block for the provided hash,
|
|
// deserializes it, and returns a util.Block of it.
|
|
func fetchBlockByHash(dbContext dbaccess.Context, hash *daghash.Hash) (*util.Block, error) {
|
|
blockBytes, err := dbaccess.FetchBlock(dbContext, hash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return util.NewBlockFromBytes(blockBytes)
|
|
}
|
|
|
|
func storeBlock(dbContext *dbaccess.TxContext, block *util.Block) error {
|
|
blockBytes, err := block.Bytes()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return dbaccess.StoreBlock(dbContext, block.Hash(), blockBytes)
|
|
}
|
|
|
|
func serializeBlockNode(node *blockNode) ([]byte, error) {
|
|
w := bytes.NewBuffer(make([]byte, 0, wire.MaxBlockHeaderPayload+1))
|
|
header := node.Header()
|
|
err := header.Serialize(w)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = w.WriteByte(byte(node.status))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Because genesis doesn't have selected parent, it's serialized as zero hash
|
|
selectedParentHash := &daghash.ZeroHash
|
|
if node.selectedParent != nil {
|
|
selectedParentHash = node.selectedParent.hash
|
|
}
|
|
_, err = w.Write(selectedParentHash[:])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = binaryserializer.PutUint64(w, byteOrder, node.blueScore)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = wire.WriteVarInt(w, uint64(len(node.blues)))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, blue := range node.blues {
|
|
_, err = w.Write(blue.hash[:])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
err = wire.WriteVarInt(w, uint64(len(node.bluesAnticoneSizes)))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for blue, blueAnticoneSize := range node.bluesAnticoneSizes {
|
|
_, err = w.Write(blue.hash[:])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = binaryserializer.PutUint8(w, uint8(blueAnticoneSize))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return w.Bytes(), nil
|
|
}
|
|
|
|
// blockIndexKey generates the binary key for an entry in the block index
|
|
// bucket. The key is composed of the block blue score encoded as a big-endian
|
|
// 64-bit unsigned int followed by the 32 byte block hash.
|
|
// The blue score component is important for iteration order.
|
|
func blockIndexKey(blockHash *daghash.Hash, blueScore uint64) []byte {
|
|
indexKey := make([]byte, daghash.HashSize+8)
|
|
binary.BigEndian.PutUint64(indexKey[0:8], blueScore)
|
|
copy(indexKey[8:daghash.HashSize+8], blockHash[:])
|
|
return indexKey
|
|
}
|
|
|
|
func blockHashFromBlockIndexKey(BlockIndexKey []byte) (*daghash.Hash, error) {
|
|
return daghash.NewHash(BlockIndexKey[8 : daghash.HashSize+8])
|
|
}
|
|
|
|
// BlockByHash returns the block from the DAG with the given hash.
|
|
//
|
|
// This function is safe for concurrent access.
|
|
func (dag *BlockDAG) BlockByHash(hash *daghash.Hash) (*util.Block, error) {
|
|
// Lookup the block hash in block index and ensure it is in the DAG
|
|
node := dag.index.LookupNode(hash)
|
|
if node == nil {
|
|
str := fmt.Sprintf("block %s is not in the DAG", hash)
|
|
return nil, errNotInDAG(str)
|
|
}
|
|
|
|
block, err := fetchBlockByHash(dbaccess.NoTx(), node.hash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return block, err
|
|
}
|
|
|
|
// BlockHashesFrom returns a slice of blocks starting from lowHash
|
|
// ordered by blueScore. If lowHash is nil then the genesis block is used.
|
|
//
|
|
// This method MUST be called with the DAG lock held
|
|
func (dag *BlockDAG) BlockHashesFrom(lowHash *daghash.Hash, limit int) ([]*daghash.Hash, error) {
|
|
blockHashes := make([]*daghash.Hash, 0, limit)
|
|
if lowHash == nil {
|
|
lowHash = dag.genesis.hash
|
|
|
|
// If we're starting from the beginning we should include the
|
|
// genesis hash in the result
|
|
blockHashes = append(blockHashes, dag.genesis.hash)
|
|
}
|
|
if !dag.IsInDAG(lowHash) {
|
|
return nil, errors.Errorf("block %s not found", lowHash)
|
|
}
|
|
blueScore, err := dag.BlueScoreByBlockHash(lowHash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
key := blockIndexKey(lowHash, blueScore)
|
|
cursor, err := dbaccess.BlockIndexCursorFrom(dbaccess.NoTx(), key)
|
|
if dbaccess.IsNotFoundError(err) {
|
|
return nil, errors.Wrapf(err, "block %s not in block index", lowHash)
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer cursor.Close()
|
|
|
|
for cursor.Next() && len(blockHashes) < limit {
|
|
key, err := cursor.Key()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
blockHash, err := blockHashFromBlockIndexKey(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
blockHashes = append(blockHashes, blockHash)
|
|
}
|
|
|
|
return blockHashes, nil
|
|
}
|