2014-12-29 17:08:13 +00:00
|
|
|
#!/bin/python
|
|
|
|
#
|
|
|
|
# Cryptonote tipbot - payment
|
|
|
|
# Copyright 2014 moneromooo
|
|
|
|
# Inspired by "Simple Python IRC bot" by berend
|
|
|
|
#
|
|
|
|
# The Cryptonote tipbot 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 2, or (at your option)
|
|
|
|
# any later version.
|
|
|
|
#
|
|
|
|
|
|
|
|
import redis
|
|
|
|
import time
|
|
|
|
import tipbot.config as config
|
|
|
|
from tipbot.log import log_error, log_warn, log_info, log_log
|
|
|
|
from tipbot.utils import *
|
|
|
|
from tipbot.redisdb import *
|
2014-12-31 10:30:07 +00:00
|
|
|
from tipbot.command_manager import *
|
2014-12-29 17:08:13 +00:00
|
|
|
|
|
|
|
last_wallet_update_time = None
|
|
|
|
|
2015-01-01 14:23:34 +00:00
|
|
|
def GetTipbotAddress():
|
|
|
|
try:
|
|
|
|
j = SendWalletJSONRPCCommand("getaddress",None)
|
|
|
|
if not "result" in j:
|
|
|
|
log_error('GetTipbotAddress: No result found in getaddress reply')
|
2015-01-24 11:33:56 +00:00
|
|
|
return None
|
2015-01-01 14:23:34 +00:00
|
|
|
result = j["result"]
|
|
|
|
if not "address" in result:
|
|
|
|
log_error('GetTipbotAddress: No address found in getaddress reply')
|
2015-01-24 11:33:56 +00:00
|
|
|
return None
|
2015-01-01 14:23:34 +00:00
|
|
|
return result["address"]
|
|
|
|
except Exception,e:
|
|
|
|
log_error("GetTipbotAddress: Error retrieving %s's address: %s" % (config.tipbot_name, str(e)))
|
2015-01-24 11:33:56 +00:00
|
|
|
return None
|
2015-01-01 14:23:34 +00:00
|
|
|
|
2015-01-13 12:28:05 +00:00
|
|
|
def UpdateCoin(data):
|
2014-12-29 17:08:13 +00:00
|
|
|
global last_wallet_update_time
|
|
|
|
if last_wallet_update_time == None:
|
|
|
|
last_wallet_update_time = 0
|
|
|
|
t=time.time()
|
|
|
|
dt = t - last_wallet_update_time
|
|
|
|
if dt < config.wallet_update_time:
|
|
|
|
return
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
scan_block_height = redis_get("scan_block_height")
|
|
|
|
scan_block_height = long(scan_block_height)
|
|
|
|
except Exception,e:
|
|
|
|
log_error('Failed to get scan_block_height: %s' % str(e))
|
|
|
|
last_wallet_update_time = time.time()
|
|
|
|
return
|
|
|
|
|
2015-01-25 18:36:17 +00:00
|
|
|
try:
|
|
|
|
j = SendDaemonHTMLCommand("getheight")
|
|
|
|
except Exception,e:
|
|
|
|
log_error('UpdateCoin: error getting height: %s' % str(e))
|
|
|
|
return
|
|
|
|
if not "height" in j:
|
|
|
|
log_error('UpdateCoin: error getting height: height not found in %s' % str(j))
|
|
|
|
return
|
|
|
|
try:
|
|
|
|
height=long(j["height"])
|
|
|
|
except Exception,e:
|
|
|
|
log_error('UpdateCoin: error getting height: %s' % str(e))
|
|
|
|
return
|
|
|
|
|
2014-12-29 17:08:13 +00:00
|
|
|
full_payment_ids = redis_hgetall("paymentid")
|
|
|
|
#print 'Got full payment ids: %s' % str(full_payment_ids)
|
|
|
|
payment_ids = []
|
|
|
|
for pid in full_payment_ids:
|
|
|
|
payment_ids.append(pid)
|
|
|
|
#print 'Got payment ids: %s' % str(payment_ids)
|
|
|
|
params = {
|
|
|
|
"payment_ids": payment_ids,
|
|
|
|
"min_block_height": scan_block_height
|
|
|
|
}
|
|
|
|
j = SendWalletJSONRPCCommand("get_bulk_payments",params)
|
|
|
|
#print 'Got j: %s' % str(j)
|
|
|
|
if "result" in j:
|
|
|
|
result = j["result"]
|
2015-01-26 17:52:39 +00:00
|
|
|
cp = redis_pipeline()
|
|
|
|
cp.delete('confirming_payments')
|
2014-12-29 17:08:13 +00:00
|
|
|
if "payments" in result:
|
|
|
|
payments = result["payments"]
|
2015-01-25 18:36:17 +00:00
|
|
|
new_payments = []
|
|
|
|
n_confirming = 0
|
2015-01-25 23:14:52 +00:00
|
|
|
new_scan_block_height = scan_block_height
|
2014-12-29 17:08:13 +00:00
|
|
|
for p in payments:
|
2015-01-26 17:52:39 +00:00
|
|
|
payment_id=p["payment_id"]
|
2015-01-25 23:14:52 +00:00
|
|
|
tx_hash = p["tx_hash"]
|
2014-12-29 17:08:13 +00:00
|
|
|
bh = p["block_height"]
|
2015-01-25 23:14:52 +00:00
|
|
|
ut = p["block_height"]
|
2015-01-26 17:52:39 +00:00
|
|
|
amount=p["amount"]
|
2015-01-25 23:14:52 +00:00
|
|
|
if redis_sismember("processed_txs",tx_hash):
|
|
|
|
continue
|
|
|
|
log_log('UpdateCoin: Looking at payment %s' % str(p))
|
2015-01-25 18:36:17 +00:00
|
|
|
confirmations = height-1-bh
|
2015-01-25 23:14:52 +00:00
|
|
|
confirmations_needed = max(config.payment_confirmations,ut-height)
|
|
|
|
if confirmations >= confirmations_needed:
|
2015-01-25 18:36:17 +00:00
|
|
|
log_info('Payment %s is now confirmed' % str(p))
|
|
|
|
new_payments.append(p)
|
2015-01-25 23:14:52 +00:00
|
|
|
if new_scan_block_height and bh > new_scan_block_height:
|
|
|
|
new_scan_block_height = bh
|
2015-01-25 18:36:17 +00:00
|
|
|
else:
|
2015-01-25 23:14:52 +00:00
|
|
|
log_info('Payment %s has %d/%d confirmations' % (str(p),confirmations,confirmations_needed))
|
2015-01-25 18:36:17 +00:00
|
|
|
n_confirming += 1
|
2015-01-25 23:14:52 +00:00
|
|
|
new_scan_block_height = None
|
2015-01-26 17:52:39 +00:00
|
|
|
try:
|
|
|
|
recipient = GetIdentityFromPaymentID(payment_id)
|
|
|
|
if not recipient:
|
|
|
|
raise RuntimeError('Payment ID %s not found' % payment_id)
|
2015-01-31 23:40:13 +00:00
|
|
|
account = redis_hget('accounts',recipient)
|
|
|
|
if accounts == None:
|
|
|
|
raise RuntimeError('No account found for %s' % recipient)
|
2015-01-26 17:52:39 +00:00
|
|
|
log_info('UpdateCoin: Found payment %s to %s for %s' % (tx_hash,recipient, AmountToString(amount)))
|
2015-01-31 23:40:13 +00:00
|
|
|
cp.hincrby('confirming_payments',account,amount)
|
2015-01-26 17:52:39 +00:00
|
|
|
except Exception,e:
|
|
|
|
log_error('UpdateCoin: No identity found for payment id %s, tx hash %s, amount %s: %s' % (payment_id, tx_hash, amount, str(e)))
|
2015-01-25 18:36:17 +00:00
|
|
|
payments=new_payments
|
2015-01-25 20:15:53 +00:00
|
|
|
log_info('UpdateCoin: Got %d mature payments and %d confirming payments' % (len(payments),n_confirming))
|
2015-01-25 18:36:17 +00:00
|
|
|
if len(payments) > 0:
|
2015-01-25 23:14:52 +00:00
|
|
|
if new_scan_block_height and new_scan_block_height > scan_block_height:
|
|
|
|
scan_block_height = new_scan_block_height
|
|
|
|
log_log('UpdateCoin: increasing scan_block_height to %d' % scan_block_height)
|
2015-01-25 18:36:17 +00:00
|
|
|
try:
|
|
|
|
pipe = redis_pipeline()
|
|
|
|
pipe.set("scan_block_height", scan_block_height)
|
|
|
|
log_log('UpdateCoin: processing payments')
|
|
|
|
for p in payments:
|
|
|
|
payment_id=p["payment_id"]
|
|
|
|
tx_hash=p["tx_hash"]
|
|
|
|
amount=p["amount"]
|
|
|
|
bh = p["block_height"]
|
2015-01-25 23:14:52 +00:00
|
|
|
ut = p["unlock_time"]
|
2015-01-25 18:36:17 +00:00
|
|
|
try:
|
|
|
|
recipient = GetIdentityFromPaymentID(payment_id)
|
|
|
|
if not recipient:
|
|
|
|
raise RuntimeError('Payment ID %s not found' % payment_id)
|
2015-01-31 23:40:13 +00:00
|
|
|
if accounts == None:
|
|
|
|
raise RuntimeError('No account found for %s' % recipient)
|
2015-01-25 18:36:17 +00:00
|
|
|
log_info('UpdateCoin: Found payment %s to %s for %s' % (tx_hash,recipient, AmountToString(amount)))
|
2015-01-31 23:40:13 +00:00
|
|
|
pipe.hincrby("balances",account,amount)
|
2015-01-25 23:14:52 +00:00
|
|
|
pipe.sadd("processed_txs",tx_hash)
|
2015-01-25 18:36:17 +00:00
|
|
|
except Exception,e:
|
|
|
|
log_error('UpdateCoin: No identity found for payment id %s, tx hash %s, amount %s: %s' % (payment_id, tx_hash, amount, str(e)))
|
|
|
|
log_log('UpdateCoin: Executing received payments pipeline')
|
|
|
|
pipe.execute()
|
|
|
|
except Exception,e:
|
|
|
|
log_error('UpdateCoin: failed to set scan_block_height: %s' % str(e))
|
2015-01-26 17:52:39 +00:00
|
|
|
cp.execute()
|
2014-12-29 17:08:13 +00:00
|
|
|
else:
|
|
|
|
log_error('UpdateCoin: No results in get_bulk_payments reply')
|
|
|
|
except Exception,e:
|
|
|
|
log_error('UpdateCoin: Failed to get bulk payments: %s' % str(e))
|
|
|
|
last_wallet_update_time = time.time()
|
|
|
|
|
2015-01-13 12:28:05 +00:00
|
|
|
def Deposit(link,cmd):
|
|
|
|
Help(link)
|
2015-01-08 15:08:28 +00:00
|
|
|
|
2015-01-13 12:28:05 +00:00
|
|
|
def Help(link):
|
2015-01-20 17:18:15 +00:00
|
|
|
link.send_private("You can send %s to your account:" % coinspecs.name);
|
2015-01-24 11:33:56 +00:00
|
|
|
address=GetTipbotAddress() or 'ERROR'
|
|
|
|
link.send_private(" Address: %s" % address)
|
2015-01-20 17:18:15 +00:00
|
|
|
link.send_private(" Payment ID: %s" % GetPaymentID(link))
|
2015-01-25 21:25:46 +00:00
|
|
|
link.send_private("Incoming transactions are credited after %d confirmations" % config.payment_confirmations)
|
2015-01-01 14:23:34 +00:00
|
|
|
|
2015-01-03 18:32:09 +00:00
|
|
|
RegisterModule({
|
|
|
|
'name': __name__,
|
|
|
|
'help': Help,
|
2015-01-22 19:29:31 +00:00
|
|
|
'idle': UpdateCoin
|
2015-01-03 18:32:09 +00:00
|
|
|
})
|
2015-01-08 15:08:28 +00:00
|
|
|
RegisterCommand({
|
|
|
|
'module': __name__,
|
|
|
|
'name': 'deposit',
|
|
|
|
'function': Deposit,
|
|
|
|
'help': "Show instructions about depositing %s" % coinspecs.name
|
|
|
|
})
|
2014-12-29 17:08:13 +00:00
|
|
|
|