github.com/fozzysec/SiaPrime@v0.0.0-20190612043147-66c8e8d11fe3/modules/miner/blockmanager.go (about)

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