github.com/johnathanhowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/modules/miner/blockmanager.go (about)

     1  package miner
     2  
     3  import (
     4  	"crypto/rand"
     5  	"errors"
     6  	"time"
     7  
     8  	"github.com/NebulousLabs/Sia/crypto"
     9  	"github.com/NebulousLabs/Sia/modules"
    10  	"github.com/NebulousLabs/Sia/types"
    11  )
    12  
    13  var (
    14  	errLateHeader = errors.New("header is old, block could not be recovered")
    15  )
    16  
    17  // blockForWork returns a block that is ready for nonce grinding, including
    18  // correct miner payouts and a random transaction to prevent collisions and
    19  // overlapping work with other blocks being mined in parallel or for different
    20  // forks (during testing).
    21  func (m *Miner) blockForWork() types.Block {
    22  	b := m.persist.UnsolvedBlock
    23  
    24  	// Update the timestmap.
    25  	if b.Timestamp < types.CurrentTimestamp() {
    26  		b.Timestamp = types.CurrentTimestamp()
    27  	}
    28  
    29  	// Update the address + payouts.
    30  	err := m.checkAddress()
    31  	if err != nil {
    32  		m.log.Println(err)
    33  	}
    34  	b.MinerPayouts = []types.SiacoinOutput{{Value: b.CalculateSubsidy(m.persist.Height + 1), UnlockHash: m.persist.Address}}
    35  
    36  	// Add an arb-data txn to the block to create a unique merkle root.
    37  	randBytes, _ := crypto.RandBytes(types.SpecifierLen)
    38  	randTxn := types.Transaction{
    39  		ArbitraryData: [][]byte{append(modules.PrefixNonSia[:], randBytes...)},
    40  	}
    41  	b.Transactions = append([]types.Transaction{randTxn}, b.Transactions...)
    42  
    43  	return b
    44  }
    45  
    46  // newSourceBlock creates a new source block for the block manager so that new
    47  // headers will use the updated source block.
    48  func (m *Miner) newSourceBlock() {
    49  	// To guarantee garbage collection of old blocks, delete all header entries
    50  	// that have not been reached for the current block.
    51  	for m.memProgress%(HeaderMemory/BlockMemory) != 0 {
    52  		delete(m.blockMem, m.headerMem[m.memProgress])
    53  		delete(m.arbDataMem, m.headerMem[m.memProgress])
    54  		m.memProgress++
    55  		if m.memProgress == HeaderMemory {
    56  			m.memProgress = 0
    57  		}
    58  	}
    59  
    60  	// Update the source block.
    61  	block := m.blockForWork()
    62  	m.sourceBlock = &block
    63  	m.sourceBlockTime = time.Now()
    64  }
    65  
    66  // HeaderForWork returns a header that is ready for nonce grinding. The miner
    67  // will store the header in memory for a while, depending on the constants
    68  // 'HeaderMemory', 'BlockMemory', and 'MaxSourceBlockAge'. On the full network,
    69  // it is typically safe to assume that headers will be remembered for
    70  // min(10 minutes, 10e3 requests).
    71  func (m *Miner) HeaderForWork() (types.BlockHeader, types.Target, error) {
    72  	m.mu.Lock()
    73  	defer m.mu.Unlock()
    74  
    75  	// Return a blank header with an error if the wallet is locked.
    76  	if !m.wallet.Unlocked() {
    77  		return types.BlockHeader{}, types.Target{}, modules.ErrLockedWallet
    78  	}
    79  
    80  	// Check that the wallet has been initialized, and that the miner has
    81  	// successfully fetched an address.
    82  	err := m.checkAddress()
    83  	if err != nil {
    84  		return types.BlockHeader{}, types.Target{}, err
    85  	}
    86  
    87  	// If too much time has elapsed since the last source block, get a new one.
    88  	// This typically only happens if the miner has just turned on after being
    89  	// off for a while. If the current block has been used for too many
    90  	// requests, fetch a new source block.
    91  	if time.Since(m.sourceBlockTime) > MaxSourceBlockAge || m.memProgress%(HeaderMemory/BlockMemory) == 0 {
    92  		m.newSourceBlock()
    93  	}
    94  
    95  	// Create a header from the source block - this may be a race condition,
    96  	// but I don't think so (underlying slice may be shared with other blocks
    97  	// accessible outside the miner).
    98  	var arbData [crypto.EntropySize]byte
    99  	_, err = rand.Read(arbData[:])
   100  	if err != nil {
   101  		return types.BlockHeader{}, types.Target{}, err
   102  	}
   103  	copy(m.sourceBlock.Transactions[0].ArbitraryData[0], arbData[:])
   104  	header := m.sourceBlock.Header()
   105  
   106  	// Save the mapping from the header to its block and from the header to its
   107  	// arbitrary data, replacing whatever header already exists.
   108  	delete(m.blockMem, m.headerMem[m.memProgress])
   109  	delete(m.arbDataMem, m.headerMem[m.memProgress])
   110  	m.blockMem[header] = m.sourceBlock
   111  	m.arbDataMem[header] = arbData
   112  	m.headerMem[m.memProgress] = header
   113  	m.memProgress++
   114  	if m.memProgress == HeaderMemory {
   115  		m.memProgress = 0
   116  	}
   117  
   118  	// Return the header and target.
   119  	return header, m.persist.Target, nil
   120  }
   121  
   122  // managedSubmitBlock takes a solved block and submits it to the blockchain.
   123  // managedSubmitBlock should not be called with a lock.
   124  func (m *Miner) managedSubmitBlock(b types.Block) error {
   125  	// Give the block to the consensus set.
   126  	err := m.cs.AcceptBlock(b)
   127  	// Add the miner to the blocks list if the only problem is that it's stale.
   128  	if err == modules.ErrNonExtendingBlock {
   129  		m.mu.Lock()
   130  		m.persist.BlocksFound = append(m.persist.BlocksFound, b.ID())
   131  		m.mu.Unlock()
   132  		m.log.Println("Mined a stale block - block appears valid but does not extend the blockchain")
   133  		return err
   134  	}
   135  	if err == modules.ErrBlockUnsolved {
   136  		m.log.Println("Mined an unsolved block - header submission appears to be incorrect")
   137  		return err
   138  	}
   139  	if err != nil {
   140  		m.tpool.PurgeTransactionPool()
   141  		m.log.Critical("ERROR: an invalid block was submitted:", err)
   142  		return err
   143  	}
   144  	m.mu.Lock()
   145  	defer m.mu.Unlock()
   146  
   147  	// Grab a new address for the miner. Call may fail if the wallet is locked
   148  	// or if the wallet addresses have been exhausted.
   149  	m.persist.BlocksFound = append(m.persist.BlocksFound, b.ID())
   150  	var uc types.UnlockConditions
   151  	uc, err = m.wallet.NextAddress()
   152  	if err != nil {
   153  		return err
   154  	}
   155  	m.persist.Address = uc.UnlockHash()
   156  	return m.saveSync()
   157  }
   158  
   159  // SubmitHeader accepts a block header.
   160  func (m *Miner) SubmitHeader(bh types.BlockHeader) error {
   161  	// Because a call to managedSubmitBlock is required at the end of this
   162  	// function, the first part needs to be wrapped in an anonymous function
   163  	// for lock safety.
   164  	var b types.Block
   165  	err := func() error {
   166  		m.mu.Lock()
   167  		defer m.mu.Unlock()
   168  
   169  		// Lookup the block that corresponds to the provided header.
   170  		nonce := bh.Nonce
   171  		bh.Nonce = [8]byte{}
   172  		bPointer, bExists := m.blockMem[bh]
   173  		arbData, arbExists := m.arbDataMem[bh]
   174  		if !bExists || !arbExists {
   175  			return errLateHeader
   176  		}
   177  
   178  		// Block is going to be passed to external memory, but the memory pointed
   179  		// to by the transactions slice is still being modified - needs to be
   180  		// copied. Same with the memory being pointed to by the arb data slice.
   181  		b = *bPointer
   182  		txns := make([]types.Transaction, len(b.Transactions))
   183  		copy(txns, b.Transactions)
   184  		b.Transactions = txns
   185  		b.Transactions[0].ArbitraryData = [][]byte{arbData[:]}
   186  		b.Nonce = nonce
   187  
   188  		// Sanity check - block should have same id as header.
   189  		bh.Nonce = nonce
   190  		if types.BlockID(crypto.HashObject(bh)) != b.ID() {
   191  			m.log.Critical("block reconstruction failed")
   192  		}
   193  		return nil
   194  	}()
   195  	if err != nil {
   196  		m.log.Println("ERROR during call to SubmitHeader, pre SubmitBlock:", err)
   197  		return err
   198  	}
   199  	err = m.managedSubmitBlock(b)
   200  	if err != nil {
   201  		m.log.Println("ERROR returned by managedSubmitBlock:", err)
   202  		return err
   203  	}
   204  	return nil
   205  }