package chain import ( "fmt" "io" "math" "net" "runtime" "sync" "time" "github.com/lbryio/lbcd/blockchain" "github.com/lbryio/lbcd/chaincfg" "github.com/lbryio/lbcd/chaincfg/chainhash" "github.com/lbryio/lbcd/wire" btcutil "github.com/lbryio/lbcutil" ) var chainParams = chaincfg.RegressionNetParams // conn mocks a network connection by implementing the net.Conn interface. It is // used to test peer connection without actually opening a network connection. type conn struct { io.Reader io.Writer io.Closer localAddr string remoteAddr string } func (c conn) LocalAddr() net.Addr { return &addr{"tcp", c.localAddr} } func (c conn) RemoteAddr() net.Addr { return &addr{"tcp", c.remoteAddr} } func (c conn) SetDeadline(t time.Time) error { return nil } func (c conn) SetReadDeadline(t time.Time) error { return nil } func (c conn) SetWriteDeadline(t time.Time) error { return nil } // addr mocks a network address. type addr struct { net, address string } func (m addr) Network() string { return m.net } func (m addr) String() string { return m.address } // pipe turns two mock connections into a full-duplex connection similar to // net.Pipe to allow pipe's with (fake) addresses. func pipe(c1, c2 *conn) (*conn, *conn) { r1, w1 := io.Pipe() r2, w2 := io.Pipe() c1.Writer = w1 c1.Closer = w1 c2.Reader = r1 c1.Reader = r2 c2.Writer = w2 c2.Closer = w2 return c1, c2 } // calcMerkleRoot creates a merkle tree from the slice of transactions and // returns the root of the tree. // // This function was copied from: // https://github.com/lbryio/lbcd/blob/36a96f6a0025b6aeaebe4106821c2d46ee4be8d4/blockchain/fullblocktests/generate.go#L303 func calcMerkleRoot(txns []*wire.MsgTx) chainhash.Hash { if len(txns) == 0 { return chainhash.Hash{} } utilTxns := make([]*btcutil.Tx, 0, len(txns)) for _, tx := range txns { utilTxns = append(utilTxns, btcutil.NewTx(tx)) } merkles := blockchain.BuildMerkleTreeStore(utilTxns, false) return *merkles[len(merkles)-1] } // 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. // // This function was copied from: // https://github.com/lbryio/lbcd/blob/36a96f6a0025b6aeaebe4106821c2d46ee4be8d4/blockchain/fullblocktests/generate.go#L324 func solveBlock(header *wire.BlockHeader) bool { // sbResult is used by the solver goroutines to send results. type sbResult struct { found bool nonce uint32 } // Make sure all spawned goroutines finish executing before returning. var wg sync.WaitGroup defer func() { wg.Wait() }() // solver accepts a block header and a nonce range to test. It is // intended to be run as a goroutine. targetDifficulty := blockchain.CompactToBig(header.Bits) quit := make(chan bool) results := make(chan sbResult) solver := func(hdr wire.BlockHeader, startNonce, stopNonce uint32) { defer wg.Done() // 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 { select { case results <- sbResult{true, i}: case <-quit: } return } } } select { case results <- sbResult{false, 0}: case <-quit: } } startNonce := uint32(1) stopNonce := uint32(math.MaxUint32) numCores := uint32(runtime.NumCPU()) noncesPerCore := (stopNonce - startNonce) / numCores wg.Add(int(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 } // genBlockChain generates a test chain with the given number of blocks. func genBlockChain(numBlocks uint32) ([]*chainhash.Hash, map[chainhash.Hash]*wire.MsgBlock) { prevHash := chainParams.GenesisHash prevHeader := &chainParams.GenesisBlock.Header hashes := make([]*chainhash.Hash, numBlocks) blocks := make(map[chainhash.Hash]*wire.MsgBlock, numBlocks) // Each block contains three transactions, including the coinbase // transaction. Each non-coinbase transaction spends outputs from // the previous block. We also need to produce blocks that succeed // validation through blockchain.CheckBlockSanity. script := []byte{0x01, 0x01} createTx := func(prevOut wire.OutPoint) *wire.MsgTx { return &wire.MsgTx{ TxIn: []*wire.TxIn{{ PreviousOutPoint: prevOut, SignatureScript: script, }}, TxOut: []*wire.TxOut{{PkScript: script}}, } } for i := uint32(0); i < numBlocks; i++ { txs := []*wire.MsgTx{ createTx(wire.OutPoint{Index: wire.MaxPrevOutIndex}), createTx(wire.OutPoint{Hash: *prevHash, Index: 0}), createTx(wire.OutPoint{Hash: *prevHash, Index: 1}), } header := &wire.BlockHeader{ Version: 1, PrevBlock: *prevHash, MerkleRoot: calcMerkleRoot(txs), Timestamp: prevHeader.Timestamp.Add(10 * time.Minute), Bits: chainParams.PowLimitBits, Nonce: 0, } if !solveBlock(header) { panic(fmt.Sprintf("could not solve block at idx %v", i)) } block := &wire.MsgBlock{ Header: *header, Transactions: txs, } blockHash := block.BlockHash() hashes[i] = &blockHash blocks[blockHash] = block prevHash = &blockHash prevHeader = header } return hashes, blocks } // producesInvalidBlock produces a copy of the block that duplicates the last // transaction. When the block has an odd number of transactions, this results // in the invalid block maintaining the same hash as the valid block. func produceInvalidBlock(block *wire.MsgBlock) *wire.MsgBlock { numTxs := len(block.Transactions) lastTx := block.Transactions[numTxs-1] blockCopy := &wire.MsgBlock{ Header: block.Header, Transactions: make([]*wire.MsgTx, numTxs), } copy(blockCopy.Transactions, block.Transactions) blockCopy.AddTransaction(lastTx) return blockCopy }