diff options
Diffstat (limited to 'pkg/blockchain/coindatabase')
-rw-r--r-- | pkg/blockchain/coindatabase/coindatabase.go | 154 |
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 |