electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 2be906fde2c48c74a2fa0ee17e10ec82db0d7ba9
parent e9cc1d30be1c1841f3b282031e567f787ce16ede
Author: ThomasV <thomasv@electrum.org>
Date:   Mon, 30 Nov 2015 10:59:39 +0100

remove merchant script; it is now replaced by daemon and jsonrpc

Diffstat:
Dscripts/merchant/merchant.conf.template | 16----------------
Dscripts/merchant/merchant.py | 300-------------------------------------------------------------------------------
Dscripts/merchant/merchant.readme | 36------------------------------------
3 files changed, 0 insertions(+), 352 deletions(-)

diff --git a/scripts/merchant/merchant.conf.template b/scripts/merchant/merchant.conf.template @@ -1,16 +0,0 @@ -[main] -host = hostname of the machine where you run this program -port = choose a port number -password = choose a password - -[sqlite3] -database = database filename - -[electrum] -xpub = the master public key of your wallet -wallet_path = path where the script will save the wallet - -[callback] -received = URL where we POST json data when payment has been received -expired = URL where we POST json data if payment has expired -password = password sent in the json data, for authentication diff --git a/scripts/merchant/merchant.py b/scripts/merchant/merchant.py @@ -1,300 +0,0 @@ -#!/usr/bin/env python -# -# Electrum - lightweight Bitcoin client -# Copyright (C) 2011 thomasv@gitorious -# -# 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 <http://www.gnu.org/licenses/>. - - -import time, sys, socket, os -import threading -import urllib2 -import json -import Queue -import sqlite3 - -import electrum -electrum.set_verbosity(False) - -import ConfigParser -config = ConfigParser.ConfigParser() -config.read("merchant.conf") - -my_password = config.get('main','password') -my_host = config.get('main','host') -my_port = config.getint('main','port') - -database = config.get('sqlite3','database') - -received_url = config.get('callback','received') -expired_url = config.get('callback','expired') -cb_password = config.get('callback','password') - -wallet_path = config.get('electrum','wallet_path') -xpub = config.get('electrum','xpub') - - -pending_requests = {} - -num = 0 - -def check_create_table(conn): - global num - c = conn.cursor() - c.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='electrum_payments';") - data = c.fetchall() - if not data: - c.execute("""CREATE TABLE electrum_payments (address VARCHAR(40), amount FLOAT, confirmations INT(8), received_at TIMESTAMP, expires_at TIMESTAMP, paid INT(1), processed INT(1));""") - conn.commit() - - c.execute("SELECT Count(address) FROM 'electrum_payments'") - num = c.fetchone()[0] - print "num rows", num - - -def row_to_dict(x): - return { - 'id':x[0], - 'address':x[1], - 'amount':x[2], - 'confirmations':x[3], - 'received_at':x[4], - 'expires_at':x[5], - 'paid':x[6], - 'processed':x[7] - } - - - -# this process detects when addresses have received payments -def on_wallet_update(event): - for addr, v in pending_requests.items(): - h = wallet.history.get(addr, []) - requested_amount = v.get('requested') - requested_confs = v.get('confirmations') - value = 0 - for tx_hash, tx_height in h: - tx = wallet.transactions.get(tx_hash) - if not tx: continue - if wallet.get_confirmations(tx_hash)[0] < requested_confs: continue - for o in tx.outputs: - o_type, o_address, o_value = o - if o_address == addr: - value += o_value - - s = (value)/1.e8 - print "balance for %s:"%addr, s, requested_amount - if s>= requested_amount: - print "payment accepted", addr - out_queue.put( ('payment', addr)) - - -stopping = False - -def do_stop(password): - global stopping - if password != my_password: - return "wrong password" - stopping = True - return "ok" - -def process_request(amount, confirmations, expires_in, password): - global num - if password != my_password: - return "wrong password" - - try: - amount = float(amount) - confirmations = int(confirmations) - expires_in = float(expires_in) - except Exception: - return "incorrect parameters" - - account = wallet.default_account() - pubkeys = account.derive_pubkeys(0, num) - addr = account.pubkeys_to_address(pubkeys) - num += 1 - - out_queue.put( ('request', (addr, amount, confirmations, expires_in) )) - return addr - - - -def do_dump(password): - if password != my_password: - return "wrong password" - conn = sqlite3.connect(database); - cur = conn.cursor() - # read pending requests from table - cur.execute("SELECT oid, * FROM electrum_payments;") - data = cur.fetchall() - return map(row_to_dict, data) - - -def getrequest(oid, password): - if password != my_password: - return "wrong password" - oid = int(oid) - conn = sqlite3.connect(database); - cur = conn.cursor() - # read pending requests from table - cur.execute("SELECT oid, * FROM electrum_payments WHERE oid=%d;"%(oid)) - data = cur.fetchone() - return row_to_dict(data) - - -def send_command(cmd, params): - import jsonrpclib - server = jsonrpclib.Server('http://%s:%d'%(my_host, my_port)) - try: - f = getattr(server, cmd) - except socket.error: - print "Server not running" - return 1 - - try: - out = f(*params) - except socket.error: - print "Server not running" - return 1 - - print json.dumps(out, indent=4) - return 0 - - - -def db_thread(): - conn = sqlite3.connect(database); - # create table if needed - check_create_table(conn) - while not stopping: - cur = conn.cursor() - - # read pending requests from table - cur.execute("SELECT address, amount, confirmations FROM electrum_payments WHERE paid IS NULL;") - data = cur.fetchall() - - # add pending requests to the wallet - for item in data: - addr, amount, confirmations = item - if addr in pending_requests: - continue - else: - with wallet.lock: - print "subscribing to %s"%addr - pending_requests[addr] = {'requested':float(amount), 'confirmations':int(confirmations)} - wallet.synchronizer.subscribe_to_addresses([addr]) - wallet.up_to_date = False - - try: - cmd, params = out_queue.get(True, 10) - except Queue.Empty: - cmd = '' - - if cmd == 'payment': - addr = params - # set paid=1 for received payments - print "received payment from", addr - cur.execute("update electrum_payments set paid=1 where address='%s'"%addr) - - elif cmd == 'request': - # add a new request to the table. - addr, amount, confs, minutes = params - sql = "INSERT INTO electrum_payments (address, amount, confirmations, received_at, expires_at, paid, processed)"\ - + " VALUES ('%s', %.8f, %d, datetime('now'), datetime('now', '+%d Minutes'), NULL, NULL);"%(addr, amount, confs, minutes) - print sql - cur.execute(sql) - - # set paid=0 for expired requests - cur.execute("""UPDATE electrum_payments set paid=0 WHERE expires_at < CURRENT_TIMESTAMP and paid is NULL;""") - - # do callback for addresses that received payment or expired - cur.execute("""SELECT oid, address, paid from electrum_payments WHERE paid is not NULL and processed is NULL;""") - data = cur.fetchall() - for item in data: - oid, address, paid = item - paid = bool(paid) - headers = {'content-type':'application/json'} - data_json = { 'address':address, 'password':cb_password, 'paid':paid } - data_json = json.dumps(data_json) - url = received_url if paid else expired_url - if not url: - continue - req = urllib2.Request(url, data_json, headers) - try: - response_stream = urllib2.urlopen(req) - print 'Got Response for %s' % address - cur.execute("UPDATE electrum_payments SET processed=1 WHERE oid=%d;"%(oid)) - except urllib2.HTTPError: - print "cannot do callback", data_json - except ValueError, e: - print e - print "cannot do callback", data_json - - conn.commit() - - conn.close() - print "database closed" - - - -if __name__ == '__main__': - - if len(sys.argv) > 1: - cmd = sys.argv[1] - params = sys.argv[2:] + [my_password] - ret = send_command(cmd, params) - sys.exit(ret) - - # start network - c = electrum.SimpleConfig({'wallet_path':wallet_path}) - network = electrum.Network(config) - network.start() - - # wait until connected - while network.is_connecting(): - time.sleep(0.1) - - if not network.is_connected(): - print "daemon is not connected" - sys.exit(1) - - # create watching_only wallet - storage = electrum.WalletStorage(c) - if not storage.file_exists: - print "creating wallet file" - wallet = electrum.wallet.Wallet.from_xpub(xpub, storage) - else: - wallet = electrum.wallet.Wallet(storage) - - wallet.synchronize = lambda: None # prevent address creation by the wallet - wallet.start_threads(network) - network.register_callback(on_wallet_update, ['updated']) - - threading.Thread(target=db_thread, args=()).start() - - out_queue = Queue.Queue() - # server thread - from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer - server = SimpleJSONRPCServer(( my_host, my_port)) - server.register_function(process_request, 'request') - server.register_function(do_dump, 'dump') - server.register_function(getrequest, 'getrequest') - server.register_function(do_stop, 'stop') - server.socket.settimeout(1) - while not stopping: - try: - server.handle_request() - except socket.timeout: - continue diff --git a/scripts/merchant/merchant.readme b/scripts/merchant/merchant.readme @@ -1,36 +0,0 @@ -merchant.py is a daemon that manages payments for a web server. It -creates Bitcoin addresses using a master public key (so you do not -leave your private keys on the server), detects when payments are -received and notifies your web application. - -The workflow goes like this: - - - the server sends a request to the daemon via POST. the request - contains an amount to be paid, a number of confirmations, and an - expiration period in hours. - - - the daemon answers with a Bitcoin address, where the customer needs - to send the coins. - - - later, the daemon will send a POST to the webserver, to notify that - the payment has been received OR that the request has expired - - -Since addresses are generated using an Electrum master public key, it -is possible to visualize payments in the Electrum client; you will, -however, need to manually adjust your "gap limit". - -In order to use this script, first edit and rename -merchant.conf.template to merchant.conf - -to launch it, type: -> python merchant.py - -In another terminal, you may send a request from the command line (it -will send the request to the running daemon via http). For example, -here is a request for 0.1 bitcoins, that requires two confirmations, -and expires in 120 minutes: - -> python merchant.py request 0.1 2 120 - -