1// Copyright (c) 2016 The btcsuite developers 2// Use of this source code is governed by an ISC 3// license that can be found in the LICENSE file. 4 5package rpctest 6 7import ( 8 "errors" 9 "math" 10 "math/big" 11 "runtime" 12 "time" 13 14 "github.com/btcsuite/btcd/blockchain" 15 "github.com/btcsuite/btcd/chaincfg" 16 "github.com/btcsuite/btcd/chaincfg/chainhash" 17 "github.com/btcsuite/btcd/txscript" 18 "github.com/btcsuite/btcd/wire" 19 "github.com/btcsuite/btcutil" 20) 21 22// solveBlock attempts to find a nonce which makes the passed block header hash 23// to a value less than the target difficulty. When a successful solution is 24// found true is returned and the nonce field of the passed header is updated 25// with the solution. False is returned if no solution exists. 26func solveBlock(header *wire.BlockHeader, targetDifficulty *big.Int) bool { 27 // sbResult is used by the solver goroutines to send results. 28 type sbResult struct { 29 found bool 30 nonce uint32 31 } 32 33 // solver accepts a block header and a nonce range to test. It is 34 // intended to be run as a goroutine. 35 quit := make(chan bool) 36 results := make(chan sbResult) 37 solver := func(hdr wire.BlockHeader, startNonce, stopNonce uint32) { 38 // We need to modify the nonce field of the header, so make sure 39 // we work with a copy of the original header. 40 for i := startNonce; i >= startNonce && i <= stopNonce; i++ { 41 select { 42 case <-quit: 43 return 44 default: 45 hdr.Nonce = i 46 hash := hdr.BlockHash() 47 if blockchain.HashToBig(&hash).Cmp(targetDifficulty) <= 0 { 48 select { 49 case results <- sbResult{true, i}: 50 return 51 case <-quit: 52 return 53 } 54 } 55 } 56 } 57 select { 58 case results <- sbResult{false, 0}: 59 case <-quit: 60 return 61 } 62 } 63 64 startNonce := uint32(0) 65 stopNonce := uint32(math.MaxUint32) 66 numCores := uint32(runtime.NumCPU()) 67 noncesPerCore := (stopNonce - startNonce) / numCores 68 for i := uint32(0); i < numCores; i++ { 69 rangeStart := startNonce + (noncesPerCore * i) 70 rangeStop := startNonce + (noncesPerCore * (i + 1)) - 1 71 if i == numCores-1 { 72 rangeStop = stopNonce 73 } 74 go solver(*header, rangeStart, rangeStop) 75 } 76 for i := uint32(0); i < numCores; i++ { 77 result := <-results 78 if result.found { 79 close(quit) 80 header.Nonce = result.nonce 81 return true 82 } 83 } 84 85 return false 86} 87 88// standardCoinbaseScript returns a standard script suitable for use as the 89// signature script of the coinbase transaction of a new block. In particular, 90// it starts with the block height that is required by version 2 blocks. 91func standardCoinbaseScript(nextBlockHeight int32, extraNonce uint64) ([]byte, error) { 92 return txscript.NewScriptBuilder().AddInt64(int64(nextBlockHeight)). 93 AddInt64(int64(extraNonce)).Script() 94} 95 96// createCoinbaseTx returns a coinbase transaction paying an appropriate 97// subsidy based on the passed block height to the provided address. 98func createCoinbaseTx(coinbaseScript []byte, nextBlockHeight int32, 99 addr btcutil.Address, mineTo []wire.TxOut, 100 net *chaincfg.Params) (*btcutil.Tx, error) { 101 102 // Create the script to pay to the provided payment address. 103 pkScript, err := txscript.PayToAddrScript(addr) 104 if err != nil { 105 return nil, err 106 } 107 108 tx := wire.NewMsgTx(wire.TxVersion) 109 tx.AddTxIn(&wire.TxIn{ 110 // Coinbase transactions have no inputs, so previous outpoint is 111 // zero hash and max index. 112 PreviousOutPoint: *wire.NewOutPoint(&chainhash.Hash{}, 113 wire.MaxPrevOutIndex), 114 SignatureScript: coinbaseScript, 115 Sequence: wire.MaxTxInSequenceNum, 116 }) 117 if len(mineTo) == 0 { 118 tx.AddTxOut(&wire.TxOut{ 119 Value: blockchain.CalcBlockSubsidy(nextBlockHeight, net), 120 PkScript: pkScript, 121 }) 122 } else { 123 for i := range mineTo { 124 tx.AddTxOut(&mineTo[i]) 125 } 126 } 127 return btcutil.NewTx(tx), nil 128} 129 130// CreateBlock creates a new block building from the previous block with a 131// specified blockversion and timestamp. If the timestamp passed is zero (not 132// initialized), then the timestamp of the previous block will be used plus 1 133// second is used. Passing nil for the previous block results in a block that 134// builds off of the genesis block for the specified chain. 135func CreateBlock(prevBlock *btcutil.Block, inclusionTxs []*btcutil.Tx, 136 blockVersion int32, blockTime time.Time, miningAddr btcutil.Address, 137 mineTo []wire.TxOut, net *chaincfg.Params) (*btcutil.Block, error) { 138 139 var ( 140 prevHash *chainhash.Hash 141 blockHeight int32 142 prevBlockTime time.Time 143 ) 144 145 // If the previous block isn't specified, then we'll construct a block 146 // that builds off of the genesis block for the chain. 147 if prevBlock == nil { 148 prevHash = net.GenesisHash 149 blockHeight = 1 150 prevBlockTime = net.GenesisBlock.Header.Timestamp.Add(time.Minute) 151 } else { 152 prevHash = prevBlock.Hash() 153 blockHeight = prevBlock.Height() + 1 154 prevBlockTime = prevBlock.MsgBlock().Header.Timestamp 155 } 156 157 // If a target block time was specified, then use that as the header's 158 // timestamp. Otherwise, add one second to the previous block unless 159 // it's the genesis block in which case use the current time. 160 var ts time.Time 161 switch { 162 case !blockTime.IsZero(): 163 ts = blockTime 164 default: 165 ts = prevBlockTime.Add(time.Second) 166 } 167 168 extraNonce := uint64(0) 169 coinbaseScript, err := standardCoinbaseScript(blockHeight, extraNonce) 170 if err != nil { 171 return nil, err 172 } 173 coinbaseTx, err := createCoinbaseTx(coinbaseScript, blockHeight, 174 miningAddr, mineTo, net) 175 if err != nil { 176 return nil, err 177 } 178 179 // Create a new block ready to be solved. 180 blockTxns := []*btcutil.Tx{coinbaseTx} 181 if inclusionTxs != nil { 182 blockTxns = append(blockTxns, inclusionTxs...) 183 } 184 merkles := blockchain.BuildMerkleTreeStore(blockTxns, false) 185 var block wire.MsgBlock 186 block.Header = wire.BlockHeader{ 187 Version: blockVersion, 188 PrevBlock: *prevHash, 189 MerkleRoot: *merkles[len(merkles)-1], 190 Timestamp: ts, 191 Bits: net.PowLimitBits, 192 } 193 for _, tx := range blockTxns { 194 if err := block.AddTransaction(tx.MsgTx()); err != nil { 195 return nil, err 196 } 197 } 198 199 found := solveBlock(&block.Header, net.PowLimit) 200 if !found { 201 return nil, errors.New("Unable to solve block") 202 } 203 204 utilBlock := btcutil.NewBlock(&block) 205 utilBlock.SetHeight(blockHeight) 206 return utilBlock, nil 207} 208