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 }