diff --git a/rc.local b/rc.local
index dec333643..9dea15bd2 100644
--- a/rc.local
+++ b/rc.local
@@ -15,6 +15,7 @@ STRATUM_DIR=/var/stratum
screen -dmS main $WEB_DIR/main.sh
screen -dmS loop2 $WEB_DIR/loop2.sh
screen -dmS blocks $WEB_DIR/blocks.sh
+#screen -dmS ln $WEB_DIR/ln.sh
screen -dmS debug tail -f $LOG_DIR/debug.log
# Stratum instances (skipped/exit if no .conf)
diff --git a/sql/2018-08-lightning-testnet.sql b/sql/2018-08-lightning-testnet.sql
new file mode 100644
index 000000000..6b274e432
--- /dev/null
+++ b/sql/2018-08-lightning-testnet.sql
@@ -0,0 +1,14 @@
+CREATE TABLE `invoices` (
+ `id` int(11) NOT NULL,
+ `userid` int(11) NOT NULL,
+ `workerid` int(11) NOT NULL,
+ `bolt11` varchar(1000) NOT NULL,
+ `amount` int(11) NOT NULL,
+ `paid` int(11) NOT NULL,
+ `shop` text,
+ `description` text NOT NULL,
+ `status` varchar(80) DEFAULT NULL,
+ `exectime` int(10) UNSIGNED NOT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+ALTER TABLE `workers` ADD `work` DOUBLE NOT NULL DEFAULT '0.0' AFTER `algo`;
diff --git a/web/ln.sh b/web/ln.sh
new file mode 100644
index 000000000..124c5c83f
--- /dev/null
+++ b/web/ln.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+PHP_CLI='php -d max_execution_time=60'
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+cd ${DIR}
+
+date
+echo started in ${DIR}
+
+while true; do
+ ${PHP_CLI} runconsole.php cronjob/runLightning
+ sleep 20
+done
+exec bash
diff --git a/web/yaamp/core/backend/blocks.php b/web/yaamp/core/backend/blocks.php
index 8b82548d2..ca5c0bcf5 100644
--- a/web/yaamp/core/backend/blocks.php
+++ b/web/yaamp/core/backend/blocks.php
@@ -61,6 +61,12 @@ function BackendBlockNew($coin, $db_block)
$user->save();
}
+ $target = yaamp_hashrate_constant($coin->algo);
+ $interval = yaamp_hashrate_step();
+ $delay = time()-$interval;
+
+ dborun("UPDATE workers w SET work = w.work + (SELECT (sum(difficulty) * $target / $interval / 1000) FROM shares WHERE valid AND time>".time()-$interval." AND workerid=w.id)");
+
$delay = time() - 5*60;
$sqlCond = "time < $delay";
if(!YAAMP_ALLOW_EXCHANGE) // only one coin mined
diff --git a/web/yaamp/core/backend/payment.php b/web/yaamp/core/backend/payment.php
index b3704ea95..3f2915857 100644
--- a/web/yaamp/core/backend/payment.php
+++ b/web/yaamp/core/backend/payment.php
@@ -32,6 +32,14 @@ function BackendUserCancelFailedPayment($userid)
return 0.0;
}
+function is_ln_invoice($bill)
+{
+if (!empty($bill) && preg_match('/[^A-Za-z0-9]/', $bill))
+ return false;
+else
+ return true; // TODO: Enhance the check of the bolt11 invoice (length, first characters, ...)
+}
+
function BackendCoinPayments($coin)
{
// debuglog("BackendCoinPayments $coin->symbol");
@@ -106,6 +114,34 @@ function BackendCoinPayments($coin)
{
$total_to_pay += round($user->balance, 8);
$addresses[$user->username] = round($user->balance, 8);
+ if ($coin->symbol==LN_COIN && LN_ENABLED == true && YAAMP_LN_WORKERS == true) {
+ $ln_works = getdbolist('db_workers', "work > 0 AND worker <> '' AND userid=".$user->id);
+ foreach($ln_works as $ln_work) {
+ if (is_ln_invoice($ln_work->worker)) {
+ $output = shell_exec('sudo lightning-cli -J decodepay '.$ln_work->worker);
+ $bill = json_decode($output);
+ // TODO: Enhance and add pool fee
+ if ($ln_work->work * YAAMP_LN_FACTOR > $bill->msatoshi / 1000 && $user->balance > $bill->msatoshi / 1000) {
+ $db_bill = getdbosql('db_invoices', "bolt11=:bolt11", array(':bolt11'=>$ln_work->worker));
+ if (!$db_bill) {
+ dborun("INSERT IGNORE INTO invoices(bolt11, status) VALUES (:key,:val)", array(
+ ':key'=>$bill,':val'=>"New"
+ ));
+ }
+ if ($db_bill && $db_bill->status == "complete") {
+ // Invoice is already paid, no need to change total_to_pay and addresses
+ }
+ else {
+ // Invoice is not paid yet: some value should be kept to pay it
+ // TODO: Add pool fee
+ $total_to_pay -= round($bill->msatoshi / 1000, 8);
+ $addresses[$user->username] -= round($bill->msatoshi / 1000, 8);
+ }
+ }
+ }
+ }
+ }
+
// transaction xxx has too many sigops: 1035 > 1000
if ($coin->symbol == 'DCR' && count($addresses) > 990) {
debuglog("payment: more than 990 {$coin->symbol} users to pay, limit to top balances...");
diff --git a/web/yaamp/defaultconfig.php b/web/yaamp/defaultconfig.php
index 71e89b769..7d95e97f6 100644
--- a/web/yaamp/defaultconfig.php
+++ b/web/yaamp/defaultconfig.php
@@ -72,3 +72,25 @@
// cli stuff
if (!defined('YIIMP_CLI_ALLOW_TXS')) define('YIIMP_CLI_ALLOW_TXS', false);
+// Lightning Network
+if (!defined('LN_ENABLED')) define('LN_ENABLED', false);
+if (!defined('LN_MY_BTC_ADDRESS')) define('LN_MY_BTC_ADDRESS', '');
+if (!defined('LN_MY_LN_ADDRESS')) define('LN_MY_LN_ADDRESS', '');
+if (!defined('LN_COIN')) define('LN_COIN', 'BTC');
+if (!defined('LN_MY_IP')) define('LN_MY_IP', '');
+if (!defined('LN_MY_PORT')) define('LN_MY_PORT', '9735');
+if (!defined('LN_FRACTION')) define('LN_FRACTION', 7); // Fraction of main BTC wallet to fund channels
+if (!defined('LN_MIN_PAY')) define('LN_MIN_PAY', 1000); // min payout in satoshis
+if (!defined('LN_MAIN_NODE')) define('LN_MAIN_NODE', '');
+if (!define('YAAMP_LN_NET')) define('YAAMP_LN_NET', 'TESTNET'); // TESTNET or MAINET
+if (!define('YAAMP_LN_WORKERS')) define('YAAMP_LN_WORKERS', false);
+if (!define('YAAMP_LN_FACTOR')) define('YAAMP_LN_FACTOR', 0.8);
+
+if (!isset($configLNGamePlayers)) $configLNGamePlayers = array (
+ /*
+ // testnet:
+ 'testnet.millionbitcoinhomepage.net' => array('023bcc1daeb7c85208991e993a2eacf86f7d9584a6dc33291bbe5e19c986a31568', '51.15.250.152', '9735'),
+ 'yalls.org' => array('02212d3ec887188b284dbb7b2e6eb40629a6e14fb049673f22d2a0aa05f902090e', '54.236.55.50', '9735'),
+ 'testnet.satoshis.place' => array('02dd4cef0192611bc34cd1c3a0a7eb0f381e7229aa3309ae961a7fc0076b4d2bb6', '35.198.136.5', '9735')
+ */
+ );
diff --git a/web/yaamp/modules/site/SiteController.php b/web/yaamp/modules/site/SiteController.php
index 80cf6a54c..c06a571e2 100644
--- a/web/yaamp/modules/site/SiteController.php
+++ b/web/yaamp/modules/site/SiteController.php
@@ -1192,4 +1192,9 @@ public function actionMainbtc()
setcookie('mainbtc', '1', time()+60*60*24, '/');
}
+ public function actionLn()
+ {
+ if(LN_ENABLED == false || (YAAMP_LN_NET == 'MAINET' && !$this->admin)) return;
+ $this->render('/site/ln');
+ }
}
diff --git a/web/yaamp/modules/site/index.php b/web/yaamp/modules/site/index.php
index a1888b8ce..824eaffd2 100644
--- a/web/yaamp/modules/site/index.php
+++ b/web/yaamp/modules/site/index.php
@@ -67,6 +67,18 @@
As optional password, you can use -p c=<SYMBOL> if yiimp does not set the currency correctly on the Wallet page.
See the "Pool Status" area on the right for PORT numbers. Algorithms without associated coins are disabled.
+
+Lightning Network payments are enabled on this pool on . You can send Lightning Network invoices using LN page';
+if (YAAMP_LN_NET == 'TESTNET' && YAAMP_LN_WORKERS == true) echo ' or ';
+if (YAAMP_LN_WORKERS == true) {
+ ?>the workername to send the invoice. Example:
+
+ -o stratum+tcp://= YAAMP_STRATUM_URL ?>:<PORT> -u <WALLET_ADDRESS>.<invoice> [-p <OPTIONS>]
+ Lightning Network invoices can be generated from a LN wallet or directly on a merchant's website (List of merchants (mainet), List of merchants (testnet)).
+
diff --git a/web/yaamp/modules/site/ln.php b/web/yaamp/modules/site/ln.php
new file mode 100644
index 000000000..da5c40eed
--- /dev/null
+++ b/web/yaamp/modules/site/ln.php
@@ -0,0 +1,240 @@
+
+ Auto refresh is paused - Click to resume
+-->
+
+|
+END;
+
+echo <<
+ Lightning Network (testnet)
+
+END;
+
+//echo " Lightning Network (testnet)";
+
+echo " Lightning Network (LN) is a second-layer network based on bitcoin on-chain transactions. ";
+echo "It allows to perform bitcoin transactions with less fees and high speed. ";
+//echo "More informations: lightning.network";
+echo " This page allows you to pay LN bills/invoices on testnet for any service on testnet. ";
+
+//echo " ?";
+?>
+
+
+
+
+
+
+
+ ";
+?>
+
+
+
+
+
+";
+echo <<
+
+
+END;
+
+echo << | |
+
+
+
+END;
+?>
diff --git a/web/yaamp/modules/thread/CronjobController.php b/web/yaamp/modules/thread/CronjobController.php
index 814e8329e..0f7b026ca 100644
--- a/web/yaamp/modules/thread/CronjobController.php
+++ b/web/yaamp/modules/thread/CronjobController.php
@@ -39,6 +39,319 @@ private function monitorApache()
}
}
+
+ public function actionRunLightning()
+ {
+ debuglog(__METHOD__);
+ set_time_limit(0);
+
+// $this->monitorApache();
+
+ $last_complete = memcache_get($this->memcache->memcache, "cronjob_ln_time_start");
+
+ // debuglog("Lightning turned on ?");
+ $output = shell_exec('sudo lightning-cli -J getinfo');
+ $output = json_decode($output);
+
+ $lightning = false;
+ foreach ($output as $key => $out)
+ {
+ if ($key == 'id') {
+ debuglog("[LN] 1. Lightning is turned ON: OK");
+ debuglog("[LN] My LN id = $out");
+ $lightning = true;
+ }
+ }
+ if (!$lightning)
+ debuglog("[LN] Error: Please run lightningd in a screen");
+ else {
+ debuglog("[LN] 2. Check LN BTC address");
+ $outpute = shell_exec("sudo lightning-cli -J dev-listaddrs");
+ $output = json_decode($outpute);
+ //debuglog("ok");
+// debuglog($outpute);
+
+ $found = false;
+ //if (isset($output->addresses))
+ foreach ($output->addresses as $out) {
+// debuglog("in"); // value = ".$out["value"]." status = ".$out["status"]);
+ if (LN_MY_BTC_ADDRESS == $out->p2sh) {
+ $found = true;
+ debuglog("[LN] LN_MY_BTC_ADDRESS found and own : OK");
+ break;
+ }
+ }
+ if ($found == false) {
+ debuglog("[LN] Error: Please create a p2sh-segwit address using lightning-cli newaddr then fill LN_MY_BTC_ADDRESS");
+ }
+ else {
+ debuglog("[LN] 3. Check funds"); // TODO: Enhance the logic
+
+ $output = shell_exec("sudo lightning-cli -J listfunds");
+ $listfunds = json_decode($output);
+// debuglog($output);
+
+ $ln_balance = 0;
+
+ $found = false;
+ if (count($listfunds->outputs) > 0)
+ foreach ($listfunds->outputs as $out) {
+// debuglog("in"); // value = ".$out["value"]." status = ".$out["status"]);
+ if ($out->status == "unconfirmed" && $out->output == 1 && count($listfunds->channels) > 0) {
+ debuglog("[LN] Channel creation ongoing");
+ $found = true;
+ }
+
+ if ($out->status == "confirmed" && $out->output == 1 && count($listfunds->channels) > 0 && $out->value > 100) {
+ $ln_balance = $out->value;
+ debuglog("[LN] seems ok (logic to enhance)");
+ $found = true;
+ }
+ if ("confirmed" == $out->status && 0 == $out->output && count($listfunds->channels) > 0) {
+ $ln_balance = $out->value;
+ $found = true;
+ debuglog("[LN] Initial funding onchain : OK");
+ break;
+ }
+ }
+ if ($found == false) {
+ debuglog("[LN] Error: Please perform a Bitcoin onchain transaction to ".LN_MY_BTC_ADDRESS);
+ // testnet faucet : https://testnet.manu.backend.hamburg/faucet
+ }
+ else {
+
+ debuglog("LN Balance (not in channels) = ".$ln_balance." sat = ".($ln_balance/100000000)." BTC");
+
+ debuglog("[LN] 4. Check connections");
+
+ $output = shell_exec('sudo lightning-cli -J listpeers');
+ $output = json_decode($output);
+
+ global $configLNGamePlayers;
+
+ foreach ($configLNGamePlayers as $player) {
+ //debuglog($player[0]);
+ $connected = false;
+ foreach ($output->peers as $out) {
+ if ($out->id == $player[1]) {
+ debuglog("Connected to player OK");
+ $connected = true;
+ break;
+ }
+ }
+ if ($connected == false) {
+ debuglog("Connect to player...");
+ $ttt = 'sudo lightning-cli -J connect ' . $player[1] . ' ' . $player[2] . ' ' . $player[3]; // . "'";
+ //debuglog("OK");
+ debuglog($ttt);
+
+ $output = shell_exec($ttt);
+ debuglog($output);
+ // todo: handle errors to avoid this peer (into memcache ?)
+ // -1, "message" : "Connection establishment: Connection timed out
+ }
+ }
+
+ debuglog("[LN] 5. Check channels funds : cancelled !");
+
+// $output = shell_exec('sudo lightning-cli -J list');
+// $output = json_decode($output);
+ $found = true;
+/* if (count($listfunds->channels) > 0)
+ foreach ($listfunds->channels as $out) {
+// debuglog("in"); // value = ".$out["value"]." status = ".$out["status"]);
+ if ($out->channel_sat > 100 && isset($out->id_peer) && $out->id_peer == LN_MAIN_NODE) {
+ $found = true;
+ debuglog("[LN] Channel credit > 100 : OK");
+ //break;
+ }
+ else {
+ debuglog("[LN] Channel $out->peer_id with less than 100 !!! To refund ...");
+ // I dont think that the function to refund a channel is already developped in LN ...
+ $output = shell_exec('sudo lightning-cli close '.$out->id_peer); //. ' ' . max( 16777215, round($ln_balance / LN_FRACTION)));
+ $output = json_decode($output);
+ debuglog($output);
+ break;
+ }
+ }
+*/
+ if ($found == false) {
+ debuglog("[LN] No channels credited: Channel credit with a fraction of the remaining funds");
+ //foreach ($configLNGamePlayers as $player) {
+ // code : -32602 [message] => 'Funding satoshi must be <= 16777215'
+// $output = shell_exec('sudo lightning-cli -J fundchannel '.$out->id_peer. ' ' . min( 16777215, round($ln_balance / LN_FRACTION)));
+// $output = json_decode($output);
+// debuglog($output);
+ //}
+
+ }
+ else {
+ debuglog("[LN] 6. Channel open on LN : cancelled !");
+/*
+ $outpute = shell_exec('sudo lightning-cli -J listpeers');
+ $output = json_decode($outpute);
+//debuglog($outpute);
+ $found = true; // false;
+ if (isset($output->peers))
+ if(count($output->peers) > 0)
+ foreach ($output->peers as $out) {
+ debuglog("Peer ".$out->alias);
+ if (count($out->channels) > 0)
+ foreach ($out->channels as $o) {
+ if ($o->state == "CHANNELD_AWAITING_LOCKIN") {
+ debuglog("[LN] Waiting for 3 to 6 confirmations on bitcoin network. CHANNELD_AWAITING_LOCKIN:Funding needs more confirmations.");
+ }
+ if ($o->state == "CHANNELD_NORMAL") {
+ debuglog("[LN] Channel open : OK");
+ }
+ if ($o->state == "GOSSIPING") {
+ $output = shell_exec('sudo lightning-cli -J fundchannel '.$out->id_peer. ' ' . min( 16777215, round($ln_balance / LN_FRACTION)));
+ $output = json_decode($output);
+ debuglog($output);
+ }
+ if ($o->state == "ONCHAIN")
+ && in_array("ONCHAIN:All outputs resolved: waiting 99 more blocks before forgetting channel", $o->states)) {
+ $output = shell_exec('sudo lightning-cli -J fundchannel '.$out->id_peer. ' ' . min( 16777215, round($ln_balance / LN_FRACTION)));
+ $output = json_decode($output);
+ debuglog($output);
+ }
+ }
+ }
+ debuglog("channels chekcs ended");
+*/
+ if (true) {
+ debuglog("Payment will be done");
+ if (true) { // $ln_balance > LN_MIN_PAY) {
+
+ $db_bills = dbolist("SELECT bolt11 from invoices WHERE status='New' ORDER by id ASC");
+ if (!$db_bills) {
+ debuglog("Nothing to pay");
+ }
+ else {
+ debuglog("There are LN invoices to pay");
+ foreach ($db_bills as $bill) {
+// debuglog("Bill");
+// break;
+// }
+// }
+
+//if (!empty($bill) && preg_match('/[^A-Za-z0-9]/', $bill)) {
+//echo "Please type a bolt11 testnet bill. Try again";
+// die;
+//}
+
+// debuglog("before");
+// debuglog($bill['bolt11']);
+ $oo = "sudo lightning-cli -J decodepay ".$bill['bolt11'];
+ debuglog($oo);
+ $outpute = shell_exec($oo);
+ $output = json_decode($outpute);
+ debuglog($outpute);
+ if (isset($output->message) && $output->message == "Invalid bolt11: Bad bech32 string") {
+ // debuglog("clean invalid");
+ dborun("UPDATE invoices SET status = 'Invalid' WHERE bolt11='".$bill['bolt11']."'");
+ debuglog("Invoice with invalid bolt11");
+ break;
+ }
+ if (isset($output->description)) {
+ dborun("UPDATE invoices SET description = '".addslashes(htmlentities($output->description))."' WHERE bolt11='".$bill['bolt11']."'");
+ debuglog("Update description"); // TODO: add filter to prevent injections here
+ }
+ if (isset($output->payee)) {
+ dborun("UPDATE invoices SET shop = '".$output->payee."' WHERE bolt11='".$bill['bolt11']."'");
+ debuglog("Update shop"); // TODO: add filter to prevent injections here
+ }
+ if (isset($output->msatoshi)) {
+ dborun("UPDATE invoices SET amount = '".$output->msatoshi."' WHERE bolt11='".$bill['bolt11']."'");
+ debuglog("Update amount");
+ }
+
+ $oo = "sudo lightning-cli -J pay maxfeepercent=4 maxdelay=1200 bolt11=".$bill['bolt11'];
+ debuglog($oo);
+ $outpute = shell_exec($oo);
+// debuglog("ok");
+ $output = json_decode($outpute);
+ debuglog($outpute);
+ if (isset($output->message) && $output->message == "Invoice expired") {
+ dborun("UPDATE invoices SET status = 'Expired' WHERE bolt11='".$bill['bolt11']."'");
+ debuglog("Invoice expired");
+ break;
+ }
+
+ if (isset($output->message) && $output->message == "Invalid bolt11: Bad bech32 string") {
+ // debuglog("clean invalid");
+ dborun("UPDATE invoices SET status = 'Invalid' WHERE bolt11='".$bill['bolt11']."'");
+ debuglog("Invoice with invalid bolt11");
+ break;
+ }
+ if (isset($output->message) && (strpos($output->message == "max fee requested is") !== false)) {
+ // Fee 2001 is 1.352941% of payment 147900; max fee requested is
+ // debuglog("clean invalid");
+ dborun("UPDATE invoices SET status = 'maxfee' WHERE bolt11='".$bill['bolt11']."'");
+ debuglog("Invoice with too much fee.");
+ break;
+ }
+
+ if (isset($output->status) && $output->status == "complete") {
+ // debuglog("clean invalid");
+ dborun("UPDATE invoices SET status = 'complete', exectime = '".time()."' WHERE bolt11='".$bill['bolt11']."'");
+ debuglog("Bill paid ! :-)");
+ if (isset($output->msatoshi_sent)) {
+ dborun("UPDATE invoices SET paid = '".$output->msatoshi_sent."' WHERE bolt11='".$bill['bolt11']."'");
+ }
+ break;
+ }
+
+/*
+
+ //debuglog($output);
+// if ($output->msatoshi == LN_MIN_PAY * 1000) { // * 1000 because millisat
+ $outpute = shell_exec('sudo lightning-cli -J pay '.$bill);
+ $output = json_decode($outpute);
+ debuglog($outpute);
+ if ($output->code == 206) // "code" : 206, "message" : "Delay (locktime) is 576 blocks; max delay requested is 500."
+ // https://github.com/ElementsProject/lightning/issues/1586 issue still opened
+ {
+ $outpute = shell_exec('sudo lightning-cli -J pay maxdelay=577 bolt11='.$bill);
+ $output = json_decode($outpute);
+ debuglog($outpute);
+ }
+ if ($output->code == 200) // "code" : 200, "message" : "Stopped retrying during payment attempt; continue monitoring with pay or listpayments"
+ {
+ debuglog("[LN] Err 200: Stopped retrying during payment attempt; continue monitoring with pay or l");
+ }
+ if ($output->code == 205) // "code" : 205, "message" : "Could not find a route"
+ {
+ debuglog("[LN] Err 205");
+ }
+ }
+ else
+ debuglog("[LN] Bill: bad amount ".$output->msatoshi." ".(LN_MIN_PAY * 1000));
+
+*/
+ break;
+ }
+ }
+
+
+// $bill = file_get_contents("");
+ }
+ }
+ //debuglog($outpute);
+ }
+
+ //debuglog($output["id"]);
+ //debuglog("Refund channel if new payment occured");
+
+ }
+ }
+ }
+
+ memcache_set($this->memcache->memcache, "cronjob_ln_time_start", time());
+// debuglog(__METHOD__);
+ }
+
+
public function actionRunBlocks()
{
// screenlog(__FUNCTION__);
diff --git a/web/yaamp/ui/main.php b/web/yaamp/ui/main.php
index 693791d2f..68be000cd 100644
--- a/web/yaamp/ui/main.php
+++ b/web/yaamp/ui/main.php
@@ -120,8 +120,14 @@ function showPageHeader()
if (YAAMP_USE_NICEHASH_API)
showItemHeader(controller()->id=='nicehash', '/nicehash', 'Nicehash');
+
+ if (LN_ENABLED)
+ showItemHeader(controller()->id=='ln', '/site/ln', 'LN');
}
-
+
+ if (LN_ENABLED && YAAMP_LN_NET == 'TESTNET' && !controller()->admin)
+ showItemHeader(controller()->id=='ln', '/site/ln', 'LN (testnet)');
+
echo '';
$mining = getdbosql('db_mining');