gitlab.com/jokerrs1/Sia@v1.3.2/modules/miner/blockmanager.go (about)

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