// +build bitcoind package lntest import ( "errors" "fmt" "io/ioutil" "math/rand" "os" "os/exec" "path/filepath" "time" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/rpcclient" ) // logDir is the name of the temporary log directory. const logDir = "./.backendlogs" // BitcoindBackendConfig is an implementation of the BackendConfig interface // backed by a Bitcoind node. type BitcoindBackendConfig struct { rpcHost string rpcUser string rpcPass string zmqBlockPath string zmqTxPath string p2pPort int rpcClient *rpcclient.Client // minerAddr is the p2p address of the miner to connect to. minerAddr string } // A compile time assertion to ensure BitcoindBackendConfig meets the // BackendConfig interface. var _ BackendConfig = (*BitcoindBackendConfig)(nil) // GenArgs returns the arguments needed to be passed to LND at startup for // using this node as a chain backend. func (b BitcoindBackendConfig) GenArgs() []string { var args []string args = append(args, "--bitcoin.node=bitcoind") args = append(args, fmt.Sprintf("--bitcoind.rpchost=%v", b.rpcHost)) args = append(args, fmt.Sprintf("--bitcoind.rpcuser=%v", b.rpcUser)) args = append(args, fmt.Sprintf("--bitcoind.rpcpass=%v", b.rpcPass)) args = append(args, fmt.Sprintf("--bitcoind.zmqpubrawblock=%v", b.zmqBlockPath)) args = append(args, fmt.Sprintf("--bitcoind.zmqpubrawtx=%v", b.zmqTxPath)) return args } // ConnectMiner is called to establish a connection to the test miner. func (b BitcoindBackendConfig) ConnectMiner() error { return b.rpcClient.AddNode(b.minerAddr, rpcclient.ANAdd) } // DisconnectMiner is called to disconnect the miner. func (b BitcoindBackendConfig) DisconnectMiner() error { return b.rpcClient.AddNode(b.minerAddr, rpcclient.ANRemove) } // Name returns the name of the backend type. func (b BitcoindBackendConfig) Name() string { return "bitcoind" } // NewBackend starts a bitcoind node and returns a BitoindBackendConfig for // that node. func NewBackend(miner string, netParams *chaincfg.Params) ( *BitcoindBackendConfig, func() error, error) { if netParams != &chaincfg.RegressionNetParams { return nil, nil, fmt.Errorf("only regtest supported") } if err := os.MkdirAll(logDir, 0700); err != nil { return nil, nil, err } logFile, err := filepath.Abs(logDir + "/bitcoind.log") if err != nil { return nil, nil, err } tempBitcoindDir, err := ioutil.TempDir("", "bitcoind") if err != nil { return nil, nil, fmt.Errorf("unable to create temp directory: %v", err) } zmqBlockPath := "ipc:///" + tempBitcoindDir + "/blocks.socket" zmqTxPath := "ipc:///" + tempBitcoindDir + "/txs.socket" rpcPort := rand.Int()%(65536-1024) + 1024 p2pPort := rand.Int()%(65536-1024) + 1024 bitcoind := exec.Command( "bitcoind", "-datadir="+tempBitcoindDir, "-debug", "-regtest", "-txindex", "-whitelist=127.0.0.1", // whitelist localhost to speed up relay "-rpcauth=weks:469e9bb14ab2360f8e226efed5ca6f"+ "d$507c670e800a95284294edb5773b05544b"+ "220110063096c221be9933c82d38e1", fmt.Sprintf("-rpcport=%d", rpcPort), fmt.Sprintf("-port=%d", p2pPort), "-disablewallet", "-zmqpubrawblock="+zmqBlockPath, "-zmqpubrawtx="+zmqTxPath, "-debuglogfile="+logFile, ) err = bitcoind.Start() if err != nil { os.RemoveAll(tempBitcoindDir) return nil, nil, fmt.Errorf("couldn't start bitcoind: %v", err) } cleanUp := func() error { bitcoind.Process.Kill() bitcoind.Wait() var errStr string // After shutting down the chain backend, we'll make a copy of // the log file before deleting the temporary log dir. err := CopyFile("./output_bitcoind_chainbackend.log", logFile) if err != nil { errStr += fmt.Sprintf("unable to copy file: %v\n", err) } if err = os.RemoveAll(logDir); err != nil { errStr += fmt.Sprintf( "cannot remove dir %s: %v\n", logDir, err, ) } if err := os.RemoveAll(tempBitcoindDir); err != nil { errStr += fmt.Sprintf( "cannot remove dir %s: %v\n", tempBitcoindDir, err, ) } if errStr != "" { return errors.New(errStr) } return nil } // Allow process to start. time.Sleep(1 * time.Second) rpcHost := fmt.Sprintf("127.0.0.1:%d", rpcPort) rpcUser := "weks" rpcPass := "weks" rpcCfg := rpcclient.ConnConfig{ Host: rpcHost, User: rpcUser, Pass: rpcPass, DisableConnectOnNew: true, DisableAutoReconnect: false, DisableTLS: true, HTTPPostMode: true, } client, err := rpcclient.New(&rpcCfg, nil) if err != nil { cleanUp() return nil, nil, fmt.Errorf("unable to create rpc client: %v", err) } // We start by adding the miner to the bitcoind addnode list. We do // this instead of connecting using command line flags, because it will // allow us to disconnect the miner using the AddNode command later. if err := client.AddNode(miner, rpcclient.ANAdd); err != nil { cleanUp() return nil, nil, fmt.Errorf("unable to add node: %v", err) } bd := BitcoindBackendConfig{ rpcHost: rpcHost, rpcUser: rpcUser, rpcPass: rpcPass, zmqBlockPath: zmqBlockPath, zmqTxPath: zmqTxPath, p2pPort: p2pPort, rpcClient: client, minerAddr: miner, } return &bd, cleanUp, nil }