github.com/decred/dcrd/blockchain@v1.2.1/stakeext.go (about)

     1  // Copyright (c) 2013-2014 The btcsuite developers
     2  // Copyright (c) 2015-2018 The Decred developers
     3  // Use of this source code is governed by an ISC
     4  // license that can be found in the LICENSE file.
     5  
     6  package blockchain
     7  
     8  import (
     9  	"fmt"
    10  
    11  	"github.com/decred/dcrd/chaincfg/chainhash"
    12  	"github.com/decred/dcrd/database"
    13  	"github.com/decred/dcrd/dcrutil"
    14  	"github.com/decred/dcrd/txscript"
    15  )
    16  
    17  // NextLotteryData returns the next tickets eligible for spending as SSGen
    18  // on the top block.  It also returns the ticket pool size and the PRNG
    19  // state checksum.
    20  //
    21  // This function is safe for concurrent access.
    22  func (b *BlockChain) NextLotteryData() ([]chainhash.Hash, int, [6]byte, error) {
    23  	b.chainLock.RLock()
    24  	defer b.chainLock.RUnlock()
    25  
    26  	tipStakeNode := b.bestChain.Tip().stakeNode
    27  	return tipStakeNode.Winners(), tipStakeNode.PoolSize(),
    28  		tipStakeNode.FinalState(), nil
    29  }
    30  
    31  // lotteryDataForNode is a helper function that returns winning tickets
    32  // along with the ticket pool size and PRNG checksum for a given node.
    33  //
    34  // This function is NOT safe for concurrent access and MUST be called
    35  // with the chainLock held for writes.
    36  func (b *BlockChain) lotteryDataForNode(node *blockNode) ([]chainhash.Hash, int, [6]byte, error) {
    37  	if node.height < b.chainParams.StakeEnabledHeight {
    38  		return nil, 0, [6]byte{}, nil
    39  	}
    40  	stakeNode, err := b.fetchStakeNode(node)
    41  	if err != nil {
    42  		return nil, 0, [6]byte{}, err
    43  	}
    44  
    45  	return stakeNode.Winners(), stakeNode.PoolSize(), stakeNode.FinalState(), nil
    46  }
    47  
    48  // lotteryDataForBlock takes a node block hash and returns the next tickets
    49  // eligible for voting, the number of tickets in the ticket pool, and the
    50  // final state of the PRNG.
    51  //
    52  // This function is NOT safe for concurrent access and must have the chainLock
    53  // held for write access.
    54  func (b *BlockChain) lotteryDataForBlock(hash *chainhash.Hash) ([]chainhash.Hash, int, [6]byte, error) {
    55  	node := b.index.LookupNode(hash)
    56  	if node == nil {
    57  		return nil, 0, [6]byte{}, fmt.Errorf("block %s is not known", hash)
    58  	}
    59  
    60  	winningTickets, poolSize, finalState, err := b.lotteryDataForNode(node)
    61  	if err != nil {
    62  		return nil, 0, [6]byte{}, err
    63  	}
    64  
    65  	return winningTickets, poolSize, finalState, nil
    66  }
    67  
    68  // LotteryDataForBlock returns lottery data for a given block in the block
    69  // chain, including side chain blocks.
    70  //
    71  // It is safe for concurrent access.
    72  func (b *BlockChain) LotteryDataForBlock(hash *chainhash.Hash) ([]chainhash.Hash, int, [6]byte, error) {
    73  	// TODO: An optimization can be added that only calls the read lock if the
    74  	// block is not minMemoryStakeNodes blocks before the current best node.
    75  	// This is because all the data for these nodes can be assumed to be
    76  	// in memory.
    77  	b.chainLock.Lock()
    78  	winningTickets, poolSize, finalState, err := b.lotteryDataForBlock(hash)
    79  	b.chainLock.Unlock()
    80  	return winningTickets, poolSize, finalState, err
    81  }
    82  
    83  // LiveTickets returns all currently live tickets from the stake database.
    84  //
    85  // This function is NOT safe for concurrent access.
    86  func (b *BlockChain) LiveTickets() ([]chainhash.Hash, error) {
    87  	b.chainLock.RLock()
    88  	sn := b.bestChain.Tip().stakeNode
    89  	b.chainLock.RUnlock()
    90  
    91  	return sn.LiveTickets(), nil
    92  }
    93  
    94  // MissedTickets returns all currently missed tickets from the stake database.
    95  //
    96  // This function is NOT safe for concurrent access.
    97  func (b *BlockChain) MissedTickets() ([]chainhash.Hash, error) {
    98  	b.chainLock.RLock()
    99  	sn := b.bestChain.Tip().stakeNode
   100  	b.chainLock.RUnlock()
   101  
   102  	return sn.MissedTickets(), nil
   103  }
   104  
   105  // TicketsWithAddress returns a slice of ticket hashes that are currently live
   106  // corresponding to the given address.
   107  //
   108  // This function is safe for concurrent access.
   109  func (b *BlockChain) TicketsWithAddress(address dcrutil.Address) ([]chainhash.Hash, error) {
   110  	b.chainLock.RLock()
   111  	sn := b.bestChain.Tip().stakeNode
   112  	b.chainLock.RUnlock()
   113  
   114  	tickets := sn.LiveTickets()
   115  
   116  	var ticketsWithAddr []chainhash.Hash
   117  	err := b.db.View(func(dbTx database.Tx) error {
   118  		for _, hash := range tickets {
   119  			utxo, err := dbFetchUtxoEntry(dbTx, &hash)
   120  			if err != nil {
   121  				return err
   122  			}
   123  
   124  			_, addrs, _, err :=
   125  				txscript.ExtractPkScriptAddrs(txscript.DefaultScriptVersion,
   126  					utxo.PkScriptByIndex(0), b.chainParams)
   127  			if err != nil {
   128  				return err
   129  			}
   130  			if addrs[0].EncodeAddress() == address.EncodeAddress() {
   131  				ticketsWithAddr = append(ticketsWithAddr, hash)
   132  			}
   133  		}
   134  		return nil
   135  	})
   136  	if err != nil {
   137  		return nil, err
   138  	}
   139  
   140  	return ticketsWithAddr, nil
   141  }
   142  
   143  // CheckLiveTicket returns whether or not a ticket exists in the live ticket
   144  // treap of the best node.
   145  //
   146  // This function is safe for concurrent access.
   147  func (b *BlockChain) CheckLiveTicket(hash chainhash.Hash) bool {
   148  	b.chainLock.RLock()
   149  	sn := b.bestChain.Tip().stakeNode
   150  	b.chainLock.RUnlock()
   151  
   152  	return sn.ExistsLiveTicket(hash)
   153  }
   154  
   155  // CheckLiveTickets returns whether or not a slice of tickets exist in the live
   156  // ticket treap of the best node.
   157  //
   158  // This function is safe for concurrent access.
   159  func (b *BlockChain) CheckLiveTickets(hashes []chainhash.Hash) []bool {
   160  	b.chainLock.RLock()
   161  	sn := b.bestChain.Tip().stakeNode
   162  	b.chainLock.RUnlock()
   163  
   164  	existsSlice := make([]bool, len(hashes))
   165  	for i := range hashes {
   166  		existsSlice[i] = sn.ExistsLiveTicket(hashes[i])
   167  	}
   168  
   169  	return existsSlice
   170  }
   171  
   172  // CheckMissedTickets returns a slice of bools representing whether each ticket
   173  // hash has been missed in the live ticket treap of the best node.
   174  //
   175  // This function is safe for concurrent access.
   176  func (b *BlockChain) CheckMissedTickets(hashes []chainhash.Hash) []bool {
   177  	b.chainLock.RLock()
   178  	sn := b.bestChain.Tip().stakeNode
   179  	b.chainLock.RUnlock()
   180  
   181  	existsSlice := make([]bool, len(hashes))
   182  	for i := range hashes {
   183  		existsSlice[i] = sn.ExistsMissedTicket(hashes[i])
   184  	}
   185  
   186  	return existsSlice
   187  }
   188  
   189  // CheckExpiredTicket returns whether or not a ticket was ever expired.
   190  //
   191  // This function is safe for concurrent access.
   192  func (b *BlockChain) CheckExpiredTicket(hash chainhash.Hash) bool {
   193  	b.chainLock.RLock()
   194  	sn := b.bestChain.Tip().stakeNode
   195  	b.chainLock.RUnlock()
   196  
   197  	return sn.ExistsExpiredTicket(hash)
   198  }
   199  
   200  // CheckExpiredTickets returns whether or not a ticket in a slice of
   201  // tickets was ever expired.
   202  //
   203  // This function is safe for concurrent access.
   204  func (b *BlockChain) CheckExpiredTickets(hashes []chainhash.Hash) []bool {
   205  	b.chainLock.RLock()
   206  	sn := b.bestChain.Tip().stakeNode
   207  	b.chainLock.RUnlock()
   208  
   209  	existsSlice := make([]bool, len(hashes))
   210  	for i := range hashes {
   211  		existsSlice[i] = sn.ExistsExpiredTicket(hashes[i])
   212  	}
   213  
   214  	return existsSlice
   215  }
   216  
   217  // TicketPoolValue returns the current value of all the locked funds in the
   218  // ticket pool.
   219  //
   220  // This function is safe for concurrent access.  All live tickets are at least
   221  // 256 blocks deep on mainnet, so the UTXO set should generally always have
   222  // the asked for transactions.
   223  func (b *BlockChain) TicketPoolValue() (dcrutil.Amount, error) {
   224  	b.chainLock.RLock()
   225  	sn := b.bestChain.Tip().stakeNode
   226  	b.chainLock.RUnlock()
   227  
   228  	var amt int64
   229  	err := b.db.View(func(dbTx database.Tx) error {
   230  		for _, hash := range sn.LiveTickets() {
   231  			utxo, err := dbFetchUtxoEntry(dbTx, &hash)
   232  			if err != nil {
   233  				return err
   234  			}
   235  
   236  			amt += utxo.sparseOutputs[0].amount
   237  		}
   238  		return nil
   239  	})
   240  	if err != nil {
   241  		return 0, err
   242  	}
   243  	return dcrutil.Amount(amt), nil
   244  }