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