github.com/NebulousLabs/Sia@v1.3.7/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 unlocked, err := m.wallet.Unlocked() 85 if err != nil { 86 return types.BlockHeader{}, types.Target{}, err 87 } 88 if !unlocked { 89 return types.BlockHeader{}, types.Target{}, modules.ErrLockedWallet 90 } 91 92 // Check that the wallet has been initialized, and that the miner has 93 // successfully fetched an address. 94 err = m.checkAddress() 95 if err != nil { 96 return types.BlockHeader{}, types.Target{}, err 97 } 98 99 // If too much time has elapsed since the last source block, get a new one. 100 // This typically only happens if the miner has just turned on after being 101 // off for a while. If the current block has been used for too many 102 // requests, fetch a new source block. 103 if time.Since(m.sourceBlockTime) > MaxSourceBlockAge || m.memProgress%(HeaderMemory/BlockMemory) == 0 { 104 m.newSourceBlock() 105 } 106 107 // Create a header from the source block - this may be a race condition, 108 // but I don't think so (underlying slice may be shared with other blocks 109 // accessible outside the miner). 110 var arbData [crypto.EntropySize]byte 111 fastrand.Read(arbData[:]) 112 copy(m.sourceBlock.Transactions[0].ArbitraryData[0], arbData[:]) 113 header := m.sourceBlock.Header() 114 115 // Save the mapping from the header to its block and from the header to its 116 // arbitrary data, replacing whatever header already exists. 117 delete(m.blockMem, m.headerMem[m.memProgress]) 118 delete(m.arbDataMem, m.headerMem[m.memProgress]) 119 m.blockMem[header] = m.sourceBlock 120 m.arbDataMem[header] = arbData 121 m.headerMem[m.memProgress] = header 122 m.memProgress++ 123 if m.memProgress == HeaderMemory { 124 m.memProgress = 0 125 } 126 127 // Return the header and target. 128 return header, m.persist.Target, nil 129 } 130 131 // managedSubmitBlock takes a solved block and submits it to the blockchain. 132 func (m *Miner) managedSubmitBlock(b types.Block) error { 133 // Give the block to the consensus set. 134 err := m.cs.AcceptBlock(b) 135 // Add the miner to the blocks list if the only problem is that it's stale. 136 if err == modules.ErrNonExtendingBlock { 137 m.mu.Lock() 138 m.persist.BlocksFound = append(m.persist.BlocksFound, b.ID()) 139 m.mu.Unlock() 140 m.log.Println("Mined a stale block - block appears valid but does not extend the blockchain") 141 return err 142 } 143 if err == modules.ErrBlockUnsolved { 144 m.log.Println("Mined an unsolved block - header submission appears to be incorrect") 145 return err 146 } 147 if err != nil { 148 m.tpool.PurgeTransactionPool() 149 m.log.Critical("ERROR: an invalid block was submitted:", err) 150 return err 151 } 152 m.mu.Lock() 153 defer m.mu.Unlock() 154 155 // Grab a new address for the miner. Call may fail if the wallet is locked 156 // or if the wallet addresses have been exhausted. 157 m.persist.BlocksFound = append(m.persist.BlocksFound, b.ID()) 158 var uc types.UnlockConditions 159 uc, err = m.wallet.NextAddress() 160 if err != nil { 161 return err 162 } 163 m.persist.Address = uc.UnlockHash() 164 return m.saveSync() 165 } 166 167 // SubmitHeader accepts a block header. 168 func (m *Miner) SubmitHeader(bh types.BlockHeader) error { 169 if err := m.tg.Add(); err != nil { 170 return err 171 } 172 defer m.tg.Done() 173 174 // Because a call to managedSubmitBlock is required at the end of this 175 // function, the first part needs to be wrapped in an anonymous function 176 // for lock safety. 177 var b types.Block 178 err := func() error { 179 m.mu.Lock() 180 defer m.mu.Unlock() 181 182 // Lookup the block that corresponds to the provided header. 183 nonce := bh.Nonce 184 bh.Nonce = [8]byte{} 185 bPointer, bExists := m.blockMem[bh] 186 arbData, arbExists := m.arbDataMem[bh] 187 if !bExists || !arbExists { 188 return errLateHeader 189 } 190 191 // Block is going to be passed to external memory, but the memory pointed 192 // to by the transactions slice is still being modified - needs to be 193 // copied. Same with the memory being pointed to by the arb data slice. 194 b = *bPointer 195 txns := make([]types.Transaction, len(b.Transactions)) 196 copy(txns, b.Transactions) 197 b.Transactions = txns 198 b.Transactions[0].ArbitraryData = [][]byte{arbData[:]} 199 b.Nonce = nonce 200 201 // Sanity check - block should have same id as header. 202 bh.Nonce = nonce 203 if types.BlockID(crypto.HashObject(bh)) != b.ID() { 204 m.log.Critical("block reconstruction failed") 205 } 206 return nil 207 }() 208 if err != nil { 209 m.log.Println("ERROR during call to SubmitHeader, pre SubmitBlock:", err) 210 return err 211 } 212 err = m.managedSubmitBlock(b) 213 if err != nil { 214 m.log.Println("ERROR returned by managedSubmitBlock:", err) 215 return err 216 } 217 return nil 218 }