В данном топике хочу рассказать о своих потугах написать на питоне биллинг для практически любого проекта на основе bitcoin. Материал сырой, кодер я не супер, поэтому критика принимается. Поехали... Первое что нам понадобится - сервер. Я использовал debian linux (свой любимый) 64 bit. Нам для старта понадобится скачать оффициальный клиент bitcoin. Download page Скачиваем клиент, распаковываем в произвольную папку пользователя. В моем случае используется /home/usr/bitcoind Переходим в папку bin/64/ Запускаем ./bitcoind Все что необходимо он уже создал. Далее нам предстоит настроить данный софт. Пишем в консоль killall bitcoind Идем по адресу /home/usr/.bitcoin И создаем тут файл конфигурации. mcedit bitcoin.conf Я использовал следующие настройки: daemon=1 gen=0 #proxy=127.0.0.1:9050 dns=1 upnp=1 noirc=1 server=1 rpcuser=usr rpcpassword=fake_passwords rpcport=8455 rpctimeout=30 paytxfee=0.0002 addnode=69.207.126.238:8333 addnode=73.189.41.65:8333 addnode=69.65.67.66:8333 connect=69.207.126.238:8333 connect=73.189.41.65:8333 connect=69.65.67.66:8333 Code daemon=1 gen=0 #proxy=127.0.0.1:9050 dns=1 upnp=1 noirc=1 server=1 rpcuser=usr rpcpassword=fake_passwords rpcport=8455 rpctimeout=30 paytxfee=0.0002 addnode=69.207.126.238:8333 addnode=73.189.41.65:8333 addnode=69.65.67.66:8333 connect=69.207.126.238:8333 connect=73.189.41.65:8333 connect=69.65.67.66:8333 Возвращаемся в папку /home/usr/bitcoind/bin/64 Стартуем демон снова ./bitcoind Все. Наш клиент стал демоном и начал синхронизировать блоки. Для любопытных - вторая строка - это настройка работы биткоин через сеть тор. У меня на сервере он установлен, но для ускорения синхронизации не используется. Далее примем за аксиому тот момент, что я сам пишу софт и отталкиваюсь от того что мне в голову стукнет. А мои клиенты все определяются по jabber аккаунту. Я создаю следующие папки: /home/usr/bitcoind/src/ /home/usr/bitcoind/src/db/ Выставляю права 777 на папку db. Это важно. И создаем следующие файлы: cat>btc.log chmod 777 btc.log mcedit btc.py #!/usr/bin/env python #-*- coding: utf-8 -*- # ---------------------------------------------------------- # coded by ar3s # How to use: python btc-e.py # Profit! # ---------------------------------------------------------- import os import sys import time import json import base64 import hashlib import urllib2 import datetime import socket import sqlite3 import subprocess from Crypto.Cipher import AES # encryption library #system os.system('clear') #sqlite conn = sqlite3.connect('db/base.db') conn.isolation_level = None conn.text_factory = str #crypt BLOCK_SIZE = 32 PADDING = '{' pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING cipher = AES.new('Jns70wJnc92LmaTw') def log(log): if (len(log) != 0): now = datetime.datetime.now() f = open('btc.log', 'a') f.write(str(now.day)+"."+str(now.month)+"."+str(now.year)+" "+str(now.hour)+":"+str(now.minute)+":"+str(now.second)+" - "+log+"\n") f.close() else: print "enter a log data" def rate(): response = urllib2.urlopen(urllib2.Request(url='https://btc-e.com/api/2/btc_usd/ticker')) objFromJSON = json.loads(response.read()) ticker = objFromJSON['ticker'] return format(ticker['last']) def bitcoin(cmd, jid): if (cmd == "bal"): try: cmd = './bitcoind getbalance' PIPE = subprocess.PIPE p = subprocess.Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=subprocess.STDOUT, close_fds=True, cwd='/home/usr/bitcoind/bin/64/') bal = p.stdout.read() bal = float(bal.replace('\n','')) return bal except: print "bitcoind not respond a ballance" if (cmd == "add"): try: cmd = './bitcoind getnewaddress '+jid PIPE = subprocess.PIPE p = subprocess.Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=subprocess.STDOUT, close_fds=True, cwd='/home/usr/bitcoind/bin/64/') btc = p.stdout.read() btc = btc.replace('\n','') except: print "bitcoind not respond a BTC address" return btc if (cmd == "pars"): ball = 0.0 account = "" try: cmd = './bitcoind listaccounts' PIPE = subprocess.PIPE p = subprocess.Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=subprocess.STDOUT, close_fds=True, cwd='/home/usr/bitcoind/bin/64/') btc = p.stdout.read() # сделать парсинг не нулевых баллансов и возврат имени и суммы в запрос except: log("bitcoind send not finished") return account, ball def sql(query): #-------------------------- # Коннектимся к Базе данных #-------------------------- connection = 0 try: #conn = sqlite3.connect(":memory:") cur = conn.cursor() connection = 1 except sqlite3.Error: connection = 0 log("Соединение с БД НЕ установлено!!!") if ((query=='def') and (connection == 1)): #print "Исполняем def запрос" try: cur.execute("Select * from user") conn.commit() #print "DEF запрос прошел успешно" except: print 'db not issue. Creating.' cur.execute('''CREATE TABLE [user] ([id] INTEGER NOT NULL ON CONFLICT ABORT PRIMARY KEY ON CONFLICT ABORT AUTOINCREMENT, [jid] CHAR NOT NULL ON CONFLICT ABORT, [btc] CHAR NOT NULL ON CONFLICT ABORT, [ballance] FLOAT NOT NULL ON CONFLICT ABORT DEFAULT 0)''') cur.execute("insert into user values (null,'ar3s@dlab.im','FakeAddress','0')") conn.commit() if ((query != 'def') and (connection == 1)): #print "Исполняем запрос на пользователя" # Тут проверяем имеется ли такой пользователь btc = "" jid = query #print "Проверка по БД" try: cur.execute("select btc from user where jid=?",(jid,)) conn.commit() except: print "BAD sql query in section select BTC from DB" #print "Парсим результат" data = cur.fetchall() if (len(data) != 0): #print "Вошли в проверку" adr = str(data[0]) adr = adr.split("'") btc = adr[1] #print btc #print "Распарсили" else: # если ответ нулевой - то создаем пользователя btc = bitcoin("add", jid) print "Добавляем пользователя "+jid+":"+btc+"\n" try: cur.execute("insert into user values (null,?,?,'0')",(jid,btc,)) conn.commit() except: print "Error in sql query for add user in DB" return btc def crypt(msg): EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s))) encoded = EncodeAES(cipher, msg) return encoded def decrypt(msg): DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING) decoded = DecodeAES(cipher, msg) return decoded def main(): #------------------------------- # Тест записи в лог log("start") #------------------------------- # Читаем курс kurs = rate() print "Текущий курс BTC=>USD:"+kurs log("Текущий курс BTC=>USD:"+kurs) #------------------------------- # читаем наш балланс из демона bal = bitcoin("bal", "") print "Текущий балланс кошельков:"+str(bal) log("Текущий балланс кошельков:"+str(bal)) if (bal > 0): account, ball = bitcoint("pars", "") # тут сделать зачисление на балланс и отправку мне на кош #------------------------------- # Инициализируем БД sql("def") log("Инициализируем БД") #------------------------------- # инициализируем чтение сокета try: sock = socket.socket() sock.bind(('', 4563)) sock.listen(1) except Exception as e: log("Не могу открыть сокет: %s" % e) sys.exit() while True: srv, addr = sock.accept() log("Socket opened: %s" % str(addr)) data = srv.recv(1024) data = decrypt(data) if data == 'PPDvtmw1POWFSkwmNH61WF0Vlhvhq5Gc': log("Проверка ключа прошла успешно") srv.send(crypt('true')) data = '' # обнуляем данные от прошлого запроса while 1: # ждем запрос от клиента data = srv.recv(1024) if data: break jid = decrypt(data) if jid: print jid btc = sql(jid) print "user "+jid+" : "+ btc srv.send(crypt(btc)) log("Answer sendet") else: srv.send("error!") log("!!! Проверка ключа НЕ прошла") srv.close() # start main function if __name__ == '__main__': main() #-------------------------------------------------------------------------------------------------------------- # Happy end! #-------------------------------------------------------------------------------------------------------------- Code #!/usr/bin/env python #-*- coding: utf-8 -*- # ---------------------------------------------------------- # coded by ar3s # How to use: python btc-e.py # Profit! # ---------------------------------------------------------- import os import sys import time import json import base64 import hashlib import urllib2 import datetime import socket import sqlite3 import subprocess from Crypto.Cipher import AES # encryption library #system os.system('clear') #sqlite conn = sqlite3.connect('db/base.db') conn.isolation_level = None conn.text_factory = str #crypt BLOCK_SIZE = 32 PADDING = '{' pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING cipher = AES.new('Jns70wJnc92LmaTw') def log(log): if (len(log) != 0): now = datetime.datetime.now() f = open('btc.log', 'a') f.write(str(now.day)+"."+str(now.month)+"."+str(now.year)+" "+str(now.hour)+":"+str(now.minute)+":"+str(now.second)+" - "+log+"\n") f.close() else: print "enter a log data" def rate(): response = urllib2.urlopen(urllib2.Request(url='https://btc-e.com/api/2/btc_usd/ticker')) objFromJSON = json.loads(response.read()) ticker = objFromJSON['ticker'] return format(ticker['last']) def bitcoin(cmd, jid): if (cmd == "bal"): try: cmd = './bitcoind getbalance' PIPE = subprocess.PIPE p = subprocess.Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=subprocess.STDOUT, close_fds=True, cwd='/home/usr/bitcoind/bin/64/') bal = p.stdout.read() bal = float(bal.replace('\n','')) return bal except: print "bitcoind not respond a ballance" if (cmd == "add"): try: cmd = './bitcoind getnewaddress '+jid PIPE = subprocess.PIPE p = subprocess.Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=subprocess.STDOUT, close_fds=True, cwd='/home/usr/bitcoind/bin/64/') btc = p.stdout.read() btc = btc.replace('\n','') except: print "bitcoind not respond a BTC address" return btc if (cmd == "pars"): ball = 0.0 account = "" try: cmd = './bitcoind listaccounts' PIPE = subprocess.PIPE p = subprocess.Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=subprocess.STDOUT, close_fds=True, cwd='/home/usr/bitcoind/bin/64/') btc = p.stdout.read() # сделать парсинг не нулевых баллансов и возврат имени и суммы в запрос except: log("bitcoind send not finished") return account, ball def sql(query): #-------------------------- # Коннектимся к Базе данных #-------------------------- connection = 0 try: #conn = sqlite3.connect(":memory:") cur = conn.cursor() connection = 1 except sqlite3.Error: connection = 0 log("Соединение с БД НЕ установлено!!!") if ((query=='def') and (connection == 1)): #print "Исполняем def запрос" try: cur.execute("Select * from user") conn.commit() #print "DEF запрос прошел успешно" except: print 'db not issue. Creating.' cur.execute('''CREATE TABLE [user] ([id] INTEGER NOT NULL ON CONFLICT ABORT PRIMARY KEY ON CONFLICT ABORT AUTOINCREMENT, [jid] CHAR NOT NULL ON CONFLICT ABORT, [btc] CHAR NOT NULL ON CONFLICT ABORT, [ballance] FLOAT NOT NULL ON CONFLICT ABORT DEFAULT 0)''') cur.execute("insert into user values (null,'ar3s@dlab.im','FakeAddress','0')") conn.commit() if ((query != 'def') and (connection == 1)): #print "Исполняем запрос на пользователя" # Тут проверяем имеется ли такой пользователь btc = "" jid = query #print "Проверка по БД" try: cur.execute("select btc from user where jid=?",(jid,)) conn.commit() except: print "BAD sql query in section select BTC from DB" #print "Парсим результат" data = cur.fetchall() if (len(data) != 0): #print "Вошли в проверку" adr = str(data[0]) adr = adr.split("'") btc = adr[1] #print btc #print "Распарсили" else: # если ответ нулевой - то создаем пользователя btc = bitcoin("add", jid) print "Добавляем пользователя "+jid+":"+btc+"\n" try: cur.execute("insert into user values (null,?,?,'0')",(jid,btc,)) conn.commit() except: print "Error in sql query for add user in DB" return btc def crypt(msg): EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s))) encoded = EncodeAES(cipher, msg) return encoded def decrypt(msg): DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING) decoded = DecodeAES(cipher, msg) return decoded def main(): #------------------------------- # Тест записи в лог log("start") #------------------------------- # Читаем курс kurs = rate() print "Текущий курс BTC=>USD:"+kurs log("Текущий курс BTC=>USD:"+kurs) #------------------------------- # читаем наш балланс из демона bal = bitcoin("bal", "") print "Текущий балланс кошельков:"+str(bal) log("Текущий балланс кошельков:"+str(bal)) if (bal > 0): account, ball = bitcoint("pars", "") # тут сделать зачисление на балланс и отправку мне на кош #------------------------------- # Инициализируем БД sql("def") log("Инициализируем БД") #------------------------------- # инициализируем чтение сокета try: sock = socket.socket() sock.bind(('', 4563)) sock.listen(1) except Exception as e: log("Не могу открыть сокет: %s" % e) sys.exit() while True: srv, addr = sock.accept() log("Socket opened: %s" % str(addr)) data = srv.recv(1024) data = decrypt(data) if data == 'PPDvtmw1POWFSkwmNH61WF0Vlhvhq5Gc': log("Проверка ключа прошла успешно") srv.send(crypt('true')) data = '' # обнуляем данные от прошлого запроса while 1: # ждем запрос от клиента data = srv.recv(1024) if data: break jid = decrypt(data) if jid: print jid btc = sql(jid) print "user "+jid+" : "+ btc srv.send(crypt(btc)) log("Answer sendet") else: srv.send("error!") log("!!! Проверка ключа НЕ прошла") srv.close() # start main function if __name__ == '__main__': main() #-------------------------------------------------------------------------------------------------------------- # Happy end! #-------------------------------------------------------------------------------------------------------------- Протокол запросов идет у нас шифрованным. Ключ один, но можно использовать два. Один от клиента второй от сервера. Принцип работы прост. Есть демон и есть бд. Что бы не дергать демон каждый раз я записываю данные в БД. Сделал специально на sqlite для большей портабельности. Код клиента: #!/usr/bin/env python # -*- coding: utf-8 -*- #http://stackoverflow.com/questions/2490334/simple-way-to-encode-a-string-according-to-a-password import socket import base64 import hashlib from Crypto.Cipher import AES # encryption library BLOCK_SIZE = 32 PADDING = '{' pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING cipher = AES.new('Jns70wJnc92LmaTw') def crypt(msg): EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s))) encoded = EncodeAES(cipher, msg) return encoded def decrypt(msg): DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING) decoded = DecodeAES(cipher, msg) return decoded sock = socket.socket() sock.connect(('127.0.0.1', 4563)) sock.send(crypt('PPDvtmw1POWFSkwmNH61WF0Vlhvhq5Gc')) data = sock.recv(1024) data = decrypt(data) print data if (data == 'true'): sock.send(crypt('new_user@server.im')) data = sock.recv(1024) data = decrypt(data) print data sock.close() Code #!/usr/bin/env python # -*- coding: utf-8 -*- #http://stackoverflow.com/questions/2490334/simple-way-to-encode-a-string-according-to-a-password import socket import base64 import hashlib from Crypto.Cipher import AES # encryption library BLOCK_SIZE = 32 PADDING = '{' pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING cipher = AES.new('Jns70wJnc92LmaTw') def crypt(msg): EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s))) encoded = EncodeAES(cipher, msg) return encoded def decrypt(msg): DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING) decoded = DecodeAES(cipher, msg) return decoded sock = socket.socket() sock.connect(('127.0.0.1', 4563)) sock.send(crypt('PPDvtmw1POWFSkwmNH61WF0Vlhvhq5Gc')) data = sock.recv(1024) data = decrypt(data) print data if (data == 'true'): sock.send(crypt('new_user@server.im')) data = sock.recv(1024) data = decrypt(data) print data sock.close() Клиент первым делом шифрует ключевую фразу и шлет серверу. Если все ок - сервер отвечает шифрованным true. Ну и дальше идет запрос на клиента (отправляем жабу или мыло), ответ BTC адрес. В данный момент чего не доделал: 1. определение у кого какой балланс 2. отправку комманды на пополнение балланса в панель клиента 3. перекидывание денег с сервака биллинга мне на кош. Написано данное чудо просто от балды. От большого нехер делать и может быть кому-то полезно. Жду реакции и критики кода. А так же желающих доработать данное творчество.