• XSS.stack #1 – первый литературный журнал от юзеров форума

Биллинг Bitcoin + python

Ar3s

Старожил форума
Легенда
Регистрация
30.12.2004
Сообщения
3 357
Реакции
1 404
В данном топике хочу рассказать о своих потугах написать на питоне биллинг для практически любого проекта на основе 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

Возвращаемся в папку /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!
#--------------------------------------------------------------------------------------------------------------

Протокол запросов идет у нас шифрованным. Ключ один, но можно использовать два. Один от клиента второй от сервера.

Принцип работы прост. Есть демон и есть бд. Что бы не дергать демон каждый раз я записываю данные в БД. Сделал специально на 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()

Клиент первым делом шифрует ключевую фразу и шлет серверу. Если все ок - сервер отвечает шифрованным true. Ну и дальше идет запрос на клиента (отправляем жабу или мыло), ответ BTC адрес.

В данный момент чего не доделал:
1. определение у кого какой балланс
2. отправку комманды на пополнение балланса в панель клиента
3. перекидывание денег с сервака биллинга мне на кош.

Написано данное чудо просто от балды. От большого нехер делать и может быть кому-то полезно. Жду реакции и критики кода. А так же желающих доработать данное творчество.
 
На самом деле интересно.

Но блин питон. ;) Хотя он достаточно простой в понимании, но я так и не могу привыкнуть к нему.

Еще было бы хорошо генерировать один адрес биткоин для каждого клиента, записывать в базу и раз в n-минут проверять его на поступление денег. Тогда (в свете дальнейшего попадания этого в анд.билдер как я подозреваю) было бы удобно. Нужно пополнить баланс - кинул денег на заранее полученный адрес и через n-минут баланс уже пополнился. И не надо каждый раз делать запрос на получение адреса и пополнение баланса.
 
Так сейчас и генерируется один адрес каждому клиенту и записывается в базу.
Осталось только сделать чекалку кто пополнил + моментальный перевод в баксы по текущему курсу. Работы на пару часов по идее. У меня просто руки опустились чет последнее время. Ничего уже не хочется :(
 
А почему не написать (или взять готовый) модуль генерации биткоин адреса и приват кея, генерируешь адрес отдаёшь пользователю, через сервисы с публичными API проверяешь оплату.
Просто также можно поступить и с ETH и другими альткоинами.
Плюсы: меньше платим за сервер, можно принимать сразу несколько валют.
Минусы: если публичный API сервис отвалится, то печаль.
 
А почему не написать (или взять готовый) модуль генерации биткоин адреса и приват кея, генерируешь адрес отдаёшь пользователю, через сервисы с публичными API проверяешь оплату.
Просто также можно поступить и с ETH и другими альткоинами.
Плюсы: меньше платим за сервер, можно принимать сразу несколько валют.
Минусы: если публичный API сервис отвалится, то печаль.

Ну давайте поясню по пунктам:
1. посмотрите на дату моего поста. Написано от скуки и в продакшене не использовалось.
2. Потому что любой публичный сервис отдаст логи всей вашей работы. А если клиент ваш и вы под каждого генерите кошель - то узнать сумму ваших поступлений не представляется возможным (зная данные только одного вашего клиента).
3. Потому что уже проходили путь агрегаторов, которые неожиданно закрываются с вашим баблом. И хрен вы что сделаете. (пример btc.com)
4. Нагрузка минимальна. И мощностей под это не требуется. Вот место на харде - это да. Но и тут есть варианты. Например ограничить начало БД определенной датой.

Автор топика уже отсидел) не в обиду
Да какие тут обиды? Все так и есть. И могу сказать что доблестный blockchain, на котором у нас был кошель - не самый лучший выбор.
 
Ну давайте поясню по пунктам:
1. посмотрите на дату моего поста. Написано от скуки и в продакшене не использовалось.
2. Потому что любой публичный сервис отдаст логи всей вашей работы. А если клиент ваш и вы под каждого генерите кошель - то узнать сумму ваших поступлений не представляется возможным (зная данные только одного вашего клиента).
3. Потому что уже проходили путь агрегаторов, которые неожиданно закрываются с вашим баблом. И хрен вы что сделаете. (пример btc.com)
4. Нагрузка минимальна. И мощностей под это не требуется. Вот место на харде - это да. Но и тут есть варианты. Например ограничить начало БД определенной датой.


Да какие тут обиды? Все так и есть. И могу сказать что доблестный blockchain, на котором у нас был кошель - не самый лучший выбор.
Это диалог, а не в коем случае не претензия.
И я не писал о кошельке на blockchain, о приватном API или каком-то криптомерче, я писал о публичном API (обозревателе блоков).
  1. Сгенерировали у себя адрес под клиента (Пример: Address: 1xss7Z4vR6JmjjYYPdPkuNjByi9rFnUSf Privkey: 5JRggzKKN2DpW7ujZLoqmwNcSX8aviaR7hu4t1pq2QDczxbuG2E)
  2. Показали клиенту для оплаты и переодически проверяем пока не пройдёт оплата (Пример: https://chain.so/api/v2/address/BTC/1xss7Z4vR6JmjjYYPdPkuNjByi9rFnUSf)
 
Логика понятна. И она имеет право на жизнь.
Но есть минусы. Например:
1. До какого периода вы будете чекать указанный адрес? Ну не заплатил клиент во-время.
2. Не заблочит ли вас тот же chain.so за слишком частые обращения?
3. Выводить как? Импортировать каждый раз массу нагенеренных кошелей к себе в клиент и потом аккумулировать на одном счету или сразу лить на биржу?

Допустим вы оказываете услугу по 20$. Для сотни вам придется пять раз импортировать кошельки. Все-таки хотелось бы не делать лишнего и не нужного. А одной командой вывести с горячего кошелька все. Или даже сделать автоматизацию, которая сама будет раз в сутки выводить с кошельков клиентов на общий счет (допустим миксер).
 
Логика понятна. И она имеет право на жизнь.
Но есть минусы. Например:
1. До какого периода вы будете чекать указанный адрес? Ну не заплатил клиент во-время.
2. Не заблочит ли вас тот же chain.so за слишком частые обращения?
3. Выводить как? Импортировать каждый раз массу нагенеренных кошелей к себе в клиент и потом аккумулировать на одном счету или сразу лить на биржу?

Допустим вы оказываете услугу по 20$. Для сотни вам придется пять раз импортировать кошельки. Все-таки хотелось бы не делать лишнего и не нужного. А одной командой вывести с горячего кошелька все. Или даже сделать автоматизацию, которая сама будет раз в сутки выводить с кошельков клиентов на общий счет (допустим миксер).

1. Минут 15-30, далее заглушка "Время оплаты истекло, сформируйте новый счёт", ну и хэш опирации, если клиент всё-таки успел оплатить и ему вылезла заглушка.
2. Тут нужно смотреть, можно пользоваться несколькими сервисами.
3. Не понимаю, какая разница между обработкой платежей на сервере и на своём собственном пк? Тот же скрипт на python, который будет экспортировать операции оплаты с сайта и далее загонять их в Bitcoin Core, который в свою очередь будет расскидывать их по нужным кошелькам.

Единственный существенный плюс наверное, это мультивалютность.

Мой hello world:
Python:
#!/usr/bin/python
from pybitcoin import BitcoinPrivateKey
import requests
import json

# Генерация приватного ключа
private_key = BitcoinPrivateKey()
pk = private_key.to_wif()

# Генерация публичного ключа
public_key = private_key.public_key()
address = public_key.address()

print ('Address: {}\nPrivate Key: {}'.format(address, pk))
'''
Address: 15qrRgnffrcZWxPHooo5HTSqzE28VgfFMc
Private Key: 5J6kVNQmouNUSr18i3vGNDVy3W8fEdvwtiix9PFdpLVzFtLqem8
'''

# Проверка адреса
r = requests.get('https://chain.so/api/v2/address/BTC/{}'.format(address))
print (json.loads(r.content)['data'])
'''
{
    u'total_txs': 0,
    u'network':
    u'BTC',
    u'received_value': u'0.00000000',
    u'address': u'15qrRgnffrcZWxPHooo5HTSqzE28VgfFMc',
    u'pending_value': u'0.00000000',
    u'balance': u'0.00000000',
    u'txs': []
}
'''

Код для ETH будет не сложнее, проверять транзакции можно например на https://etherscan.io/
 
Единственный существенный плюс наверное, это мультивалютность.
/QUOTE]

Всякое мнение и стремление имеет право на жизнь. Я предпочитаю все на своем держать.
 
1. Минут 15-30, далее заглушка "Время оплаты истекло, сформируйте новый счёт", ну и хэш опирации, если клиент всё-таки успел оплатить и ему вылезла заглушка.
2. Тут нужно смотреть, можно пользоваться несколькими сервисами.
3. Не понимаю, какая разница между обработкой платежей на сервере и на своём собственном пк? Тот же скрипт на python, который будет экспортировать операции оплаты с сайта и далее загонять их в Bitcoin Core, который в свою очередь будет расскидывать их по нужным кошелькам.

Единственный существенный плюс наверное, это мультивалютность.

Мой hello world:
Python:
#!/usr/bin/python
from pybitcoin import BitcoinPrivateKey
import requests
import json

# Генерация приватного ключа
private_key = BitcoinPrivateKey()
pk = private_key.to_wif()

# Генерация публичного ключа
public_key = private_key.public_key()
address = public_key.address()

print ('Address: {}\nPrivate Key: {}'.format(address, pk))
'''
Address: 15qrRgnffrcZWxPHooo5HTSqzE28VgfFMc
Private Key: 5J6kVNQmouNUSr18i3vGNDVy3W8fEdvwtiix9PFdpLVzFtLqem8
'''

# Проверка адреса
r = requests.get('https://chain.so/api/v2/address/BTC/{}'.format(address))
print (json.loads(r.content)['data'])
'''
{
    u'total_txs': 0,
    u'network':
    u'BTC',
    u'received_value': u'0.00000000',
    u'address': u'15qrRgnffrcZWxPHooo5HTSqzE28VgfFMc',
    u'pending_value': u'0.00000000',
    u'balance': u'0.00000000',
    u'txs': []
}
'''

Код для ETH будет не сложнее, проверять транзакции можно например на https://etherscan.io/
Зачем использовать сторонний сервис? Вот есть opensource - BTCPAYSERVER, я его однажды мучал, так там свой встроенный обозреватель блоков. Можно интегрировать его сюды
 
Зачем использовать сторонний сервис? Вот есть opensource - BTCPAYSERVER, я его однажды мучал, так там свой встроенный обозреватель блоков. Можно интегрировать его сюды
Это обёртка над bitcoind, всё равно придётся сливать десятки гигов на сервер чтобы работать с ним.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Это обёртка над bitcoind, всё равно придётся сливать десятки гигов на сервер чтобы работать с ним.
Зато это автономное решение, не использующее сторонние сервисы
 
Зато это автономное решение, не использующее сторонние сервисы
Как по мне, это единственный вариант если ты более-менее серьезно криптой пользуешься. И аргумент что "так это же места много занимает!" - вообще не канает. Когда очередная биржа накроется или её ебнут, тогда тебе будет не до места. Сейчас 500ГБ не так и много, и сохранность своих денег стоит дороже.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Как по мне, это единственный вариант если ты более-менее серьезно криптой пользуешься. И аргумент что "так это же места много занимает!" - вообще не канает. Когда очередная биржа накроется или её ебнут, тогда тебе будет не до места. Сейчас 500ГБ не так и много, и сохранность своих денег стоит дороже.
Все верно, потратить 5т.р. на место под блоки лучше чем потом проебать репутацию, деньги и ресурс в целом
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх