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 }