diff --git a/Gruntfile.js b/Gruntfile.js index 124bc7529..b6c248e0c 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -23,13 +23,13 @@ module.exports = function(grunt) { directory: 'www/bower_components/bitcoinjs-lib' } }, - "crypto-js": { + "crypto-js": { options: { repository: "https://github.com/scintill/crypto-js.git", branch: 'master', directory: 'www/bower_components/bitcoinjs-lib/src/crypto-js' } - } + } }, shell: { html: { @@ -59,14 +59,14 @@ module.exports = function(grunt) { }, command: 'npm install ; npm run minify' }, - "omni-websocket": { - options: { - execOptions: { - cwd: "api/websocket" - } - }, - command: 'npm install' - }, + "omni-websocket": { + options: { + execOptions: { + cwd: "api/websocket" + } + }, + command: 'npm install' + }, cryptolib: { options: { stdout: true, diff --git a/api/balancehelper.py b/api/balancehelper.py new file mode 100644 index 000000000..8b499d3f7 --- /dev/null +++ b/api/balancehelper.py @@ -0,0 +1,86 @@ +import json +from msc_apps import * + +def get_balancedata(address): + addr = re.sub(r'\W+', '', address) #check alphanumeric + ROWS=dbSelect("""select + f1.propertyid, sp.propertytype, f1.balanceavailable, f1.pendingpos, f1.pendingneg + from + (select + COALESCE(s1.propertyid,s2.propertyid) as propertyid, COALESCE(s1.balanceavailable,0) as balanceavailable, + COALESCE(s2.pendingpos,0) as pendingpos,COALESCE(s2.pendingneg,0) as pendingneg + from + (select propertyid,balanceavailable + from addressbalances + where address=%s) s1 + full join + (SELECT atx.propertyid, + sum(CASE WHEN atx.balanceavailablecreditdebit > 0 THEN atx.balanceavailablecreditdebit ELSE 0 END) AS pendingpos, + sum(CASE WHEN atx.balanceavailablecreditdebit < 0 THEN atx.balanceavailablecreditdebit ELSE 0 END) AS pendingneg + from + addressesintxs atx, transactions tx + where + atx.txdbserialnum=tx.txdbserialnum + and tx.txstate='pending' + and tx.txdbserialnum<-1 + and atx.address=%s + group by + atx.propertyid) s2 + on s1.propertyid=s2.propertyid) f1 + inner join smartproperties sp + on f1.propertyid=sp.propertyid and (sp.protocol='Mastercoin' or sp.protocol='Bitcoin') + order by f1.propertyid""",(addr,addr)) + + balance_data = { 'balance': [] } + out, err = run_command(TIMEOUT+ 'sx balance -j ' + addr ) + for balrow in ROWS: + cID = str(int(balrow[0])) #currency id + sym_t = ('BTC' if cID == '0' else ('MSC' if cID == '1' else ('TMSC' if cID == '2' else 'SP' + cID) ) ) #symbol template + #1 = new indivisible property, 2=new divisible property (per spec) + divi = True if int(balrow[1]) == 2 else False + res = { 'symbol' : sym_t, 'divisible' : divi, 'id' : cID } + res['pendingpos'] = ('%.8f' % float(balrow[3])).rstrip('0').rstrip('.') + res['pendingneg'] = ('%.8f' % float(balrow[4])).rstrip('0').rstrip('.') + if cID == '0': + #get btc balance from sx + if err != None or out == '': + btc_balance[ 'value' ] = int(-555) + else: + try: + if balrow[4] < 0: + res['value'] = int( json.loads( out )[0][ 'paid' ]) + int(balrow[4]) + else: + res['value'] = int( json.loads( out )[0][ 'paid' ]) + except ValueError: + btc_balance[ 'value' ] = int(-555) + else: + #get regular balance from db + if balrow[4] < 0: + #update the 'available' balance immediately when the sender sent something. prevent double spend + res['value'] = ('%.8f' % float( (balrow[2]+balrow[4]) )).rstrip('0').rstrip('.') + else: + res['value'] = ('%.8f' % float(balrow[2])).rstrip('0').rstrip('.') + + #res['reserved_balance'] = ('%.8f' % float(balrow[5])).rstrip('0').rstrip('.') + balance_data['balance'].append(res) + + #check if we got BTC data from DB, if not trigger manually add + addbtc=True + for x in balance_data['balance']: + if "BTC" in x['symbol']: + addbtc=False + + if addbtc: + btc_balance = { 'symbol': 'BTC', 'divisible': True, 'id' : 0 } + if err != None or out == '': + btc_balance[ 'value' ] = int(-555) + else: + try: + btc_balance[ 'value' ] = int( json.loads( out )[0][ 'paid' ]) + except ValueError: + btc_balance[ 'value' ] = int(-555) + btc_balance['pendingpos'] = int(0) + btc_balance['pendingneg'] = int(0) + balance_data['balance'].append(btc_balance) + + return balance_data diff --git a/api/decode.py b/api/decode.py new file mode 100644 index 000000000..3b9ecf944 --- /dev/null +++ b/api/decode.py @@ -0,0 +1,232 @@ +from flask import Flask, abort, json, jsonify +import hashlib +import pybitcointools +import decimal +from rpcclient import * + +app = Flask(__name__) +app.debug = True + +@app.route('/') +def decode_handler(rawhex): + return jsonify(decode(rawhex)) + + +def getinputs(rawtx): + retval={'invalid':False, 'inputs':{}} + for input in rawtx['vin']: + prevtx=getrawtransaction(input['txid']) + if prevtx['result']['vout'][input['vout']]['scriptPubKey']['type'] not in ['pubkeyhash','scripthash']: + #Valid MP tx's only have pubkeyhash and scripthash as inputs + retval['invalid']=True + inputamount= int(decimal.Decimal(str( prevtx['result']['vout'][input['vout']]['value']))*decimal.Decimal(1e8)) + for addr in prevtx['result']['vout'][input['vout']]['scriptPubKey']['addresses']: + if addr in retval['inputs']: + retval['inputs'][addr] += inputamount + else: + retval['inputs'][addr] = inputamount + return retval + + +def decode(rawhex): + + rawBTC = decoderawtransaction(rawhex)['result'] + sia=0 + reference="" + inputs=getinputs(rawBTC) + senders=inputs['inputs'] + for sender in senders: + if senders[sender] > sia: + reference = sender + sia = senders[sender] + + if reference == "": + retval = {"Error":"Can\'t decode MP TX. No valid sending address found."} + return {'Sender':reference,'BTC':rawBTC, 'MP':retval,'inputs':senders} + + if inputs['invalid']: + retval = {"Error":"Can\'t decode MP TX. Invalid input type detected"} + return {'Sender':reference,'BTC':rawBTC, 'MP':retval,'inputs':senders} + + + #senders = rawtx['result']['vout'][rawBTC['vin'][0]['vout']]['scriptPubKey']['addresses'] + #reference = senders[0] + + #get all multisigs + multisig_output = [] + dest="" + for output in rawBTC['vout']: + if output['scriptPubKey']['type'] == 'multisig': + multisig_output.append(output) #grab msigs + elif output['scriptPubKey']['type'] in ['pubkeyhash','scripthash']: + try: + for address in output['scriptPubKey']['addresses']: + if address not in ['1EXoDusjGwvnjZUyKkxZ4UHEf77z6A5S4P','mpexoDuSkGGqvqrkrjiFng38QPkJQVFyqv'] and address not in reference: + dest=address + #return on first successful dest address per spec (highest vout) + break + except KeyError: + pass + + #extract compressed keys + scriptkeys = [] + for output in multisig_output: #seqnums start at 1, so adjust range + split_script = output['scriptPubKey']['asm'].split(' ') + for val in split_script: + if len(val) == 66: + scriptkeys.append(val) + + #filter keys that are ref + nonrefkeys = [] + + #check for testnet addresses + if reference[:1] in ['2','m','n']: + #testnet address + offset=111 + else: + offset=0 + + for compressedkey in scriptkeys: + if pybitcointools.pubtoaddr(compressedkey,offset) not in senders : + nonrefkeys.append(compressedkey) + + max_seqnum = len(nonrefkeys) + sha_keys = [ hashlib.sha256(reference).digest().encode('hex').upper()] #first sha256 of ref addr, see class B for more info + for i in range(max_seqnum): + if i < (max_seqnum-1): + sha_keys.append(hashlib.sha256(sha_keys[i]).digest().encode('hex').upper()) #keep sha'ing to generate more packets + + pairs = [] + for i in range(len(nonrefkeys)): + pairs.append((nonrefkeys[i], sha_keys[i] )) + + packets = [] + for pair in pairs: + obpacket = pair[0].upper()[2:-2] + shaaddress = pair[1][:-2] + print 'Obfus/SHA', obpacket, shaaddress + datapacket = '' + for i in range(len(obpacket)): + if obpacket[i] == shaaddress[i]: + datapacket = datapacket + '0' + else: + bin_ob = int('0x' + obpacket[i], 16) + bin_sha = int('0x' + shaaddress[i], 16) + xored = hex(bin_ob ^ bin_sha)[2:].upper() + datapacket = datapacket + xored + packets.append(datapacket) + + long_packet = '' + for packet in packets: + print 'Decoded packet #' + str(packet[0:2]) + ' : ' + packet + long_packet += packet[2:] + + retval="" + if long_packet[4:8] == '0032': + #Create Fixed Issuance + spare_bytes = ''.join(long_packet[22:]) + #DEBUG print spare_bytes.split('00') + len_var_fields = len(''.join(spare_bytes.split('00')[0:5])+'0000000000') + #DEBUG print len_var_fields, spare_bytes[len_var_fields:len_var_fields+16],spare_bytes + + retval = { 'TxVersion': int(long_packet[0:4],16), + 'TxType': int(long_packet[4:8],16), + 'TxTypeString': 'Create Fixed Issuance', + 'Ecosystem': int(long_packet[8:10],16), + 'Property Type': int(long_packet[10:14],16), + 'Previous Property ID': int(long_packet[14:22],16), + 'Property Category': spare_bytes.split('00')[0].decode('hex'), + 'Property Subcategory': spare_bytes.split('00')[1].decode('hex'), + 'Property Name': spare_bytes.split('00')[2].decode('hex'), + 'Property URL': spare_bytes.split('00')[3].decode('hex'), + 'Property Data': ''.join(spare_bytes.split('00')[4]).decode('hex'), + 'Number of Properties: ': int(str(int(spare_bytes[len_var_fields:len_var_fields+16],16))) + } + + if long_packet[4:8] == '0033': + #Create Variable issuance (Crowdsale) + spare_bytes = ''.join(long_packet[22:]) + #DEBUG print spare_bytes.split('00') + len_var_fields = len(''.join(spare_bytes.split('00')[0:5])+'0000000000') + #DEBUG print len_var_fields, spare_bytes[len_var_fields:len_var_fields+16],spare_bytes + + retval = { 'TxVersion': int(long_packet[0:4],16), + 'TxType': int(long_packet[4:8],16), + 'TxTypeString': 'Create Variable Issuance (Crowdsale)', + 'Ecosystem': int(long_packet[8:10],16), + 'Property Type': int(long_packet[10:14],16), + 'Previous Property ID': int(long_packet[14:22],16), + 'Property Category': spare_bytes.split('00')[0].decode('hex'), + 'Property Subcategory': spare_bytes.split('00')[1].decode('hex'), + 'Property Name': spare_bytes.split('00')[2].decode('hex'), + 'Property URL': spare_bytes.split('00')[3].decode('hex'), + 'Property Data': ''.join(spare_bytes.split('00')[4]).decode('hex'), + 'PropertyID Desired': str(int(spare_bytes[len_var_fields:len_var_fields+8],16)), + 'Number of Properties': str(int(spare_bytes[len_var_fields+8:len_var_fields+8+16],16)), + 'Deadline': str(int(spare_bytes[len_var_fields+8+16:len_var_fields+8+16+16],16)), + 'Earlybird Bonus': str(int(spare_bytes[len_var_fields+8+16+16:len_var_fields+8+16+16+2],16)), + 'Percentage for Issuer': str(int(spare_bytes[len_var_fields+8+16+16+2:len_var_fields+8+16+16+2+2],16)) + } + + if long_packet[4:8] == '0000': + #simple send + retval = { 'TxVersion': int(long_packet[0:4],16), + 'TxType': int(long_packet[4:8],16), + 'TxTypeString': 'Simple Send', + 'PropertyID': int(long_packet[8:16],16), + 'Amount': int(long_packet[16:32],16) + } + + if long_packet[4:8] == '0003': + #STO + retval = { 'TxVersion': int(long_packet[0:4],16), + 'TxType': int(long_packet[4:8],16), + 'TxTypeString': 'Send To Owners', + 'PropertyID': int(long_packet[8:16],16), + 'Amount': int(long_packet[16:32],16) + } + + if long_packet[4:8] == '0014': + #DEx Sell Offer + retval = { 'TxVersion': int(long_packet[0:4],16), + 'TxType': int(long_packet[4:8],16), + 'TxTypeString': 'DEx Sell Offer', + 'PropertyID': int(long_packet[8:16],16), + 'Amount': int(long_packet[16:32],16), + 'BTCDesired': int(long_packet[32:48],16), + 'TimePeriod': int(long_packet[48:50],16), + 'FeeRequired': int(long_packet[50:66],16), + 'Action': int(long_packet[66:68],16) + } + + if long_packet[4:8] == '0035': + #Close Crowdsale Manually + retval = { 'TxVersion': int(long_packet[0:4],16), + 'TxType': int(long_packet[4:8],16), + 'TxTypeString': 'Close Crowdsale Manually', + 'PropertyID': int(long_packet[8:16],16) + } + + if long_packet[4:8] == '0037': + #grant properties + retval = { 'TxVersion': int(long_packet[0:4],16), + 'TxType': int(long_packet[4:8],16), + 'TxTypeString': 'Grant Properties', + 'PropertyID': int(long_packet[8:16],16), + 'Amount': int(long_packet[16:32],16) + } + + if long_packet[4:8] == '0038': + #revoke properties + retval = { 'TxVersion': int(long_packet[0:4],16), + 'TxType': int(long_packet[4:8],16), + 'TxTypeString': 'Revoke Properties', + 'PropertyID': int(long_packet[8:16],16), + 'Amount': int(long_packet[16:32],16) + } + + if retval == "": + retval = {"Error":"Can\'t decode MP TX"} + dest = "" + print retval + return {'Sender':reference,'Receiver':dest,'MP':retval,'BTC':rawBTC, 'inputs':senders} diff --git a/api/get_balance.py b/api/get_balance.py index 2908c223b..dcdeb1fdf 100644 --- a/api/get_balance.py +++ b/api/get_balance.py @@ -6,7 +6,7 @@ #sys.path.append(lib_path) from msc_apps import * from debug import * - +from balancehelper import * TIMEOUT='timeout -s 9 10 ' # Get the Mastercoin balances. Not that this is also creating the default balance @@ -71,19 +71,22 @@ def get_balance_response(request_dict): addr = re.sub(r'\W+', '', addr) #check alphanumeric - address_data, err = get_msc_balances( addr ) - if err != None: - address_data = {} - address_data[ 'address' ] = addr - address_data[ 'balance' ] = [] + #Use new balance function call + return (json.dumps( get_balancedata(addr) ), None) + + #address_data, err = get_msc_balances( addr ) + #if err != None: + # address_data = {} + # address_data[ 'address' ] = addr + # address_data[ 'balance' ] = [] - bitcoin_balances, err = get_btc_balances( addr ) + #bitcoin_balances, err = get_btc_balances( addr ) - if err == None: - for i in xrange(0,len( bitcoin_balances )): - address_data[ 'balance' ].append( bitcoin_balances[i] ) + #if err == None: + # for i in xrange(0,len( bitcoin_balances )): + # address_data[ 'balance' ].append( bitcoin_balances[i] ) - return (json.dumps( address_data ), None) + #return (json.dumps( address_data ), None) def get_balance_handler(environ, start_response): return general_handler(environ, start_response, get_balance_response) diff --git a/api/pending.py b/api/pending.py new file mode 100644 index 000000000..f1f7a2eb1 --- /dev/null +++ b/api/pending.py @@ -0,0 +1,113 @@ +from decode import * +from sqltools import * +import decimal + +def insertpending(txhex): + + try: + rawtx = decode(txhex) + except Exception,e: + print "Error: ", e, "\n Could not decode PendingTx: ", txhex + return + + if 'BTC' in rawtx: + #handle btc pending amounts + insertbtc(rawtx) + + if 'Amount' in rawtx['MP'] and rawtx['MP']['Amount']>0: + #only run if we have a non zero positive amount to process, otherwise exit + insertmsc(rawtx) + +def insertbtc(rawtx): + try: + inputs=rawtx['inputs'] + propertyid = 0 + txtype = 0 + txversion = rawtx['BTC']['version'] + txhash = rawtx['BTC']['txid'] + protocol = "Bitcoin" + txdbserialnum = dbSelect("select least(-1,min(txdbserialnum)) from transactions;")[0][0] + txdbserialnum -= 1 + addresstxindex = 0 + + dbExecute("insert into transactions (txhash,protocol,txdbserialnum,txtype,txversion) values(%s,%s,%s,%s,%s)", + (txhash,protocol,txdbserialnum,txtype,txversion)) + + addressrole="sender" + for address in inputs: + inputamount= - inputs[address] + #insert the addressesintxs entry for the sender + dbExecute("insert into addressesintxs (address,propertyid,protocol,txdbserialnum,addresstxindex,addressrole,balanceavailablecreditdebit) " + "values(%s,%s,%s,%s,%s,%s,%s)", (address,propertyid,protocol,txdbserialnum,addresstxindex,addressrole,inputamount)) + addresstxindex+=1 + + + addresstxindex = 0 + addressrole="recipient" + for output in rawtx['BTC']['vout']: + outputamount = int(decimal.Decimal(str(output['value']))*decimal.Decimal(1e8)) + for addr in output['scriptPubKey']['addresses']: + address=addr + dbExecute("insert into addressesintxs (address,propertyid,protocol,txdbserialnum,addresstxindex,addressrole,balanceavailablecreditdebit) " + "values(%s,%s,%s,%s,%s,%s,%s)", (address,propertyid,protocol,txdbserialnum,addresstxindex,addressrole,outputamount)) + addresstxindex+=1 + + #store signed tx until it confirms + dbExecute("insert into txjson (txdbserialnum, protocol, txdata) values (%s,%s,%s)", (txdbserialnum, protocol, json.dumps(rawtx['BTC'])) ) + + dbCommit() + except Exception,e: + print "Error: ", e, "\n Could not add BTC PendingTx: ", rawtx + dbRollback() + +def insertmsc(rawtx): + try: + sender = rawtx['Sender'] + receiver = rawtx['Receiver'] + propertyid = rawtx['MP']['PropertyID'] + txtype = rawtx['MP']['TxType'] + txversion = rawtx['MP']['TxVersion'] + txhash = rawtx['BTC']['txid'] + protocol = "Mastercoin" + addresstxindex=0 + txdbserialnum = dbSelect("select least(-1,min(txdbserialnum)) from transactions;")[0][0] + txdbserialnum -= 1 + amount = rawtx['MP']['Amount'] + + if txtype == 55: + #handle grants to ourself or others + if receiver == "": + sendamount=amount + recvamount=0 + else: + sendamount=0 + recvamount=amount + else: + #all other txs deduct from our balance and, where applicable, apply to the reciever + sendamount=-amount + recvamount=amount + + dbExecute("insert into transactions (txhash,protocol,txdbserialnum,txtype,txversion) values(%s,%s,%s,%s,%s)", + (txhash,protocol,txdbserialnum,txtype,txversion)) + + address=sender + addressrole="sender" + #insert the addressesintxs entry for the sender + dbExecute("insert into addressesintxs (address,propertyid,protocol,txdbserialnum,addresstxindex,addressrole,balanceavailablecreditdebit) " + "values(%s,%s,%s,%s,%s,%s,%s)", (address,propertyid,protocol,txdbserialnum,addresstxindex,addressrole,sendamount)) + + #update pending balance + #dbExecute("update addressbalances set balancepending=balancepending+%s::numeric where address=%s and propertyid=%s and protocol=%s", (sendamount,address,propertyid,protocol)) + + if receiver != "": + address=receiver + addressrole="recipient" + dbExecute("insert into addressesintxs (address,propertyid,protocol,txdbserialnum,addresstxindex,addressrole,balanceavailablecreditdebit) " + "values(%s,%s,%s,%s,%s,%s,%s)", (address,propertyid,protocol,txdbserialnum,addresstxindex,addressrole,recvamount)) + #update pending balance + #dbExecute("update addressbalances set balancepending=balancepending+%s::numeric where address=%s and propertyid=%s and protocol=%s", (recvamount,address,propertyid,protocol)) + dbCommit() + except Exception,e: + print "Error: ", e, "\n Could not add MSC PendingTx: ", rawtx + dbRollback() + diff --git a/api/pushtx.py b/api/pushtx.py index 254c055a4..dd50e09e9 100644 --- a/api/pushtx.py +++ b/api/pushtx.py @@ -5,6 +5,7 @@ sys.path.append(lib_path) from msc_utils_parsing import * from msc_apps import * +from pending import * import tempfile sys.path.append(os.path.abspath("../lib")) @@ -41,6 +42,10 @@ def pushtx_response(response_dict): signed_tx=response_dict['signedTransaction'][0] response=pushtxnode(signed_tx) + + if "NOTOK" not in response: + insertpending(signed_tx) + print signed_tx,'\n', response return (response, None) diff --git a/api/transaction_service.py b/api/transaction_service.py index 889117d64..1f3da8350 100644 --- a/api/transaction_service.py +++ b/api/transaction_service.py @@ -15,7 +15,7 @@ def getaddress(): except ValueError: abort(make_response('This endpoint only consumes valid input', 400)) - ROWS=dbSelect("select * from transactions t, addressesintxs atx where t.txdbserialnum = atx.txdbserialnum and atx.address=%s order by t.txblocknumber DESC", [address]) + ROWS=dbSelect("select * from transactions t, addressesintxs atx where t.txdbserialnum = atx.txdbserialnum and atx.address=%s and t.txdbserialnum >0 order by t.txdbserialnum DESC", [address]) response = { 'address': {}, 'balance': {}, '0' : { 'transactions': [] } } #To preserve compatability, 'currID': {'txdata'} if len(ROWS) > 0: @@ -106,7 +106,7 @@ def dehexify(hex_str): "tx_time": str(txJson['blocktime']) + '000', } - if txType != -22: #Dex purchases don't have these fields + if txType != -22 and txType != 21: #Dex purchases don't have these fields ret['currencyId'] = txJson['propertyid'] ret['currency_str'] = 'Mastercoin' if txJson['propertyid'] == 1 else 'Test Mastercoin' if txJson['propertyid'] == 2 else "Smart Property" ret['invalid'] = False if txJson['valid'] == True else True diff --git a/api/websocket.py b/api/websocket.py index b4a9f56e2..7e431b78c 100644 --- a/api/websocket.py +++ b/api/websocket.py @@ -7,6 +7,7 @@ from flask import Flask, render_template, session, request from flask.ext.socketio import SocketIO, emit, join_room, leave_room from msc_apps import * +from balancehelper import * import config app = Flask(__name__) @@ -24,37 +25,6 @@ def printmsg(msg): print msg sys.stdout.flush() -def get_balancedata(address): - addr = re.sub(r'\W+', '', address) #check alphanumeric - ROWS=dbSelect("select * from addressbalances ab, smartproperties sp where ab.address=%s and ab.propertyid=sp.propertyid " - "and sp.protocol='Mastercoin'", [addr]) - - balance_data = { 'balance': [] } - for balrow in ROWS: - cID = str(int(balrow[2])) #currency id - sym_t = ('BTC' if cID == '0' else ('MSC' if cID == '1' else ('TMSC' if cID == '2' else 'SP' + cID) ) ) #symbol template - divi = balrow[-1]['divisible'] if type(balrow[-1]) == type({}) else json.loads(balrow[-1])['divisible'] #Divisibility - res = { 'symbol' : sym_t, 'divisible' : divi, 'id' : cID } - res['value'] = ('%.8f' % float(balrow[4])).rstrip('0').rstrip('.') - #res['reserved_balance'] = ('%.8f' % float(balrow[5])).rstrip('0').rstrip('.') - balance_data['balance'].append(res) - - # if 0 >= len(ROWS): - # return ( None, '{ "status": "NOT FOUND: ' + addr + '" }' ) - - btc_balance = { 'symbol': 'BTC', 'divisible': True, 'id' : 0 } - out, err = run_command(TIMEOUT+ 'sx balance -j ' + addr ) - if err != None or out == '': - btc_balance[ 'value' ] = int(-666) - else: - try: - btc_balance[ 'value' ] = int( json.loads( out )[0][ 'paid' ]) - except ValueError: - btc_balance[ 'value' ] = int(-666) - - balance_data['balance'].append(btc_balance) - return balance_data - def balance_thread(): """Send balance data for the connected clients.""" @@ -139,6 +109,18 @@ def add_address(message): balance_data, namespace='/balance') +@socketio.on("address:refresh", namespace='/balance') +def refresh_address(message): + global addresses + + address = message['data'] + if str(address) in addresses: + balance_data=get_balancedata(address) + emit('address:'+address, + balance_data, + namespace='/balance') + else: + add_address(message) if __name__ == '__main__': socketio.run(app, '127.0.0.1',1091) diff --git a/etc/nginx/sites-available/README b/etc/nginx/sites-available/README new file mode 100644 index 000000000..8afa33186 --- /dev/null +++ b/etc/nginx/sites-available/README @@ -0,0 +1,6 @@ +Copy/install the 'default' file into yoru nginx/sites-available/ folder +Then modify/update it to fit your server setup. +There are 2 items that need to be updated at the bottom. +1. Points to the location of the omniwallet www folder +2. Points to the location of the omni-redirects file in this folder + This is the core of the API and has been abstracted out to allow easier updates in future. diff --git a/etc/nginx/sites-available/default b/etc/nginx/sites-available/default index 58e7007a9..5d6665fc6 100644 --- a/etc/nginx/sites-available/default +++ b/etc/nginx/sites-available/default @@ -30,263 +30,24 @@ server { add_header Pragma "no-cache"; add_header Expires "0"; - ## Set this to reflect the location of the www directory within the omniwallet repo. - root /home/myUser/omniwallet/www/; - index index.htm index.html; - #enable gzip compression gzip on; gzip_min_length 20; gzip_proxied expired no-cache no-store private auth; gzip_types text/plain application/xml application/x-javascript application/javascript text/css text/javascript text/html application/json; - #Re-route nested routed through index - location / { - try_files $uri $uri/ /index.html =404; - } - - location ~ "^/wallet/$" { - return 301 /wallet/overview; - } - - #Limit Perl Access per phil's recommendation - if ($http_user_agent ~ "libwww-perl.*"){ - return 403; - break; - } - # Make site accessible from http://localhost/ server_name localhost; - set $WEBSOCKET_PORT 1091; - location /socket.io { - proxy_pass http://127.0.0.1:1091/socket.io; - proxy_redirect off; - proxy_buffering off; - - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; - } - - location ~ "^/v1/transaction/values.json$" { - alias /var/lib/omniwallet/www/values.json; - } - - location ~ "^/v1/system/stats.json$" { - #alias /var/lib/omniwallet/www/stats.json; - rewrite ^ /v1/system/stats; - - } - - location ~ "^/v1/system/status.html$" { - alias /var/lib/omniwallet/www/status.html; - } - - location ~ "^/v1/system/revision.json$" { - #alias /var/lib/omniwallet/www/revision.json; - rewrite ^ /v1/system/revision; - } - - location ~ "^/v1/exchange/offers/(.*)$" { - alias /var/lib/omniwallet/www/offers/$1; - } - - #location ~ "^/v1/transaction/general/(.*)$" { - # alias /var/lib/omniwallet/www/general/$1; - #} - - #location ~ "^/v1/transaction/tx/(.*)$" { - # alias /var/lib/omniwallet/www/tx/$1; - #} - - #location ~ "^/v1/property/([0-9]*.json)$" { - # add_header Access-Control-Allow-Origin *; - # alias /var/lib/omniwallet/www/properties/properties-$1; - #} - - #location ~ "^/v1/values/(.*)$" { - # add_header Access-Control-Allow-Origin *; - # alias /var/lib/omniwallet/www/values/$1; - #} - - #location ~ "^/v1/values/history/(.*)$" { - # alias /var/lib/omniwallet/www/values/history/$1; - #} - - #location ~ "^/v1/address/verify/(.*)$" { - # alias /var/lib/omniwallet/www/mastercoin_verify/addresses/$1; - #} + ############## + #### Update the two entries below and replace 'myUser' with the user omniwallet is installed under + ############## - #location ~ "^/v1/transaction/verify/(.*)$" { - # alias /var/lib/omniwallet/www/mastercoin_verify/transactions/$1; - #} - - include uwsgi_params; - uwsgi_param SCRIPT_NAME $app; - uwsgi_param UWSGI_MODULE $app; - uwsgi_param UWSGI_CALLABLE "${app}_handler"; - - set $PORT 1088; - location /v1/address/addr/ { - add_header Access-Control-Allow-Origin *; - set $app get_balance; - uwsgi_pass 127.0.0.1:$PORT; - } - - #location /v1/transaction/validateaddr/ { - # set $app validateaddr; - # uwsgi_pass 127.0.0.1:$PORT; - #} - - location /v1/transaction/send/ { - set $app send; - uwsgi_pass 127.0.0.1:$PORT; - } - - location /v1/exchange/sell/ { - set $app sell; - uwsgi_pass 127.0.0.1:$PORT; - } - - location /v1/exchange/accept/ { - set $app accept; - uwsgi_pass 127.0.0.1:$PORT; - } - - location /v1/transaction/pushtx/ { - set $app pushtx; - uwsgi_pass 127.0.0.1:$PORT; - } - - location /v1/exchange/offers { - set $app offers; - uwsgi_pass 127.0.0.1:$PORT; - } - - location = /v1/user/wallet { rewrite ^ /v1/user/wallet/; } - location /v1/user/wallet { try_files $uri @flask_user_service; } - location @flask_user_service { - include uwsgi_params; - uwsgi_param SCRIPT_NAME /v1/user/wallet; - uwsgi_param UWSGI_MODULE user_service; - uwsgi_param UWSGI_CALLABLE app; - uwsgi_modifier1 30; - uwsgi_pass 127.0.0.1:$PORT; - } - - location = /v1/properties { rewrite ^ /v1/properties/; } - location /v1/properties { try_files $uri @flask_properties_service; } - location @flask_properties_service { - include uwsgi_params; - uwsgi_param SCRIPT_NAME /v1/properties; - uwsgi_param UWSGI_MODULE properties_service; - uwsgi_param UWSGI_CALLABLE app; - uwsgi_modifier1 30; - uwsgi_pass 127.0.0.1:$PORT; - } - - location = /v1/property { rewrite ^ /v1/property/; } - location /v1/property { try_files $uri @flask_property_service; } - location @flask_property_service { - include uwsgi_params; - uwsgi_param SCRIPT_NAME /v1/property; - uwsgi_param UWSGI_MODULE property_service; - uwsgi_param UWSGI_CALLABLE app; - uwsgi_modifier1 30; - uwsgi_pass 127.0.0.1:$PORT; - } - - location = /v1/transaction { rewrite ^ /v1/transaction; } - location /v1/transaction { try_files $uri @flask_transaction_service; } - location @flask_transaction_service { - include uwsgi_params; - uwsgi_param SCRIPT_NAME /v1/transaction; - uwsgi_param UWSGI_MODULE transaction_service; - uwsgi_param UWSGI_CALLABLE app; - uwsgi_modifier1 30; - uwsgi_pass 127.0.0.1:$PORT; - } - - location = /v1/mastercoin_verify { rewrite ^ /v1/mastercoin_verify/; } - location /v1/mastercoin_verify { try_files $uri @flask_mastercoin_verify; } - location @flask_mastercoin_verify { - include uwsgi_params; - uwsgi_param SCRIPT_NAME /v1/mastercoin_verify; - uwsgi_param UWSGI_MODULE mastercoin_verify; - uwsgi_param UWSGI_CALLABLE app; - uwsgi_modifier1 30; - uwsgi_pass 127.0.0.1:$PORT; - } - - location = /v1/transaction/getunsigned { rewrite ^ /v1/transaction/getunsigned; } - location /v1/transaction/getunsigned { try_files $uri @flask_tx_generate_service; } - location @flask_tx_generate_service { - include uwsgi_params; - uwsgi_param SCRIPT_NAME /v1/transaction/getunsigned; - uwsgi_param UWSGI_MODULE tx_generate_service; - uwsgi_param UWSGI_CALLABLE app; - uwsgi_modifier1 30; - uwsgi_pass 127.0.0.1:$PORT; - } - - location = /v1/armory { rewrite ^ /v1/armory; } - location /v1/armory { try_files $uri @flask_armory_service; } - location @flask_armory_service { - include uwsgi_params; - uwsgi_param SCRIPT_NAME /v1/armory; - uwsgi_param UWSGI_MODULE armory_service; - uwsgi_param UWSGI_CALLABLE app; - uwsgi_modifier1 30; - uwsgi_pass 127.0.0.1:$PORT; - } - - - location = /v1/search { rewrite ^ /v1/search; } - location /v1/search { try_files $uri @flask_search_service; } - location @flask_search_service { - include uwsgi_params; - uwsgi_param SCRIPT_NAME /v1/search; - uwsgi_param UWSGI_MODULE search_service; - uwsgi_param UWSGI_CALLABLE app; - uwsgi_modifier1 30; - uwsgi_pass 127.0.0.1:$PORT; - } - - location = /v1/blocks { rewrite ^ /v1/blocks/; } - location /v1/blocks { try_files $uri @flask_getblocks; } - location @flask_getblocks { - include uwsgi_params; - uwsgi_param SCRIPT_NAME /v1/blocks; - uwsgi_param UWSGI_MODULE getblocks; - uwsgi_param UWSGI_CALLABLE app; - uwsgi_modifier1 30; - uwsgi_pass 127.0.0.1:$PORT; - } + ## Set this to reflect the location of the www directory within the omniwallet repo. + root /home/myUser/omniwallet/www/; + index index.htm index.html; - location = /v1/system { rewrite ^ /v1/system; } - location /v1/system { try_files $uri @flask_stats_service; } - location @flask_stats_service { - include uwsgi_params; - uwsgi_param SCRIPT_NAME /v1/system; - uwsgi_param UWSGI_MODULE stats_service; - uwsgi_param UWSGI_CALLABLE app; - uwsgi_modifier1 30; - uwsgi_pass 127.0.0.1:$PORT; - } + ## Set this to reflect the location of the omni-redirects file within the omniwallet repo + include /home/myUser/omniwallet/etc/nginx/sites-available/omni-redirects; - location = /v1/values { rewrite ^ /v1/values; } - location /v1/values { try_files $uri @flask_values_service; } - location @flask_values_service { - include uwsgi_params; - uwsgi_param SCRIPT_NAME /v1/values; - uwsgi_param UWSGI_MODULE values_service; - uwsgi_param UWSGI_CALLABLE app; - uwsgi_modifier1 30; - uwsgi_pass 127.0.0.1:$PORT; - } } diff --git a/etc/nginx/sites-available/omni-redirects b/etc/nginx/sites-available/omni-redirects new file mode 100644 index 000000000..2c518f668 --- /dev/null +++ b/etc/nginx/sites-available/omni-redirects @@ -0,0 +1,222 @@ + #Re-route nested routed through index + location / { + try_files $uri $uri/ /index.html =404; + } + + location ~ "^/wallet/$" { + return 301 /wallet/overview; + } + + #Limit Perl Access per phil's recommendation + if ($http_user_agent ~ "libwww-perl.*"){ + return 403; + break; + } + + set $WEBSOCKET_PORT 1091; + location /socket.io { + proxy_pass http://127.0.0.1:1091/socket.io; + proxy_redirect off; + proxy_buffering off; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + } + + location ~ "^/v1/transaction/values.json$" { + alias /var/lib/omniwallet/www/values.json; + } + + location ~ "^/v1/system/stats.json$" { + #alias /var/lib/omniwallet/www/stats.json; + rewrite ^ /v1/system/stats; + + } + + location ~ "^/v1/system/status.html$" { + alias /var/lib/omniwallet/www/status.html; + } + + location ~ "^/v1/system/revision.json$" { + #alias /var/lib/omniwallet/www/revision.json; + rewrite ^ /v1/system/revision; + } + + location ~ "^/v1/exchange/offers/(.*)$" { + alias /var/lib/omniwallet/www/offers/$1; + } + + include uwsgi_params; + uwsgi_param SCRIPT_NAME $app; + uwsgi_param UWSGI_MODULE $app; + uwsgi_param UWSGI_CALLABLE "${app}_handler"; + + set $PORT 1088; + location /v1/address/addr/ { + add_header Access-Control-Allow-Origin *; + set $app get_balance; + uwsgi_pass 127.0.0.1:$PORT; + } + + location /v1/transaction/send/ { + set $app send; + uwsgi_pass 127.0.0.1:$PORT; + } + + location /v1/exchange/sell/ { + set $app sell; + uwsgi_pass 127.0.0.1:$PORT; + } + + location /v1/exchange/accept/ { + set $app accept; + uwsgi_pass 127.0.0.1:$PORT; + } + + location /v1/transaction/pushtx/ { + set $app pushtx; + uwsgi_pass 127.0.0.1:$PORT; + } + + location /v1/exchange/offers { + set $app offers; + uwsgi_pass 127.0.0.1:$PORT; + } + + location = /v1/user/wallet { rewrite ^ /v1/user/wallet/; } + location /v1/user/wallet { try_files $uri @flask_user_service; } + location @flask_user_service { + include uwsgi_params; + uwsgi_param SCRIPT_NAME /v1/user/wallet; + uwsgi_param UWSGI_MODULE user_service; + uwsgi_param UWSGI_CALLABLE app; + uwsgi_modifier1 30; + uwsgi_pass 127.0.0.1:$PORT; + } + + location = /v1/properties { rewrite ^ /v1/properties/; } + location /v1/properties { try_files $uri @flask_properties_service; } + location @flask_properties_service { + include uwsgi_params; + uwsgi_param SCRIPT_NAME /v1/properties; + uwsgi_param UWSGI_MODULE properties_service; + uwsgi_param UWSGI_CALLABLE app; + uwsgi_modifier1 30; + uwsgi_pass 127.0.0.1:$PORT; + } + + location = /v1/property { rewrite ^ /v1/property/; } + location /v1/property { try_files $uri @flask_property_service; } + location @flask_property_service { + include uwsgi_params; + uwsgi_param SCRIPT_NAME /v1/property; + uwsgi_param UWSGI_MODULE property_service; + uwsgi_param UWSGI_CALLABLE app; + uwsgi_modifier1 30; + uwsgi_pass 127.0.0.1:$PORT; + } + + location = /v1/transaction { rewrite ^ /v1/transaction; } + location /v1/transaction { try_files $uri @flask_transaction_service; } + location @flask_transaction_service { + include uwsgi_params; + uwsgi_param SCRIPT_NAME /v1/transaction; + uwsgi_param UWSGI_MODULE transaction_service; + uwsgi_param UWSGI_CALLABLE app; + uwsgi_modifier1 30; + uwsgi_pass 127.0.0.1:$PORT; + } + + location = /v1/mastercoin_verify { rewrite ^ /v1/mastercoin_verify/; } + location /v1/mastercoin_verify { try_files $uri @flask_mastercoin_verify; } + location @flask_mastercoin_verify { + include uwsgi_params; + uwsgi_param SCRIPT_NAME /v1/mastercoin_verify; + uwsgi_param UWSGI_MODULE mastercoin_verify; + uwsgi_param UWSGI_CALLABLE app; + uwsgi_modifier1 30; + uwsgi_pass 127.0.0.1:$PORT; + } + + location = /v1/transaction/getunsigned { rewrite ^ /v1/transaction/getunsigned; } + location /v1/transaction/getunsigned { try_files $uri @flask_tx_generate_service; } + location @flask_tx_generate_service { + include uwsgi_params; + uwsgi_param SCRIPT_NAME /v1/transaction/getunsigned; + uwsgi_param UWSGI_MODULE tx_generate_service; + uwsgi_param UWSGI_CALLABLE app; + uwsgi_modifier1 30; + uwsgi_pass 127.0.0.1:$PORT; + } + + location = /v1/armory { rewrite ^ /v1/armory; } + location /v1/armory { try_files $uri @flask_armory_service; } + location @flask_armory_service { + include uwsgi_params; + uwsgi_param SCRIPT_NAME /v1/armory; + uwsgi_param UWSGI_MODULE armory_service; + uwsgi_param UWSGI_CALLABLE app; + uwsgi_modifier1 30; + uwsgi_pass 127.0.0.1:$PORT; + } + + + location = /v1/search { rewrite ^ /v1/search; } + location /v1/search { try_files $uri @flask_search_service; } + location @flask_search_service { + include uwsgi_params; + uwsgi_param SCRIPT_NAME /v1/search; + uwsgi_param UWSGI_MODULE search_service; + uwsgi_param UWSGI_CALLABLE app; + uwsgi_modifier1 30; + uwsgi_pass 127.0.0.1:$PORT; + } + + location = /v1/blocks { rewrite ^ /v1/blocks/; } + location /v1/blocks { try_files $uri @flask_getblocks; } + location @flask_getblocks { + include uwsgi_params; + uwsgi_param SCRIPT_NAME /v1/blocks; + uwsgi_param UWSGI_MODULE getblocks; + uwsgi_param UWSGI_CALLABLE app; + uwsgi_modifier1 30; + uwsgi_pass 127.0.0.1:$PORT; + } + + location = /v1/system { rewrite ^ /v1/system; } + location /v1/system { try_files $uri @flask_stats_service; } + location @flask_stats_service { + include uwsgi_params; + uwsgi_param SCRIPT_NAME /v1/system; + uwsgi_param UWSGI_MODULE stats_service; + uwsgi_param UWSGI_CALLABLE app; + uwsgi_modifier1 30; + uwsgi_pass 127.0.0.1:$PORT; + } + + location = /v1/values { rewrite ^ /v1/values; } + location /v1/values { try_files $uri @flask_values_service; } + location @flask_values_service { + include uwsgi_params; + uwsgi_param SCRIPT_NAME /v1/values; + uwsgi_param UWSGI_MODULE values_service; + uwsgi_param UWSGI_CALLABLE app; + uwsgi_modifier1 30; + uwsgi_pass 127.0.0.1:$PORT; + } + + location = /v1/decode { rewrite ^ /v1/decode; } + location /v1/decode { try_files $uri @flask_decode; } + location @flask_decode { + include uwsgi_params; + uwsgi_param SCRIPT_NAME /v1/decode; + uwsgi_param UWSGI_MODULE decode; + uwsgi_param UWSGI_CALLABLE app; + uwsgi_modifier1 30; + uwsgi_pass 127.0.0.1:$PORT; + } diff --git a/www/homepage.html b/www/homepage.html index 4dabc6857..d9a5f576e 100644 --- a/www/homepage.html +++ b/www/homepage.html @@ -27,6 +27,10 @@

{{ 'HOMEPAGE_BALANCECHECK' | translate }}

{{currency.symbol}} + + + +
(+ Unconfirmed)
diff --git a/www/js/AssetTypesController.js b/www/js/AssetTypesController.js index c078df9ac..1be4554e5 100644 --- a/www/js/AssetTypesController.js +++ b/www/js/AssetTypesController.js @@ -30,8 +30,15 @@ angular.module('omniwallet') Wallet.addresses.forEach(function(addr) { addr.balance.forEach(function(currencyItem) { if ((parseInt(currencyItem.id,10) < 2147483648) && (parseInt(currencyItem.id,10) != 2) || showtesteco === 'true'){ - if(currencyItem.divisible) + if(currencyItem.divisible) { var value=new Big(currencyItem.value).times(WHOLE_UNIT).valueOf(); + var pendingpos=new Big(currencyItem.pendingpos).times(WHOLE_UNIT).valueOf(); + var pendingneg=new Big(currencyItem.pendingneg).times(WHOLE_UNIT).valueOf(); + } else { + var pendingpos=currencyItem.pendingpos; + var pendingneg=currencyItem.pendingneg; + } + if (!balances.hasOwnProperty(currencyItem.symbol)) { var asset = Wallet balances[currencyItem.symbol] = { @@ -39,10 +46,14 @@ angular.module('omniwallet') "id" : currencyItem.id, "balance": +value || +currencyItem.value, "value": appraiser.getValue(currencyItem.value, currencyItem.symbol, currencyItem.divisible), + "pendingpos": +pendingpos, + "pendingneg": +pendingneg }; } else { balances[currencyItem.symbol].balance += +value || +currencyItem.value; balances[currencyItem.symbol].value += appraiser.getValue(currencyItem.value, currencyItem.symbol, currencyItem.divisible); + balances[currencyItem.symbol].pendingpos += +pendingpos; + balances[currencyItem.symbol].pendingneg += +pendingneg; } //console.log(balances); if (currencyItem.symbol == 'BTC') { @@ -168,15 +179,22 @@ angular.module('omniwallet') for (var i in resultBalances) { var value = null; var item = resultBalances[i]; - if(item.divisible) + if(item.divisible) { value=new Big(item.value).times(WHOLE_UNIT).valueOf(); + var pendingpos=new Big(item.pendingpos).times(WHOLE_UNIT).valueOf(); + var pendingneg=new Big(item.pendingneg).times(WHOLE_UNIT).valueOf(); + } else { + var pendingpos=item.pendingpos; + var pendingneg=item.pendingneg; + } if (item.symbol == currencySymbol) { balances.push({ "address": addr, "balance": +value || item.value, - "value": appraiser.getValue(item.value, currencySymbol, item.divisible) + "value": appraiser.getValue(item.value, currencySymbol, item.divisible), + "pendingpos": +pendingpos, + "pendingneg": +pendingneg }); - } } }) diff --git a/www/js/TransactionGenerationController.js b/www/js/TransactionGenerationController.js index e8ae7e40e..b459ef38b 100644 --- a/www/js/TransactionGenerationController.js +++ b/www/js/TransactionGenerationController.js @@ -1,4 +1,4 @@ -function TransactionGenerationController($scope, $modal, Wallet, walletTransactionService){ +function TransactionGenerationController($scope, $modal, Wallet, walletTransactionService, BalanceSocket){ $scope.prepareTransaction = function(txType, rawdata, from, $modalScope){ var addressData = Wallet.getAddress(from); @@ -75,10 +75,11 @@ function TransactionGenerationController($scope, $modal, Wallet, walletTransacti if(index +1 == transactions.length){ $modalScope.waiting = false; $modalScope.transactionSuccess = true; - if(TESTNET) - $modalScope.url = 'http://tbtc.blockr.io/tx/info/' + successData.tx; - else - $modalScope.url = 'http://blockchain.info/address/' + from + '?sort=0'; + BalanceSocket.emit("address:refresh", {data:from}); + if(TESTNET) + $modalScope.url = 'http://tbtc.blockr.io/tx/info/' + successData.tx; + else + $modalScope.url = 'http://blockchain.info/address/' + from + '?sort=0'; } else { pushOrderedTransactions(transactions,index+1); } @@ -159,7 +160,7 @@ function TransactionGenerationController($scope, $modal, Wallet, walletTransacti } }; - var modalBaseController = $scope.modalBaseController = function($scope, $modalInstance, data, prepareTransaction, setModalScope, walletAssets) { + var modalBaseController = $scope.modalBaseController = function($scope, $modalInstance, data, prepareTransaction, setModalScope, walletAssets, BalanceSocket) { setModalScope($scope); $scope.signOffline= walletAssets.offline; @@ -223,10 +224,11 @@ function TransactionGenerationController($scope, $modal, Wallet, walletTransacti if (successData.pushed.match(/submitted|success/gi) != null) { $modalScope.waiting = false; $modalScope.transactionSuccess = true; - if(TESTNET) - $modalScope.url = 'http://tbtc.blockr.io/tx/info/' + successData.tx; - else - $modalScope.url = 'http://blockchain.info/address/' + from + '?sort=0'; + BalanceSocket.emit("address:refresh", {data:from}); + if(TESTNET) + $modalScope.url = 'http://tbtc.blockr.io/tx/info/' + successData.tx; + else + $modalScope.url = 'http://blockchain.info/address/' + from + '?sort=0'; } else { $modalScope.waiting = false; $modalScope.transactionError = true; diff --git a/www/js/WalletAddressesController.js b/www/js/WalletAddressesController.js index bea79be37..584271e5b 100644 --- a/www/js/WalletAddressesController.js +++ b/www/js/WalletAddressesController.js @@ -35,18 +35,29 @@ angular.module('omniwallet') } else { addr.balance.forEach(function(currencyItem) { if ((parseInt(currencyItem.id,10) < 2147483648) && (parseInt(currencyItem.id,10) != 2) || showtesteco === 'true'){ - if(currencyItem.divisible) + if(currencyItem.divisible){ var value=new Big(currencyItem.value).times(WHOLE_UNIT).valueOf(); + var pendingpos=new Big(currencyItem.pendingpos).times(WHOLE_UNIT).valueOf(); + var pendingneg=new Big(currencyItem.pendingneg).times(WHOLE_UNIT).valueOf(); + } else { + var pendingpos=currencyItem.pendingpos; + var pendingneg=currencyItem.pendingneg; + } + if (!balances.hasOwnProperty(currencyItem.symbol)) { balances[currencyItem.symbol] = { "symbol": currencyItem.symbol, "balance": +value || +currencyItem.value, "value": appraiser.getValue(currencyItem.value, currencyItem.symbol, currencyItem.divisible), + "pendingpos": +pendingpos, + "pendingneg": +pendingneg, "addresses": {} }; } else { balances[currencyItem.symbol].balance += +value || +currencyItem.value; balances[currencyItem.symbol].value += appraiser.getValue(currencyItem.value, currencyItem.symbol, currencyItem.divisible); + balances[currencyItem.symbol].pendingpos += +pendingpos; + balances[currencyItem.symbol].pendingneg += +pendingneg; } if (currencyItem.symbol == 'BTC') { @@ -66,6 +77,8 @@ angular.module('omniwallet') "address": addr.address, "qr": "https://chart.googleapis.com/chart?chs=150x150&cht=qr&chl="+addr.address+"&choe=UTF-8", "balance": +value || currencyItem.value, + "pendingpos": pendingpos, + "pendingneg": pendingneg, "value": appraiser.getValue(currencyItem.value, currencyItem.symbol, currencyItem.divisible), "private": hasPrivate, "offline": isOffline @@ -155,6 +168,10 @@ angular.module('omniwallet') $rootScope.$on('APPRAISER_VALUE_CHANGED', function() { $scope.refresh(); }); + $rootScope.$on('BALANCE_CHANGED', function() { + if(!$scope.isLoading) + $scope.refresh(); + }); $scope.openDeleteConfirmForm = function(addritem) { if (!$scope.modalOpened) { @@ -192,10 +209,11 @@ angular.module('omniwallet') if (successData.pushed.match(/submitted|success/gi) != null) { $modalScope.waiting = false; $modalScope.transactionSuccess = true; - if(TESTNET) - $modalScope.url = 'http://tbtc.blockr.io/tx/info/' + successData.tx; - else - $modalScope.url = 'http://blockchain.info/address/' + from + '?sort=0'; + $scope.refresh(); + if(TESTNET) + $modalScope.url = 'http://tbtc.blockr.io/tx/info/' + successData.tx; + else + $modalScope.url = 'http://blockchain.info/address/' + from + '?sort=0'; } else { $modalScope.waiting = false; $modalScope.transactionError = true; diff --git a/www/js/controller.js b/www/js/controller.js index 794425f41..2f4076dd4 100644 --- a/www/js/controller.js +++ b/www/js/controller.js @@ -25,14 +25,21 @@ function HomeCtrl($scope, $templateCache, $injector, $location, $http, $q, Accou var appraiser = $injector.get('appraiser'); $injector.get('balanceService').balance($scope.balanceAddress).then(function(result) { result.data.balance.forEach(function(currencyItem, index, collection) { - if(currencyItem.divisible) + if(currencyItem.divisible) { var value=new Big(currencyItem.value).times(WHOLE_UNIT).valueOf(); - + var pendingpos= new Big(currencyItem.pendingpos).times(WHOLE_UNIT).valueOf(); + var pendingneg= new Big(currencyItem.pendingneg).times(WHOLE_UNIT).valueOf(); + } else { + var pendingpos=currencyItem.pendingpos; + var pendingneg=currencyItem.pendingneg; + } appraiser.updateValue(function() { balances[currencyItem.symbol] = { "symbol": currencyItem.symbol, "balance": +value || currencyItem.value, "value": appraiser.getValue(currencyItem.value, currencyItem.symbol, currencyItem.divisible), + "pendingpos": +pendingpos, + "pendingneg": +pendingneg }; if (currencyItem.symbol == 'BTC') { balances[currencyItem.symbol].name = "Bitcoin"; diff --git a/www/partials/about_faq.html b/www/partials/about_faq.html index d77579a48..b37e98339 100644 --- a/www/partials/about_faq.html +++ b/www/partials/about_faq.html @@ -53,7 +53,15 @@ so a typical MasterProtocol Transaction ends up costing about .00035 BTC (35000 satoshis) to send. Since some of these outputs are part of a multisig output (your sending address is always one of the outputs) part of this cost is recoverable. We are currently working to make redeeming these costs easier for you. -
  • How long does it take for a MasterProtocol Transaction to show up in Omniwallet? When you send a MasterProtocol transaction, it has be processed on the Bitcoin blockchain and confirmed as a valid transaction. It usually takes about 10 minutes for a transaction to be confirmed once. Omniwallet displays a transaction when it has been confirmed 3 times (for increased confidence that the transaction is valid), so you can expect to see the transaction after about a half an hour, but that time may vary.
  • +
  • How long does it take for a MasterProtocol Transaction to show up in Omniwallet? When you send a MasterProtocol transaction, it has to be processed on the Bitcoin blockchain and confirmed + as a valid transaction. It usually takes about 10 minutes for a transaction to be confirmed once. Omniwallet displays a transaction when it has been confirmed + so you can expect to see the transaction in about 5-10 minutes, but that time may vary. The exception to this is if the transaction was sent from Omni. If it was sent from Omni see the Unconfirmed/Pending Transaction Support section below.
  • +
  • What is Unconfirmed or Pending Transaction (Tx) Support? When you broadcast any type of Transaction (Tx) it is received by the Bitcoin network as unconfirmed or pending. + Unconfirmed or pending tx are not usable by the reciever until it has recieved at least 1 confirmations (this number varies by software). Typically + a wallet or another user will have no knowledge about this tx until it recieves at least one confirmation on the network. Every tx that Omni sends is recorded in our DB so we can display + realtime updates about your balances. If you send a tx from Omni you will notice your balance decreases in Omni immediately and a new icon appears next to the address balance. + This icon indicates you have a pending tx that has affected your overall usable balance. On the corresponding side, when an Omni user sends another Omni user something, the reciever will see + a new icon and the unconfirmed amount under their address balance. Once confirmed this will be added to their usable balance and the unconfirmed line will disappear.
  • How do I Receive Coins? After you sign in to your wallet you may notice only a Bitcoin address displayed. All MasterProtocol Properties (coins) work with any Bitcoin address where you control the private key. So to receive any MasterProtocol Property simply provide a Bitcoin address from your Omniwallet to the remote sender. Once their send transaction has confirmed on the blockchain your updated balance will display within your wallet.
  • How do I Send Coins? After you sign in to your wallet, click the 'Send' link in the left sidebar to send coins from an address that has a private key in your wallet (see Private Keys above).
  • (DEx) How do I Buy Coins? Buying coins on the Distributed Exchange (DEx) is a 2 part process. Part 1: On the Trade page find a Sale offer diff --git a/www/partials/asset_types.html b/www/partials/asset_types.html index afe2e1f26..8e10f9787 100644 --- a/www/partials/asset_types.html +++ b/www/partials/asset_types.html @@ -22,7 +22,11 @@
    - + + + + +
    (+ )
    diff --git a/www/partials/currency_detail_modal.html b/www/partials/currency_detail_modal.html index 8437588b4..dd42cbd7b 100644 --- a/www/partials/currency_detail_modal.html +++ b/www/partials/currency_detail_modal.html @@ -14,9 +14,13 @@

    Asset Detail: {{currencyName}} ({{currencySymbol}})

    {{item.address.address}} - - - + + + + +
    (+ )
    + + diff --git a/www/partials/wallet_address_list.html b/www/partials/wallet_address_list.html index 249c2f812..1acfc9aee 100644 --- a/www/partials/wallet_address_list.html +++ b/www/partials/wallet_address_list.html @@ -20,10 +20,14 @@

    Invalid Addresses in Your Wallet

    {{currency.name ? currency.name : currency.symbol}}
    -
    +
    {{currency.symbol}} + + + +
    (+ Unconfirmed)
    @@ -40,12 +44,37 @@

    Invalid Addresses in Your Wallet

    - {{item.address}} - {{item.address}} - {{item.address}} - - - {{currency.symbol}} + + + {{item.address}} + + + + {{item.address}} + + + + {{item.address}} + + + + + +
    (+ Unconfirmed)
    + + + + + +
    (+ Unconfirmed)
    + + + {{currency.symbol}} + + +
    (+ Unconfirmed)
    + {{currency.symbol}} +