174 lines
5.1 KiB
Go
174 lines
5.1 KiB
Go
|
// Copyright (c) 2016 The btcsuite developers
|
||
|
// Use of this source code is governed by an ISC
|
||
|
// license that can be found in the LICENSE file.
|
||
|
|
||
|
package rpctest
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"math"
|
||
|
"math/big"
|
||
|
"runtime"
|
||
|
"time"
|
||
|
|
||
|
"github.com/btcsuite/btcd/blockchain"
|
||
|
"github.com/btcsuite/btcd/chaincfg"
|
||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||
|
"github.com/btcsuite/btcd/txscript"
|
||
|
"github.com/btcsuite/btcd/wire"
|
||
|
"github.com/btcsuite/btcutil"
|
||
|
)
|
||
|
|
||
|
// solveBlock attempts to find a nonce which makes the passed block header hash
|
||
|
// to a value less than the target difficulty. When a successful solution is
|
||
|
// found true is returned and the nonce field of the passed header is updated
|
||
|
// with the solution. False is returned if no solution exists.
|
||
|
func solveBlock(header *wire.BlockHeader, targetDifficulty *big.Int) bool {
|
||
|
// sbResult is used by the solver goroutines to send results.
|
||
|
type sbResult struct {
|
||
|
found bool
|
||
|
nonce uint32
|
||
|
}
|
||
|
|
||
|
// solver accepts a block header and a nonce range to test. It is
|
||
|
// intended to be run as a goroutine.
|
||
|
quit := make(chan bool)
|
||
|
results := make(chan sbResult)
|
||
|
solver := func(hdr wire.BlockHeader, startNonce, stopNonce uint32) {
|
||
|
// We need to modify the nonce field of the header, so make sure
|
||
|
// we work with a copy of the original header.
|
||
|
for i := startNonce; i >= startNonce && i <= stopNonce; i++ {
|
||
|
select {
|
||
|
case <-quit:
|
||
|
return
|
||
|
default:
|
||
|
hdr.Nonce = i
|
||
|
hash := hdr.BlockHash()
|
||
|
if blockchain.HashToBig(&hash).Cmp(targetDifficulty) <= 0 {
|
||
|
results <- sbResult{true, i}
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
results <- sbResult{false, 0}
|
||
|
}
|
||
|
|
||
|
startNonce := uint32(0)
|
||
|
stopNonce := uint32(math.MaxUint32)
|
||
|
numCores := uint32(runtime.NumCPU())
|
||
|
noncesPerCore := (stopNonce - startNonce) / numCores
|
||
|
for i := uint32(0); i < numCores; i++ {
|
||
|
rangeStart := startNonce + (noncesPerCore * i)
|
||
|
rangeStop := startNonce + (noncesPerCore * (i + 1)) - 1
|
||
|
if i == numCores-1 {
|
||
|
rangeStop = stopNonce
|
||
|
}
|
||
|
go solver(*header, rangeStart, rangeStop)
|
||
|
}
|
||
|
for i := uint32(0); i < numCores; i++ {
|
||
|
result := <-results
|
||
|
if result.found {
|
||
|
close(quit)
|
||
|
header.Nonce = result.nonce
|
||
|
return true
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
// standardCoinbaseScript returns a standard script suitable for use as the
|
||
|
// signature script of the coinbase transaction of a new block. In particular,
|
||
|
// it starts with the block height that is required by version 2 blocks.
|
||
|
func standardCoinbaseScript(nextBlockHeight int32, extraNonce uint64) ([]byte, error) {
|
||
|
return txscript.NewScriptBuilder().AddInt64(int64(nextBlockHeight)).
|
||
|
AddInt64(int64(extraNonce)).Script()
|
||
|
}
|
||
|
|
||
|
// createCoinbaseTx returns a coinbase transaction paying an appropriate
|
||
|
// subsidy based on the passed block height to the provided address.
|
||
|
func createCoinbaseTx(coinbaseScript []byte, nextBlockHeight int32,
|
||
|
addr btcutil.Address, net *chaincfg.Params) (*btcutil.Tx, error) {
|
||
|
|
||
|
// Create the script to pay to the provided payment address.
|
||
|
pkScript, err := txscript.PayToAddrScript(addr)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
tx := wire.NewMsgTx()
|
||
|
tx.AddTxIn(&wire.TxIn{
|
||
|
// Coinbase transactions have no inputs, so previous outpoint is
|
||
|
// zero hash and max index.
|
||
|
PreviousOutPoint: *wire.NewOutPoint(&chainhash.Hash{},
|
||
|
wire.MaxPrevOutIndex),
|
||
|
SignatureScript: coinbaseScript,
|
||
|
Sequence: wire.MaxTxInSequenceNum,
|
||
|
})
|
||
|
tx.AddTxOut(&wire.TxOut{
|
||
|
Value: blockchain.CalcBlockSubsidy(nextBlockHeight, net),
|
||
|
PkScript: pkScript,
|
||
|
})
|
||
|
return btcutil.NewTx(tx), nil
|
||
|
}
|
||
|
|
||
|
// createBlock creates a new block building from the previous block.
|
||
|
func createBlock(prevBlock *btcutil.Block, inclusionTxs []*btcutil.Tx,
|
||
|
blockVersion int32, blockTime time.Time,
|
||
|
miningAddr btcutil.Address, net *chaincfg.Params) (*btcutil.Block, error) {
|
||
|
|
||
|
prevHash := prevBlock.Hash()
|
||
|
blockHeight := prevBlock.Height() + 1
|
||
|
|
||
|
// If a target block time was specified, then use that as the header's
|
||
|
// timestamp. Otherwise, add one second to the previous block unless
|
||
|
// it's the genesis block in which case use the current time.
|
||
|
var ts time.Time
|
||
|
switch {
|
||
|
case !blockTime.IsZero():
|
||
|
ts = blockTime
|
||
|
default:
|
||
|
ts = prevBlock.MsgBlock().Header.Timestamp.Add(time.Second)
|
||
|
}
|
||
|
|
||
|
extraNonce := uint64(0)
|
||
|
coinbaseScript, err := standardCoinbaseScript(blockHeight, extraNonce)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
coinbaseTx, err := createCoinbaseTx(coinbaseScript, blockHeight,
|
||
|
miningAddr, net)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
// Create a new block ready to be solved.
|
||
|
blockTxns := []*btcutil.Tx{coinbaseTx}
|
||
|
if inclusionTxs != nil {
|
||
|
blockTxns = append(blockTxns, inclusionTxs...)
|
||
|
}
|
||
|
merkles := blockchain.BuildMerkleTreeStore(blockTxns)
|
||
|
var block wire.MsgBlock
|
||
|
block.Header = wire.BlockHeader{
|
||
|
Version: blockVersion,
|
||
|
PrevBlock: *prevHash,
|
||
|
MerkleRoot: *merkles[len(merkles)-1],
|
||
|
Timestamp: ts,
|
||
|
Bits: net.PowLimitBits,
|
||
|
}
|
||
|
for _, tx := range blockTxns {
|
||
|
if err := block.AddTransaction(tx.MsgTx()); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
found := solveBlock(&block.Header, net.PowLimit)
|
||
|
if !found {
|
||
|
return nil, errors.New("Unable to solve block")
|
||
|
}
|
||
|
|
||
|
utilBlock := btcutil.NewBlock(&block)
|
||
|
utilBlock.SetHeight(blockHeight)
|
||
|
return utilBlock, nil
|
||
|
}
|