mirror of
https://github.com/jwinterm/python-monero.git
synced 2026-10-17 05:51:43 +02:00
First version, adapted from my CN tipbot
This commit is contained in:
@@ -0,0 +1,237 @@
|
||||
#!/bin/python
|
||||
#
|
||||
# python-monero - payment
|
||||
# Copyright 2014,2015 moneromooo
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification, are
|
||||
# permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
# conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
# of conditions and the following disclaimer in the documentation and/or other
|
||||
# materials provided with the distribution.
|
||||
#
|
||||
# 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
# used to endorse or promote products derived from this software without specific
|
||||
# prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
|
||||
import redis
|
||||
import time
|
||||
from log import log_error, log_warn, log_info, log_log
|
||||
from utils import *
|
||||
from redisdb import *
|
||||
|
||||
def GetWalletAddress(wallet_host,wallet_port):
|
||||
try:
|
||||
j = SendJSONRPCCommand(wallet_host,wallet_port,"getaddress",None)
|
||||
if not "result" in j:
|
||||
log_error('GetWalletAddress: No result found in getaddress reply')
|
||||
return None
|
||||
result = j["result"]
|
||||
if not "address" in result:
|
||||
log_error('GetWalletAddress: No address found in getaddress reply')
|
||||
return None
|
||||
return result["address"]
|
||||
except Exception,e:
|
||||
log_error("GetWalletAddress: Error retrieving wallet address: %s" % (str(e)))
|
||||
return None
|
||||
|
||||
def UpdateCoin(daemon_host,daemon_port,wallet_host,wallet_port,full_history=False,paymentid=None,config_confirmations=6):
|
||||
txs=[]
|
||||
executed_txs=[]
|
||||
|
||||
try:
|
||||
try:
|
||||
if full_history:
|
||||
scan_block_height = 0
|
||||
else:
|
||||
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))
|
||||
return None
|
||||
|
||||
try:
|
||||
j = SendHTMLCommand(daemon_host,daemon_port,"getheight")
|
||||
except Exception,e:
|
||||
log_error('UpdateCoin: error getting height: %s' % str(e))
|
||||
return None
|
||||
if not "height" in j:
|
||||
log_error('UpdateCoin: error getting height: height not found in %s' % str(j))
|
||||
return None
|
||||
try:
|
||||
height=long(j["height"])
|
||||
except Exception,e:
|
||||
log_error('UpdateCoin: error getting height: %s' % str(e))
|
||||
return None
|
||||
|
||||
if paymentid != None:
|
||||
if isinstance(paymentid,list):
|
||||
payment_ids = paymentid
|
||||
else:
|
||||
payment_ids = [paymentid]
|
||||
else:
|
||||
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 = SendJSONRPCCommand(wallet_host,wallet_port,"get_bulk_payments",params)
|
||||
#print 'Got j: %s' % str(j)
|
||||
if "result" in j:
|
||||
result = j["result"]
|
||||
cp = redis_pipeline()
|
||||
if not full_history:
|
||||
cp.delete('confirming_payments')
|
||||
if "payments" in result:
|
||||
payments = result["payments"]
|
||||
new_payments = []
|
||||
n_confirming = 0
|
||||
new_scan_block_height = scan_block_height
|
||||
for p in payments:
|
||||
payment_id=p["payment_id"]
|
||||
tx_hash = p["tx_hash"]
|
||||
bh = p["block_height"]
|
||||
ut = p["block_height"]
|
||||
amount=p["amount"]
|
||||
if not full_history and redis_sismember("processed_txs",tx_hash):
|
||||
continue
|
||||
log_log('UpdateCoin: Looking at payment %s' % str(p))
|
||||
confirmations = height-1-bh
|
||||
confirmations_needed = max(config_confirmations,ut-height)
|
||||
if confirmations >= confirmations_needed:
|
||||
log_info('Payment %s is now confirmed' % str(p))
|
||||
new_payments.append(p)
|
||||
if new_scan_block_height and bh > new_scan_block_height:
|
||||
new_scan_block_height = bh
|
||||
else:
|
||||
log_info('Payment %s has %d/%d confirmations' % (str(p),confirmations,confirmations_needed))
|
||||
n_confirming += 1
|
||||
new_scan_block_height = None
|
||||
try:
|
||||
recipient = GetUserIDFromPaymentID(payment_id)
|
||||
if not recipient:
|
||||
raise RuntimeError('Payment ID %s not found' % payment_id)
|
||||
log_info('UpdateCoin: Found payment %s to %s for %s' % (tx_hash,recipient, AmountToString(amount)))
|
||||
if not full_history:
|
||||
cp.hincrby('confirming_payments',recipient,amount)
|
||||
txs.append({'tx_hash':tx_hash,'amount':amount,'confirmed':False,'confirmations':confirmations,'payment_id':payment_id,'recipient':recipient})
|
||||
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)))
|
||||
payments=new_payments
|
||||
log_info('UpdateCoin: Got %d mature payments and %d confirming payments' % (len(payments),n_confirming))
|
||||
if len(payments) > 0:
|
||||
if not full_history and 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)
|
||||
try:
|
||||
pipe = redis_pipeline()
|
||||
if not full_history:
|
||||
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"]
|
||||
ut = p["unlock_time"]
|
||||
confirmations = height-1-bh
|
||||
try:
|
||||
recipient = GetUserIDFromPaymentID(payment_id)
|
||||
if not recipient:
|
||||
raise RuntimeError('Payment ID %s not found' % payment_id)
|
||||
log_info('UpdateCoin: Found payment %s to %s for %s' % (tx_hash,recipient, AmountToString(amount)))
|
||||
if not full_history:
|
||||
pipe.sadd("processed_txs",tx_hash)
|
||||
txs.append({'tx_hash':tx_hash,'amount':amount,'confirmed':True,'confirmations':confirmations,'payment_id':payment_id,'recipient':recipient})
|
||||
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')
|
||||
if not full_history:
|
||||
pipe.execute()
|
||||
except Exception,e:
|
||||
log_error('UpdateCoin: failed to set scan_block_height: %s' % str(e))
|
||||
if not full_history:
|
||||
cp.execute()
|
||||
executed_txs=txs
|
||||
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))
|
||||
return executed_txs
|
||||
|
||||
def SendMany(wallet_host,wallet_port,recipients,paymentid=None,mixin=None):
|
||||
for address in recipients:
|
||||
if not IsValidAddress(address):
|
||||
log_error("Invalid address: %s" % address)
|
||||
return
|
||||
amount = recipients[address]
|
||||
if amount <= 0:
|
||||
log_error("Invalid amount: %s" % str(amount))
|
||||
return
|
||||
|
||||
if paymentid != None:
|
||||
if not IsValidPaymentID(paymentid):
|
||||
log_error("Invalid payment ID")
|
||||
return
|
||||
|
||||
if mixin < 0:
|
||||
log_error("Invalid mixin: %d")
|
||||
return
|
||||
|
||||
log_info("Pay: sending %s, payment id %s, mixin %d" % (str(recipients), str(paymentid),mixin))
|
||||
|
||||
try:
|
||||
destinations=[]
|
||||
for address in recipients:
|
||||
destinations.append({'address':address,'amount':recipients[address]})
|
||||
params = {
|
||||
'destinations':destinations,
|
||||
'payment_id': paymentid,
|
||||
'fee': 0,
|
||||
'mixin': mixin,
|
||||
'unlock_time': 0,
|
||||
}
|
||||
j = SendJSONRPCCommand(wallet_host,wallet_port,"transfer",params)
|
||||
except Exception,e:
|
||||
log_error('Withdraw: Error in transfer: %s' % str(e))
|
||||
return
|
||||
if not "result" in j:
|
||||
log_error('Withdraw: No result in transfer reply')
|
||||
return
|
||||
result = j["result"]
|
||||
if not "tx_hash" in result:
|
||||
log_error('Withdraw: No tx_hash in transfer reply')
|
||||
return
|
||||
tx_hash = result["tx_hash"]
|
||||
log_info('tx sent, tx_hash %s' % (str(tx_hash)))
|
||||
return tx_hash
|
||||
|
||||
def Send(wallet_host,wallet_port,address,amount,paymentid=None,mixin=None):
|
||||
return SendMany(wallet_host,wallet_port,{address: amount},paymentid,mixin)
|
||||
|
||||
def CheckForDeposits(daemon_host,daemon_port,wallet_host,wallet_port,confirmations):
|
||||
return UpdateCoin(daemon_host,daemon_port,wallet_host,wallet_port,full_history=False,config_confirmations=confirmations)
|
||||
|
||||
def GetDepositHistory(daemon_host,daemon_port,wallet_host,wallet_port,paymentid=[]):
|
||||
return UpdateCoin(daemon_host,daemon_port,wallet_host,wallet_port,full_history=True,paymentid=paymentid)
|
||||
|
||||
Reference in New Issue
Block a user