diff --git a/scripts/generate.php b/scripts/generate.php
new file mode 100644
index 00000000..c51fc69f
--- /dev/null
+++ b/scripts/generate.php
@@ -0,0 +1,195 @@
+
+ * @since 2014-03
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+date_default_timezone_set("Etc/GMT+0");
+
+/*
+ * config settings
+ */
+$_CFG = array();
+// API Key for Chain.com
+$_CFG['API-KEY-ID'] = "DEMO-4a5e1e4";
+// server bitcoin address which receive btc transactions from customers
+$_CFG['server_baddress'] = "1LDjqcu3rSUkWjL219oZTsHg79S9K6vPwP";
+$_CFG['cache_file'] = "/var/ss/{$_CFG['server_baddress']}-cached_txs.txt";
+$_CFG['ss_server_file'] = "/var/ss/{$_CFG['server_baddress']}-list-ss-server.txt";
+// price for one day, unit: satoshi
+$_CFG['satoshi_per_day'] = 15000;
+
+// load exist parsed txs
+$exist_txs = get_exist_txs();
+// try load new txs
+$exist_txs = try_load_txs($exist_txs);
+// write cache
+put_exist_txs($exist_txs);
+// dump file for ss-server
+dump_for_ss_server($exist_txs);
+
+exit;
+
+
+
+function dump_for_ss_server($exist_txs) {
+ global $_CFG;
+
+ $address = array();
+ $now = time();
+ foreach ($exist_txs as $_tx) {
+ // total days
+ $days = bcdiv($_tx['value'], $_CFG['satoshi_per_day'], 2);
+
+ // passed days
+ assert($now > $_tx['timestamp']);
+ $passed_days = bcdiv($now - $_tx['timestamp'], 86400, 2);
+
+ if ($days <= $passed_days) {
+ continue; // expired
+ }
+ $address[] = $_tx['address'];
+ }
+ $str = implode("\n", $address)."\n";
+
+ LOGI("dump ".count($address)." address to ss_server_file");
+ if (file_put_contents($_CFG['ss_server_file'], $str) == false) {
+ LOGI("write file fail: {$_CFG['ss_server_file']}");
+ }
+}
+
+
+function try_load_txs($exist_txs) {
+ global $_CFG;
+
+ $next_range = "";
+ $run_flag = 1;
+ while ($run_flag) {
+ $res = get_address_list(100/*limit*/, $next_range);
+ // var_dump($res);
+ usleep(500000); // 500ms
+
+ if (empty($res['body'])) {
+ break; // no more records
+ }
+ if (!empty($res['next_range'])) {
+ $next_range = $res['next_range'];
+ } else {
+ $run_flag = 0; // next page is empty
+ }
+
+ $new_cnt = 0;
+ foreach ($res['body'] as $_tx) {
+ if (!empty($exist_txs[$_tx['hash']])) {
+ continue; // already exist
+ }
+ if (empty($_tx['inputs'][0]['addresses'][0])) {
+ continue; // can't get first input address
+ }
+ // use first input address as payment address
+ $input_addr = $_tx['inputs'][0]['addresses'][0];
+
+ // check server address
+ foreach ($_tx['outputs'] as $_output) {
+ if (empty($_output['addresses'][0]) ||
+ $_output['addresses'][0] != $_CFG['server_baddress']) {
+ continue;
+ }
+ $exist_txs[$_tx['hash']] = array(
+ 'address' => $input_addr,
+ 'value' => $_output['value'],
+ 'timestamp' => strtotime($_tx['chain_received_at']),
+ );
+ $new_cnt++;
+ LOGI("find new tx: {$_tx['hash']}, value: {$_output['value']}, address: {$input_addr}, received: {$_tx['chain_received_at']}");
+ break;
+ }
+ }
+
+ if ($new_cnt == 0 && count($res['body']) > 0) {
+ break; // stop, mean all records are exists
+ }
+ }
+ return $exist_txs;
+}
+
+function get_exist_txs() {
+ global $_CFG;
+ if (is_readable($_CFG['cache_file'])) {
+ return json_decode(file_get_contents($_CFG['cache_file']), 1);
+ }
+ return array();
+}
+
+function put_exist_txs($exist_txs) {
+ global $_CFG;
+ if (!file_put_contents($_CFG['cache_file'], json_encode($exist_txs))) {
+ LOGI("file put contents fail, file: {$_CFG['cache_file']}");
+ }
+}
+
+function get_address_list($limit, $range) {
+ global $_CFG;
+
+ $res = array();
+ $custom_headers = array();
+
+ $url = "https://api.chain.com/v2/bitcoin/addresses/";
+ $url .= rawurlencode($_CFG['server_baddress'])."/transactions?api-key-id=";
+ $url .= rawurlencode($_CFG['API-KEY-ID']);
+ $url .= "&limit=".intval($limit);
+ if ($range != "") {
+ $custom_headers[] = "Range: ".$range;
+ }
+
+ $ch = curl_init();
+ curl_setopt($ch, CURLOPT_AUTOREFERER, TRUE);
+ curl_setopt($ch, CURLOPT_HEADER, 1);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
+ curl_setopt($ch, CURLOPT_URL, $url);
+ curl_setopt($ch, CURLOPT_TIMEOUT, 30);
+ if (!empty($custom_headers)) {
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $custom_headers);
+ }
+
+ $r = curl_exec($ch);
+ $curl_errno = curl_errno($ch);
+ $curl_error = curl_error($ch);
+ curl_close($ch);
+
+ if ($curl_errno > 0) {
+ LOGI("curl request fail, errno: {$curl_errno}, error: {$curl_error}");
+ return false;
+ }
+
+ list($header, $body) = explode("\r\n\r\n", $r, 2);
+ // find next range
+ preg_match("/Next-Range: ([^\r]*)/", $header, $matches);
+ if (!empty($matches[1])) {
+ $res['next_range'] = $matches[1];
+ }
+ $res['body'] = json_decode($body, 1);
+ return $res;
+}
+
+
+function LOGI($msg) {
+ echo date("Y-m-d H:i:s"), " $msg\n";
+}
diff --git a/src/bitcoin.c b/src/bitcoin.c
index 2284ac89..62dac59f 100644
--- a/src/bitcoin.c
+++ b/src/bitcoin.c
@@ -22,6 +22,7 @@
#include "bitcoin.h"
#include
+#include
#include
#include
#include
@@ -641,8 +642,7 @@ extern size_t bitcoin_tryload_list(struct btc_list *list) {
clients = calloc(size, sizeof(struct btc_client));
while (fgets(line, sizeof(line), f)) {
- line[strlen(line) - 1] = '\0'; // replace \n
- if (line[strlen(line) - 1] == '\r') {
+ while (strlen(line) > 0 && !isalpha(line[strlen(line) - 1])) {
line[strlen(line) - 1] = '\0';
}
if (strlen(line) > 35 || strlen(line) < 26) {