├── VPC_network_-_Litecoin_Node.png ├── etc └── systemd │ └── system │ └── stratum-server.service ├── test ├── README.md └── server.js ├── merge-mining ├── generate-test-addresses.js ├── README.md ├── server-test.js └── server.js ├── server.js └── README.md /VPC_network_-_Litecoin_Node.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeghen/litecoin-solo-mining-tutorial/HEAD/VPC_network_-_Litecoin_Node.png -------------------------------------------------------------------------------- /etc/systemd/system/stratum-server.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Stratum Server daemon 3 | After=network.target 4 | 5 | [Service] 6 | WorkingDirectory=/opt/stratum-server 7 | User=root 8 | Group=root 9 | Type=simple 10 | ExecStart=/home/mike/.nvm/v0.10.25/bin/node /opt/stratum-server/server.js 11 | Restart=on-failure 12 | RestartSec=5s 13 | PrivateTmp=true 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | # Solo Litecoin Mining on Testnet 2 | This directory contains information about setting up for mining on the Litecoin test network. 3 | 4 | I used this as a way to test that I am properly configuring for solo mining. 5 | 6 | Just follow the normal setup but use this for `litecoin.conf` 7 | 8 | **Sample Litecoin Testnet Daemon Configuration** 9 | ``` 10 | datadir=/ltctest # Keep data isolated 11 | testnet=1 # Tell the daemon to use the testnet 12 | 13 | # RPC commands so NOMP can connect for mining 14 | rpcuser=litecointestrpc 15 | rpcpassword=pickASecureTestPassword 16 | rpcport=2300 17 | daemon=1 18 | server=1 19 | gen=0 20 | ``` 21 | 22 | You will also need a Testnet address and you will need to adjust your `server.js` file. 23 | -------------------------------------------------------------------------------- /merge-mining/generate-test-addresses.js: -------------------------------------------------------------------------------- 1 | // Shared private key for all coins 2 | var ci = require('coininfo') 3 | var CoinKey = require('coinkey') 4 | var secureRandom = require('secure-random') 5 | var privateKey = secureRandom.randomBuffer(32) 6 | 7 | console.log("PRIVATE KEY: ", privateKey) // SAVE THIS! 8 | 9 | var dogeKey = new CoinKey(privateKey, ci('DOGE')) 10 | var ltcKey = new CoinKey(privateKey, ci('LTC')) 11 | var viaKey = new CoinKey(privateKey, ci('VIA')) 12 | 13 | console.log("Litecoin Public Address : ", ltcKey.publicAddress) // For mining 14 | console.log("Litecoin Private WIF: ", ltcKey.privateWif) // Import to wallet 15 | console.log("Dogecoin Private WIF: ", dogeKey.privateWif) // Import to wallet 16 | 17 | // NOTE: For more wallet addresses, use: 18 | // https://github.com/MichaelMure/WalletGenerator.net 19 | -------------------------------------------------------------------------------- /merge-mining/README.md: -------------------------------------------------------------------------------- 1 | # Merge Mining on Dogecoin and Litecoin Testnet 2 | This directory contains information about testing merge mining on the Litecoin and Dogecoin test networks. 3 | 4 | I used this as a way to test that merge mining is working my codebase. 5 | 6 | :email: mike@mikeghen.com 7 | 8 | :electric_plug: Will get you set up and provide support/training for just 1 LTC! 9 | 10 | :pray: LaY9tLS9DmbW5FXpnXYCSjVL3hTB7xyL 11 | 12 | ## Setup Daemons 13 | The first step involves installing the Daemons on the server. 14 | 15 | ### Litecoin 16 | 1. Download and extract Litecoin Core: 17 | ``` 18 | cd /opt 19 | wget https://download.litecoin.org/litecoin-0.15.1/linux/litecoin-0.15.1-x86_64-linux-gnu.tar.gz 20 | tar -xvzf litecoin-0.14.2-x86_64-linux-gnu.tar.gz 21 | rm -rf litecoin-0.14.2-x86_64-linux-gnu.tar.gz 22 | cd /opt/litecoin-0.14.2 23 | ``` 24 | 2. Edit the config file: 25 | ``` 26 | mkdir /dogetest 27 | vi /dogetest/dogecoin.conf 28 | ``` 29 | **Sample Litecoin Testnet Daemon Configuration** 30 | ``` 31 | datadir=/ltctest # Keep data isolated 32 | testnet=1 # Tell the daemon to use the testnet 33 | 34 | # RPC commands so NOMP can connect for mining 35 | rpcuser=litecointestrpc 36 | rpcpassword=pickASecureTestPassword 37 | rpcport=2300 38 | daemon=1 39 | server=1 40 | gen=1 41 | ``` 42 | 3. Start the daemon: 43 | ``` 44 | bin/litecoind -datadir="/ltctest" 45 | ``` 46 | 4. Confirm its running: 47 | ``` 48 | bin/litecoin-cli -datadir="/ltctest" getinfo 49 | ``` 50 | :star: Remember to use `-datadir` on all Litecoin CLI calls when working with test. 51 | ### Dogecoin 52 | ``` 53 | cd /opt 54 | wget https://github.com/dogecoin/dogecoin/releases/download/v1.10.0/dogecoin-1.10.0-linux64.tar.gz 55 | tar -xvzf dogecoin-1.10.0-linux64.tar.gz 56 | rm -rf dogecoin-1.10.0-linux64.tar.gz 57 | cd /opt/dogecoin-1.10.0 58 | ``` 59 | Edit the config file: 60 | ``` 61 | mkdir /dogetest 62 | vi /dogetest/dogecoin.conf 63 | ``` 64 | **Sample Dogecoin Testnet Daemon Configuration** 65 | ``` 66 | datadir=/dogetest # Keep data isolated 67 | testnet=1 # Tell the daemon to use the testnet 68 | 69 | # RPC commands so NOMP can connect for mining 70 | rpcuser=dogecointestrpc 71 | rpcpassword=pickASecureTestPassword 72 | rpcport=2301 73 | daemon=1 74 | server=1 75 | gen=1 76 | ``` 77 | Start the daemon: 78 | ``` 79 | cd /opt/dogecoin-1.10.0 80 | bin/dogecoind -datadir="/dogetest" 81 | ``` 82 | Confirm its running: 83 | ``` 84 | bin/dogecoin-cli -datadir="/dogetest" getinfo 85 | ``` 86 | :star: Remember to use `-datadir` on all Dogecoin CLI calls when working with test. 87 | 88 | ## Generating Merge Mining Addresses 89 | This example will generate a testnet address for Litecoin and Dogecoin using the same private key. Apparently this is how we merge mine (maybe?). 90 | 91 | First, install some Node.js Packages. 92 | 93 | ```javascript 94 | // Shared private key for all coins 95 | var ci = require('coininfo') 96 | var CoinKey = require('coinkey') 97 | var secureRandom = require('secure-random') 98 | var privateKey = secureRandom.randomBuffer(32) 99 | 100 | console.log("PRIVATE KEY: ", privateKey) // SAVE THIS! 101 | 102 | var dogeKey = new CoinKey(privateKey, ci('DOGE-TEST')) 103 | var ltcKey = new CoinKey(privateKey, ci('LTC-TEST')) 104 | 105 | console.log("Litecoin Public Address : ", ltcKey.publicAddress) // For mining 106 | console.log("Litecoin Private WIF: ", ltcKey.privateWif) // Import to wallet 107 | console.log("Dogecoin Private WIF: ", dogeKey.privateWif) // Import to wallet 108 | ``` 109 | You will be able to import the Private WIF (which means Wallet Import Format) using these commands: 110 | ``` 111 | /opt/litecoin-0.14.2/bin/litecoin-cli -datadir="/ltctest" importprivkey "cTv1M7ZBveUkByFPkhmfzFANuB5pN94UC2Q4zir1nDDZV9goLxoe" 112 | /opt/dogecoin-1.10.0/bin/dogecoin-cli -datadir="/dogetest" importprivkey "cm5GVSGzGxK291GvrrZTabMMQ3V82ybvWueL7dYCmAiz3ozaPZYq" 113 | ``` 114 | :information_source: I don't know why but one time this command took a very long time to import... so be patient after you run these commands. 115 | 116 | ## Setup Stratum Server for Merge Mining 117 | You will also need a Testnet address and you will need to adjust your `server.js` file. See my example file. 118 | 119 | # Comments on Merge Mining in Production 120 | Currently working on getting this working in Production with: 121 | * Litecoin 122 | * Dogecoin 123 | * Viacoin 124 | * Myraidcoin 125 | Because these coins have stable releases. I just repeated the process above for each coin and use production configurations (remove `testnet`). 126 | ## Considerations 127 | * The blockchains take a while to download 128 | * Needed a beefier instance (2 vCPU, 8 GB memory) 129 | * Need to generate wallet addresses that coininfo/coinkey NPM packages weren't capable of building, used [MichaelMure/WalletGenerator.net](https://github.com/MichaelMure/WalletGenerator.net) 130 | -------------------------------------------------------------------------------- /test/server.js: -------------------------------------------------------------------------------- 1 | var myCoin = { 2 | "name": "Litecoin", 3 | "symbol": "LTC", 4 | "algorithm": "scrypt", 5 | "peerMagic": "fbc0b6db", 6 | "peerMagicTestnet": "fcc1b7dc" 7 | }; 8 | 9 | var Stratum = require('stratum-pool'); 10 | 11 | var pool = Stratum.createPool({ 12 | 13 | "coin": myCoin, 14 | 15 | "auxes": [], 16 | 17 | // Litecoin Testnet address 18 | "address": "mj439rRH1bspfC9NJPezgrSoLVvg2ztUfX", //Address to where block rewards are given 19 | 20 | /* Block rewards go to the configured pool wallet address to later be paid out to miners, 21 | except for a percentage that can go to, for examples, pool operator(s) as pool fees or 22 | or to donations address. Addresses or hashed public keys can be used. Here is an example 23 | of rewards going to the main pool op, a pool co-owner, and NOMP donation. */ 24 | // "rewardRecipients": { 25 | // "n37vuNFkXfk15uFnGoVyHZ6PYQxppD3QqK": 1.5, //1.5% goes to pool op 26 | // "mirj3LtZxbSTharhtXvotqtJXUY7ki5qfx": 0.5, //0.5% goes to a pool co-owner 27 | // }, 28 | 29 | "blockRefreshInterval": 1000, //How often to poll RPC daemons for new blocks, in milliseconds 30 | 31 | 32 | /* Some miner apps will consider the pool dead/offline if it doesn't receive anything new jobs 33 | for around a minute, so every time we broadcast jobs, set a timeout to rebroadcast 34 | in this many seconds unless we find a new job. Set to zero or remove to disable this. */ 35 | "jobRebroadcastTimeout": 55, 36 | 37 | /* Some attackers will create thousands of workers that use up all available socket connections, 38 | usually the workers are zombies and don't submit shares after connecting. This features 39 | detects those and disconnects them. */ 40 | "connectionTimeout": 1200, //Remove workers that haven't been in contact for this many seconds 41 | 42 | /* Sometimes you want the block hashes even for shares that aren't block candidates. */ 43 | "emitInvalidBlockHashes": false, 44 | 45 | /* Enable for client IP addresses to be detected when using a load balancer with TCP proxy 46 | protocol enabled, such as HAProxy with 'send-proxy' param: 47 | http://haproxy.1wt.eu/download/1.5/doc/configuration.txt */ 48 | "tcpProxyProtocol": false, 49 | 50 | /* If a worker is submitting a high threshold of invalid shares we can temporarily ban their IP 51 | to reduce system/network load. Also useful to fight against flooding attacks. If running 52 | behind something like HAProxy be sure to enable 'tcpProxyProtocol', otherwise you'll end up 53 | banning your own IP address (and therefore all workers). */ 54 | "banning": { 55 | "enabled": true, 56 | "time": 600, //How many seconds to ban worker for 57 | "invalidPercent": 50, //What percent of invalid shares triggers ban 58 | "checkThreshold": 500, //Check invalid percent when this many shares have been submitted 59 | "purgeInterval": 300 //Every this many seconds clear out the list of old bans 60 | }, 61 | 62 | /* Each pool can have as many ports for your miners to connect to as you wish. Each port can 63 | be configured to use its own pool difficulty and variable difficulty settings. varDiff is 64 | optional and will only be used for the ports you configure it for. */ 65 | "ports": { 66 | "3333": { 67 | "diff": 131072 68 | }, 69 | "3334": { 70 | "diff": 131072 71 | }, 72 | "3334": { 73 | "diff": 128 74 | } 75 | }, 76 | 77 | /* Recommended to have at least two daemon instances running in case one drops out-of-sync 78 | or offline. For redundancy, all instances will be polled for block/transaction updates 79 | and be used for submitting blocks. Creating a backup daemon involves spawning a daemon 80 | using the "-datadir=/backup" argument which creates a new daemon instance with it's own 81 | RPC config. For more info on this see: 82 | - https://en.bitcoin.it/wiki/Data_directory 83 | - https://en.bitcoin.it/wiki/Running_bitcoind */ 84 | "daemons": [ 85 | { //Main daemon instance 86 | "host": "127.0.0.1", 87 | "port": 2300, 88 | "user": "litecointestrpc", 89 | "password": "pickASecureTestPassword" 90 | } 91 | // { //Backup daemon instance 92 | // "host": "127.0.0.1", 93 | // "port": 19344, 94 | // "user": "litecoinrpc", 95 | // "password": "testnet" 96 | // } 97 | ], 98 | 99 | 100 | /* This allows the pool to connect to the daemon as a node peer to receive block updates. 101 | It may be the most efficient way to get block updates (faster than polling, less 102 | intensive than blocknotify script). It requires the additional field "peerMagic" in 103 | the coin config. */ 104 | "p2p": { 105 | "enabled": false, 106 | 107 | /* Host for daemon */ 108 | "host": "127.0.0.1", 109 | 110 | /* Port configured for daemon (this is the actual peer port not RPC port) */ 111 | "port": 9333, 112 | 113 | /* If your coin daemon is new enough (i.e. not a shitcoin) then it will support a p2p 114 | feature that prevents the daemon from spamming our peer node with unnecessary 115 | transaction data. Assume its supported but if you have problems try disabling it. */ 116 | "disableTransactions": true 117 | 118 | } 119 | 120 | }, function(ip, port , workerName, password, callback){ //stratum authorization function 121 | console.log("Authorize " + workerName + ":" + password + "@" + ip); 122 | callback({ 123 | error: null, 124 | authorized: true, 125 | disconnect: false 126 | }); 127 | }); 128 | 129 | pool.on('share', function(isValidShare, isValidBlock, data){ 130 | 131 | if (isValidBlock) 132 | console.log('Block found'); 133 | else if (isValidShare) 134 | console.log('Valid share submitted'); 135 | else if (data.blockHash) 136 | console.log('We thought a block was found but it was rejected by the daemon'); 137 | else 138 | console.log('Invalid share submitted') 139 | 140 | console.log('share data: ' + JSON.stringify(data)); 141 | }); 142 | 143 | pool.on('log', function(severity, logKey, logText){ 144 | console.log(severity + ': ' + '[' + logKey + '] ' + logText); 145 | }); 146 | 147 | console.log("Starting Pool") 148 | pool.start(); 149 | console.log("Pool started", pool) 150 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var myCoin = { 2 | "name": "Litecoin", 3 | "symbol": "LTC", 4 | "algorithm": "scrypt", 5 | /* litecoin mainnet magic: http://git.io/Bi8YFw 6 | litecoin testnet magic: http://git.io/NXBYJA */ 7 | "peerMagic": "fbc0b6db", 8 | "peerMagicTestnet": "fcc1b7dc" 9 | }; 10 | 11 | var Stratum = require('stratum-pool'); 12 | 13 | var pool = Stratum.createPool({ 14 | 15 | "coin": myCoin, 16 | 17 | "auxes": [], 18 | 19 | "address": "LYQb5ALaKQYufQouRg5Dvig2tBBWvL3MSg", //Address to where block rewards are given 20 | 21 | /* Block rewards go to the configured pool wallet address to later be paid out to miners, 22 | except for a percentage that can go to, for examples, pool operator(s) as pool fees or 23 | or to donations address. Addresses or hashed public keys can be used. Here is an example 24 | of rewards going to the main pool op, a pool co-owner, and NOMP donation. */ 25 | // "rewardRecipients": { 26 | // "n37vuNFkXfk15uFnGoVyHZ6PYQxppD3QqK": 1.5, //1.5% goes to pool op 27 | // "mirj3LtZxbSTharhtXvotqtJXUY7ki5qfx": 0.5, //0.5% goes to a pool co-owner 28 | // }, 29 | 30 | "blockRefreshInterval": 1000, //How often to poll RPC daemons for new blocks, in milliseconds 31 | 32 | 33 | /* Some miner apps will consider the pool dead/offline if it doesn't receive anything new jobs 34 | for around a minute, so every time we broadcast jobs, set a timeout to rebroadcast 35 | in this many seconds unless we find a new job. Set to zero or remove to disable this. */ 36 | "jobRebroadcastTimeout": 55, 37 | 38 | /* Some attackers will create thousands of workers that use up all available socket connections, 39 | usually the workers are zombies and don't submit shares after connecting. This features 40 | detects those and disconnects them. */ 41 | "connectionTimeout": 1200, //Remove workers that haven't been in contact for this many seconds 42 | 43 | /* Sometimes you want the block hashes even for shares that aren't block candidates. */ 44 | "emitInvalidBlockHashes": false, 45 | 46 | /* Enable for client IP addresses to be detected when using a load balancer with TCP proxy 47 | protocol enabled, such as HAProxy with 'send-proxy' param: 48 | http://haproxy.1wt.eu/download/1.5/doc/configuration.txt */ 49 | "tcpProxyProtocol": false, 50 | 51 | /* If a worker is submitting a high threshold of invalid shares we can temporarily ban their IP 52 | to reduce system/network load. Also useful to fight against flooding attacks. If running 53 | behind something like HAProxy be sure to enable 'tcpProxyProtocol', otherwise you'll end up 54 | banning your own IP address (and therefore all workers). */ 55 | "banning": { 56 | "enabled": true, 57 | "time": 600, //How many seconds to ban worker for 58 | "invalidPercent": 50, //What percent of invalid shares triggers ban 59 | "checkThreshold": 500, //Check invalid percent when this many shares have been submitted 60 | "purgeInterval": 300 //Every this many seconds clear out the list of old bans 61 | }, 62 | 63 | /* Each pool can have as many ports for your miners to connect to as you wish. Each port can 64 | be configured to use its own pool difficulty and variable difficulty settings. varDiff is 65 | optional and will only be used for the ports you configure it for. */ 66 | "ports": { 67 | "3333": { //A port for your miners to connect to 68 | "diff": 32, //the pool difficulty for this port 69 | 70 | /* Variable difficulty is a feature that will automatically adjust difficulty for 71 | individual miners based on their hashrate in order to lower networking overhead */ 72 | "varDiff": { 73 | "minDiff": 8, //Minimum difficulty 74 | "maxDiff": 512, //Network difficulty will be used if it is lower than this 75 | "targetTime": 15, //Try to get 1 share per this many seconds 76 | "retargetTime": 90, //Check to see if we should retarget every this many seconds 77 | "variancePercent": 30 //Allow time to very this % from target without retargeting 78 | } 79 | }, 80 | "3256": { //Another port for your miners to connect to, this port does not use varDiff 81 | "diff": 256 //The pool difficulty 82 | } 83 | }, 84 | 85 | /* Recommended to have at least two daemon instances running in case one drops out-of-sync 86 | or offline. For redundancy, all instances will be polled for block/transaction updates 87 | and be used for submitting blocks. Creating a backup daemon involves spawning a daemon 88 | using the "-datadir=/backup" argument which creates a new daemon instance with it's own 89 | RPC config. For more info on this see: 90 | - https://en.bitcoin.it/wiki/Data_directory 91 | - https://en.bitcoin.it/wiki/Running_bitcoind */ 92 | "daemons": [ 93 | { //Main daemon instance 94 | "host": "127.0.0.1", 95 | "port": 2300, 96 | "user": "litecoinrpc", 97 | "password": "pickASecurePassword" 98 | } 99 | // { //Backup daemon instance 100 | // "host": "127.0.0.1", 101 | // "port": 19344, 102 | // "user": "litecoinrpc", 103 | // "password": "testnet" 104 | // } 105 | ], 106 | 107 | 108 | /* This allows the pool to connect to the daemon as a node peer to receive block updates. 109 | It may be the most efficient way to get block updates (faster than polling, less 110 | intensive than blocknotify script). It requires the additional field "peerMagic" in 111 | the coin config. */ 112 | "p2p": { 113 | "enabled": false, 114 | 115 | /* Host for daemon */ 116 | "host": "127.0.0.1", 117 | 118 | /* Port configured for daemon (this is the actual peer port not RPC port) */ 119 | "port": 9333, 120 | 121 | /* If your coin daemon is new enough (i.e. not a shitcoin) then it will support a p2p 122 | feature that prevents the daemon from spamming our peer node with unnecessary 123 | transaction data. Assume its supported but if you have problems try disabling it. */ 124 | "disableTransactions": true 125 | 126 | } 127 | 128 | }, function(ip, port , workerName, password, callback){ //stratum authorization function 129 | console.log("Authorize " + workerName + ":" + password + "@" + ip); 130 | callback({ 131 | error: null, 132 | authorized: true, 133 | disconnect: false 134 | }); 135 | }); 136 | 137 | pool.on('share', function(isValidShare, isValidBlock, data){ 138 | 139 | if (isValidBlock) 140 | console.log('Block found'); 141 | else if (isValidShare) 142 | console.log('Valid share submitted'); 143 | else if (data.blockHash) 144 | console.log('We thought a block was found but it was rejected by the daemon'); 145 | else 146 | console.log('Invalid share submitted') 147 | 148 | console.log('share data: ' + JSON.stringify(data)); 149 | }); 150 | 151 | pool.on('log', function(severity, logKey, logText){ 152 | console.log(severity + ': ' + '[' + logKey + '] ' + logText); 153 | }); 154 | 155 | console.log("Starting Pool") 156 | pool.start(); 157 | console.log("Pool started", pool) 158 | -------------------------------------------------------------------------------- /merge-mining/server-test.js: -------------------------------------------------------------------------------- 1 | var Stratum = require('stratum-pool'); 2 | 3 | var myCoin = { 4 | "name": "Litecoin", 5 | "symbol": "LTC", 6 | "algorithm": "scrypt", 7 | "peerMagic": "fbc0b6db", 8 | "peerMagicTestnet": "fcc1b7dc" 9 | }; 10 | 11 | var myAuxCoins = [{ 12 | "name": "Dogecoin", 13 | "symbol": "DOGE", 14 | "algorithm": "scrypt", 15 | "peerMagic": "fbc0b6db", 16 | "peerMagicTestnet": "fcc1b7dc", 17 | 18 | /* */ 19 | "daemons": [ 20 | { //Main daemon instance 21 | "host": "127.0.0.1", 22 | "port": 2301, // **NOT ACTUAL PORT** 23 | "user": "dogecoinrpc", 24 | "password": "securepasswordfordogecoinmining" 25 | } 26 | ], 27 | }]; 28 | 29 | var pool = Stratum.createPool({ 30 | 31 | "coin": myCoin, 32 | 33 | "auxes": myAuxCoins, 34 | 35 | // For merge mining you need to generate an address using a private key you 36 | // will share across all the wallets you're merge mining with. 37 | // See: generate-test-addresses.js 38 | "address": "LczqGbNvdeszU8UdVdms1p2yU9CHb1KRPe", 39 | 40 | "blockRefreshInterval": 1000, 41 | 42 | /* Some miner apps will consider the pool dead/offline if it doesn't receive anything new jobs 43 | for around a minute, so every time we broadcast jobs, set a timeout to rebroadcast 44 | in this many seconds unless we find a new job. Set to zero or remove to disable this. */ 45 | "jobRebroadcastTimeout": 55, 46 | 47 | /* Some attackers will create thousands of workers that use up all available socket connections, 48 | usually the workers are zombies and don't submit shares after connecting. This features 49 | detects those and disconnects them. */ 50 | "connectionTimeout": 600, //Remove workers that haven't been in contact for this many seconds 51 | 52 | /* Sometimes you want the block hashes even for shares that aren't block candidates. */ 53 | "emitInvalidBlockHashes": false, 54 | 55 | /* Enable for client IP addresses to be detected when using a load balancer with TCP proxy 56 | protocol enabled, such as HAProxy with 'send-proxy' param: 57 | http://haproxy.1wt.eu/download/1.5/doc/configuration.txt */ 58 | "tcpProxyProtocol": false, 59 | 60 | /* If a worker is submitting a high threshold of invalid shares we can temporarily ban their IP 61 | to reduce system/network load. Also useful to fight against flooding attacks. If running 62 | behind something like HAProxy be sure to enable 'tcpProxyProtocol', otherwise you'll end up 63 | banning your own IP address (and therefore all workers). */ 64 | "banning": { 65 | "enabled": true, 66 | "time": 600, //How many seconds to ban worker for 67 | "invalidPercent": 50, //What percent of invalid shares triggers ban 68 | "checkThreshold": 500, //Check invalid percent when this many shares have been submitted 69 | "purgeInterval": 300 //Every this many seconds clear out the list of old bans 70 | }, 71 | 72 | /* Each pool can have as many ports for your miners to connect to as you wish. Each port can 73 | be configured to use its own pool difficulty and variable difficulty settings. varDiff is 74 | optional and will only be used for the ports you configure it for. */ 75 | "ports": { 76 | "3333": { //A port for your miners to connect to 77 | "diff": 131072, //the pool difficulty for this port 78 | }, 79 | "3334": { //Another port for your miners to connect to, this port does not use varDiff 80 | "diff": 131072/2 //The pool difficulty 81 | } 82 | }, 83 | 84 | /* Recommended to have at least two daemon instances running in case one drops out-of-sync 85 | or offline. For redundancy, all instances will be polled for block/transaction updates 86 | and be used for submitting blocks. Creating a backup daemon involves spawning a daemon 87 | using the "-datadir=/backup" argument which creates a new daemon instance with it's own 88 | RPC config. For more info on this see: 89 | - https://en.bitcoin.it/wiki/Data_directory 90 | - https://en.bitcoin.it/wiki/Running_bitcoind */ 91 | // This is just for the primary coin 92 | "daemons": [ 93 | { //Main daemon instance 94 | "host": "127.0.0.1", 95 | "port": 2300, 96 | "user": "litecoinrpc", 97 | "password": "securePasswordForLitecoinDaemon" 98 | } 99 | ], 100 | 101 | 102 | /* This allows the pool to connect to the daemon as a node peer to receive block updates. 103 | It may be the most efficient way to get block updates (faster than polling, less 104 | intensive than blocknotify script). It requires the additional field "peerMagic" in 105 | the coin config. */ 106 | // Again, this is just for the primary coin 107 | "p2p": { 108 | "enabled": false, 109 | 110 | /* Host for daemon */ 111 | "host": "127.0.0.1", 112 | 113 | /* Port configured for daemon (this is the actual peer port not RPC port) */ 114 | "port": 19333, 115 | 116 | /* If your coin daemon is new enough (i.e. not a shitcoin) then it will support a p2p 117 | feature that prevents the daemon from spamming our peer node with unnecessary 118 | transaction data. Assume its supported but if you have problems try disabling it. */ 119 | "disableTransactions": true 120 | } 121 | 122 | // This is the authorize function. Connect this to the database in your code to check if the client is valid 123 | }, function(ip, port , workerName, password, callback){ //stratum authorization function 124 | console.log("Authorize " + workerName + ":" + password + "@" + ip); 125 | callback({ 126 | error: null, 127 | authorized: true, 128 | disconnect: false 129 | }); 130 | }); 131 | 132 | /* 133 | 'data' object contains: 134 | job: 4, //stratum work job ID 135 | ip: '71.33.19.37', //ip address of client 136 | port: 3333, //port of the client 137 | worker: 'matt.worker1', //stratum worker name 138 | height: 443795, //block height 139 | blockReward: 5000000000, //the number of satoshis received as payment for solving this block 140 | difficulty: 64, //stratum worker difficulty 141 | shareDiff: 78, //actual difficulty of the share 142 | blockDiff: 3349, //block difficulty adjusted for share padding 143 | blockDiffActual: 3349 //actual difficulty for this block 144 | //AKA the block solution - set if block was found 145 | blockHash: '110c0447171ad819dd181216d5d80f41e9218e25d833a2789cb8ba289a52eee4', 146 | //Exists if "emitInvalidBlockHashes" is set to true 147 | blockHashInvalid: '110c0447171ad819dd181216d5d80f41e9218e25d833a2789cb8ba289a52eee4' 148 | //txHash is the coinbase transaction hash from the block 149 | txHash: '41bb22d6cc409f9c0bae2c39cecd2b3e3e1be213754f23d12c5d6d2003d59b1d, 150 | error: 'low share difficulty' //set if share is rejected for some reason 151 | */ 152 | pool.on('share', function(isValidShare, isValidBlock, data){ 153 | 154 | //maincoin 155 | var coin = myCoin.name; 156 | storeShare(isValidShare, isValidBlock, data, coin); 157 | 158 | //loop through auxcoins 159 | for(var i = 0; i < myAuxCoins.length; i++) { 160 | coin = myAuxCoins[i].name; 161 | storeShare(isValidShare, isValidBlock, data, coin); 162 | } 163 | 164 | }); 165 | 166 | // Don't store share... just log 167 | function storeShare(isValidShare, isValidBlock, data, coin) { 168 | 169 | console.log('share data: ' + JSON.stringify(data)); 170 | } 171 | 172 | /* 173 | Called when a block, auxillery or primary, is found 174 | coin: The symbol of the coin found. ex: 'LTC' 175 | blockHash: The hash of the block found and confirmed (at least for now) is in the blockchain. 176 | */ 177 | pool.on('block', function(coin, height, blockHash, txHash) { 178 | console.log('Mined block on ' + coin + ' network!'); 179 | console.log('HEIGHT: ' + height); 180 | console.log('HASH: ' + blockHash); 181 | console.log('TX: ' + txHash); 182 | }); 183 | 184 | /* 185 | 'severity': can be 'debug', 'warning', 'error' 186 | 'logKey': can be 'system' or 'client' indicating if the error 187 | was caused by our system or a stratum client 188 | */ 189 | // Go ahead and combine these with your logs if you want :) 190 | pool.on('log', function(severity, logKey, logText){ 191 | console.log(severity + ': ' + '[' + logKey + '] ' + logText); 192 | }); 193 | 194 | // Start the pool! 195 | pool.start(); 196 | -------------------------------------------------------------------------------- /merge-mining/server.js: -------------------------------------------------------------------------------- 1 | var Stratum = require('stratum-pool'); 2 | 3 | var myCoin = { 4 | "name": "Litecoin", 5 | "symbol": "LTC", 6 | "algorithm": "scrypt", 7 | "peerMagic": "fbc0b6db", 8 | "peerMagicTestnet": "fcc1b7dc" 9 | }; 10 | 11 | var myAuxCoins = [ 12 | { 13 | "name": "Dogecoin", 14 | "symbol": "DOGE", 15 | "algorithm": "scrypt", 16 | "peerMagic": "fbc0b6db", 17 | "peerMagicTestnet": "fcc1b7dc", 18 | 19 | /* */ 20 | "daemons": [ 21 | { //Main daemon instance 22 | "host": "127.0.0.1", 23 | "port": 2301, // **NOT ACTUAL PORT** 24 | "user": "dogecoinrpc", 25 | "password": "securepasswordfordogecoinmining" 26 | } 27 | ], 28 | }, 29 | { 30 | "name": "Viacoin", 31 | "symbol": "VIA", 32 | "algorithm": "scrypt", 33 | "peerMagic": "0f68c6cb", 34 | 35 | /* */ 36 | "daemons": [ 37 | { //Main daemon instance 38 | "host": "127.0.0.1", 39 | "port": 2302, 40 | "user": "viacoinrpc", 41 | "password": "securepasswordforviacoinmining" 42 | } 43 | ], 44 | }, 45 | { 46 | "name": "Myraidcoin", 47 | "symbol": "XMY", 48 | "algorithm": "scrypt", 49 | "peerMagic": "af4576ee", 50 | 51 | /* */ 52 | "daemons": [ 53 | { //Main daemon instance 54 | "host": "127.0.0.1", 55 | "port": 2303, 56 | "user": "myriadcoinrpc", 57 | "password": "securepasswordformyriadcoinmining" 58 | } 59 | ], 60 | } 61 | ]; 62 | 63 | var pool = Stratum.createPool({ 64 | 65 | "coin": myCoin, 66 | 67 | "auxes": myAuxCoins, 68 | 69 | // For merge mining you need to generate an address using a private key you 70 | // will share across all the wallets you're merge mining with. 71 | // See: generate-test-addresses.js 72 | "address": "LczqGbNvdeszU8UdVdms1p2yU9CHb1KRPe", 73 | 74 | "blockRefreshInterval": 1000, 75 | 76 | /* Some miner apps will consider the pool dead/offline if it doesn't receive anything new jobs 77 | for around a minute, so every time we broadcast jobs, set a timeout to rebroadcast 78 | in this many seconds unless we find a new job. Set to zero or remove to disable this. */ 79 | "jobRebroadcastTimeout": 55, 80 | 81 | /* Some attackers will create thousands of workers that use up all available socket connections, 82 | usually the workers are zombies and don't submit shares after connecting. This features 83 | detects those and disconnects them. */ 84 | "connectionTimeout": 600, //Remove workers that haven't been in contact for this many seconds 85 | 86 | /* Sometimes you want the block hashes even for shares that aren't block candidates. */ 87 | "emitInvalidBlockHashes": false, 88 | 89 | /* Enable for client IP addresses to be detected when using a load balancer with TCP proxy 90 | protocol enabled, such as HAProxy with 'send-proxy' param: 91 | http://haproxy.1wt.eu/download/1.5/doc/configuration.txt */ 92 | "tcpProxyProtocol": false, 93 | 94 | /* If a worker is submitting a high threshold of invalid shares we can temporarily ban their IP 95 | to reduce system/network load. Also useful to fight against flooding attacks. If running 96 | behind something like HAProxy be sure to enable 'tcpProxyProtocol', otherwise you'll end up 97 | banning your own IP address (and therefore all workers). */ 98 | "banning": { 99 | "enabled": true, 100 | "time": 600, //How many seconds to ban worker for 101 | "invalidPercent": 50, //What percent of invalid shares triggers ban 102 | "checkThreshold": 500, //Check invalid percent when this many shares have been submitted 103 | "purgeInterval": 300 //Every this many seconds clear out the list of old bans 104 | }, 105 | 106 | /* Each pool can have as many ports for your miners to connect to as you wish. Each port can 107 | be configured to use its own pool difficulty and variable difficulty settings. varDiff is 108 | optional and will only be used for the ports you configure it for. */ 109 | "ports": { 110 | "3333": { //A port for your miners to connect to 111 | "diff": 131072, //the pool difficulty for this port 112 | }, 113 | "3334": { //Another port for your miners to connect to, this port does not use varDiff 114 | "diff": 131072/2 //The pool difficulty 115 | } 116 | }, 117 | 118 | /* Recommended to have at least two daemon instances running in case one drops out-of-sync 119 | or offline. For redundancy, all instances will be polled for block/transaction updates 120 | and be used for submitting blocks. Creating a backup daemon involves spawning a daemon 121 | using the "-datadir=/backup" argument which creates a new daemon instance with it's own 122 | RPC config. For more info on this see: 123 | - https://en.bitcoin.it/wiki/Data_directory 124 | - https://en.bitcoin.it/wiki/Running_bitcoind */ 125 | // This is just for the primary coin 126 | "daemons": [ 127 | { //Main daemon instance 128 | "host": "127.0.0.1", 129 | "port": 2300, 130 | "user": "litecoinrpc", 131 | "password": "securePasswordForLitecoinDaemon" 132 | } 133 | ], 134 | 135 | 136 | /* This allows the pool to connect to the daemon as a node peer to receive block updates. 137 | It may be the most efficient way to get block updates (faster than polling, less 138 | intensive than blocknotify script). It requires the additional field "peerMagic" in 139 | the coin config. */ 140 | // Again, this is just for the primary coin 141 | "p2p": { 142 | "enabled": false, 143 | 144 | /* Host for daemon */ 145 | "host": "127.0.0.1", 146 | 147 | /* Port configured for daemon (this is the actual peer port not RPC port) */ 148 | "port": 19333, 149 | 150 | /* If your coin daemon is new enough (i.e. not a shitcoin) then it will support a p2p 151 | feature that prevents the daemon from spamming our peer node with unnecessary 152 | transaction data. Assume its supported but if you have problems try disabling it. */ 153 | "disableTransactions": true 154 | } 155 | 156 | // This is the authorize function. Connect this to the database in your code to check if the client is valid 157 | }, function(ip, port , workerName, password, callback){ //stratum authorization function 158 | console.log("Authorize " + workerName + ":" + password + "@" + ip); 159 | callback({ 160 | error: null, 161 | authorized: true, 162 | disconnect: false 163 | }); 164 | }); 165 | 166 | /* 167 | 'data' object contains: 168 | job: 4, //stratum work job ID 169 | ip: '71.33.19.37', //ip address of client 170 | port: 3333, //port of the client 171 | worker: 'matt.worker1', //stratum worker name 172 | height: 443795, //block height 173 | blockReward: 5000000000, //the number of satoshis received as payment for solving this block 174 | difficulty: 64, //stratum worker difficulty 175 | shareDiff: 78, //actual difficulty of the share 176 | blockDiff: 3349, //block difficulty adjusted for share padding 177 | blockDiffActual: 3349 //actual difficulty for this block 178 | //AKA the block solution - set if block was found 179 | blockHash: '110c0447171ad819dd181216d5d80f41e9218e25d833a2789cb8ba289a52eee4', 180 | //Exists if "emitInvalidBlockHashes" is set to true 181 | blockHashInvalid: '110c0447171ad819dd181216d5d80f41e9218e25d833a2789cb8ba289a52eee4' 182 | //txHash is the coinbase transaction hash from the block 183 | txHash: '41bb22d6cc409f9c0bae2c39cecd2b3e3e1be213754f23d12c5d6d2003d59b1d, 184 | error: 'low share difficulty' //set if share is rejected for some reason 185 | */ 186 | pool.on('share', function(isValidShare, isValidBlock, data){ 187 | 188 | //maincoin 189 | var coin = myCoin.name; 190 | storeShare(isValidShare, isValidBlock, data, coin); 191 | 192 | //loop through auxcoins 193 | for(var i = 0; i < myAuxCoins.length; i++) { 194 | coin = myAuxCoins[i].name; 195 | storeShare(isValidShare, isValidBlock, data, coin); 196 | } 197 | 198 | }); 199 | 200 | // Don't store share... just log 201 | function storeShare(isValidShare, isValidBlock, data, coin) { 202 | 203 | console.log('share data: ' + JSON.stringify({ 204 | job: data.job, 205 | worker: data.worker, 206 | coin: coin 207 | })); 208 | // TODO: Save the share 209 | } 210 | 211 | /* 212 | Called when a block, auxillery or primary, is found 213 | coin: The symbol of the coin found. ex: 'LTC' 214 | blockHash: The hash of the block found and confirmed (at least for now) is in the blockchain. 215 | */ 216 | pool.on('block', function(coin, height, blockHash, txHash) { 217 | console.log('Mined block on ' + coin + ' network!'); 218 | console.log('HEIGHT: ' + height); 219 | console.log('HASH: ' + blockHash); 220 | console.log('TX: ' + txHash); 221 | }); 222 | 223 | /* 224 | 'severity': can be 'debug', 'warning', 'error' 225 | 'logKey': can be 'system' or 'client' indicating if the error 226 | was caused by our system or a stratum client 227 | */ 228 | // Go ahead and combine these with your logs if you want :) 229 | pool.on('log', function(severity, logKey, logText){ 230 | console.log(severity + ': ' + '[' + logKey + '] ' + logText); 231 | }); 232 | 233 | // Start the pool! 234 | pool.start(); 235 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Solo Mining Litecoin Tutorial 2 | This tutorial provides instructions for setting up a Litecoin Node and a mining pool. For this tutorial, I used an old Gridseed that I have mining on "lottery" mode with Nice hash. 3 | 4 | :email: mike@mikeghen.com 5 | 6 | :electric_plug: Will get you set up and provide support/training for just 1 LTC! 7 | 8 | :pray: LaY9tLS9DmbW5FXpnXYCSjVL3hTB7xyLsQ 9 | 10 | 11 | ## Definitions 12 | **Litecoin Node:** A server running [Litecoin Core](https://litecoin.org/) that maintains it's own blockchain. Litecoin nodes make up the backbone of the Litecoin network. By running your own node, you serve as a peer in the peer-to-peer network that is Litecoin. 13 | 14 | **Mining Pool:** A server running [Stratum protocol](https://en.bitcoin.it/wiki/Stratum_mining_protocol) that can be connected to by mining rigs (e.g. A4+ LTC Master, Antminer L3+, Gridseed) using HTTP(S). The Stratum pool used in this tutorial is [UNOMP's Node Merged Pool](https://github.com/UNOMP/node-merged-pool). 15 | 16 | ## Prerequisites 17 | You should be comfortable working on the command line and using Linux. For this tutorial, we will use a Ubuntu 16 virtual machine (I'm use Google Cloud Engine). 18 | 19 | You will need to install and configure the Litecoin daemon running on Ubuntu. You will also need to install and configure Node Merged Pool which uses Node.js. 20 | 21 | Understanding networking will help as well. Since you'll be running a few services that heavily depend on a reliable network connection. 22 | 23 | You don't need to be a developer but to support this setup and solo mine for a long enough time to actually mine a block, you need system admin skills (might be wise to find a techie friend who can help admin this setup if this is something new to you) 24 | 25 | ## Getting Started 26 | 27 | :information_source: If you're doing this on AWS or GCP, use a large instance with good network connection then after you finish, dial it down to a smaller instance. 28 | 29 | First, begin by setting up a Ubuntu server. You will need to have enough disk space for the blockchain. I'm using these specs on Google Cloud Engine: 30 | 31 | * Ubuntu 16.04 32 | * 1 vCPU 33 | * 2.5 GB memory 34 | * 100 GB disk 35 | 36 | :star: Once you have your server all setup just do an update before we get going: 37 | ``` 38 | sudo apt-get update 39 | ``` 40 | 41 | ## Step 1: Installing Litecoin Core 42 | You will need to install and configure Litecoin Core on your server. You will need to be running litecoind (Litecoin Daemon) as well. Litecoin Daemon needs to run 24/7/365 while mining is happening. Here's how I recommend to install it. 43 | 44 | 1. Change to `/opt` since that's where software should be install on Linux OSs: 45 | ``` 46 | cd /opt 47 | ``` 48 | 2. Get Litecoin Core (double check you're getting the latest, at the time of writing 0.14.2 was the latest): 49 | ``` 50 | wget https://download.litecoin.org/litecoin-0.15.1/linux/litecoin-0.15.1-x86_64-linux-gnu.tar.gz 51 | ``` 52 | 3. Uncompress Litecoin Core and remove the compressed file: 53 | ``` 54 | tar -xvzf litecoin-0.14.2-x86_64-linux-gnu.tar.gz 55 | rm -rf litecoin-0.14.2-x86_64-linux-gnu.tar.gz 56 | ``` 57 | 4. At this point, you've got Litecoin Core software in `/opt/litecoin-0.14.2`. Next, we need to configure the Litecoin Daemon 58 | 59 | ## Step 2: Starting Litecoin Daemon 60 | First we will configure the Litecoin Daemon then we'll start it. Litecoin Daemon looks for a `litecoin.conf` file. If you go to `/opt/litecoin-0.14.2` and try running the daemon without a config you can see where it's looking for you configuration file: 61 | ``` 62 | cd /opt/litecoin-0.14.2 63 | bin/litecoin-cli getinfo 64 | ``` 65 | This will produce the following message: 66 | ``` 67 | error: Could not locate RPC credentials. No authentication cookie could be found, and no rpcpassword is set in the 68 | configuration file (/home/ubuntu/.litecoin/litecoin.conf) 69 | ``` 70 | At this point, you can go ahead and create the configuration file in the path the the Deamon is looking (in my case `/home/ubuntu/.litecoin/litecoin.conf`) or you can choose your only place to put it if your more advanced (maybe `/etc`). 71 | 72 | Once you know where you will put your config file, here's how I recommend configuring your daemon: 73 | 74 | 1. Write the configuration below to `litecoin.conf`. In my case I did this like so: 75 | ``` 76 | vi /home/ubuntu/.litecoin/litecoin.conf 77 | ``` 78 | Then I insert the configuration code: 79 | **Sample Litecoin Daemon Configuration** 80 | ``` 81 | rpcuser=litecoinrpc 82 | rpcpassword=pickASecurePassword 83 | rpcport=2300 84 | daemon=1 85 | server=1 86 | gen=1 87 | ``` 88 | 2. Now you can start the Litecoin Daemon: 89 | ``` 90 | bin/litecoind 91 | ``` 92 | You should see: 93 | ``` 94 | Litecoin server starting 95 | ``` 96 | 3. Wait a minute then run: 97 | ``` 98 | bin/litecoin-cli getinfo 99 | ``` 100 | And look at the output for the `"blocks"`. Run the command again and look at `"blocks"` again. Confirm the number of block is increasing. This means that the Litecoin Daemon is downloading the blockchain. This will continue until the daemon has downloaded the full blockchain (this is why you needed 100GB of disk). 101 | 102 | :warning: You can configure the Litecoin Daemon to start after your server is restarted. You can do with by editting `crontab`. Run `crontab -e` and add: 103 | ``` 104 | @reboot /opt/litecoin-0.14.2/bin/litecoind 105 | ``` 106 | :warning: You will probably want to run Litecoin Daemon on two different servers for high availability. Not going to get into that here. (Email me for more about that.) 107 | 108 | :information_source: You DO NOT need to wait to download the full block chain to move onto the next steps. 109 | 110 | ## Step 3: Creating your Stratum Server (aka Mining Pool) 111 | Now that we have Litecoin Daemon running, we can setup out Stratum server where we can connect our mining rig and start working. 112 | 113 | First, we will install the OS dependancies needed. 114 | 115 | 1. Install Node.js, NPM, and NVM: 116 | ``` 117 | sudo apt-get install nodejs-legacy npm 118 | curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.6/install.sh | bash 119 | ``` 120 | After running `curl` to install NVM, you should see in your output something like: 121 | ``` 122 | => Appending nvm source string to /home/ubuntu/.bashrc 123 | ``` 124 | To use NVM, you'll first need to restart your terminal or reload `.bashrc`: 125 | ``` 126 | source /home/ubuntu/.bashrc 127 | ``` 128 | We want to use Node.js 0.10 so run: 129 | ``` 130 | nvm install v0.10.25 131 | ``` 132 | 133 | :warning: Using a version other than Node.js 0.10 was causing problems for me... 134 | 135 | 2. Create a space to install [UNOMP's Node Merged Pool](https://github.com/UNOMP/node-merged-pool). 136 | ``` 137 | cd /opt 138 | mkdir stratum-server 139 | cd stratum-server 140 | ``` 141 | I like putting my software in `/opt`. 142 | 143 | 3. Next, we need to get the stratum software and install it: 144 | ``` 145 | git clone https://github.com/zone117x/node-stratum-pool 146 | cp node-stratum-pool/package.json package.json 147 | npm install 148 | mv node-stratum-pool node_modules/node-stratum-pool 149 | ``` 150 | 4. Now that we have UNOMP's Node.js software, we need to create and configure our own Statrum Server. My example server code lives in `server.js`. From the server in `/opt/stratum-server` I do: 151 | ``` 152 | vi server.js 153 | ``` 154 | Then put in the contents of `server.js` and save the file. 155 | 156 | At this point, I can run the stratum server like this: 157 | ``` 158 | node server.js 159 | ``` 160 | If the server was started successfully, you will start getting log messages on the command line. Since I wasn't done downloading the blockchain, I had these messages. I also remove the part about fees since this is a pool just for myself: 161 | ``` 162 | error: [POOL] No rewardRecipients have been setup which means no fees will be taken 163 | error: [POOL] Daemon is still syncing with network (download blockchain) - server will be started once synced 164 | warning: [POOL] Downloaded 95.50% of blockchain from 8 peers 165 | warning: [POOL] Downloaded 95.51% of blockchain from 8 peers 166 | warning: [POOL] Downloaded 95.53% of blockchain from 8 peers 167 | warning: [POOL] Downloaded 95.56% of blockchain from 8 peers 168 | warning: [POOL] Downloaded 95.59% of blockchain from 8 peers 169 | warning: [POOL] Downloaded 95.62% of blockchain from 8 peers 170 | ``` 171 | 172 | Eventually, you will see this log: 173 | ``` 174 | special: [POOL] Stratum Pool Server Started for Litecoin [LTC] {scrypt} 175 | Network Connected: Mainnet 176 | Detected Reward Type: POW 177 | Current Block Height: 1323394 178 | Current Connect Peers: 8 179 | Current Block Diff: 98489231438.12776 180 | Network Difficulty: 31197366290.4415 181 | Network Hash Rate: 11.39 TH 182 | Stratum Port(s): 3256, 3333 183 | Pool Fee Percent: 0% 184 | Block polling every: 1000 ms 185 | ``` 186 | And then: 187 | ``` 188 | debug: [POOL] Block notification via RPC polling 189 | debug: [POOL] No new blocks for 55 seconds - updating transactions & rebroadcasting work 190 | debug: [POOL] Block notification via RPC polling 191 | ``` 192 | Then you're all set! 193 | 194 | ## Step 4: Daemonizing the Stratum Server 195 | I'm a big fan of systemd on Ubuntu so I decided to daemonize my Statrum server using systemd. To do so, you will need to create a file `/etc/systemd/system/stratum-server.service`. The advantage of this is: 196 | * Logs go to `system.log` 197 | * You can use `service stratum-server start|stop|restart|status` 198 | * You can check logs with `journalctl -u statum-server -e` or `-f` to tail 199 | To set this up: 200 | 201 | 1. Create the service file by copying the contents of `etc/systemd/system/stratum-server.service` to your server: 202 | ``` 203 | vi /etc/systemd/system/stratum-server.service 204 | ``` 205 | Change the `ExecStart` line to match your setup for Node. 206 | 2. Start the service: 207 | ``` 208 | service stratum-server start 209 | ``` 210 | Check the status to make sure it was successful: 211 | ``` 212 | service stratum-server status 213 | ``` 214 | This should output: 215 | ``` 216 | ● stratum-server.service - Stratum Server daemon 217 | Loaded: loaded (/etc/systemd/system/stratum-server.service; disabled; vendor preset: enabled) 218 | Active: active (running) since Sat 2017-12-02 20:10:05 UTC; 5min ago 219 | ``` 220 | 3. Check the logs to make sure it's running: 221 | ``` 222 | journalctl -u stratum-server -e 223 | ``` 224 | 225 | ## Step 5: Connect your rigs 226 | At this point, your pool is all set and you can start working. Connect your miners to your pool as: 227 | 228 | ``` 229 | stratum+tcp://EXTERNAL_IP:3333 230 | ``` 231 | 232 | Where the EXTERNAL_IP is the external IP of your machine. 233 | 234 | :warning: Make sure your firewall rules allow connection on this port. 235 | 236 | Read the `server.js` file for more information about how to change pool settings. 237 | 238 | ## Why I Created My Own Pool and This Tutorial 239 | I was participating in a pool and found 2 blocks which would have earn me a 50 LTC reward. As part of the pool however, I only earned a few LTC in that duration since it was a PPS pool. 240 | 241 | I looked into solo mining and couldn't find anything about solo mining with Innoslicion equipment. I didn't want to SSH into my controllers and install anything so I looked into creating a pool where I could then connect my rigs to. 242 | 243 | ### More Nodes on the Network, Metcalf's Law 244 | I also realized that creating my own pool meant I would be adding a node to the Litecoin network. More nodes mean more robust network so in a way I'm adding some value to the Litecoin network. This follows [Metcalf's Law](https://en.wikipedia.org/wiki/Metcalfe%27s_law) which values a network as the number of nodes to the power of 2. So by solo mining and running my own node, I'm actually increasing the value of the Litecoin network from `n^2` to `(n+1)^2` 245 | 246 | ## FAQ and Troubleshooting 247 | 248 | #### What is socket flooding? 249 | This means the difficulty is too low for your miner. I experienced this when I was using diff=32 and my A4+ LTCMaster cranks at 620MHs. Bumped the diff to 1024 and the problem went away. If you get socket flooding, you need to adjust your difficulty. 250 | 251 | #### Why can I not reach my node on port 3333? 252 | You probably forgot to open up the port at the VPC level. 253 | ![Firewall](VPC_network_-_Litecoin_Node.png) 254 | Try using telnet to make sure the port is open: 255 | ``` 256 | telnet EXTERNAL_IP 3333 257 | ``` 258 | Make sure you can ping your instance first: 259 | ``` 260 | ping EXTERNAL_IP 261 | ``` 262 | 263 | #### Why am I getting a connection error? 264 | I got this when I set this up on AWS: 265 | ``` 266 | Dec 12 00:40:44 ip-172-31-22-159 node[11648]: error: [POOL] Could not start pool, error with init batch RPC call: {"type":"offline","message":"connect ECONNREFUSED"} 267 | ``` 268 | First, check your password and such in `server.js`. After I did that... 269 | 270 | I did a `getinfo` to see what's up with Litecoin Daemon: 271 | ``` 272 | $ bin/litecoin-cli getinfo 273 | error: couldn't connect to server: unknown (code -1) 274 | (make sure server is running and you are connecting to the correct RPC port) 275 | ``` 276 | I did a quick `netstat -nlp` and saw that the Litecoin Daemon wasn't running. So I started it again: 277 | ``` 278 | bin/litecoind 279 | ``` 280 | and then did `getinfo` and saw this: 281 | ``` 282 | error code: -28 283 | error message: 284 | Loading block index... 285 | ``` 286 | I waited 30 seconds and did `getinfo` again and then things looked good. 287 | 288 | #### How to I monitor my Stratum Server? 289 | Since we used systemd, you can use a logging agent. On Google Cloud, you can use stackdriver. On AWS, you can use [Cloud Watch agent](https://devopscube.com/setup-aws-logs-agent-ubuntu-16/). 290 | 291 | You'll want to create the follow role for CloudWatch: 292 | ``` 293 | { 294 | "Version": "2012-10-17", 295 | "Statement": [ 296 | { 297 | "Effect": "Allow", 298 | "Action": [ 299 | "logs:CreateLogGroup", 300 | "logs:CreateLogStream", 301 | "logs:PutLogEvents", 302 | "logs:DescribeLogStreams" 303 | ], 304 | "Resource": [ 305 | "arn:aws:logs:*:*:*" 306 | ] 307 | } 308 | ] 309 | } 310 | ``` 311 | 312 | #### bignum Error 313 | ``` 314 | root@litecoin-node-primary:/opt/stratum-server# node server.js 315 | module.js:328 316 | throw err; 317 | ^ 318 | Error: Cannot find module 'bignum' 319 | at Function.Module._resolveFilename (module.js:326:15) 320 | at Function.Module._load (module.js:277:25) 321 | at Module.require (module.js:354:17) 322 | at require (internal/module.js:12:17) 323 | at Object. (/opt/stratum-server/node_modules/stratum-pool/lib/alg 324 | oProperties.js:1:76) 325 | at Module._compile (module.js:410:26) 326 | at Object.Module._extensions..js (module.js:417:10) 327 | at Module.load (module.js:344:32) 328 | at Function.Module._load (module.js:301:12) 329 | at Module.require (module.js:354:17) 330 | ``` 331 | Odds are you messed up with `nvm`. Make sure you run Step 3 correctly. You may need to replace `ubuntu` with a different username. 332 | --------------------------------------------------------------------------------