Skip to content

Commit

Permalink
Replace the ticket database with an efficient, atomic implementation
Browse files Browse the repository at this point in the history
The legacy ticket database, which was GOB serialized and stored on
shut down, has been removed.  Ticket state information is now held in
a stake node, which acts as a modularized "black box" to contain all
information about the state of the stake system.  Stake nodes are now
a component of the blockchain blockNode struct, and are updated with
them.

Stake nodes, like their internal treap primitives, are immutable
objects that are created with their connect and disconnect node
functions.  The blockchain database now stores all information about
the stake state of the best node in the block database.  The blockchain
makes the assumption that the stake state of the best node is known at
any given time.  If the states of former blocks or sidechains must be
evaluated, this can be achieved by iterating backwards along the
blockchain from the best node, and then connecting stake nodes
iteratively if necessary.

Performance improvements with this new module are dramatic.  The long
delays on start up and shut down are removed.  Blockchain
synchronization time is improved approximately 5-10x on the mainnet
chain.  The state of the database is atomic, so unexpected shut downs
should no longer have the ability to disrupt the chain state.

An upgrade path has been added for version 1 blockchain databases.
Users with this blockchain database will automatically update when
they start their clients.
  • Loading branch information
cjepson committed Oct 4, 2016
1 parent af40801 commit d98fc83
Show file tree
Hide file tree
Showing 51 changed files with 5,376 additions and 4,833 deletions.
56 changes: 45 additions & 11 deletions blockchain/accept.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"time"

"github.com/decred/dcrd/blockchain/stake"
"github.com/decred/dcrd/chaincfg/chainhash"
"github.com/decred/dcrd/txscript"
"github.com/decred/dcrutil"
)
Expand Down Expand Up @@ -172,6 +173,32 @@ func (b *BlockChain) checkBlockContext(block *dcrutil.Block, prevNode *blockNode
return nil
}

// ticketsSpentInBlock fetches a list of tickets that were spent in the
// block.
func ticketsSpentInBlock(bl *dcrutil.Block) []chainhash.Hash {
var tickets []chainhash.Hash
for _, stx := range bl.STransactions() {
if stake.DetermineTxType(stx) == stake.TxTypeSSGen {
tickets = append(tickets, stx.MsgTx().TxIn[1].PreviousOutPoint.Hash)
}
}

return tickets
}

// ticketsRevokedInBlock fetches a list of tickets that were revoked in the
// block.
func ticketsRevokedInBlock(bl *dcrutil.Block) []chainhash.Hash {
var tickets []chainhash.Hash
for _, stx := range bl.STransactions() {
if stake.DetermineTxType(stx) == stake.TxTypeSSRtx {
tickets = append(tickets, stx.MsgTx().TxIn[0].PreviousOutPoint.Hash)
}
}

return tickets
}

// maybeAcceptBlock potentially accepts a block into the memory block chain.
// It performs several validation checks which depend on its position within
// the block chain before adding it. The block is expected to have already gone
Expand Down Expand Up @@ -209,9 +236,14 @@ func (b *BlockChain) maybeAcceptBlock(block *dcrutil.Block,
return false, err
}

// Prune block nodes which are no longer needed before creating
// a new node.
// Prune stake nodes and block nodes which are no longer needed before
// creating a new node.
if !dryRun {
err = b.pruneStakeNodes()
if err != nil {
return false, err
}

err = b.pruneBlockNodes()
if err != nil {
return false, err
Expand All @@ -221,21 +253,23 @@ func (b *BlockChain) maybeAcceptBlock(block *dcrutil.Block,
// Create a new block node for the block and add it to the in-memory
// block chain (could be either a side chain or the main chain).
blockHeader := &block.MsgBlock().Header
var voteBitsStake []uint16
for _, stx := range block.STransactions() {
if is, _ := stake.IsSSGen(stx); is {
vb := stake.SSGenVoteBits(stx)
voteBitsStake = append(voteBitsStake, vb)
}
}

newNode := newBlockNode(blockHeader, block.Sha(), blockHeight, voteBitsStake)
newNode := newBlockNode(blockHeader, block.Sha(), blockHeight, ticketsSpentInBlock(block), ticketsRevokedInBlock(block))
if prevNode != nil {
newNode.parent = prevNode
newNode.height = blockHeight
newNode.workSum.Add(prevNode.workSum, newNode.workSum)
}

// Fetching a stake node could enable a new DoS vector, so restrict
// this only to blocks that are recent in history.
if newNode.height < b.bestNode.height-minMemoryNodes {
newNode.stakeNode, err = b.fetchStakeNode(newNode)
if err != nil {
return false, err
}
newNode.stakeUndoData = newNode.stakeNode.UndoData()
}

// Connect the passed block to the chain while respecting proper chain
// selection according to the chain with the most proof of work. This
// also handles validation of the transaction scripts.
Expand Down
4 changes: 2 additions & 2 deletions blockchain/blocklocator.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ func (b *BlockChain) blockLocatorFromHash(hash *chainhash.Hash) BlockLocator {
iterNode = iterNode.parent
}
if iterNode != nil && iterNode.height == blockHeight {
locator = append(locator, iterNode.hash)
locator = append(locator, &iterNode.hash)
}
continue
}
Expand Down Expand Up @@ -174,7 +174,7 @@ func (b *BlockChain) BlockLocatorFromHash(hash *chainhash.Hash) BlockLocator {
// This function is safe for concurrent access.
func (b *BlockChain) LatestBlockLocator() (BlockLocator, error) {
b.chainLock.RLock()
locator := b.blockLocatorFromHash(b.bestNode.hash)
locator := b.blockLocatorFromHash(&b.bestNode.hash)
b.chainLock.RUnlock()
return locator, nil
}
Loading

0 comments on commit d98fc83

Please sign in to comment.