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