diff --git a/rein/cli.py b/rein/cli.py index b40fc5a..daa0dd3 100644 --- a/rein/cli.py +++ b/rein/cli.py @@ -27,7 +27,7 @@ from .lib.io import safe_get from .lib.script import build_2_of_3, build_mandatory_multisig, check_redeem_scripts from .lib.localization import init_localization -from .lib.transaction import partial_spend_p2sh, spend_p2sh, spend_p2sh_mediator, partial_spend_p2sh_mediator, partial_spend_p2sh_mediator_2 +from .lib.transaction import partial_spend_p2sh, spend_p2sh, spend_p2sh_mediator, partial_spend_p2sh_mediator, partial_spend_p2sh_mediator_2, unspent_txins, withdraw_from_wallet from .lib.rating import add_rating, get_user_jobs, get_average_user_rating, get_average_user_rating_display, get_all_user_ratings, calculate_trust_score # Import config @@ -38,6 +38,8 @@ # Import models from .lib.persistconfig import PersistConfig +from .lib.wallet import Wallet +from .lib.pubkeys import Pubkeys from .lib.user import User from .lib.bucket import Bucket from .lib.document import Document @@ -188,30 +190,32 @@ def post(multi, identity, defaults, dry_run): log.info('got user and key for post') job_guid = ''.join(random.SystemRandom().choice(string.ascii_lowercase + string.digits) for _ in range(20)) fields = [ - {'label': 'Job name', 'not_null': form, - 'help': 'Choose a brief but descriptive name for the job.'}, - {'label': 'Job ID', 'value': job_guid}, - {'label': 'Tags', 'validator': is_tags, 'not_null': form, - 'help': 'Each post can have a set of tags associated with it. Though not implemented yet,\n' - 'these tags may be used in searches and filters later. No spaces, dashes, or\n' - 'special characters are allowed. Please enter them as a comma-separated list.\n' - 'Example: software, 3dprinting'}, - {'label': 'Description', 'not_null': form}, - {'label': 'Block hash', 'value': block_hash}, - {'label': 'Time', 'value': str(block_time)}, - {'label': 'Expiration (days)', 'validator': is_int}, - {'label': 'Mediator', 'value': mediator['User']}, - {'label': 'Mediator contact', 'value': mediator['Contact']}, - {'label': 'Mediator fee', 'value': mediator['Mediator fee']}, - {'label': 'Mediator public key', 'value': mediator['Mediator public key']}, - {'label': 'Mediator master address', 'value': mediator['Master signing address']}, - {'label': 'Job creator', 'value': user.name}, - {'label': 'Job creator contact', 'value': user.contact}, - {'label': 'Job creator public key', 'value': key}, - {'label': 'Job creator master address', 'value': user.maddr}, - {'label': 'Job creator delegate address', 'value': user.daddr}, - ] + {'label': 'Job name', 'not_null': form, + 'help': 'Choose a brief but descriptive name for the job.'}, + {'label': 'Job ID', 'value': job_guid}, + {'label': 'Tags', 'validator': is_tags, 'not_null': form, + 'help': 'Each post can have a set of tags associated with it. Though not implemented yet,\n' + 'these tags may be used in searches and filters later. No spaces, dashes, or\n' + 'special characters are allowed. Please enter them as a comma-separated list.\n' + 'Example: software, 3dprinting'}, + {'label': 'Description', 'not_null': form}, + {'label': 'Block hash', 'value': block_hash}, + {'label': 'Time', 'value': str(block_time)}, + {'label': 'Expiration (days)', 'validator': is_int}, + {'label': 'Mediator', 'value': mediator['User']}, + {'label': 'Mediator contact', 'value': mediator['Contact']}, + {'label': 'Mediator fee', 'value': mediator['Mediator fee']}, + {'label': 'Mediator public key', 'value': mediator['Mediator public key']}, + {'label': 'Mediator master address', 'value': mediator['Master signing address']}, + {'label': 'Mediator delegate address', 'value': mediator['Delegate signing address']}, + {'label': 'Job creator', 'value': user.name}, + {'label': 'Job creator contact', 'value': user.contact}, + {'label': 'Job creator public key', 'value': key}, + {'label': 'Job creator master address', 'value': user.maddr}, + {'label': 'Job creator delegate address', 'value': user.daddr} + ] document_text = assemble_document('Job', fields) + print("document_text = "+document_text) if not rein.testnet: m = re.search('test', document_text, re.IGNORECASE) if m: @@ -286,11 +290,17 @@ def bid(multi, identity, defaults, dry_run): return log.info('got job for bid') - primary_redeem_script, primary_addr = build_2_of_3([job['Job creator public key'], - job['Mediator public key'], - key]) - mediator_redeem_script, mediator_escrow_addr = build_mandatory_multisig(job['Mediator public key'], - [job['Job creator public key'],key]) + next_addr_index_str = PersistConfig.get(rein, 'next_addr_index') + if not next_addr_index_str: + next_addr_index_str = '0' + next_addr_index = int(next_addr_index_str) + (pubkey_for_escrow,privkey_for_escrow) = bip32.generate_new_escrow_pubkey(bip32.from_xprv(user.dxprv),next_addr_index) + (primary_payment_address,primary_payment_privkey) = bip32.generate_new_payment_address(bip32.from_xprv(user.dxprv),next_addr_index) + next_addr_index += 1 + PersistConfig.set(rein,'next_addr_index',str(next_addr_index)) + Pubkeys.set(rein,pubkey_for_escrow,privkey_for_escrow,userid=rein.user.id) + Wallet.set(rein,primary_payment_address,primary_payment_privkey,ref=job['Job ID'],userid=rein.user.id) + fields = [ {'label': 'Job name', 'value_from': job}, {'label': 'Worker', 'value': user.name}, @@ -299,15 +309,13 @@ def bid(multi, identity, defaults, dry_run): {'label': 'Worker delegate address', 'value': user.daddr}, {'label': 'Description', 'not_null': form}, {'label': 'Bid amount (BTC)', 'not_null': form}, - {'label': 'Primary escrow address', 'value': primary_addr}, - {'label': 'Mediator escrow address', 'value': mediator_escrow_addr}, {'label': 'Job ID', 'value_from': job}, {'label': 'Job creator', 'value_from': job}, {'label': 'Job creator public key', 'value_from': job}, {'label': 'Mediator public key', 'value_from': job}, {'label': 'Worker public key', 'value': key}, - {'label': 'Primary escrow redeem script', 'value': primary_redeem_script}, - {'label': 'Mediator escrow redeem script', 'value': mediator_redeem_script}, + {'label': 'Primary payment address', 'value': primary_payment_address}, + {'label': 'Worker public key for escrow', 'value': pubkey_for_escrow} ] document = assemble_document('Bid', fields) res = sign_and_store_document(rein, 'bid', document, user.daddr, user.dkey, store) @@ -369,20 +377,38 @@ def offer(multi, identity, defaults, dry_run): return log.info('got bid to offer') + next_addr_index_str = PersistConfig.get(rein, 'next_addr_index') + if not next_addr_index_str: + next_addr_index_str = '0' + next_addr_index = int(next_addr_index_str) + (pubkey_for_escrow,privkey_for_escrow) = bip32.generate_new_escrow_pubkey(bip32.from_xprv(user.dxprv),next_addr_index) + (payment_address,payment_privkey) = bip32.generate_new_payment_address(bip32.from_xprv(user.dxprv),next_addr_index) + next_addr_index += 1 + PersistConfig.set(rein,'next_addr_index',str(next_addr_index)) + Pubkeys.set(rein,pubkey_for_escrow,privkey_for_escrow,userid=rein.user.id) + Wallet.set(rein,payment_address,payment_privkey,ref=bid['Job ID'],userid=rein.user.id) + worker_pubkey_for_escrow = bid['Worker public key for escrow'] + primary_redeem_script, primary_addr = build_2_of_3([pubkey_for_escrow,bid['Mediator public key'],worker_pubkey_for_escrow]) + mediator_redeem_script, mediator_escrow_addr = build_mandatory_multisig(bid['Mediator public key'],[pubkey_for_escrow,worker_pubkey_for_escrow]) fields = [ {'label': 'Job name', 'value_from': bid}, {'label': 'Worker', 'value_from': bid}, {'label': 'Description', 'value_from': bid}, {'label': 'Bid amount (BTC)', 'value_from': bid}, - {'label': 'Primary escrow address', 'value_from': bid}, - {'label': 'Mediator escrow address', 'value_from': bid}, + {'label': 'Primary escrow address', 'value': primary_addr}, + {'label': 'Mediator escrow address', 'value': mediator_escrow_addr}, {'label': 'Job ID', 'value_from': bid}, {'label': 'Job creator', 'value_from': bid}, {'label': 'Job creator public key', 'value_from': bid}, {'label': 'Mediator public key', 'value_from': bid}, {'label': 'Worker public key', 'value_from': bid}, - {'label': 'Primary escrow redeem script', 'value_from': bid}, - {'label': 'Mediator escrow redeem script', 'value_from': bid}, + {'label': 'Primary escrow redeem script', 'value': primary_redeem_script}, + {'label': 'Mediator escrow redeem script', 'value': mediator_redeem_script}, + {'label': 'Primary payment address', 'value': bid['Primary payment address']}, + {'label': 'Primary client payment address', 'value': payment_address}, + {'label': 'Worker public key for escrow', 'value': worker_pubkey_for_escrow}, + {'label': 'Job creator public key for escrow', 'value': pubkey_for_escrow} + ] document = assemble_document('Offer', fields) if not click.confirm('Are you sure you want to award this bid?'): @@ -438,7 +464,9 @@ def deliver(multi, identity, defaults, dry_run): redeemScript = doc['Primary escrow redeem script'] mediatorRedeemScript = doc['Mediator escrow redeem script'] mediator_daddr = str(P2PKHBitcoinAddress.from_pubkey(x(doc['Mediator public key']))) - (payment_txins,payment_amount,payment_address,payment_sig) = partial_spend_p2sh(redeemScript,rein) + pubkey_for_escrow = doc['Worker public key for escrow'] + privkey_for_escrow = Pubkeys.get(rein,pubkey_for_escrow) + (payment_txins,payment_amount,payment_address,payment_sig) = partial_spend_p2sh(redeemScript,rein,daddr=doc['Primary payment address'],privkey=privkey_for_escrow) (mediator_payment_txins,mediator_payment_amount,mediator_payment_address) = partial_spend_p2sh_mediator(mediatorRedeemScript,rein,mediator_daddr) fields = [ {'label': 'Job name', 'value_from': doc}, @@ -452,6 +480,8 @@ def deliver(multi, identity, defaults, dry_run): {'label': 'Worker public key', 'value_from': doc}, {'label': 'Mediator public key', 'value_from': doc}, {'label': 'Job creator public key', 'value_from': doc}, + {'label': 'Worker public key for escrow', 'value_from': doc}, + {'label': 'Job creator public key for escrow', 'value_from': doc}, {'label':'Primary payment inputs','value':payment_txins}, {'label':'Primary payment amount','value':payment_amount}, {'label':'Primary payment address','value':payment_address}, @@ -515,8 +545,10 @@ def accept(multi, identity, defaults, dry_run): txins_mediator = doc['Mediator payment inputs'] amount_mediator = doc['Mediator payment amount'] daddr_mediator = doc['Mediator payment address'] - (payment_txid,client_sig) = spend_p2sh(redeemScript,txins,[float(amount)],[daddr],worker_sig,rein) - tx_for_mediator = partial_spend_p2sh_mediator_2(redeemScript_mediator,txins_mediator,float(amount_mediator),daddr_mediator,rein) + pubkey_for_escrow = delivery['Job creator public key for escrow'] + privkey_for_escrow = Pubkeys.get(rein,pubkey_for_escrow) + (payment_txid,client_sig) = spend_p2sh(redeemScript,txins,[float(amount)],[daddr],worker_sig,rein,privkey=privkey_for_escrow) + tx_for_mediator = partial_spend_p2sh_mediator_2(redeemScript_mediator,txins_mediator,float(amount_mediator),daddr_mediator,rein,privkey=privkey_for_escrow) fields = [ {'label': 'Job name', 'value_from': doc}, @@ -586,7 +618,11 @@ def creatordispute(multi, identity, defaults, dry_run): {'label': 'Mediator escrow redeem script', 'value_from': doc}, {'label': 'Job creator public key', 'value_from': doc}, {'label': 'Worker public key', 'value_from': doc}, - {'label': 'Mediator public key', 'value_from':doc} + {'label': 'Mediator public key', 'value_from':doc}, + {'label': 'Primary worker payment address', 'value':doc['Primary payment address']}, + {'label': 'Primary client payment address', 'value_from':doc}, + {'label': 'Worker public key for escrow', 'value_from': doc}, + {'label': 'Job creator public key for escrow', 'value_from': doc} ] document = assemble_document('Dispute Delivery', fields) res = sign_and_store_document(rein, 'creatordispute', document, user.daddr, user.dkey, store) @@ -637,7 +673,11 @@ def workerdispute(multi, identity, defaults, dry_run): {'label': 'Mediator escrow redeem script', 'value_from': doc}, {'label': 'Job creator public key', 'value_from': doc}, {'label': 'Worker public key','value_from': doc}, - {'label': 'Mediator public key','value_from':doc} + {'label': 'Mediator public key','value_from':doc}, + {'label': 'Primary worker payment address', 'value':doc['Primary payment address']}, + {'label': 'Primary client payment address', 'value_from':doc}, + {'label': 'Worker public key for escrow', 'value_from': doc}, + {'label': 'Job creator public key for escrow', 'value_from': doc} ] document = assemble_document('Dispute Offer', fields) res = sign_and_store_document(rein, 'workerdispute', document, user.daddr, user.dkey, store) @@ -706,8 +746,8 @@ def resolve(multi, identity, defaults, dry_run): redeemScript = doc['Primary escrow redeem script'] mediatorRedeemScript = doc['Mediator escrow redeem script'] mediator_daddr = rein.user.daddr - worker_payment_daddr = str(P2PKHBitcoinAddress.from_pubkey(x(doc['Worker public key']))); - client_payment_daddr = str(P2PKHBitcoinAddress.from_pubkey(x(doc['Job creator public key']))); + worker_payment_daddr = doc['Primary worker payment address']; + client_payment_daddr = doc['Primary client payment address']; client_payment_amount = float(click.prompt("Client payment amount")) (payment_txins,payment_amount_1,payment_address_1,payment_amount_2,payment_address_2,payment_sig) = partial_spend_p2sh(redeemScript,rein,worker_payment_daddr,client_payment_amount,client_payment_daddr) (mediator_payment_txins,mediator_payment_amount,mediator_payment_address,mediator_payment_sig) = partial_spend_p2sh_mediator(mediatorRedeemScript,rein,mediator_daddr,True) @@ -718,6 +758,8 @@ def resolve(multi, identity, defaults, dry_run): {'label': 'Job creator public key', 'value_from': doc}, {'label': 'Worker public key', 'value_from':doc}, {'label': 'Mediator public key', 'value_from':doc}, + {'label': 'Worker public key for escrow', 'value_from': doc}, + {'label': 'Job creator public key for escrow', 'value_from': doc}, {'label': 'Primary escrow redeem script', 'value_from': doc}, {'label': 'Mediator escrow redeem script', 'value_from': doc}, {'label':'Primary payment inputs','value':payment_txins}, @@ -800,10 +842,15 @@ def acceptresolution(multi, identity, defaults, dry_run): sig_mediator = doc['Mediator payment signature'] reverse_sigs = False - if key == doc['Worker public key']: + + if key == delivery['Worker public key']: reverse_sigs = True - (payment_txid,second_sig) = spend_p2sh(redeemScript,txins,[float(amount1),float(amount2)],[daddr1,daddr2],sig_primary,rein,reverse_sigs) - (payment_txid_mediator,second_sig_mediator) = spend_p2sh_mediator(redeemScript_mediator,txins_mediator,[float(amount_mediator)],[daddr_mediator],sig_mediator,rein) + pubkey_for_escrow = delivery['Worker public key for escrow'] + else: + pubkey_for_escrow = delivery['Job creator public key for escrow'] + privkey_for_escrow = Pubkeys.get(rein,pubkey_for_escrow) + (payment_txid,second_sig) = spend_p2sh(redeemScript,txins,[float(amount1),float(amount2)],[daddr1,daddr2],sig_primary,rein,reverse_sigs,privkey=privkey_for_escrow) + (payment_txid_mediator,second_sig_mediator) = spend_p2sh_mediator(redeemScript_mediator,txins_mediator,[float(amount_mediator)],[daddr_mediator],sig_mediator,rein,privkey=privkey_for_escrow) fields = [ {'label': 'Job name', 'value_from': doc}, @@ -1457,14 +1504,12 @@ def register_user(): # Enroll user enrollment = build_enrollment_from_dict(user_data) - signed_enrollment = '-----BEGIN BITCOIN SIGNED MESSAGE-----\n' + \ - enrollment + \ - '\n-----BEGIN SIGNATURE-----\n' + \ - maddr + '\n' + \ - sign(mprv, enrollment) + \ - '\n-----END BITCOIN SIGNED MESSAGE-----\n' + signature = sign(mprv,json.dumps(enrollment,sort_keys=True)) + signed_enrollment = enrollment + signed_enrollment['signature'] = signature + signed_enrollment['signature_address'] = maddr User.set_enrolled(rein, new_identity) - document = Document(rein, 'enrollment', signed_enrollment, sig_verified=True, testnet=rein.testnet) + document = Document(rein, 'enrollment', json.dumps(signed_enrollment,sort_keys=True), sig_verified=True, testnet=rein.testnet) rein.session.add(document) rein.session.commit() return json.dumps({'enrolled': True}) @@ -1502,6 +1547,7 @@ def serve_static_file(path): if rein.has_no_account() or setup: webbrowser.open('http://'+host+':' + str(port) + '/setup') + #print('open '+'http://'+host+':' + str(port) + '/setup') app.run(host=host, port=port, debug=rein.debug) return else: @@ -1638,6 +1684,55 @@ def settings(): return render_template('settings.html', user=user, hidden_jobs=hidden_jobs, hidden_bids=hidden_bids, hidden_mediators=hidden_mediators, fee=fee, trust_score=trust_score) + @app.route("/wallet", methods=['GET']) + def wallet(): + fee = float(PersistConfig.get(rein, 'fee', 0.001)) + wallet_entries = rein.session.query(Wallet).filter(Wallet.userid == rein.user.id).all() + balance = 0. + txs = [] + for we in wallet_entries: + ref = we.ref + txs_we = None + new_job = False + for txj in txs: + if txj['ref'] == ref: + txs_we = txj + break + else: + new_job = True + txs_we = {} + txs_we['ref'] = we.ref + txs_we['txs'] = [] + (txins,value) = unspent_txins(rein,we.address,rein.testnet,txin_value=True) + for (txid,txvalue) in txins: + tx = {} + tx['txid'] = txid + tx['value'] = round(txvalue,8) + txs_we['txs'].append(tx) + balance += value + if new_job: + txs.append(txs_we) + + explorer = PersistConfig.get(rein, + 'explorer', + 'https://testnet.blockexplorer.com' if rein.testnet else 'https://blockexplorer.com' + ) + + return render_template('wallet.html', user=user, fee=fee, balance=round(balance,8), txs=txs, explorer=explorer) + + @app.route("/withdraw", methods=['GET','POST']) + def withdraw(): + try: + job_id = request.json['job'] + amount = request.json['amount'] + destaddr = request.json['addr'] + wallet_entries = rein.session.query(Wallet).filter(Wallet.ref == job_id).all() + withdraw_from_wallet(rein,wallet_entries,float(amount),destaddr) + except Exception as detail: + print "Unexpected error: ", detail + return 'false' + return 'true' + @app.route('/display-users', methods=['GET']) def display_users(): data = [] @@ -1850,19 +1945,36 @@ def job_offer(): if request.method == 'POST' and form.validate_on_submit(): bid_doc = Document.get(rein, form.bid_id.data) bid = parse_document(bid_doc.contents) + next_addr_index_str = PersistConfig.get(rein, 'next_addr_index') + if not next_addr_index_str: + next_addr_index_str = '0' + next_addr_index = int(next_addr_index_str) + (pubkey_for_escrow,privkey_for_escrow) = bip32.generate_new_escrow_pubkey(bip32.from_xprv(user.dxprv),next_addr_index) + (payment_address,payment_privkey) = bip32.generate_new_payment_address(bip32.from_xprv(user.dxprv),next_addr_index) + next_addr_index += 1 + PersistConfig.set(rein,'next_addr_index',str(next_addr_index)) + Pubkeys.set(rein,pubkey_for_escrow,privkey_for_escrow,userid=rein.user.id) + Wallet.set(rein,payment_address,payment_privkey,ref=bid['Job ID'],userid=rein.user.id) + worker_pubkey_for_escrow = bid['Worker public key for escrow'] + primary_redeem_script, primary_addr = build_2_of_3([pubkey_for_escrow,bid['Mediator public key'],worker_pubkey_for_escrow]) + mediator_redeem_script, mediator_escrow_addr = build_mandatory_multisig(bid['Mediator public key'],[pubkey_for_escrow,worker_pubkey_for_escrow]) fields = [ {'label': 'Job name', 'value': bid['Job name']}, {'label': 'Job ID', 'value': bid['Job ID']}, {'label': 'Description', 'value': bid['Description']}, {'label': 'Bid amount (BTC)', 'value': bid['Bid amount (BTC)']}, - {'label': 'Primary escrow address', 'value': bid['Primary escrow address']}, - {'label': 'Mediator escrow address', 'value': bid['Mediator escrow address']}, + {'label': 'Primary escrow address', 'value': primary_addr}, + {'label': 'Mediator escrow address', 'value': mediator_escrow_addr}, {'label': 'Job creator', 'value': bid['Job creator']}, {'label': 'Job creator public key', 'value': bid['Job creator public key']}, {'label': 'Mediator public key', 'value': bid['Mediator public key']}, {'label': 'Worker public key', 'value': bid['Worker public key']}, - {'label': 'Primary escrow redeem script', 'value': bid['Primary escrow redeem script']}, - {'label': 'Mediator escrow redeem script', 'value': bid['Mediator escrow redeem script']}, + {'label': 'Primary escrow redeem script', 'value': primary_redeem_script}, + {'label': 'Mediator escrow redeem script', 'value': mediator_redeem_script}, + {'label': 'Primary payment address', 'value': bid['Primary payment address']}, + {'label': 'Primary client payment address', 'value': payment_address}, + {'label': 'Worker public key for escrow', 'value': worker_pubkey_for_escrow}, + {'label': 'Job creator public key for escrow', 'value': pubkey_for_escrow} ] document_text = assemble_document('Offer', fields) store = True @@ -1935,8 +2047,10 @@ def job_accept(): txins_mediator = delivery['Mediator payment inputs'] amount_mediator = delivery['Mediator payment amount'] daddr_mediator = delivery['Mediator payment address'] - (payment_txid,client_sig) = spend_p2sh(redeemScript,txins,[float(amount)],[daddr],worker_sig,rein) - tx_for_mediator = partial_spend_p2sh_mediator_2(redeemScript_mediator,txins_mediator,float(amount_mediator),daddr_mediator,rein) + pubkey_for_escrow = delivery['Job creator public key for escrow'] + privkey_for_escrow = Pubkeys.get(rein,pubkey_for_escrow) + (payment_txid,client_sig) = spend_p2sh(redeemScript,txins,[float(amount)],[daddr],worker_sig,rein,privkey=privkey_for_escrow) + tx_for_mediator = partial_spend_p2sh_mediator_2(redeemScript_mediator,txins_mediator,float(amount_mediator),daddr_mediator,rein,privkey=privkey_for_escrow) fields = [ {'label': 'Job name', 'value_from': delivery}, {'label': 'Job ID', 'value_from': delivery}, @@ -2041,11 +2155,16 @@ def job_acceptresolution(): daddr_mediator = delivery['Mediator payment address'] sig_mediator = delivery['Mediator payment signature'] + pubkey_for_escrow = '' reverse_sigs = False if key == delivery['Worker public key']: reverse_sigs = True - (payment_txid,second_sig) = spend_p2sh(redeemScript,txins,[float(amount1),float(amount2)],[daddr1,daddr2],sig_primary,rein,reverse_sigs) - (payment_txid_mediator,second_sig_mediator) = spend_p2sh_mediator(redeemScript_mediator,txins_mediator,[float(amount_mediator)],[daddr_mediator],sig_mediator,rein) + pubkey_for_escrow = delivery['Worker public key for escrow'] + else: + pubkey_for_escrow = delivery['Job creator public key for escrow'] + privkey_for_escrow = Pubkeys.get(rein,pubkey_for_escrow) + (payment_txid,second_sig) = spend_p2sh(redeemScript,txins,[float(amount1),float(amount2)],[daddr1,daddr2],sig_primary,rein,reverse_sigs,privkey=privkey_for_escrow) + (payment_txid_mediator,second_sig_mediator) = spend_p2sh_mediator(redeemScript_mediator,txins_mediator,[float(amount_mediator)],[daddr_mediator],sig_mediator,rein,privkey=privkey_for_escrow) fields = [ {'label': 'Job name', 'value_from': delivery}, {'label': 'Job ID', 'value_from': delivery}, @@ -2158,8 +2277,8 @@ def job_resolve(): redeemScript = dispute['Primary escrow redeem script'] mediatorRedeemScript = dispute['Mediator escrow redeem script'] mediator_daddr = rein.user.daddr - worker_payment_daddr = str(P2PKHBitcoinAddress.from_pubkey(x(dispute['Worker public key']))); - client_payment_daddr = str(P2PKHBitcoinAddress.from_pubkey(x(dispute['Job creator public key']))); + worker_payment_daddr = dispute['Primary worker payment address']; + client_payment_daddr = dispute['Primary client payment address']; client_payment_amount = float(form.client_payment_amount.data) try: (payment_txins,payment_amount_1,payment_address_1,payment_amount_2,payment_address_2,payment_sig) = partial_spend_p2sh(redeemScript,rein,worker_payment_daddr,client_payment_amount,client_payment_daddr) @@ -2175,6 +2294,8 @@ def job_resolve(): {'label': 'Job creator public key', 'value_from': dispute}, {'label': 'Worker public key', 'value_from':dispute}, {'label': 'Mediator public key', 'value_from':dispute}, + {'label': 'Worker public key for escrow', 'value_from': dispute}, + {'label': 'Job creator public key for escrow', 'value_from': dispute}, {'label': 'Primary escrow redeem script', 'value_from': dispute}, {'label': 'Mediator escrow redeem script', 'value_from': dispute}, {'label':'Primary payment inputs','value':payment_txins}, @@ -2362,7 +2483,11 @@ def job_dispute(): {'label': 'Mediator escrow redeem script', 'value_from': doc}, {'label': 'Job creator public key', 'value_from': doc}, {'label': 'Worker public key', 'value_from':doc}, - {'label': 'Mediator public key', 'value_from':doc} + {'label': 'Mediator public key', 'value_from':doc}, + {'label': 'Primary worker payment address', 'value':doc['Primary payment address']}, + {'label': 'Primary client payment address', 'value_from':doc}, + {'label': 'Worker public key for escrow', 'value_from': doc}, + {'label': 'Job creator public key for escrow', 'value_from': doc} ] if key == doc['Job creator public key']: @@ -2463,13 +2588,17 @@ def job_bid(): flash('No matching Job ID found.') return redirect("/") - primary_redeem_script, primary_addr = \ - build_2_of_3([job['Job creator public key'], - job['Mediator public key'], - key]) - mediator_redeem_script, mediator_escrow_addr = \ - build_mandatory_multisig(job['Mediator public key'], - [job['Job creator public key'],key]) + next_addr_index_str = PersistConfig.get(rein, 'next_addr_index') + if not next_addr_index_str: + next_addr_index_str = '0' + next_addr_index = int(next_addr_index_str) + (pubkey_for_escrow,privkey_for_escrow) = bip32.generate_new_escrow_pubkey(bip32.from_xprv(user.dxprv),next_addr_index) + (primary_payment_address,primary_payment_privkey) = bip32.generate_new_payment_address(bip32.from_xprv(user.dxprv),next_addr_index) + next_addr_index += 1 + PersistConfig.set(rein,'next_addr_index',str(next_addr_index)) + Pubkeys.set(rein,pubkey_for_escrow,privkey_for_escrow,userid=rein.user.id) + Wallet.set(rein,primary_payment_address,primary_payment_privkey,ref=job['Job ID'],userid=rein.user.id) + fields = [ {'label': 'Job name', 'value_from': job}, {'label': 'Worker', 'value': user.name}, @@ -2479,15 +2608,13 @@ def job_bid(): {'label': 'Worker master address', 'value': user.maddr}, {'label': 'Description', 'value': form.description.data}, {'label': 'Bid amount (BTC)', 'value': form.bid_amount.data}, - {'label': 'Primary escrow address', 'value': primary_addr}, - {'label': 'Mediator escrow address', 'value': mediator_escrow_addr}, {'label': 'Job ID', 'value_from': job}, {'label': 'Job creator', 'value_from': job}, {'label': 'Job creator public key', 'value_from': job}, {'label': 'Mediator public key', 'value_from': job}, {'label': 'Worker public key', 'value': key}, - {'label': 'Primary escrow redeem script', 'value': primary_redeem_script}, - {'label': 'Mediator escrow redeem script', 'value': mediator_redeem_script}, + {'label': 'Primary payment address', 'value': primary_payment_address}, + {'label': 'Worker public key for escrow', 'value': pubkey_for_escrow} ] document_text = assemble_document('Bid', fields) store = True @@ -2560,8 +2687,10 @@ def job_deliver(): redeemScript = doc['Primary escrow redeem script'] mediatorRedeemScript = doc['Mediator escrow redeem script'] mediator_daddr = str(P2PKHBitcoinAddress.from_pubkey(x(doc['Mediator public key']))) + pubkey_for_escrow = doc['Worker public key for escrow'] + privkey_for_escrow = Pubkeys.get(rein,pubkey_for_escrow) try: - (payment_txins,payment_amount,payment_address,payment_sig) = partial_spend_p2sh(redeemScript,rein) + (payment_txins,payment_amount,payment_address,payment_sig) = partial_spend_p2sh(redeemScript,rein,daddr=doc['Primary payment address'],privkey=privkey_for_escrow) (mediator_payment_txins,mediator_payment_amount,mediator_payment_address) = partial_spend_p2sh_mediator(mediatorRedeemScript,rein,mediator_daddr) except ValueError as e: form.deliverable.errors.append(e.message) @@ -2579,6 +2708,8 @@ def job_deliver(): {'label': 'Worker public key', 'value_from': doc}, {'label': 'Mediator public key', 'value_from': doc}, {'label': 'Job creator public key', 'value_from': doc}, + {'label': 'Worker public key for escrow', 'value_from': doc}, + {'label': 'Job creator public key for escrow', 'value_from': doc}, {'label':'Primary payment inputs','value':payment_txins}, {'label':'Primary payment amount','value':payment_amount}, {'label':'Primary payment address','value':payment_address}, @@ -2638,7 +2769,7 @@ def serve_template_file(): orders=relevant_orders) webbrowser.open('http://'+host+':' + str(port)) - print("testnet = "+str(rein.testnet)) + #print('open '+'http://'+host+':' + str(port)) app.run(host=host, port=port, debug=rein.debug) # testing steps: Disable tor. Then turn on debug because debug doesn't work when socket is overriden diff --git a/rein/html/js/wallet.js b/rein/html/js/wallet.js new file mode 100644 index 0000000..c18fdca --- /dev/null +++ b/rein/html/js/wallet.js @@ -0,0 +1,25 @@ +function postError(data) { + if (data != 'true') { + alert('Error withdrawing.') + } else { + alert('Withdrawl succeeded. Please allow some time for it to confirm.') + } +} + +function withdraw_from_job(job_id) { + var amount = $('#withdraw_amount_'+job_id).val(); + var destaddr = $('#withdraw_address_'+job_id).val(); + $.ajax({ + method: "POST", + url: "/withdraw", + contentType: "application/json", + data: JSON.stringify({ + 'job': job_id, + 'amount': amount, + 'addr': destaddr + }), + success: function(data) { + postError(data); + } + }) +} diff --git a/rein/html/layout.html b/rein/html/layout.html index 769d5c9..f1aa8de 100644 --- a/rein/html/layout.html +++ b/rein/html/layout.html @@ -164,12 +164,18 @@ Rate -
txid | +value | + + + {% for tx in txj.txs %} +
---|---|
{{tx.txid}} | +{{tx.value}} | +
No transactions.
+ {% endif %} +