aboutsummaryrefslogtreecommitdiff
path: root/pkg/blockchain/coindatabase
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/blockchain/coindatabase')
-rw-r--r--pkg/blockchain/coindatabase/coindatabase.go154
1 files changed, 146 insertions, 8 deletions
diff --git a/pkg/blockchain/coindatabase/coindatabase.go b/pkg/blockchain/coindatabase/coindatabase.go
index 83b3026..f61e178 100644
--- a/pkg/blockchain/coindatabase/coindatabase.go
+++ b/pkg/blockchain/coindatabase/coindatabase.go
@@ -80,15 +80,69 @@ func (coinDB *CoinDatabase) validateTransaction(transaction *block.Transaction)
// (1) erases the Coins created by a Block and
// (2) marks the Coins used to create those Transactions as unspent.
func (coinDB *CoinDatabase) UndoCoins(blocks []*block.Block, undoBlocks []*chainwriter.UndoBlock) {
- //TODO
+ // loop through all the block/undoBlock pairings || len(blocks) = len(undoBlocks)
+ for i := 0; i < len(blocks); i++ {
+ // (1) deal with Blocks
+ for _, tx := range blocks[i].Transactions {
+ txHash := tx.Hash()
+ for j := 0; j < len(tx.Outputs); j++ {
+ // create the coin locator
+ cl := CoinLocator{
+ ReferenceTransactionHash: txHash,
+ OutputIndex: uint32(j),
+ }
+ // Remove the coin from the main cache (if it exists there) and update size
+ if _, ok := coinDB.mainCache[cl]; ok {
+ delete(coinDB.mainCache, cl)
+ coinDB.mainCacheSize--
+ }
+ }
+ // delete the coin record from the db (erases all the coins in the process)
+ if err := coinDB.db.Delete([]byte(txHash), nil); err != nil {
+ utils.Debug.Printf("Error deleting coin record for transaction {%v}", txHash)
+ }
+ }
+ // (2) deal with UndoBlocks
+ for j := 0; j < len(undoBlocks[i].TransactionInputHashes); j++ {
+ txHash := undoBlocks[i].TransactionInputHashes[j]
+ // retrieve coin record from db
+ cr := coinDB.getCoinRecordFromDB(txHash)
+ //
+ if cr != nil {
+ // Add coins to record. This is the reestablishing part.
+ cr = coinDB.addCoinToRecord(cr, undoBlocks[i], j)
+ cl := CoinLocator{
+ ReferenceTransactionHash: txHash,
+ OutputIndex: undoBlocks[i].OutputIndexes[j],
+ }
+ // Mark the coin in the main cache as unspent
+ if coin, ok := coinDB.mainCache[cl]; ok {
+ coin.IsSpent = false
+ coinDB.mainCache[cl] = coin
+ }
+ } else {
+ // if there was no coin record to get from the db, we
+ // need to make a new one with all the coins from
+ // the undoBlock
+ cr = &CoinRecord{
+ Version: 0,
+ OutputIndexes: undoBlocks[i].OutputIndexes,
+ Amounts: undoBlocks[i].Amounts,
+ LockingScripts: undoBlocks[i].LockingScripts,
+ }
+ }
+ // put the updated record back in the db.
+ coinDB.putRecordInDB(txHash, cr)
+ }
+ }
}
-// addCoinsToRecord adds coins to a CoinRecord given an UndoBlock and
-// returns the updated CoinRecord.
-func (coinDB *CoinDatabase) addCoinsToRecord(cr *CoinRecord, ub *chainwriter.UndoBlock) *CoinRecord {
- cr.OutputIndexes = append(cr.OutputIndexes, ub.OutputIndexes...)
- cr.Amounts = append(cr.Amounts, ub.Amounts...)
- cr.LockingScripts = append(cr.LockingScripts, ub.LockingScripts...)
+// addCoinToRecord adds a Coin to a CoinRecord given an UndoBlock and index,
+// returning the updated CoinRecord.
+func (coinDB *CoinDatabase) addCoinToRecord(cr *CoinRecord, ub *chainwriter.UndoBlock, index int) *CoinRecord {
+ cr.OutputIndexes = append(cr.OutputIndexes, ub.OutputIndexes[index])
+ cr.Amounts = append(cr.Amounts, ub.Amounts[index])
+ cr.LockingScripts = append(cr.LockingScripts, ub.LockingScripts[index])
return cr
}
@@ -143,7 +197,91 @@ func (coinDB *CoinDatabase) FlushMainCache() {
// (2) stores new TransactionOutputs as Coins in the mainCache (if active)
// (3) stores CoinRecords for the Transactions in the db.
func (coinDB *CoinDatabase) StoreBlock(transactions []*block.Transaction, active bool) {
- //TODO
+ // do (1) and (2) only if active
+ if active {
+ coinDB.updateSpentCoins(transactions)
+ coinDB.storeTransactionsInMainCache(transactions)
+ }
+ // always do (3)
+ coinDB.storeTransactionsInDB(transactions)
+}
+
+// storeTransactionsInDB generates CoinRecords from a slice of Transactions and
+// stores them in the CoinDatabase's db.
+//
+// At a high level, this function:
+// (1) creates coin records for the block's transactions
+// (2) stores those coin records in the db
+//
+// Note: NOT included in the stencil.
+func (coinDB *CoinDatabase) storeTransactionsInDB(transactions []*block.Transaction) {
+ for _, tx := range transactions {
+ cr := coinDB.createCoinRecord(tx)
+ txHash := tx.Hash()
+ coinDB.putRecordInDB(txHash, cr)
+ }
+}
+
+// storeTransactionsInMainCache generates Coins from a slice of Transactions
+// and stores them in the CoinDatabase's mainCache. It flushes the mainCache
+// if it reaches mainCacheCapacity.
+//
+// At a high level, this function:
+// (1) loops through the newly created transaction outputs from the Block's
+// transactions.
+// (2) flushes our cache if we reach capacity
+// (3) creates a coin (value) and coin locator (key) for each output,
+// adding them to the main cache.
+//
+// Note: NOT included in the stencil.
+func (coinDB *CoinDatabase) storeTransactionsInMainCache(transactions []*block.Transaction) {
+ for _, tx := range transactions {
+ // get hash now, which we will use in creating coin locators
+ // for each output later
+ txHash := tx.Hash()
+ for i, txo := range tx.Outputs {
+ // check whether we're approaching our capacity and flush if we are
+ if coinDB.mainCacheSize+uint32(len(tx.Outputs)) >= coinDB.mainCacheCapacity {
+ coinDB.FlushMainCache()
+ }
+ // actually create the coin
+ coin := &Coin{
+ TransactionOutput: txo,
+ IsSpent: false,
+ }
+ // create the coin locator, which is they key to the coin
+ cl := CoinLocator{
+ ReferenceTransactionHash: txHash,
+ OutputIndex: uint32(i),
+ }
+ // add the coin to main cach and increment the size of the main cache.
+ coinDB.mainCache[cl] = coin
+ coinDB.mainCacheSize++
+ }
+ }
+}
+
+func (coinDB *CoinDatabase) updateSpentCoins(transactions []*block.Transaction) {
+ // loop through all the transactions from the block,
+ // marking the coins used to create the inputs as spent.
+ for _, tx := range transactions {
+ for _, txi := range tx.Inputs {
+ // get the coin locator for the input
+ cl := makeCoinLocator(txi)
+ // mark coins in the main cache as spent
+ if coin, ok := coinDB.mainCache[cl]; ok {
+ coin.IsSpent = true
+ coinDB.mainCache[cl] = coin
+ } else {
+ // if the coin is not in the cache,
+ // we have to remove the coin from the
+ // database.
+ txHash := tx.Hash()
+ // remove the spent coin from the db
+ coinDB.removeCoinFromDB(txHash, cl)
+ }
+ }
+ }
}
// removeCoinFromDB removes a Coin from a CoinRecord, deleting the CoinRecord