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  }