First version, adapted from my CN tipbot

This commit is contained in:
moneromooo
2015-03-08 17:23:23 +00:00
commit fa581fac00
7 changed files with 842 additions and 0 deletions
View File
+48
View File
@@ -0,0 +1,48 @@
#!/bin/python
#
# oython-monero - logging
# Copyright 2014 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 time
def log(stype,msg):
header = "%s\t%s\t" % (time.ctime(time.time()),stype)
print "%s%s" % (header, str(msg).replace("\n","\n"+header))
def log_error(msg):
log("ERROR",msg)
def log_warn(msg):
log("WARNING",msg)
def log_info(msg):
log("INFO",msg)
def log_log(msg):
log("LOG",msg)
+63
View File
@@ -0,0 +1,63 @@
#!/bin/python
#
# python-monero
# Copyright 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 utils
import redisdb
import payment
class Monero:
def __init__(self,daemon_host,daemon_port,wallet_host,wallet_port,redis_host,redis_port):
redisdb.connect_to_redis(redis_host,redis_port)
self.daemon_host=daemon_host
self.daemon_port=daemon_port
self.wallet_host=wallet_host
self.wallet_port=wallet_port
def GetWalletBalance(self):
return utils.RetrieveWalletBalance(self.wallet_host,self.wallet_port)
def GetPaymentID(self,recipient):
return utils.GetPaymentIDFromUserID(recipient)
def GetRecipient(self,payment_id):
return utils.GetUserIDFromPaymentID(payment_id)
def CheckForDeposits(self,confirmations=6):
return payment.CheckForDeposits(self.daemon_host,self.daemon_port,self.wallet_host,self.wallet_port,confirmations)
def GetDepositHistory(self,paymentid=[]):
return payment.GetDepositHistory(self.daemon_host,self.daemon_port,self.wallet_host,self.wallet_port,paymentid)
def Send(self,address,amount,paymentid=None,mixin=3):
return payment.Send(self.wallet_host,self.wallet_port,address,amount,paymentid,mixin)
def SendMany(self,recipients,paymentid=None,mixin=3):
return payment.SendMany(self.wallet_host,self.wallet_port,recipients,paymentid,mixin)
+237
View File
@@ -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)
+153
View File
@@ -0,0 +1,153 @@
#!/bin/python
#
# python-monero
# 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
from log import log_error, log_warn, log_info, log_log
redisdb = None
def connect_to_redis(host,port):
log_info('Connecting to Redis at %s:%u' % (host, port))
try:
global redisdb
redisdb = redis.Redis(host=host,port=port)
return redisdb
except Exception, e:
log_error( 'Error initializing redis: %s' % str(e))
exit()
def redis_pipeline():
return redisdb.pipeline()
def redis_exists(k):
return redisdb.exists(k)
def redis_get(k):
return redisdb.get(k)
def redis_set(k,v):
return redisdb.set(k,v)
def redis_hexists(t,k):
return redisdb.hexists(t,k)
def redis_hget(t,k):
return redisdb.hget(t,k)
def redis_hgetall(t):
return redisdb.hgetall(t)
def redis_hset(t,k,v):
return redisdb.hset(t,k,v)
def redis_hincrby(t,k,v):
return redisdb.hincrby(t,k,v)
def redis_hdel(t,k):
return redisdb.hdel(t,k)
def redis_incrby(k,v):
return redisdb.incrby(k,v)
def redis_sadd(k,v):
return redisdb.sadd(k,v)
def redis_smembers(k):
return redisdb.smembers(k)
def redis_sismember(k,v):
return redisdb.sismember(k,v)
def redis_rpush(k,v):
return redisdb.rpush(k,v)
def redis_lpop(k):
return redisdb.lpop(k)
def redis_llen(k):
return redisdb.llen(k)
def redis_lindex(k,n):
return redisdb.lindex(k,n)
def redis_lset(k,i,v):
return redisdb.lset(k,i,v)
def redis_zincrby(t,k,s):
return redisdb.zincrby(t,k,s)
def redis_zscore(t,k):
return redisdb.zscore(t,k)
def redis_zrangebylex(t,x0,x1):
return redisdb.zrangebylex(t,x0,x1)
def redis_keys(s):
return redisdb.keys(s)
def redis_delete(k):
return redisdb.delete(k)
def CompatibilityCheck():
try:
r = redis.Redis()
if not r.pipeline: raise RuntimeError('pipeline call not found')
p = r.pipeline()
if not p.exists: raise RuntimeError('exists call not found')
if not p.get: raise RuntimeError('get call not found')
if not p.set: raise RuntimeError('set call not found')
if not p.hexists: raise RuntimeError('hexists call not found')
if not p.hget: raise RuntimeError('hget call not found')
if not p.hgetall: raise RuntimeError('hgetall call not found')
if not p.hset: raise RuntimeError('hset call not found')
if not p.hincrby: raise RuntimeError('hincrby call not found')
if not p.hdel: raise RuntimeError('hdel call not found')
if not p.incrby: raise RuntimeError('incrby call not found')
if not p.sadd: raise RuntimeError('sadd call not found')
if not p.smembers: raise RuntimeError('smembers call not found')
if not p.sismember: raise RuntimeError('sismember call not found')
if not p.rpush: raise RuntimeError('rpush call not found')
if not p.lpop: raise RuntimeError('lpop call not found')
if not p.llen: raise RuntimeError('llen call not found')
if not p.lindex: raise RuntimeError('lindex call not found')
if not p.lset: raise RuntimeError('lset call not found')
if not p.zincrby: raise RuntimeError('zincrby call not found')
if not p.zscore: raise RuntimeError('zscore call not found')
if not p.zrangebylex: raise RuntimeError('zrangebylex call not found')
if not p.keys: raise RuntimeError('keys call not found')
if not p.execute: raise RuntimeError('execute call not found')
if not p.delete: raise RuntimeError('delete call not found')
except Exception,e:
log_error('Error checking redis compatibility: %s' % str(e))
exit(1)
CompatibilityCheck()
+243
View File
@@ -0,0 +1,243 @@
#!/bin/python
#
# python-monero - utility functions
# 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 hashlib
import json
import httplib
import time
import string
from decimal import *
from log import log_error, log_warn, log_info, log_log
from redisdb import *
address_length = [95, 95] # min/max size of addresses
address_prefix = ['4', '9'] # allowed prefixes of addresses
cached_wallet_balance=None
cached_wallet_unlocked_balance=None
cached_wallet_balance_timestamp=None
def GetPaymentIDFromUserID(user_id):
salt="nveuweaiirv-"
p = hashlib.sha256(salt+user_id).hexdigest()
try:
redis_hset("paymentid",p,link.identity())
except Exception,e:
log_error('GetPaymentIDFromUserID: failed to set payment ID for %s to redis: %s' % (user_id,str(e)))
return p
def GetUserIDFromPaymentID(paymentid):
if not redis_hexists("paymentid",paymentid):
log_log('PaymentID %s not found' % paymentid)
return None
identity = redis_hget("paymentid",paymentid)
log_log('PaymentID %s => %s' % (paymentid, str(identity)))
return identity
def IsValidAddress(address):
if len(address) < address_length[0] or len(address) > address_length[1]:
return False
for prefix in address_prefix:
if address.startswith(prefix):
return True
return False
def IsValidPaymentID(payment_id):
if len(payment_id)!=64:
return False
for char in payment_id:
if char not in string.hexdigits:
return False
return True
def SendJSONRPCCommand(host,port,method,params):
try:
http = httplib.HTTPConnection(host,port,timeout=20)
except Exception,e:
log_error('SendJSONRPCCommand: Error connecting to %s:%u: %s' % (host, port, str(e)))
raise
d = dict(id="0",jsonrpc="2.0",method=method,params=params)
try:
j = json.dumps(d).encode()
except Exception,e:
log_error('SendJSONRPCCommand: Failed to encode JSON: %s' % str(e))
http.close()
raise
log_log('SendJSONRPCCommand: Sending json as body: %s' % j)
headers = None
try:
http.request("POST","/json_rpc",body=j)
except Exception,e:
log_error('SendJSONRPCCommand: Failed to post request: %s' % str(e))
http.close()
raise
response = http.getresponse()
if response.status != 200:
log_error('SendJSONRPCCommand: Error, received reply status %s' % str(response.status))
http.close()
raise RuntimeError("Error "+response.status)
s = response.read()
log_log('SendJSONRPCCommand: Received reply status %s: %s' % (response.status, str(s).replace('\r\n',' ').replace('\n',' ')))
try:
j = json.loads(s)
except Exception,e:
log_error('SendJSONRPCCommand: Failed to decode JSON: %s' % str(e))
http.close()
raise
http.close()
return j
def SendHTMLCommand(host,port,method):
try:
http = httplib.HTTPConnection(host,port,timeout=20)
except Exception,e:
log_error('SendHTMLCommand: Error connecting to %s:%u: %s' % (host, port, str(e)))
raise
headers = None
try:
http.request("POST","/"+method)
except Exception,e:
log_error('SendHTMLCommand: Failed to post request: %s' % str(e))
http.close()
raise
response = http.getresponse()
if response.status != 200:
log_error('SendHTMLCommand: Error, received reply status %s' % str(response.status))
http.close()
raise RuntimeError("Error "+response.status)
s = response.read()
log_log('SendHTMLCommand: Received reply status %s: %s' % (response.status,s.replace('\r\n',' ').replace('\n',' ')))
try:
j = json.loads(s)
except Exception,e:
log_error('SendHTMLCommand: Failed to decode JSON: %s' % str(e))
http.close()
raise
http.close()
return j
def RetrieveWalletBalance(wallet_host,wallet_port,force_refresh=False):
global cached_wallet_balance, cached_wallet_unlocked_balance, cached_wallet_balance_timestamp
if not force_refresh and cached_wallet_balance_timestamp and time.time()-cached_wallet_balance_timestamp < 35:
return cached_wallet_balance, cached_wallet_unlocked_balance
j = SendJSONRPCCommand(wallet_host,wallet_port,"getbalance",None)
if not "result" in j:
log_error('RetrieveWalletBalance: result not found in reply')
raise RuntimeError("")
return
result = j["result"]
if not "balance" in result:
log_error('RetrieveWalletBalance: balance not found in result')
raise RuntimeError("")
return
if not "unlocked_balance" in result:
log_error('RetrieveWalletBalance: unlocked_balance not found in result')
raise RuntimeError("")
return
balance = result["balance"]
unlocked_balance = result["unlocked_balance"]
log_log('RetrieveWalletBalance: balance: %s' % str(balance))
log_log('RetrieveWalletBalance: unlocked_balance: %s' % str(unlocked_balance))
pending = long(balance)-long(unlocked_balance)
if pending < 0:
log_error('RetrieveWalletBalance: Negative pending balance! balance %s, unlocked %s' % (str(balance),str(unlocked_balance)))
raise RuntimeError("")
return
cached_wallet_balance_timestamp=time.time()
cached_wallet_balance=balance
cached_wallet_unlocked_balance=unlocked_balance
return balance, unlocked_balance
# Code taken from the Python documentation
def moneyfmt(value, places=2, curr='', sep=',', dp='.',
pos='', neg='-', trailneg=''):
"""Convert Decimal to a money formatted string.
places: required number of places after the decimal point
curr: optional currency symbol before the sign (may be blank)
sep: optional grouping separator (comma, period, space, or blank)
dp: decimal point indicator (comma or period)
only specify as blank when places is zero
pos: optional sign for positive numbers: '+', space or blank
neg: optional sign for negative numbers: '-', '(', space or blank
trailneg:optional trailing minus indicator: '-', ')', space or blank
>>> d = Decimal('-1234567.8901')
>>> moneyfmt(d, curr='$')
'-$1,234,567.89'
>>> moneyfmt(d, places=0, sep='.', dp='', neg='', trailneg='-')
'1.234.568-'
>>> moneyfmt(d, curr='$', neg='(', trailneg=')')
'($1,234,567.89)'
>>> moneyfmt(Decimal(123456789), sep=' ')
'123 456 789.00'
>>> moneyfmt(Decimal('-0.02'), neg='<', trailneg='>')
'<0.02>'
"""
q = Decimal(10) ** -places # 2 places --> '0.01'
sign, digits, exp = value.quantize(q).as_tuple()
result = []
digits = map(str, digits)
build, next = result.append, digits.pop
if sign:
build(trailneg)
for i in range(places):
build(next() if digits else '0')
build(dp)
if not digits:
build('0')
i = 0
while digits:
build(next())
i += 1
if i == 3 and digits:
i = 0
build(sep)
build(curr)
build(neg if sign else pos)
s = ''.join(reversed(result))
if dp in s:
s=s.strip('0').rstrip(dp)
if s=="" or s[0]==dp:
s="0"+s
return s
def AmountToString(amount):
if amount == None:
amount = 0
lamount=long(amount)
samount = moneyfmt(Decimal(lamount)/Decimal(1e12),places=12) + " monero"
return samount