From a51a3cdb2173e5a284b5c205e0165d7e868ca720 Mon Sep 17 00:00:00 2001 From: Andrew K Date: Tue, 21 Mar 2017 21:30:07 +0000 Subject: [PATCH 01/42] first try for json format for docs --- rein/cli.py | 10 +++++--- rein/lib/market.py | 19 +++++++------- rein/lib/validate.py | 61 ++++++++++++++++++++++++-------------------- 3 files changed, 49 insertions(+), 41 deletions(-) diff --git a/rein/cli.py b/rein/cli.py index e5f28e6..8f5e088 100644 --- a/rein/cli.py +++ b/rein/cli.py @@ -212,6 +212,7 @@ def post(multi, identity, defaults, dry_run): {'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: @@ -1389,7 +1390,7 @@ def start(multi, identity, setup): from .lib.bitcoinaddress import generate_sin host = '127.0.0.1' - port = 5001 + port = 5002 tmpl_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'html') @@ -1501,7 +1502,8 @@ def serve_static_file(path): return send_from_directory(tmpl_dir, path) if rein.has_no_account() or setup: - webbrowser.open('http://'+host+':' + str(port) + '/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: @@ -2597,8 +2599,8 @@ def serve_template_file(): documents=documents, orders=relevant_orders) - webbrowser.open('http://'+host+':' + str(port)) - print("testnet = "+str(rein.testnet)) + #webbrowser.open('http://'+host+':' + str(port)) + 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/lib/market.py b/rein/lib/market.py index 6e8c688..19fe198 100644 --- a/rein/lib/market.py +++ b/rein/lib/market.py @@ -7,7 +7,7 @@ from sqlalchemy import and_ import os import click - +import json def assemble_document(title, fields): """ @@ -35,11 +35,12 @@ def assemble_document(title, fields): else: entry['value'] = click.prompt(prompt) data.append(entry) - document = "Rein %s\n" % title - for entry in data: - document = document + entry['label'] + ": " + entry['value'] + "\n" - return document[:-1] + document = {} + document['Title'] = "Rein %s\n" % title + for entry in data: + document[entry['label']] = entry['value'] + return json.dumps(document,sort_keys=True) def sign_and_store_document(rein, doc_type, document, signature_address=None, signature_key=None, store=True, overwrite_hash=None): """ @@ -67,10 +68,10 @@ def sign_and_store_document(rein, doc_type, document, signature_address=None, si if validated: # insert signed document into documents table - b = "-----BEGIN BITCOIN SIGNED MESSAGE-----" - c = "-----BEGIN SIGNATURE-----" - d = "-----END BITCOIN SIGNED MESSAGE-----" - signed = "%s\n%s\n%s\n%s\n%s\n%s" % (b, document, c, signature_address, signature, d) + document_json = json.loads(document) + document_json["signature_address"] = signature_address + document_json["signature"] = signature + signed = json.dumps(document_json,sort_keys=True) click.echo('\n' + signed + '\n') # If document doesn't already exist, create if store and not overwrite_hash: diff --git a/rein/lib/validate.py b/rein/lib/validate.py index 123d2f4..3495b48 100644 --- a/rein/lib/validate.py +++ b/rein/lib/validate.py @@ -80,15 +80,18 @@ def strip_armor(sig, dash_space=False): def parse_document(document): - ret = {} - m = re.search('(Rein .*)\n', document) - if m: - ret['Title'] = m.group(1) - matches = re.finditer("(.+?):\s(.+)(\n|$)", document) - for match in matches: - ret[match.group(1)] = match.group(2) - return ret - + try: + json_object = json.loads(document) + except ValueError, e: #for backwards compatibility + ret = {} + m = re.search('(Rein .*)\n', document) + if m: + ret['Title'] = m.group(1) + matches = re.finditer("(.+?):\s(.+)(\n|$)", document) + for match in matches: + ret[match.group(1)] = match.group(2) + return ret + return json_object def parse_sig(sig): ''' @@ -97,25 +100,27 @@ def parse_sig(sig): assigned within the message, for example: parse_sig(sig)['Name/handle'] === "David Sterry" ''' - ret = {} - m = re.search('\n(Rein .*)\n', sig) - if m: - ret['Title'] = m.group(1) - matches = re.finditer("(.+?):\s(.+)\n", sig) - for match in matches: - ret[match.group(1)] = match.group(2) - m = re.search( - "-{5}BEGIN SIGNATURE-{5}\n([A-z\d=+/]+)\n([A-z\d=+/]+)" - "\n-{5}END BITCOIN SIGNED MESSAGE-{5}", - sig - ) - if m: - ret['signature_address'] = m.group(1) - ret['signature'] = m.group(2) - else: - return False - return ret - + try: + json_object = json.loads(document) + except ValueError, e: #for backwards compatibility + ret = {} + m = re.search('\n(Rein .*)\n', sig) + if m: + ret['Title'] = m.group(1) + matches = re.finditer("(.+?):\s(.+)\n", sig) + for match in matches: + ret[match.group(1)] = match.group(2) + m = re.search( + "-{5}BEGIN SIGNATURE-{5}\n([A-z\d=+/]+)\n([A-z\d=+/]+)" + "\n-{5}END BITCOIN SIGNED MESSAGE-{5}", + sig + ) + if m: + ret['signature_address'] = m.group(1) + ret['signature'] = m.group(2) + else: + return False + return json_object def filter_valid_sigs(rein, docs, expected_field=None): valid = [] From 4301f30227363bf2d610391f67d56dab2899f1af Mon Sep 17 00:00:00 2001 From: Andrew K Date: Tue, 21 Mar 2017 21:33:38 +0000 Subject: [PATCH 02/42] fix return value of parse_sigs --- rein/lib/validate.py | 1 + 1 file changed, 1 insertion(+) diff --git a/rein/lib/validate.py b/rein/lib/validate.py index 3495b48..98ac6d1 100644 --- a/rein/lib/validate.py +++ b/rein/lib/validate.py @@ -120,6 +120,7 @@ def parse_sig(sig): ret['signature'] = m.group(2) else: return False + return ret return json_object def filter_valid_sigs(rein, docs, expected_field=None): From 6891e0cb12e3fcb2b5e9caaffcab3ace5fdd0ae8 Mon Sep 17 00:00:00 2001 From: Andrew K Date: Wed, 22 Mar 2017 04:28:38 +0000 Subject: [PATCH 03/42] deal with ratings in json --- rein/lib/document.py | 15 ++++++++++----- rein/lib/rating.py | 13 ++++++------- rein/lib/validate.py | 3 ++- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/rein/lib/document.py b/rein/lib/document.py index 3de460a..ebc8e62 100644 --- a/rein/lib/document.py +++ b/rein/lib/document.py @@ -6,6 +6,7 @@ from .order import Order from .io import safe_get from .util import document_to_dict +import json Base = declarative_base() @@ -106,11 +107,15 @@ def get_documents_by_job_id(rein, url, job_id): @staticmethod def get_job_id(text): - m = re.search('Job ID: (.+)\n', text) - if m: - return m.group(1) - else: - return None + try: + json_object = json.loads(text) + return json_object["Job ID"] + except ValueError, e: + m = re.search('Job ID: (.+)\n', text) + if m: + return m.group(1) + else: + return None @staticmethod def get_document_type(document): diff --git a/rein/lib/rating.py b/rein/lib/rating.py index 0e54add..a2ed96a 100644 --- a/rein/lib/rating.py +++ b/rein/lib/rating.py @@ -80,8 +80,7 @@ def rating_identifier(fields): relevant_fields = ['User msin', 'Job id', 'Rater msin'] for field in fields: if field['label'] in relevant_fields: - identifier += field['label'] + ": " + field['value'] + "\n" - + identifier += '"'+field['label']+'": "'+field['value']+'"%' return identifier def add_rating(rein, user, testnet, rating, user_msin, job_id, rated_by_msin, comments): @@ -89,15 +88,15 @@ def add_rating(rein, user, testnet, rating, user_msin, job_id, rated_by_msin, co rating is adjusted""" fields = [ {'label': 'Rating', 'value': rating}, - {'label': 'User msin', 'value': user_msin}, {'label': 'Job id', 'value': job_id}, {'label': 'Rater msin', 'value': rated_by_msin}, + {'label': 'User msin', 'value': user_msin}, {'label': 'Comments', 'value': comments}, ] document_text = assemble_document('Rating', fields) update_identifier = rating_identifier(fields) - look_for = '%\n{}%'.format(update_identifier) + look_for = '%{}'.format(update_identifier) update_rating = rein.session.query(Document).filter(and_(Document.testnet == testnet, Document.contents.like(look_for), Document.doc_type == 'rating')).first() store = True @@ -190,7 +189,7 @@ def calculate_trust_score(dest_msin=None, source_msin=None, rein=None, test=Fals if not test: ratings_by_source = rein.session.query(Document).filter(and_( Document.doc_type == 'rating', - Document.contents.like('%\nRater msin: {}%'.format(source_msin)) + Document.contents.like('%"Rater msin": "{}"%'.format(source_msin)) )).all() else: @@ -223,8 +222,8 @@ def calculate_trust_score(dest_msin=None, source_msin=None, rein=None, test=Fals dest_ratings_by_vouched_user = rein.session.query(Document).filter( and_( Document.doc_type == 'rating', - Document.contents.like('%\nRater msin: {}%'.format(vouched_user_msin)), - Document.contents.like('%\nUser msin: {}%'.format(dest_msin)) + Document.contents.like('%"Rater msin": "{}"%'.format(vouched_user_msin)), + Document.contents.like('%"User msin": "{}"%'.format(dest_msin)) )).all() else: dest_ratings_by_vouched_user = [test_rating for test_rating in test_ratings if test_rating['Rater msin'] == vouched_user_msin and test_rating['User msin'] == 'DestMsin'] diff --git a/rein/lib/validate.py b/rein/lib/validate.py index 98ac6d1..0637c69 100644 --- a/rein/lib/validate.py +++ b/rein/lib/validate.py @@ -7,6 +7,7 @@ from .io import safe_get from .util import unique from .block import Block +import json def filter_out_expired(rein, user, urls, jobs): live = [] @@ -101,7 +102,7 @@ def parse_sig(sig): parse_sig(sig)['Name/handle'] === "David Sterry" ''' try: - json_object = json.loads(document) + json_object = json.loads(sig) except ValueError, e: #for backwards compatibility ret = {} m = re.search('\n(Rein .*)\n', sig) From 99c62a5bd4e12496812d99d3188f2295a6e5f97d Mon Sep 17 00:00:00 2001 From: Andrew K Date: Wed, 22 Mar 2017 05:49:25 +0000 Subject: [PATCH 04/42] fix bugs with handling json --- rein/lib/market.py | 2 +- rein/lib/validate.py | 29 +++++++++++++++++------------ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/rein/lib/market.py b/rein/lib/market.py index 19fe198..01a3dfe 100644 --- a/rein/lib/market.py +++ b/rein/lib/market.py @@ -37,7 +37,7 @@ def assemble_document(title, fields): data.append(entry) document = {} - document['Title'] = "Rein %s\n" % title + document['Title'] = "Rein %s" % title for entry in data: document[entry['label']] = entry['value'] return json.dumps(document,sort_keys=True) diff --git a/rein/lib/validate.py b/rein/lib/validate.py index 0637c69..8363fcb 100644 --- a/rein/lib/validate.py +++ b/rein/lib/validate.py @@ -67,18 +67,23 @@ def choose_best_block(blocks): def strip_armor(sig, dash_space=False): - '''Removes ASCII-armor from a signed message by default exlcudes 'dash-space' headers''' - sig = sig.replace('- ----', '-' * 5) if dash_space else sig - sig = re.sub("-{5}BEGIN BITCOIN SIGNED MESSAGE-{5}", "", sig) - sig = re.sub( - "\n+-{5}BEGIN SIGNATURE-{5}[\n\dA-z+=/]+-{5}END BITCOIN SIGNED MESSAGE-{5}\n*", - "", - sig - ) - sig = re.sub("^\n", "", sig) - sig = re.sub("\n\n", "", sig) - return sig - + try: + json_object = json.loads(sig) + json_object.pop("signature") + json_object.pop("signature_address") + return json.dumps(json_object,sort_keys=True) + except ValueError, e: + '''Removes ASCII-armor from a signed message by default exlcudes 'dash-space' headers''' + sig = sig.replace('- ----', '-' * 5) if dash_space else sig + sig = re.sub("-{5}BEGIN BITCOIN SIGNED MESSAGE-{5}", "", sig) + sig = re.sub( + "\n+-{5}BEGIN SIGNATURE-{5}[\n\dA-z+=/]+-{5}END BITCOIN SIGNED MESSAGE-{5}\n*", + "", + sig + ) + sig = re.sub("^\n", "", sig) + sig = re.sub("\n\n", "", sig) + return sig def parse_document(document): try: From 4245422464a281d65d0c0ccbe8e586e9f5cadf0a Mon Sep 17 00:00:00 2001 From: Andrew K Date: Sat, 25 Mar 2017 01:08:47 +0000 Subject: [PATCH 05/42] use json for enrollment documents --- rein/cli.py | 12 +++----- rein/lib/ui.py | 73 +++++++++++++++++++++++--------------------- rein/lib/util.py | 38 +++++++++++++---------- rein/lib/validate.py | 2 +- 4 files changed, 67 insertions(+), 58 deletions(-) diff --git a/rein/cli.py b/rein/cli.py index 8f5e088..701aacd 100644 --- a/rein/cli.py +++ b/rein/cli.py @@ -1458,14 +1458,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}) diff --git a/rein/lib/ui.py b/rein/lib/ui.py index 9e7c954..d08f9b8 100644 --- a/rein/lib/ui.py +++ b/rein/lib/ui.py @@ -150,14 +150,12 @@ def create_account(rein): rein.user = new_identity 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() @@ -177,20 +175,23 @@ def create_account(rein): def build_enrollment_from_dict(user_data): - mediator_extras = '' + enrollment = {} + enrollment['Title'] = 'Rein User Enrollment' + enrollment['User'] = user_data['name'] + enrollment['Contact'] = user_data['contact'] + enrollment['Master signing address'] = user_data['maddr'] + enrollment['Secure Identity Number'] = user_data['msin'] + enrollment['Delegate signing address'] = user_data['daddr'] if user_data['will_mediate']: - mediator_extras = "\nMediator public key: %s\nMediator fee: %s%%" % \ - (pubkey(user_data['dkey']), user_data['mediator_fee']) - enrollment = "Rein User Enrollment\nUser: %s\nContact: %s\nMaster signing address: %s\n" \ - "Secure Identity Number: %s\nDelegate signing address: %s\n" \ - "Willing to mediate: %s%s" % \ - (user_data['name'], user_data['contact'], user_data['maddr'], user_data['msin'],\ - user_data['daddr'], user_data['will_mediate'], mediator_extras) + enrollment['Willing to mediate'] = user_data['will_mediate'] + enrollment['Mediator public key'] = pubkey(user_data['dkey']) + enrollment['Mediator fee'] = user_data['mediator_fee'] + else: + enrollment['Willing to mediate'] = 'no' if user_data['testnet']: - enrollment += '\nTestnet: True' + enrollment['Testnet'] = 'True' return enrollment - def import_account(rein, mprv=None, mnemonic=None): Base.metadata.create_all(rein.engine) backup_filename = click.prompt("Enter backup file name", type=str, default=rein.backup_filename) @@ -222,40 +223,44 @@ def import_account(rein, mprv=None, mnemonic=None): if not privkey_to_address(mprv): raise Exception('Invalid master private key.') + enrollment = build_enrollment_from_dict(user_data) - signed_enrollment = '-----BEGIN BITCOIN SIGNED MESSAGE-----\n' + \ - enrollment + \ - '\n-----BEGIN SIGNATURE-----\n' + \ - user_data['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'] = user_data['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 rein.user - def build_enrollment(rein): + + enrollment = {} user = rein.user - mediator_extras = '' + enrollment['Title'] = 'Rein User Enrollment' + enrollment['User'] = user.name + enrollment['Contact'] = user.contact + enrollment['Master signing address'] = user.maddr + enrollment['Delegate signing address'] = user.daddr if user.will_mediate: - mediator_extras = "\nMediator public key: %s\nMediator fee: %s%%" % \ - (pubkey(user.dkey), user.mediator_fee) - enrollment = "Rein User Enrollment\nUser: %s\nContact: %s\nMaster signing address: %s" \ - "\nDelegate signing address: %s\nWilling to mediate: %s%s" % \ - (user.name, user.contact, user.maddr, user.daddr, user.will_mediate, mediator_extras) + enrollment['Willing to mediate'] = user.will_mediate + enrollment['Mediator public key'] = pubkey(user.dkey) + enrollment['Mediator fee'] = user.mediator_fee + else: + enrollment['Willing to mediate'] = 'no' if rein.testnet: - enrollment += '\nTestnet: True' + enrollment['Testnet'] = 'True' return enrollment - def enroll(rein): user = rein.user Base.metadata.create_all(rein.engine) enrollment = build_enrollment(rein) f = open(rein.enroll_filename, 'w') - f.write(enrollment) + f.write(json.dumps(enrollment,sort_keys=True)) f.close() click.echo("%s\n" % enrollment) done = False diff --git a/rein/lib/util.py b/rein/lib/util.py index 5c47b91..71342a5 100644 --- a/rein/lib/util.py +++ b/rein/lib/util.py @@ -1,23 +1,29 @@ +import json + def document_to_dict(content): """Turns a document's contents into a dict""" - doc = {} try: - # Grab part of the document containing information - content = content.split('\n-----BEGIN SIGNATURE-----')[0] - # Remove heading - content = content.split('\n')[2:] - for line in content: - key_value = line.split(': ') - if len(key_value) > 1: - key = key_value[0] - value = key_value[1] - doc[key] = value - - except: - return {'error': 'unspecified error'} - - return doc + json_object = json.loads(content) + return json_object + except ValueError, e: + doc = {} + try: + # Grab part of the document containing information + content = content.split('\n-----BEGIN SIGNATURE-----')[0] + # Remove heading + content = content.split('\n')[2:] + for line in content: + key_value = line.split(': ') + if len(key_value) > 1: + key = key_value[0] + value = key_value[1] + doc[key] = value + + except: + return {'error': 'unspecified error'} + + return doc def get_user_name(log, url, user, rein, msin): """Find a user's name by his msin""" diff --git a/rein/lib/validate.py b/rein/lib/validate.py index 8363fcb..83d3d0a 100644 --- a/rein/lib/validate.py +++ b/rein/lib/validate.py @@ -218,7 +218,7 @@ def validate_review(reviewer_text): strip_armor(reviewer_text).replace('- ----', '-----') ] - +#is this function used? def validate_audit(auditor_text): a = verify_sig(auditor_text) txt = strip_armor(auditor_text) From bc80c7cbd410f93c2aa3fc1aed02838a7e47af00 Mon Sep 17 00:00:00 2001 From: Andrew K Date: Sat, 25 Mar 2017 01:39:51 +0000 Subject: [PATCH 06/42] handle case of no job id in document --- rein/lib/document.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rein/lib/document.py b/rein/lib/document.py index ebc8e62..670b908 100644 --- a/rein/lib/document.py +++ b/rein/lib/document.py @@ -109,7 +109,10 @@ def get_documents_by_job_id(rein, url, job_id): def get_job_id(text): try: json_object = json.loads(text) - return json_object["Job ID"] + if "Job ID" in json_object: + return json_object["Job ID"] + else: + return None except ValueError, e: m = re.search('Job ID: (.+)\n', text) if m: From 5db8ae8d47ebe06a9c9b4f3a4a3fafcdfa3870aa Mon Sep 17 00:00:00 2001 From: Andrew K Date: Sat, 25 Mar 2017 01:51:05 +0000 Subject: [PATCH 07/42] fix willing to mediate value --- rein/lib/ui.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rein/lib/ui.py b/rein/lib/ui.py index d08f9b8..357668e 100644 --- a/rein/lib/ui.py +++ b/rein/lib/ui.py @@ -183,11 +183,11 @@ def build_enrollment_from_dict(user_data): enrollment['Secure Identity Number'] = user_data['msin'] enrollment['Delegate signing address'] = user_data['daddr'] if user_data['will_mediate']: - enrollment['Willing to mediate'] = user_data['will_mediate'] + enrollment['Willing to mediate'] = 'True' enrollment['Mediator public key'] = pubkey(user_data['dkey']) enrollment['Mediator fee'] = user_data['mediator_fee'] else: - enrollment['Willing to mediate'] = 'no' + enrollment['Willing to mediate'] = 'False' if user_data['testnet']: enrollment['Testnet'] = 'True' return enrollment @@ -246,11 +246,11 @@ def build_enrollment(rein): enrollment['Master signing address'] = user.maddr enrollment['Delegate signing address'] = user.daddr if user.will_mediate: - enrollment['Willing to mediate'] = user.will_mediate + enrollment['Willing to mediate'] = 'True' enrollment['Mediator public key'] = pubkey(user.dkey) enrollment['Mediator fee'] = user.mediator_fee else: - enrollment['Willing to mediate'] = 'no' + enrollment['Willing to mediate'] = 'False' if rein.testnet: enrollment['Testnet'] = 'True' return enrollment From 90e33eba0e26063a0e46a8806723adacf5bda102 Mon Sep 17 00:00:00 2001 From: Andrew K Date: Sat, 25 Mar 2017 02:19:48 +0000 Subject: [PATCH 08/42] put testnet support for multisig address generation --- rein/lib/script.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rein/lib/script.py b/rein/lib/script.py index 14a5869..1521b08 100644 --- a/rein/lib/script.py +++ b/rein/lib/script.py @@ -5,6 +5,8 @@ from bitcoin.wallet import CBitcoinAddress from .validate import parse_document +if (rein.testnet): bitcoin.SelectParams('testnet') + def parse_script(text): try: parsed = bitcoin.core.script.CScript(x(text)) @@ -79,4 +81,4 @@ def check_redeem_scripts(document): ret['Mediator public key'], pubkeys): click.echo("2-of-3 check failed") return False - return True \ No newline at end of file + return True From 545377a3f5e5230e6a12887fe2c7274b24b37124 Mon Sep 17 00:00:00 2001 From: Andrew K Date: Sat, 25 Mar 2017 02:34:59 +0000 Subject: [PATCH 09/42] undo last change --- rein/lib/script.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/rein/lib/script.py b/rein/lib/script.py index 1521b08..5697e42 100644 --- a/rein/lib/script.py +++ b/rein/lib/script.py @@ -5,8 +5,6 @@ from bitcoin.wallet import CBitcoinAddress from .validate import parse_document -if (rein.testnet): bitcoin.SelectParams('testnet') - def parse_script(text): try: parsed = bitcoin.core.script.CScript(x(text)) From 05d18eb6511c521d4a6bf28cf9e6da7083bed5b5 Mon Sep 17 00:00:00 2001 From: Andrew K Date: Mon, 27 Mar 2017 19:54:31 +0000 Subject: [PATCH 10/42] enable backwards compatibility for ratings doc format --- rein/lib/rating.py | 48 +++++++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/rein/lib/rating.py b/rein/lib/rating.py index a2ed96a..c3a5139 100644 --- a/rein/lib/rating.py +++ b/rein/lib/rating.py @@ -7,7 +7,7 @@ from .order import Order, STATE from .document import Document from .bitcoinaddress import generate_sin -from sqlalchemy import and_ +from sqlalchemy import and_, or_ import json def get_job_info(rein, order): @@ -75,13 +75,21 @@ def get_user_jobs(rein, return_dict=False): return json.dumps(job_list) def rating_identifier(fields): - """Generates a string that would be found in the contents of an existing rating document""" - identifier = '' - relevant_fields = ['User msin', 'Job id', 'Rater msin'] - for field in fields: - if field['label'] in relevant_fields: - identifier += '"'+field['label']+'": "'+field['value']+'"%' - return identifier + """Generates a string that would be found in the contents of an existing rating document""" + identifier = '' + relevant_fields = ['User msin', 'Job id', 'Rater msin'] + for field in fields: + if field['label'] in relevant_fields: + identifier += '"'+field['label']+'": "'+field['value']+'"%' + return identifier + +def rating_identifier_old(fields): + identifier = '' + relevant_fields = ['User msin', 'Job id', 'Rater msin'] + for field in fields: + if field['label'] in relevant_fields: + identifier += field['label'] + ": " + field['value'] + "\n" + return identifier def add_rating(rein, user, testnet, rating, user_msin, job_id, rated_by_msin, comments): """Adds a rating to the database or updates it if an already existing @@ -91,13 +99,20 @@ def add_rating(rein, user, testnet, rating, user_msin, job_id, rated_by_msin, co {'label': 'Job id', 'value': job_id}, {'label': 'Rater msin', 'value': rated_by_msin}, {'label': 'User msin', 'value': user_msin}, - {'label': 'Comments', 'value': comments}, + {'label': 'Comments', 'value': comments} ] + fields_old = [ + {'label': 'Rating', 'value': rating}, + {'label': 'User msin', 'value': user_msin}, + {'label': 'Job id', 'value': job_id}, + {'label': 'Rater msin', 'value': rated_by_msin}, + {'label': 'Comments', 'value': comments} + ] document_text = assemble_document('Rating', fields) - update_identifier = rating_identifier(fields) - look_for = '%{}'.format(update_identifier) - update_rating = rein.session.query(Document).filter(and_(Document.testnet == testnet, Document.contents.like(look_for), Document.doc_type == 'rating')).first() + look_for = '%{}'.format(rating_identifier(fields)) + look_for_old = '%{}'.format(rating_itentifier_old(fields_old)) + update_rating = rein.session.query(Document).filter(and_(Document.testnet == testnet, or_(Document.contents.like(look_for),Document.contents.like(look_for_old)), Document.doc_type == 'rating')).first() store = True document = None @@ -189,7 +204,8 @@ def calculate_trust_score(dest_msin=None, source_msin=None, rein=None, test=Fals if not test: ratings_by_source = rein.session.query(Document).filter(and_( Document.doc_type == 'rating', - Document.contents.like('%"Rater msin": "{}"%'.format(source_msin)) + or_(Document.contents.like('%\nRater msin: {}%'.format(source_msin)), + Document.contents.like('%"Rater msin": "{}"%'.format(source_msin)) )).all() else: @@ -222,8 +238,10 @@ def calculate_trust_score(dest_msin=None, source_msin=None, rein=None, test=Fals dest_ratings_by_vouched_user = rein.session.query(Document).filter( and_( Document.doc_type == 'rating', - Document.contents.like('%"Rater msin": "{}"%'.format(vouched_user_msin)), - Document.contents.like('%"User msin": "{}"%'.format(dest_msin)) + or_(Document.contents.like('%"Rater msin": "{}"%'.format(vouched_user_msin)), + Document.contents.like('%\nRater msin: {}%'.format(vouched_user_msin))), + or_(Document.contents.like('%"User msin": "{}"%'.format(dest_msin)), + Document.contents.like('%\nUser msin: {}%'.format(dest_msin))) )).all() else: dest_ratings_by_vouched_user = [test_rating for test_rating in test_ratings if test_rating['Rater msin'] == vouched_user_msin and test_rating['User msin'] == 'DestMsin'] From 41e436a547149a0696643c769fd196b0d2934637 Mon Sep 17 00:00:00 2001 From: Andrew K Date: Wed, 29 Mar 2017 22:27:18 +0000 Subject: [PATCH 11/42] first try for unique address generation --- rein/cli.py | 63 ++++++++++++++++++++++++++-------------- rein/lib/crypto/bip32.py | 11 +++++++ 2 files changed, 52 insertions(+), 22 deletions(-) diff --git a/rein/cli.py b/rein/cli.py index 701aacd..44ac9c1 100644 --- a/rein/cli.py +++ b/rein/cli.py @@ -209,7 +209,8 @@ def post(multi, identity, defaults, dry_run): {'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 creator delegate address', 'value': user.daddr}, ] document_text = assemble_document('Job', fields) print("document_text = "+document_text) @@ -1456,7 +1457,7 @@ def register_user(): rein.session.add(new_identity) rein.session.commit() - # Enroll user + # Enroll users enrollment = build_enrollment_from_dict(user_data) signature = sign(mprv,json.dumps(enrollment,sort_keys=True)) signed_enrollment = enrollment @@ -1706,7 +1707,7 @@ def job_post(): {'label': 'Mediator master address', 'value': mediator.maddr}, {'label': 'Job creator', 'value': user.name}, {'label': 'Job creator contact', 'value': user.contact}, - {'label': 'Job creator public key', 'value': key}, + {'label': 'Job creator public key', 'value': pubkey_for_escrow}, {'label': 'Job creator delegate address', 'value': user.daddr}, {'label': 'Job creator master address', 'value': user.maddr}, ] @@ -1810,19 +1811,35 @@ 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: + next_addr_index_str = '0' + next_addr_index = int(next_addr_index_str) + pubkey_for_escrow = generate_new_escrow_pubkey(user.dxprv,next_addr_index) + payment_address = generate_new_payment_address(user.dxprv,next_addr_index) + next_addr_index += 1 + PersistConfig.set(rein,'next_addr_index',str(next_addr_index)) + stmt = update(users).where(users.c.id==5).values(name='user #5') + worker_pubkey_for_escrow = bid['Worker public key for escrow'] + primary_redeem_script, primary_addr = build_2_of_3([pubkey_for_escrow,job['Mediator public key'],worker_pubkey_for_escrow]) + mediator_redeem_script, mediator_escrow_addr = build_mandatory_multisig(job['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 @@ -2118,8 +2135,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) @@ -2322,7 +2339,9 @@ 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} ] if key == doc['Job creator public key']: @@ -2423,13 +2442,15 @@ 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: + next_addr_index_str = '0' + next_addr_index = int(next_addr_index_str) + pubkey_for_escrow = generate_new_escrow_pubkey(user.dxprv,next_addr_index) + primary_payment_address = generate_new_payment_address(user.dxprv,next_addr_index) + next_addr_index += 1 + PersistConfig.set(rein,'next_addr_index',str(next_addr_index)) + fields = [ {'label': 'Job name', 'value_from': job}, {'label': 'Worker', 'value': user.name}, @@ -2439,15 +2460,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 @@ -2521,7 +2540,7 @@ def job_deliver(): mediatorRedeemScript = doc['Mediator escrow redeem script'] mediator_daddr = str(P2PKHBitcoinAddress.from_pubkey(x(doc['Mediator public key']))) 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']) (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) diff --git a/rein/lib/crypto/bip32.py b/rein/lib/crypto/bip32.py index 241f82e..c5730b9 100644 --- a/rein/lib/crypto/bip32.py +++ b/rein/lib/crypto/bip32.py @@ -86,3 +86,14 @@ def get_delegate_extended_key(mxprv): delegate_key = get_delegate_key(mxprv) return delegate_key.ExtendedKey() +def generate_new_payment_address(dxprv,i): + parent_key = get_child_key(dxprv, 0+BIP32_HARDEN) + subparent_key = get_child_key(parent_key,i) + target_key = get_child_key(parent_key,0) + return target_key.Address() + +def generate_new_escrow_pubkey(dxprv,i): + parent_key = get_child_key(dxprv, 1+BIP32_HARDEN) + subparent_key = get_child_key(parent_key,i) + target_key = get_child_key(parent_key,0) + return target_key.Address() From 5cd52f16c8fd040b4bdf2f535a7f0b6c33e58300 Mon Sep 17 00:00:00 2001 From: Andrew K Date: Wed, 29 Mar 2017 22:34:00 +0000 Subject: [PATCH 12/42] touch up some things --- rein/cli.py | 49 ++++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/rein/cli.py b/rein/cli.py index 44ac9c1..b2b3713 100644 --- a/rein/cli.py +++ b/rein/cli.py @@ -188,30 +188,29 @@ 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': '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: @@ -1457,7 +1456,7 @@ def register_user(): rein.session.add(new_identity) rein.session.commit() - # Enroll users + # Enroll user enrollment = build_enrollment_from_dict(user_data) signature = sign(mprv,json.dumps(enrollment,sort_keys=True)) signed_enrollment = enrollment From 2e9358d361525f2ba2a0f791921288d23da7b46a Mon Sep 17 00:00:00 2001 From: Andrew K Date: Sat, 1 Apr 2017 22:50:44 +0000 Subject: [PATCH 13/42] fix small bugs --- rein/cli.py | 4 ++-- rein/lib/rating.py | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/rein/cli.py b/rein/cli.py index b2b3713..f0d393f 100644 --- a/rein/cli.py +++ b/rein/cli.py @@ -1706,7 +1706,7 @@ def job_post(): {'label': 'Mediator master address', 'value': mediator.maddr}, {'label': 'Job creator', 'value': user.name}, {'label': 'Job creator contact', 'value': user.contact}, - {'label': 'Job creator public key', 'value': pubkey_for_escrow}, + {'label': 'Job creator public key', 'value': key}, {'label': 'Job creator delegate address', 'value': user.daddr}, {'label': 'Job creator master address', 'value': user.maddr}, ] @@ -1821,7 +1821,7 @@ def job_offer(): stmt = update(users).where(users.c.id==5).values(name='user #5') worker_pubkey_for_escrow = bid['Worker public key for escrow'] primary_redeem_script, primary_addr = build_2_of_3([pubkey_for_escrow,job['Mediator public key'],worker_pubkey_for_escrow]) - mediator_redeem_script, mediator_escrow_addr = build_mandatory_multisig(job['Mediator public key'],pubkey_for_escrow,worker_pubkey_for_escrow]) + mediator_redeem_script, mediator_escrow_addr = build_mandatory_multisig(job['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']}, diff --git a/rein/lib/rating.py b/rein/lib/rating.py index c3a5139..ab74d43 100644 --- a/rein/lib/rating.py +++ b/rein/lib/rating.py @@ -206,8 +206,7 @@ def calculate_trust_score(dest_msin=None, source_msin=None, rein=None, test=Fals Document.doc_type == 'rating', or_(Document.contents.like('%\nRater msin: {}%'.format(source_msin)), Document.contents.like('%"Rater msin": "{}"%'.format(source_msin)) - )).all() - + ))).all() else: ratings_by_source = [test_rating for test_rating in test_ratings if test_rating['Rater msin'] == 'SourceMsin'] From 9402af5e431b2fe760384ca60c08e9cce4144fef Mon Sep 17 00:00:00 2001 From: Andrew K Date: Sun, 2 Apr 2017 00:07:25 +0000 Subject: [PATCH 14/42] fix more bugs --- rein/cli.py | 16 +++++++++------- rein/lib/crypto/bip32.py | 10 +++++++--- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/rein/cli.py b/rein/cli.py index f0d393f..390db56 100644 --- a/rein/cli.py +++ b/rein/cli.py @@ -1390,7 +1390,7 @@ def start(multi, identity, setup): from .lib.bitcoinaddress import generate_sin host = '127.0.0.1' - port = 5002 + port = 5004 tmpl_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'html') @@ -1811,11 +1811,11 @@ def job_offer(): 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: + if not next_addr_index_str: next_addr_index_str = '0' next_addr_index = int(next_addr_index_str) - pubkey_for_escrow = generate_new_escrow_pubkey(user.dxprv,next_addr_index) - payment_address = generate_new_payment_address(user.dxprv,next_addr_index) + pubkey_for_escrow = bip32.generate_new_escrow_pubkey(bip32.from_xprv(user.dxprv),next_addr_index) + payment_address = 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)) stmt = update(users).where(users.c.id==5).values(name='user #5') @@ -2442,13 +2442,15 @@ def job_bid(): return redirect("/") next_addr_index_str = PersistConfig.get(rein, 'next_addr_index') - if not 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 = generate_new_escrow_pubkey(user.dxprv,next_addr_index) - primary_payment_address = generate_new_payment_address(user.dxprv,next_addr_index) + pubkey_for_escrow = bip32.generate_new_escrow_pubkey(bip32.from_xprv(user.dxprv),next_addr_index) + print("pubkey_for_escrow: "+pubkey_for_escrow) + primary_payment_address = 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)) + print("have set next_addr_index to "+str(next_addr_index)) fields = [ {'label': 'Job name', 'value_from': job}, diff --git a/rein/lib/crypto/bip32.py b/rein/lib/crypto/bip32.py index c5730b9..aab68e1 100644 --- a/rein/lib/crypto/bip32.py +++ b/rein/lib/crypto/bip32.py @@ -89,11 +89,15 @@ def get_delegate_extended_key(mxprv): def generate_new_payment_address(dxprv,i): parent_key = get_child_key(dxprv, 0+BIP32_HARDEN) subparent_key = get_child_key(parent_key,i) - target_key = get_child_key(parent_key,0) + target_key = get_child_key(subparent_key,0) return target_key.Address() def generate_new_escrow_pubkey(dxprv,i): + parent_key = get_child_key(dxprv, 1+BIP32_HARDEN) subparent_key = get_child_key(parent_key,i) - target_key = get_child_key(parent_key,0) - return target_key.Address() + target_key = get_child_key(subparent_key,0) + return hexlify(target_key.PublicKey()) + +def from_xprv(xkey): + return BIP32Key.fromExtendedKey(xkey) From 66b7777f7f7209a939cd2fd2a2042e8c61a5d00b Mon Sep 17 00:00:00 2001 From: Andrew K Date: Sun, 2 Apr 2017 00:35:52 +0000 Subject: [PATCH 15/42] fix the offer handler --- rein/cli.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/rein/cli.py b/rein/cli.py index 390db56..699ed51 100644 --- a/rein/cli.py +++ b/rein/cli.py @@ -1390,7 +1390,7 @@ def start(multi, identity, setup): from .lib.bitcoinaddress import generate_sin host = '127.0.0.1' - port = 5004 + port = 5002 tmpl_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'html') @@ -1818,10 +1818,9 @@ def job_offer(): payment_address = 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)) - stmt = update(users).where(users.c.id==5).values(name='user #5') worker_pubkey_for_escrow = bid['Worker public key for escrow'] - primary_redeem_script, primary_addr = build_2_of_3([pubkey_for_escrow,job['Mediator public key'],worker_pubkey_for_escrow]) - mediator_redeem_script, mediator_escrow_addr = build_mandatory_multisig(job['Mediator public key'],[pubkey_for_escrow,worker_pubkey_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']}, From e6be28486e7f20da8b15db27ba3de8bb761c26a0 Mon Sep 17 00:00:00 2001 From: Andrew K Date: Tue, 4 Apr 2017 23:25:05 +0000 Subject: [PATCH 16/42] save privkeys for escrow and wallet --- rein/cli.py | 43 ++++++++++++++++++++++++++++++---------- rein/lib/crypto/bip32.py | 4 ++-- rein/lib/pubkeys.py | 34 +++++++++++++++++++++++++++++++ rein/lib/transaction.py | 21 ++++++++++++++------ rein/lib/wallet.py | 34 +++++++++++++++++++++++++++++++ 5 files changed, 117 insertions(+), 19 deletions(-) create mode 100644 rein/lib/pubkeys.py create mode 100644 rein/lib/wallet.py diff --git a/rein/cli.py b/rein/cli.py index 699ed51..b110e25 100644 --- a/rein/cli.py +++ b/rein/cli.py @@ -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 @@ -1814,10 +1816,12 @@ def job_offer(): if not next_addr_index_str: next_addr_index_str = '0' next_addr_index = int(next_addr_index_str) - pubkey_for_escrow = bip32.generate_new_escrow_pubkey(bip32.from_xprv(user.dxprv),next_addr_index) - payment_address = bip32.generate_new_payment_address(bip32.from_xprv(user.dxprv),next_addr_index) + (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) + Wallet.set(rein,payment_address,payment_privkey) 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]) @@ -1910,8 +1914,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}, @@ -2016,11 +2022,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(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}, @@ -2150,6 +2161,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}, @@ -2339,7 +2352,9 @@ def job_dispute(): {'label': 'Worker 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': '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']: @@ -2440,15 +2455,17 @@ def job_bid(): flash('No matching Job ID found.') return redirect("/") - next_addr_index_str = PersistConfig.get(rein, 'next_addr_index') + 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 = bip32.generate_new_escrow_pubkey(bip32.from_xprv(user.dxprv),next_addr_index) + (pubkey_for_escrow,privkey_for_escrow) = bip32.generate_new_escrow_pubkey(bip32.from_xprv(user.dxprv),next_addr_index) print("pubkey_for_escrow: "+pubkey_for_escrow) - primary_payment_address = bip32.generate_new_payment_address(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) + Wallet.set(rein,primary_payment_address,primary_payment_privkey) print("have set next_addr_index to "+str(next_addr_index)) fields = [ @@ -2539,8 +2556,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,daddr=doc['Primary payment address']) + (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) @@ -2558,6 +2577,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}, diff --git a/rein/lib/crypto/bip32.py b/rein/lib/crypto/bip32.py index aab68e1..caedc22 100644 --- a/rein/lib/crypto/bip32.py +++ b/rein/lib/crypto/bip32.py @@ -90,14 +90,14 @@ def generate_new_payment_address(dxprv,i): parent_key = get_child_key(dxprv, 0+BIP32_HARDEN) subparent_key = get_child_key(parent_key,i) target_key = get_child_key(subparent_key,0) - return target_key.Address() + return (target_key.Address(),target_key.WalletImportFormat()) def generate_new_escrow_pubkey(dxprv,i): parent_key = get_child_key(dxprv, 1+BIP32_HARDEN) subparent_key = get_child_key(parent_key,i) target_key = get_child_key(subparent_key,0) - return hexlify(target_key.PublicKey()) + return (hexlify(target_key.PublicKey()),target_key.WalletImportFormat()) def from_xprv(xkey): return BIP32Key.fromExtendedKey(xkey) diff --git a/rein/lib/pubkeys.py b/rein/lib/pubkeys.py new file mode 100644 index 0000000..b086dc3 --- /dev/null +++ b/rein/lib/pubkeys.py @@ -0,0 +1,34 @@ +from sqlalchemy import Column, Integer, String, DateTime +from sqlalchemy.ext.declarative import declarative_base +import click + +Base = declarative_base() + +class Pubkeys(Base): + __tablename__ = 'pubkeys' + + pubkey = Column(String(34), primary_key=True) + privkey = Column(String(52)) + + def __init__(self, session, pubkey, privkey): + self.pubkey = pubkey + self.privkey = privkey + session.add(self) + session.commit() + + @classmethod + def get(self, rein, pubkey, default=False): + res = rein.session.query(Wallet).filter(Wallet.pubkey == pubkey).first() + if res: + return res.privkey + else: + return default + + @classmethod + def set(self, rein, pubkey, privkey=''): + res = rein.session.query(Wallet).filter(Wallet.pubkey == pubkey).first() + if res: + res.privkey = privkey + rein.session.commit() + else: + p = Wallet(rein.session, pubkey, privkey) diff --git a/rein/lib/transaction.py b/rein/lib/transaction.py index 06328b6..e8583f9 100644 --- a/rein/lib/transaction.py +++ b/rein/lib/transaction.py @@ -61,7 +61,7 @@ def broadcast_tx (tx_hex,rein): if data and 'txid' in data: return data['txid'] -def partial_spend_p2sh (redeemScript,rein,daddr=None,alt_amount=None,alt_daddr=None): +def partial_spend_p2sh (redeemScript,rein,daddr=None,alt_amount=None,alt_daddr=None,privkey=None): if daddr is None: daddr = rein.user.daddr txin_redeemScript = CScript(x(redeemScript)) @@ -90,7 +90,10 @@ def partial_spend_p2sh (redeemScript,rein,daddr=None,alt_amount=None,alt_daddr=N txouts.append(txout_alt) tx = CMutableTransaction(txins_obj, txouts) ntxins = len(txins_obj) - seckey = CBitcoinSecret(rein.user.dkey) + if privkey: + seckey = CBitcoinSecret(privkey) + else: + seckey = CBitcoinSecret(rein.user.dkey) sig = ""; for i in range(0,ntxins): sighash = SignatureHash(txin_redeemScript, tx, i, SIGHASH_ALL) @@ -127,7 +130,7 @@ def partial_spend_p2sh_mediator (redeemScript,rein,mediator_address,mediator_sig return (txins_str[1:],"{:.8f}".format(amount),str(mediator_address),sig[1:]) return (txins_str[1:],"{:.8f}".format(amount),str(mediator_address)) -def partial_spend_p2sh_mediator_2 (redeemScript,txins_str,amount,daddr,rein): +def partial_spend_p2sh_mediator_2 (redeemScript,txins_str,amount,daddr,rein,privkey=None): txin_redeemScript = CScript(x(redeemScript)) txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey() txins_obj = [] @@ -136,7 +139,10 @@ def partial_spend_p2sh_mediator_2 (redeemScript,txins_str,amount,daddr,rein): txins_obj.append(CMutableTxIn(COutPoint(lx(txin_list[0]),int(txin_list[1])))) txout = CMutableTxOut(amount*COIN,CBitcoinAddress(daddr).to_scriptPubKey()) tx = CMutableTransaction(txins_obj,[txout]) - seckey = CBitcoinSecret(rein.user.dkey) + if privkey: + seckey = CBitcoinSecret(privkey) + else: + seckey = CBitcoinSecret(rein.user.dkey) ntxins = len(txins_obj) for i in range(0,ntxins): sighash = SignatureHash(txin_redeemScript,tx,i,SIGHASH_ALL) @@ -146,7 +152,7 @@ def partial_spend_p2sh_mediator_2 (redeemScript,txins_str,amount,daddr,rein): tx_bytes = tx.serialize() return b2x(tx_bytes) -def spend_p2sh (redeemScript,txins_str,amounts,daddrs,sig,rein,reverse_sigs=False): +def spend_p2sh (redeemScript,txins_str,amounts,daddrs,sig,rein,reverse_sigs=False,privkey=None): txin_redeemScript = CScript(x(redeemScript)) txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey() txins_obj = [] @@ -158,7 +164,10 @@ def spend_p2sh (redeemScript,txins_str,amounts,daddrs,sig,rein,reverse_sigs=Fals for i in range(0,len_amounts): txouts.append(CMutableTxOut(round(amounts[i],8)*COIN,CBitcoinAddress(daddrs[i]).to_scriptPubKey())) tx = CMutableTransaction(txins_obj,txouts) - seckey = CBitcoinSecret(rein.user.dkey) + if privkey: + seckey = CBitcoinSecret(privkey) + else: + seckey = CBitcoinSecret(rein.user.dkey) ntxins = len(txins_obj) sig_list = [] for s in sig.split(): diff --git a/rein/lib/wallet.py b/rein/lib/wallet.py new file mode 100644 index 0000000..a64989b --- /dev/null +++ b/rein/lib/wallet.py @@ -0,0 +1,34 @@ +from sqlalchemy import Column, Integer, String, DateTime +from sqlalchemy.ext.declarative import declarative_base +import click + +Base = declarative_base() + +class Wallet(Base): + __tablename__ = 'wallet' + + address = Column(String(34), primary_key=True) + privkey = Column(String(52)) + + def __init__(self, session, pubkey, privkey): + self.pubkey = pubkey + self.privkey = privkey + session.add(self) + session.commit() + + @classmethod + def get(self, rein, address, default=False): + res = rein.session.query(Wallet).filter(Wallet.address == address).first() + if res: + return res.privkey + else: + return default + + @classmethod + def set(self, rein, address, privkey=''): + res = rein.session.query(Wallet).filter(Wallet.address == address).first() + if res: + res.privkey = privkey + rein.session.commit() + else: + p = Wallet(rein.session, address, privkey) From a3634e47445b80cfbb56eb18d9c3e585c436c9d7 Mon Sep 17 00:00:00 2001 From: Andrew K Date: Tue, 4 Apr 2017 23:44:34 +0000 Subject: [PATCH 17/42] fix reference to Wallet in pubkeys.py --- rein/cli.py | 2 +- rein/lib/pubkeys.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rein/cli.py b/rein/cli.py index b110e25..35f00c1 100644 --- a/rein/cli.py +++ b/rein/cli.py @@ -2162,7 +2162,7 @@ def job_resolve(): {'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': '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}, diff --git a/rein/lib/pubkeys.py b/rein/lib/pubkeys.py index b086dc3..f639ed1 100644 --- a/rein/lib/pubkeys.py +++ b/rein/lib/pubkeys.py @@ -18,7 +18,7 @@ def __init__(self, session, pubkey, privkey): @classmethod def get(self, rein, pubkey, default=False): - res = rein.session.query(Wallet).filter(Wallet.pubkey == pubkey).first() + res = rein.session.query(Pubkeys).filter(Pubkeys.pubkey == pubkey).first() if res: return res.privkey else: @@ -26,9 +26,9 @@ def get(self, rein, pubkey, default=False): @classmethod def set(self, rein, pubkey, privkey=''): - res = rein.session.query(Wallet).filter(Wallet.pubkey == pubkey).first() + res = rein.session.query(Pubkeys).filter(Pubkeys.pubkey == pubkey).first() if res: res.privkey = privkey rein.session.commit() else: - p = Wallet(rein.session, pubkey, privkey) + p = Pubkeys(rein.session, pubkey, privkey) From b43008a1a979eec228451fff5a4149dd35583cce Mon Sep 17 00:00:00 2001 From: Andrew K Date: Wed, 5 Apr 2017 00:00:28 +0000 Subject: [PATCH 18/42] fix wallet class --- rein/lib/wallet.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rein/lib/wallet.py b/rein/lib/wallet.py index a64989b..e2346a3 100644 --- a/rein/lib/wallet.py +++ b/rein/lib/wallet.py @@ -10,8 +10,8 @@ class Wallet(Base): address = Column(String(34), primary_key=True) privkey = Column(String(52)) - def __init__(self, session, pubkey, privkey): - self.pubkey = pubkey + def __init__(self, session, address, privkey): + self.address = address self.privkey = privkey session.add(self) session.commit() From 6796444056a73f236110d44985bb0235e2e124a8 Mon Sep 17 00:00:00 2001 From: Andrew K Date: Wed, 5 Apr 2017 00:06:38 +0000 Subject: [PATCH 19/42] create pubkeys and wallet tables --- rein/lib/models.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rein/lib/models.py b/rein/lib/models.py index ca86f73..1a1f6a0 100644 --- a/rein/lib/models.py +++ b/rein/lib/models.py @@ -41,4 +41,10 @@ from .hidden_content import Base Base.metadata.create_all(engine) +from .pubkeys import Base +Base.metadata.create_all(engine) + +from .wallet import Base +Base.metadata.create_all(engine) + log.info('database tables updated') From db8b5f81cff0d1b7c7a9866577c97ed8c10af5b7 Mon Sep 17 00:00:00 2001 From: Andrew K Date: Sat, 8 Apr 2017 00:21:22 +0000 Subject: [PATCH 20/42] prepare wallet feature --- rein/cli.py | 22 ++++++++++++++++++++-- rein/html/wallet.html | 36 ++++++++++++++++++++++++++++++++++++ rein/lib/transaction.py | 12 +++++++++--- 3 files changed, 65 insertions(+), 5 deletions(-) create mode 100644 rein/html/wallet.html diff --git a/rein/cli.py b/rein/cli.py index 35f00c1..bf1272a 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 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 @@ -1392,7 +1392,7 @@ def start(multi, identity, setup): from .lib.bitcoinaddress import generate_sin host = '127.0.0.1' - port = 5002 + port = 5004 tmpl_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'html') @@ -1640,6 +1640,23 @@ 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) + balance = 0. + txs = [] + for we in wallet_entries: + (txins,value) = unspent_txins(rein,we.address,rein.testnet,txin_value=True) + tx = {} + for (txid,txvalue) in txins: + tx['txid'] = txid + tx['value'] = txvalue + print("txvalue = "+str(txvalue)) + txs.append(tx) + balance += value + return render_template('wallet.html', user=user, fee=fee, balance=balance, txs=txs) + @app.route("/post", methods=['POST', 'GET']) def job_post(): form = JobPostForm(request.form) @@ -2465,6 +2482,7 @@ def job_bid(): next_addr_index += 1 PersistConfig.set(rein,'next_addr_index',str(next_addr_index)) Pubkeys.set(rein,pubkey_for_escrow,privkey_for_escrow) + print("primary payment address = "+primary_payment_address) Wallet.set(rein,primary_payment_address,primary_payment_privkey) print("have set next_addr_index to "+str(next_addr_index)) diff --git a/rein/html/wallet.html b/rein/html/wallet.html new file mode 100644 index 0000000..ff6e570 --- /dev/null +++ b/rein/html/wallet.html @@ -0,0 +1,36 @@ +{% extends "layout.html" %} +{% from "_form_helpers.html" import render_error %} +{% block body %} + + + +
+
+

Wallet

+
+ Balance: {{ balance }} +
+ {% if txs %} +

Transactions

+ + + + + + + + {% for tx in txs %} + + + + + + {% endfor %} + +
txidvaluejob id
{{ tx.txid }}{{ tx.value }}{{ tx.job_id }}
+ {% else %} +

No transactions.

+ {% endif %} +
+ +{% endblock %} diff --git a/rein/lib/transaction.py b/rein/lib/transaction.py index e8583f9..245ec0b 100644 --- a/rein/lib/transaction.py +++ b/rein/lib/transaction.py @@ -11,7 +11,7 @@ import click -def unspent_txins(rein, address, testnet): +def unspent_txins(rein, address, testnet, txin_value=False): api = PersistConfig.get(rein, 'api', 'blockr') if (api == "blockr"): @@ -30,7 +30,10 @@ def unspent_txins(rein, address, testnet): vout = tx['n']; value = float(tx['amount']); total_value += value; - txins.append((txid, vout)) + if txin_value: + txins.append((txid, value)) + else: + txins.append((txid, vout)) else: if testnet: url = "https://api.blocktrail.com/v1/tbtc/address/"+str(address)+"/unspent-outputs?api_key=1e1ebd7ae629e031310ae9d61fe8549c82d0c589" @@ -47,7 +50,10 @@ def unspent_txins(rein, address, testnet): vout = tx['index']; value = tx['value']/100000000.; total_value += value; - txins.append((txid, vout)) + if txin_value: + txins.append((txid, value)) + else: + txins.append((txid, vout)) return (txins,total_value) From c285a620c921b5a845d89e6249a66fd258ce1465 Mon Sep 17 00:00:00 2001 From: Andrew K Date: Sat, 8 Apr 2017 00:37:46 +0000 Subject: [PATCH 21/42] keep track of job id in wallet --- rein/cli.py | 12 +++++++----- rein/html/wallet.html | 12 ++++++------ rein/lib/wallet.py | 8 +++++--- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/rein/cli.py b/rein/cli.py index bf1272a..cd0af4d 100644 --- a/rein/cli.py +++ b/rein/cli.py @@ -1647,14 +1647,16 @@ def wallet(): balance = 0. txs = [] for we in wallet_entries: + ref = we.ref + txs_we = [] (txins,value) = unspent_txins(rein,we.address,rein.testnet,txin_value=True) - tx = {} for (txid,txvalue) in txins: + tx = {} tx['txid'] = txid tx['value'] = txvalue - print("txvalue = "+str(txvalue)) - txs.append(tx) + txs_we.append(tx) balance += value + txs.append(tx) return render_template('wallet.html', user=user, fee=fee, balance=balance, txs=txs) @app.route("/post", methods=['POST', 'GET']) @@ -1838,7 +1840,7 @@ def job_offer(): next_addr_index += 1 PersistConfig.set(rein,'next_addr_index',str(next_addr_index)) Pubkeys.set(rein,pubkey_for_escrow,privkey_for_escrow) - Wallet.set(rein,payment_address,payment_privkey) + Wallet.set(rein,payment_address,payment_privkey,ref=bid['Job 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]) @@ -2483,7 +2485,7 @@ def job_bid(): PersistConfig.set(rein,'next_addr_index',str(next_addr_index)) Pubkeys.set(rein,pubkey_for_escrow,privkey_for_escrow) print("primary payment address = "+primary_payment_address) - Wallet.set(rein,primary_payment_address,primary_payment_privkey) + Wallet.set(rein,primary_payment_address,primary_payment_privkey,ref=job['Job ID']) print("have set next_addr_index to "+str(next_addr_index)) fields = [ diff --git a/rein/html/wallet.html b/rein/html/wallet.html index ff6e570..5d5b9c8 100644 --- a/rein/html/wallet.html +++ b/rein/html/wallet.html @@ -11,23 +11,23 @@

Wallet

Balance: {{ balance }}
{% if txs %} -

Transactions

+ {% for txj in txs %} +

Transactions for Job {{ txj[0].ref }}/h3> - + - - {% for tx in txs %} + {% for tx in txj %} - + - {% endfor %}
txidtxid valuejob id
{{ tx.txid }}{{ tx.txid }} {{ tx.value }}{{ tx.job_id }}
+ {% endfor %} {% else %}

No transactions.

{% endif %} diff --git a/rein/lib/wallet.py b/rein/lib/wallet.py index e2346a3..7f59851 100644 --- a/rein/lib/wallet.py +++ b/rein/lib/wallet.py @@ -9,10 +9,12 @@ class Wallet(Base): address = Column(String(34), primary_key=True) privkey = Column(String(52)) + ref = Column(String(64)) - def __init__(self, session, address, privkey): + def __init__(self, session, address, privkey, ref=None): self.address = address self.privkey = privkey + self.ref = ref session.add(self) session.commit() @@ -25,10 +27,10 @@ def get(self, rein, address, default=False): return default @classmethod - def set(self, rein, address, privkey=''): + def set(self, rein, address, privkey='', ref=None): res = rein.session.query(Wallet).filter(Wallet.address == address).first() if res: res.privkey = privkey rein.session.commit() else: - p = Wallet(rein.session, address, privkey) + p = Wallet(rein.session, address, privkey, ref=ref) From 103d0c36005dd14db13c25b979aec4fd35de1ecb Mon Sep 17 00:00:00 2001 From: Andrew K Date: Sat, 8 Apr 2017 23:18:54 +0000 Subject: [PATCH 22/42] prepare for withdrawl transaction --- rein/cli.py | 29 ++++++++++++++++++++++++----- rein/html/js/wallet.js | 25 +++++++++++++++++++++++++ rein/html/layout.html | 18 ++++++++++++------ rein/html/wallet.html | 27 +++++++++++++++++++++------ rein/lib/transaction.py | 9 +++++++-- 5 files changed, 89 insertions(+), 19 deletions(-) create mode 100644 rein/html/js/wallet.js diff --git a/rein/cli.py b/rein/cli.py index cd0af4d..7c0b55b 100644 --- a/rein/cli.py +++ b/rein/cli.py @@ -1647,17 +1647,36 @@ def wallet(): balance = 0. txs = [] for we in wallet_entries: - ref = we.ref - txs_we = [] + 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'] = txvalue - txs_we.append(tx) + txs_we['txs'].append(tx) balance += value - txs.append(tx) - return render_template('wallet.html', user=user, fee=fee, balance=balance, txs=txs) + 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=balance, 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'] + print("withdraw from job "+job_id+" "+amount+" "+destaddr) + withdraw_from_job(job_id,float(amount),destaddr) + except: + return 'false' + return 'true' @app.route("/post", methods=['POST', 'GET']) def job_post(): 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 0b476d4..2185464 100644 --- a/rein/html/layout.html +++ b/rein/html/layout.html @@ -153,12 +153,18 @@ -
  • -
  • - - -
  • - +
  • +
  • + + +
  • + +
  • +
  • + + +
  • + diff --git a/rein/html/wallet.html b/rein/html/wallet.html index 5d5b9c8..ecc3b45 100644 --- a/rein/html/wallet.html +++ b/rein/html/wallet.html @@ -2,31 +2,46 @@ {% from "_form_helpers.html" import render_error %} {% block body %} - +

    Wallet


    - Balance: {{ balance }} + Overall Balance: {{balance}}
    {% if txs %} {% for txj in txs %} -

    Transactions for Job {{ txj[0].ref }}/h3> +
    +

    Unspent Payments for Job {{txj.ref}}

    - {% for tx in txj %} + {% for tx in txj.txs %} - - + + {% endfor %}
    txid value
    {{ tx.txid }}{{ tx.value }}{{tx.txid}}{{tx.value}}
    +
    +

     Withdraw from this job's payments

    +
    +   + +
    +
    +   + +
    +
    +   +
    +
    {% endfor %} {% else %}

    No transactions.

    diff --git a/rein/lib/transaction.py b/rein/lib/transaction.py index 245ec0b..68f7955 100644 --- a/rein/lib/transaction.py +++ b/rein/lib/transaction.py @@ -10,7 +10,6 @@ from .persistconfig import PersistConfig import click - def unspent_txins(rein, address, testnet, txin_value=False): api = PersistConfig.get(rein, 'api', 'blockr') @@ -224,5 +223,11 @@ def spend_p2sh_mediator (redeemScript,txins_str,amounts,daddrs,sig,rein): txid_causeway = broadcast_tx(b2x(tx_bytes),rein) return (txid,sig2_str[1:]) +def withdraw_from_job (job_id, amount, destaddr): + + txins_obj = [] + wallet_entries = rein.session.query(Wallet).filter(Wallet.ref == job_id) + for we in wallet_entries: + print "considering address "+we.address+" for spending transaction" - + From 19fd0fa038d283c9af42fc325a7d3b5dc15350dc Mon Sep 17 00:00:00 2001 From: Andrew K Date: Sun, 9 Apr 2017 04:25:52 +0000 Subject: [PATCH 23/42] finish up wallet feature --- rein/cli.py | 27 +++++++++++++++------- rein/html/wallet.html | 4 ++-- rein/lib/crypto/bip32.py | 25 +++++++++++++++++++-- rein/lib/transaction.py | 48 +++++++++++++++++++++++++++++++++++----- 4 files changed, 86 insertions(+), 18 deletions(-) diff --git a/rein/cli.py b/rein/cli.py index 7c0b55b..53bbc04 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, unspent_txins +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 @@ -1647,9 +1647,18 @@ def wallet(): balance = 0. txs = [] for we in wallet_entries: - txs_we = {} - txs_we['ref'] = we.ref - txs_we['txs'] = [] + 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 = {} @@ -1657,7 +1666,8 @@ def wallet(): tx['value'] = txvalue txs_we['txs'].append(tx) balance += value - txs.append(txs_we) + if new_job: + txs.append(txs_we) explorer = PersistConfig.get(rein, 'explorer', @@ -1672,9 +1682,10 @@ def withdraw(): job_id = request.json['job'] amount = request.json['amount'] destaddr = request.json['addr'] - print("withdraw from job "+job_id+" "+amount+" "+destaddr) - withdraw_from_job(job_id,float(amount),destaddr) - except: + 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' diff --git a/rein/html/wallet.html b/rein/html/wallet.html index ecc3b45..992e844 100644 --- a/rein/html/wallet.html +++ b/rein/html/wallet.html @@ -12,7 +12,7 @@

    Wallet


    {% if txs %} {% for txj in txs %} -
    +

    Unspent Payments for Job {{txj.ref}}

    @@ -36,7 +36,7 @@

     Withdraw from this job's payments

    - +
    diff --git a/rein/lib/crypto/bip32.py b/rein/lib/crypto/bip32.py index caedc22..0fe22ef 100644 --- a/rein/lib/crypto/bip32.py +++ b/rein/lib/crypto/bip32.py @@ -8,6 +8,8 @@ import hmac import os import rein.lib.config as config +from rein.lib.wallet import Wallet +from rein.lib.persistconfig import PersistConfig # TODO Make Python index this file automatically script_dir = os.path.dirname(__file__) @@ -92,8 +94,7 @@ def generate_new_payment_address(dxprv,i): target_key = get_child_key(subparent_key,0) return (target_key.Address(),target_key.WalletImportFormat()) -def generate_new_escrow_pubkey(dxprv,i): - +def generate_new_escrow_pubkey(dxprv,i): parent_key = get_child_key(dxprv, 1+BIP32_HARDEN) subparent_key = get_child_key(parent_key,i) target_key = get_child_key(subparent_key,0) @@ -101,3 +102,23 @@ def generate_new_escrow_pubkey(dxprv,i): def from_xprv(xkey): return BIP32Key.fromExtendedKey(xkey) + +def get_new_change_address(dxprv,ref): + wallet_entries = rein.session.query(Wallet).filter(Wallet.ref == ref).all() + j = len(wallet_entries) + print("j = "+str(j)) + if j > 0: + parent_key = get_child_key(dxprv,0+BIP32_HARDEN) + next_addr_index = int(PersistConfig.get(rein, 'next_addr_index')) + for i in range(next_addr_index): + subparent_key = get_child_key(parent_key,i) + target_key_address = get_child_key(subparent_key,0).Address() + for we in wallet_entries: + wallet_address = we.address + if wallet_address == target_key_address: + change_key = get_child_key(subparent_key,j) + change_key_address = change_key.Address() + change_key_privkey = change_key.WalletImportFormat() + Wallet.set(rein,change_key_address,change_key_privkey,ref=ref) + return change_key_address + return None diff --git a/rein/lib/transaction.py b/rein/lib/transaction.py index 68f7955..59f89fa 100644 --- a/rein/lib/transaction.py +++ b/rein/lib/transaction.py @@ -9,6 +9,7 @@ from .io import safe_get from .persistconfig import PersistConfig import click +from .crypto.bip32 import get_new_change_address, from_xprv def unspent_txins(rein, address, testnet, txin_value=False): api = PersistConfig.get(rein, 'api', 'blockr') @@ -223,11 +224,46 @@ def spend_p2sh_mediator (redeemScript,txins_str,amounts,daddrs,sig,rein): txid_causeway = broadcast_tx(b2x(tx_bytes),rein) return (txid,sig2_str[1:]) -def withdraw_from_job (job_id, amount, destaddr): - +def withdraw_from_wallet (rein, wallet_entries, amount, destaddr): txins_obj = [] - wallet_entries = rein.session.query(Wallet).filter(Wallet.ref == job_id) + scriptpubkeys = [] + seckeys = [] + total_value = 0. for we in wallet_entries: - print "considering address "+we.address+" for spending transaction" - - + address = we.address + privkey = we.privkey + (txins,entry_value) = unspent_txins(rein, address, rein.testnet) + total_value += entry_value + for txid,vout in txins: + txins_obj.append(CMutableTxIn(COutPoint(lx(txid),vout))) + scriptpubkeys.append(CBitcoinAddress(address).to_scriptPubKey()) + seckeys.append(CBitcoinSecret(privkey)) + fee = float(PersistConfig.get(rein, 'fee', 0.001)) + total_amount = round(total_value-fee,8) + if total_amount <= 0: + raise ValueError('There is not enough funds available to withdraw') + amount_requested = round(amount,8) + if (amount_requested > total_amount+fee): + raise ValueError('The value of your funds for this job is less than what you requested') + change_address = None + if (amount_requested < total_amount): + change_address = get_new_change_address(from_xprv(rein.user.dxprv),wallet_entries[0].ref) + if change_address is None: + raise ValueError('Error generating a change address') + txouts = [] + txout = CMutableTxOut(amount_requested*COIN, CBitcoinAddress(destaddr).to_scriptPubKey()) + txouts.append(txout) + if change_address: + txout_change = CMutableTxOut(round(total_value-fee-amount_requested,8)*COIN, CBitcoinAddress(change_address).to_scriptPubKey()) + txouts.append(txout_change) + tx = CMutableTransaction(txins_obj, txouts) + ntxins = len(txins_obj) + for i in range(0,ntxins): + sighash = SignatureHash(scriptpubkeys[i],tx,i,SIGHASH_ALL) + sig = seckeys[i].sign(sighash)+x("01") + txins_obj[i].scriptSig = CScript([sig, seckeys[i].pub]) + VerifyScript(txins_obj[i].scriptSig,scriptpubkeys[i],tx,i,(SCRIPT_VERIFY_P2SH,)) + tx_bytes = tx.serialize() + hash = sha256(sha256(tx_bytes).digest()).digest() + txid = b2x(hash[::-1]) + txid_causeway = broadcast_tx(b2x(tx_bytes),rein) From 671ded09ac1847a0360f3414493717f240f7ee6b Mon Sep 17 00:00:00 2001 From: Andrew K Date: Sun, 9 Apr 2017 05:41:18 +0000 Subject: [PATCH 24/42] fix payments for disputes --- rein/cli.py | 4 ++-- rein/lib/transaction.py | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/rein/cli.py b/rein/cli.py index 53bbc04..d49b877 100644 --- a/rein/cli.py +++ b/rein/cli.py @@ -1392,7 +1392,7 @@ def start(multi, identity, setup): from .lib.bitcoinaddress import generate_sin host = '127.0.0.1' - port = 5004 + port = 5002 tmpl_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'html') @@ -2078,7 +2078,7 @@ def job_acceptresolution(): 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(pubkey_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 = [ diff --git a/rein/lib/transaction.py b/rein/lib/transaction.py index 59f89fa..6c53062 100644 --- a/rein/lib/transaction.py +++ b/rein/lib/transaction.py @@ -194,7 +194,7 @@ def spend_p2sh (redeemScript,txins_str,amounts,daddrs,sig,rein,reverse_sigs=Fals txid_causeway = broadcast_tx(b2x(tx_bytes),rein) return (txid,sig2_str[1:]) -def spend_p2sh_mediator (redeemScript,txins_str,amounts,daddrs,sig,rein): +def spend_p2sh_mediator (redeemScript,txins_str,amounts,daddrs,sig,rein,privkey=None): txin_redeemScript = CScript(x(redeemScript)) txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey() txins_obj = [] @@ -206,7 +206,10 @@ def spend_p2sh_mediator (redeemScript,txins_str,amounts,daddrs,sig,rein): for i in range(0,len_amounts): txouts.append(CMutableTxOut(round(amounts[i],8)*COIN,CBitcoinAddress(daddrs[i]).to_scriptPubKey())) tx = CMutableTransaction(txins_obj,txouts) - seckey = CBitcoinSecret(rein.user.dkey) + if privkey: + seckey = CBitcoinSecret(privkey) + else: + seckey = CBitcoinSecret(rein.user.dkey) ntxins = len(txins_obj) sig_list = [] for s in sig.split(): From 850fc7f644700229c0c8444a2f42fd08e0371a1e Mon Sep 17 00:00:00 2001 From: Andrew K Date: Sun, 9 Apr 2017 06:35:02 +0000 Subject: [PATCH 25/42] enable fresh addresses/pubkeys in cli mode --- rein/cli.py | 85 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 64 insertions(+), 21 deletions(-) diff --git a/rein/cli.py b/rein/cli.py index d49b877..075a87c 100644 --- a/rein/cli.py +++ b/rein/cli.py @@ -289,11 +289,18 @@ 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) + Wallet.set(rein,primary_payment_address,primary_payment_privkey,ref=job['Job ID']) + fields = [ {'label': 'Job name', 'value_from': job}, {'label': 'Worker', 'value': user.name}, @@ -309,8 +316,8 @@ def bid(multi, identity, defaults, dry_run): {'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) @@ -372,6 +379,19 @@ 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) + Wallet.set(rein,payment_address,payment_privkey,ref=bid['Job 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}, @@ -384,8 +404,13 @@ def offer(multi, identity, defaults, dry_run): {'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?'): @@ -441,7 +466,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}, @@ -455,6 +482,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}, @@ -518,8 +547,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}, @@ -589,7 +620,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) @@ -640,7 +675,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) @@ -709,8 +748,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) @@ -721,6 +760,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}, @@ -803,10 +844,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}, @@ -2509,14 +2555,11 @@ def job_bid(): 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) - print("pubkey_for_escrow: "+pubkey_for_escrow) (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) - print("primary payment address = "+primary_payment_address) Wallet.set(rein,primary_payment_address,primary_payment_privkey,ref=job['Job ID']) - print("have set next_addr_index to "+str(next_addr_index)) fields = [ {'label': 'Job name', 'value_from': job}, From 435cf68d6bf86a642c960a0590d7cff1528db94d Mon Sep 17 00:00:00 2001 From: Andrew K Date: Sun, 9 Apr 2017 06:44:10 +0000 Subject: [PATCH 26/42] fix typos --- rein/cli.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rein/cli.py b/rein/cli.py index 075a87c..ee0e79a 100644 --- a/rein/cli.py +++ b/rein/cli.py @@ -289,7 +289,6 @@ def bid(multi, identity, defaults, dry_run): return log.info('got job for bid') - [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' @@ -391,7 +390,7 @@ def offer(multi, identity, defaults, dry_run): Wallet.set(rein,payment_address,payment_privkey,ref=bid['Job 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]) + 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}, From a34768d8460b6b9b555a82342a4616ad4c705dcb Mon Sep 17 00:00:00 2001 From: Andrew K Date: Sun, 9 Apr 2017 07:12:45 +0000 Subject: [PATCH 27/42] fix bugs --- rein/cli.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/rein/cli.py b/rein/cli.py index ee0e79a..9e2de3f 100644 --- a/rein/cli.py +++ b/rein/cli.py @@ -207,6 +207,7 @@ def post(multi, identity, defaults, dry_run): {'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}, @@ -308,8 +309,6 @@ 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}, @@ -396,8 +395,8 @@ def offer(multi, identity, defaults, dry_run): {'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}, From 3907f40a96e8c7afe7b1d190cd32f716d8f87a83 Mon Sep 17 00:00:00 2001 From: FreakJoe Date: Wed, 15 Mar 2017 21:04:34 +0100 Subject: [PATCH 28/42] Search box and set up for api call --- rein/cli.py | 6 ++++++ rein/html/css/style_v2.css | 6 ++++++ rein/html/js/search.js | 18 ++++++++++++++++++ rein/html/layout.html | 7 +++++++ 4 files changed, 37 insertions(+) create mode 100644 rein/html/js/search.js diff --git a/rein/cli.py b/rein/cli.py index 9e2de3f..a91e51d 100644 --- a/rein/cli.py +++ b/rein/cli.py @@ -1684,6 +1684,7 @@ 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) +<<<<<<< HEAD @app.route("/wallet", methods=['GET']) def wallet(): fee = float(PersistConfig.get(rein, 'fee', 0.001)) @@ -1733,6 +1734,11 @@ def withdraw(): return 'false' return 'true' + @app.route('/user_search/', methods=['GET']) + def user_search(search_input): + if not search_input: + return 'false' + @app.route("/post", methods=['POST', 'GET']) def job_post(): form = JobPostForm(request.form) diff --git a/rein/html/css/style_v2.css b/rein/html/css/style_v2.css index 5e6f42c..cbf21b4 100644 --- a/rein/html/css/style_v2.css +++ b/rein/html/css/style_v2.css @@ -2143,6 +2143,12 @@ border-radius: 50%;} right: 36px; background: url(../img/chevron-right.png) 0 0 no-repeat; } +ul li input { + line-height: normal; +} +ul li button { + line-height: normal; +} #social a { margin:0 15px 0 0; color:#e6e6e9; diff --git a/rein/html/js/search.js b/rein/html/js/search.js new file mode 100644 index 0000000..e59edbe --- /dev/null +++ b/rein/html/js/search.js @@ -0,0 +1,18 @@ +function search(inputId) { + searchInput = $("#" + inputId).val(); + if (searchInput == "") { + alert('Please enter user name, msin or contact information of the person you\'re trying to find.') + } + + $.ajax({ + method: "GET", + url: "/user_search/" + searchInput, + contentType: "application/json" + success: function(data) { + alert(data); + if (data != 'true') { + alert('User could not be found') + } + } + }) +} \ No newline at end of file diff --git a/rein/html/layout.html b/rein/html/layout.html index 2185464..ac678ab 100644 --- a/rein/html/layout.html +++ b/rein/html/layout.html @@ -61,6 +61,12 @@
    about
    + + + + + + + + {% for user in users %} + + + + + + + {% endfor %} + +
    HandleContactSINRating
    {{ user.User|safe }}{{ user.Contact|safe }}{{ user.MSIN|safe }}{{ user.Rating|safe }}
    +

    +
    + +{% endblock %} \ No newline at end of file diff --git a/rein/html/js/search.js b/rein/html/js/search.js index 144c120..ac2c182 100644 --- a/rein/html/js/search.js +++ b/rein/html/js/search.js @@ -9,8 +9,10 @@ function search(inputId) { url: "/user_search/" + searchInput, contentType: "application/json", success: function(data) { - if (data != 'true') { - alert('User could not be found') + if (data == 'false') { + alert('User could not be found'); + } else { + document.location = '/display-users?data=' + data; } } }) From 62139e831a37dba56cb31d9e05a0b12aeec114fe Mon Sep 17 00:00:00 2001 From: FreakJoe Date: Sat, 18 Mar 2017 14:43:10 +0100 Subject: [PATCH 31/42] Table sorting --- rein/cli.py | 2 +- rein/html/display-users.html | 6 ++++-- rein/html/js/displayUsers.js | 9 +++++++++ rein/html/layout.html | 2 ++ .../html/plugins/datatables/dataTables.bootstrap.min.css | 1 + rein/html/plugins/datatables/dataTables.bootstrap.min.js | 8 ++++++++ 6 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 rein/html/js/displayUsers.js create mode 100644 rein/html/plugins/datatables/dataTables.bootstrap.min.css create mode 100644 rein/html/plugins/datatables/dataTables.bootstrap.min.js diff --git a/rein/cli.py b/rein/cli.py index d8f2bf9..11d346c 100644 --- a/rein/cli.py +++ b/rein/cli.py @@ -1749,7 +1749,7 @@ def display_users(): enrollment = document_to_dict(enrollment) enrolled_user['User'] = '{}'.format(enrollment['Master signing address'], enrollment['User']) enrolled_user['Contact'] = '{}'.format(enrollment['Contact']) - enrolled_user['MSIN'] = enrollment['Master signing address'] + enrolled_user['MSIN'] = enrollment['Secure Identity Number'] enrolled_user['Rating'] = get_average_user_rating_display(log, url, user, rein, enrolled_user['MSIN']) users.append(enrolled_user) diff --git a/rein/html/display-users.html b/rein/html/display-users.html index c9b1af8..2301aa4 100644 --- a/rein/html/display-users.html +++ b/rein/html/display-users.html @@ -2,11 +2,13 @@ {% from "_form_helpers.html" import render_error %} {% block body %} + + +

    - name/handle (linked to /profile/), contact (linked with mailto:), SIN, simple rating - +
    diff --git a/rein/html/js/displayUsers.js b/rein/html/js/displayUsers.js new file mode 100644 index 0000000..34f3eda --- /dev/null +++ b/rein/html/js/displayUsers.js @@ -0,0 +1,9 @@ +document.addEventListener("DOMContentLoaded", function(event) { + $('#userTable').DataTable( { + "paging": false, + "ordering": true, + "order": [[3, "desc"]], + "info": true, + "searching": false + }); +}); \ No newline at end of file diff --git a/rein/html/layout.html b/rein/html/layout.html index ac678ab..8b68f3a 100644 --- a/rein/html/layout.html +++ b/rein/html/layout.html @@ -226,6 +226,8 @@

    ReinProject

    + + diff --git a/rein/html/plugins/datatables/dataTables.bootstrap.min.css b/rein/html/plugins/datatables/dataTables.bootstrap.min.css new file mode 100644 index 0000000..7400cf0 --- /dev/null +++ b/rein/html/plugins/datatables/dataTables.bootstrap.min.css @@ -0,0 +1 @@ +table.dataTable{clear:both;margin-top:6px !important;margin-bottom:6px !important;max-width:none !important;border-collapse:separate !important}table.dataTable td,table.dataTable th{-webkit-box-sizing:content-box;box-sizing:content-box}table.dataTable td.dataTables_empty,table.dataTable th.dataTables_empty{text-align:center}table.dataTable.nowrap th,table.dataTable.nowrap td{white-space:nowrap}div.dataTables_wrapper div.dataTables_length label{font-weight:normal;text-align:left;white-space:nowrap}div.dataTables_wrapper div.dataTables_length select{width:75px;display:inline-block}div.dataTables_wrapper div.dataTables_filter{text-align:right}div.dataTables_wrapper div.dataTables_filter label{font-weight:normal;white-space:nowrap;text-align:left}div.dataTables_wrapper div.dataTables_filter input{margin-left:0.5em;display:inline-block;width:auto}div.dataTables_wrapper div.dataTables_info{padding-top:8px;white-space:nowrap}div.dataTables_wrapper div.dataTables_paginate{margin:0;white-space:nowrap;text-align:right}div.dataTables_wrapper div.dataTables_paginate ul.pagination{margin:2px 0;white-space:nowrap}div.dataTables_wrapper div.dataTables_processing{position:absolute;top:50%;left:50%;width:200px;margin-left:-100px;margin-top:-26px;text-align:center;padding:1em 0}table.dataTable thead>tr>th.sorting_asc,table.dataTable thead>tr>th.sorting_desc,table.dataTable thead>tr>th.sorting,table.dataTable thead>tr>td.sorting_asc,table.dataTable thead>tr>td.sorting_desc,table.dataTable thead>tr>td.sorting{padding-right:30px}table.dataTable thead>tr>th:active,table.dataTable thead>tr>td:active{outline:none}table.dataTable thead .sorting,table.dataTable thead .sorting_asc,table.dataTable thead .sorting_desc,table.dataTable thead .sorting_asc_disabled,table.dataTable thead .sorting_desc_disabled{cursor:pointer;position:relative}table.dataTable thead .sorting:after,table.dataTable thead .sorting_asc:after,table.dataTable thead .sorting_desc:after,table.dataTable thead .sorting_asc_disabled:after,table.dataTable thead .sorting_desc_disabled:after{position:absolute;bottom:8px;right:8px;display:block;font-family:'Glyphicons Halflings';opacity:0.5}table.dataTable thead .sorting:after{opacity:0.2;content:"\e150"}table.dataTable thead .sorting_asc:after{content:"\e155"}table.dataTable thead .sorting_desc:after{content:"\e156"}table.dataTable thead .sorting_asc_disabled:after,table.dataTable thead .sorting_desc_disabled:after{color:#eee}div.dataTables_scrollHead table.dataTable{margin-bottom:0 !important}div.dataTables_scrollBody table{border-top:none;margin-top:0 !important;margin-bottom:0 !important}div.dataTables_scrollBody table thead .sorting:after,div.dataTables_scrollBody table thead .sorting_asc:after,div.dataTables_scrollBody table thead .sorting_desc:after{display:none}div.dataTables_scrollBody table tbody tr:first-child th,div.dataTables_scrollBody table tbody tr:first-child td{border-top:none}div.dataTables_scrollFoot table{margin-top:0 !important;border-top:none}@media screen and (max-width: 767px){div.dataTables_wrapper div.dataTables_length,div.dataTables_wrapper div.dataTables_filter,div.dataTables_wrapper div.dataTables_info,div.dataTables_wrapper div.dataTables_paginate{text-align:center}}table.dataTable.table-condensed>thead>tr>th{padding-right:20px}table.dataTable.table-condensed .sorting:after,table.dataTable.table-condensed .sorting_asc:after,table.dataTable.table-condensed .sorting_desc:after{top:6px;right:6px}table.table-bordered.dataTable th,table.table-bordered.dataTable td{border-left-width:0}table.table-bordered.dataTable th:last-child,table.table-bordered.dataTable th:last-child,table.table-bordered.dataTable td:last-child,table.table-bordered.dataTable td:last-child{border-right-width:0}table.table-bordered.dataTable tbody th,table.table-bordered.dataTable tbody td{border-bottom-width:0}div.dataTables_scrollHead table.table-bordered{border-bottom-width:0}div.table-responsive>div.dataTables_wrapper>div.row{margin:0}div.table-responsive>div.dataTables_wrapper>div.row>div[class^="col-"]:first-child{padding-left:0}div.table-responsive>div.dataTables_wrapper>div.row>div[class^="col-"]:last-child{padding-right:0} diff --git a/rein/html/plugins/datatables/dataTables.bootstrap.min.js b/rein/html/plugins/datatables/dataTables.bootstrap.min.js new file mode 100644 index 0000000..98661c6 --- /dev/null +++ b/rein/html/plugins/datatables/dataTables.bootstrap.min.js @@ -0,0 +1,8 @@ +/*! + DataTables Bootstrap 3 integration + ©2011-2015 SpryMedia Ltd - datatables.net/license +*/ +(function(b){"function"===typeof define&&define.amd?define(["jquery","datatables.net"],function(a){return b(a,window,document)}):"object"===typeof exports?module.exports=function(a,d){a||(a=window);if(!d||!d.fn.dataTable)d=require("datatables.net")(a,d).$;return b(d,a,a.document)}:b(jQuery,window,document)})(function(b,a,d,m){var f=b.fn.dataTable;b.extend(!0,f.defaults,{dom:"<'row'<'col-sm-6'l><'col-sm-6'f>><'row'<'col-sm-12'tr>><'row'<'col-sm-5'i><'col-sm-7'p>>",renderer:"bootstrap"});b.extend(f.ext.classes, +{sWrapper:"dataTables_wrapper form-inline dt-bootstrap",sFilterInput:"form-control input-sm",sLengthSelect:"form-control input-sm",sProcessing:"dataTables_processing panel panel-default"});f.ext.renderer.pageButton.bootstrap=function(a,h,r,s,j,n){var o=new f.Api(a),t=a.oClasses,k=a.oLanguage.oPaginate,u=a.oLanguage.oAria.paginate||{},e,g,p=0,q=function(d,f){var l,h,i,c,m=function(a){a.preventDefault();!b(a.currentTarget).hasClass("disabled")&&o.page()!=a.data.action&&o.page(a.data.action).draw("page")}; +l=0;for(h=f.length;l",{"class":t.sPageButton+" "+g,id:0===r&&"string"===typeof c?a.sTableId+"_"+c:null}).append(b("",{href:"#", +"aria-controls":a.sTableId,"aria-label":u[c],"data-dt-idx":p,tabindex:a.iTabIndex}).html(e)).appendTo(d),a.oApi._fnBindAction(i,{action:c},m),p++)}},i;try{i=b(h).find(d.activeElement).data("dt-idx")}catch(v){}q(b(h).empty().html('
      ').children("ul"),s);i!==m&&b(h).find("[data-dt-idx="+i+"]").focus()};return f}); From 4095a0c2810a1060ac0922ed3a4a17e4c88aff90 Mon Sep 17 00:00:00 2001 From: FreakJoe Date: Sat, 18 Mar 2017 14:54:50 +0100 Subject: [PATCH 32/42] Use local database for ratings data instead of server --- rein/lib/rating.py | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/rein/lib/rating.py b/rein/lib/rating.py index ab74d43..4dbf2c9 100644 --- a/rein/lib/rating.py +++ b/rein/lib/rating.py @@ -129,18 +129,17 @@ def get_average_user_rating(log, url, user, rein, msin): """Gets the average rating a user (identified by his msin) has received along with the number of ratings he has received""" - sel_url = "{0}query?owner={1}&delegate={2}&query=get_user_ratings&testnet={3}&msin={4}" - data = safe_get(log, sel_url.format(url, user.maddr, user.daddr, rein.testnet, msin)) - data = data['get_user_ratings'] - - # If there was a server-side error, return None - if 'error' in data: - return None + ratings = rein.session.query(Document).filter(and_( + Document.testnet == rein.testnet, + Document.contents.like('%\nRein Rating%') + )).filter( + Document.contents.like('%\nUser msin: {}%'.format(msin)) + ).limit(100).all() # Create a list of all the ratings the user has received rating_values = [] - for rating_data in data: - rating = document_to_dict(rating_data['value']) + for rating_data in ratings: + rating = document_to_dict(rating_data.contents) try: rating_value = int(rating['Rating']) rating_values.append(rating_value) @@ -170,18 +169,17 @@ def get_average_user_rating_display(log, url, user, rein, msin, cli=False): def get_all_user_ratings(log, url, user, rein, msin): """Returns a list of a user's ratings.""" - sel_url = "{0}query?owner={1}&delegate={2}&query=get_user_ratings&testnet={3}&msin={4}" - data = safe_get(log, sel_url.format(url, user.maddr, user.daddr, rein.testnet, msin)) - data = data['get_user_ratings'] - - # If there was a server-side error, return None - if 'error' in data: - return [] + rating_docs = rein.session.query(Document).filter(and_( + Document.testnet == rein.testnet, + Document.contents.like('%\nRein Rating%') + )).filter( + Document.contents.like('%\nUser msin: {}%'.format(msin)) + ).limit(100).all() # Create a list of all the ratings the user has received ratings = [] - for rating_data in data: - rating = document_to_dict(rating_data['value']) + for rating_data in rating_docs: + rating = document_to_dict(rating_data.contents) ratings.append( { 'rating_value': '{} '.format(float(rating['Rating'])), From 74e5522df7b203ac4841c53c593682923b51bd31 Mon Sep 17 00:00:00 2001 From: FreakJoe Date: Sat, 18 Mar 2017 15:06:20 +0100 Subject: [PATCH 33/42] Search by hitting enter --- rein/html/layout.html | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/rein/html/layout.html b/rein/html/layout.html index 8b68f3a..f1aa8de 100644 --- a/rein/html/layout.html +++ b/rein/html/layout.html @@ -62,7 +62,12 @@ about
    Handle Contact
    @@ -31,17 +32,20 @@

    Unspent Payments for Job {{txj.ref}}

     Withdraw from this job's payments

    -   +  
    -   +  
    + {% else %} + None found. + {% endif %} {% endfor %} {% else %}

    No transactions.

    From 63a0d146d318cede905006ee1733f0e35e2cfc64 Mon Sep 17 00:00:00 2001 From: Andrew K Date: Mon, 10 Apr 2017 21:49:36 +0000 Subject: [PATCH 40/42] fix port and webbrowser display --- rein/cli.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rein/cli.py b/rein/cli.py index 52f3741..ad8ffd6 100644 --- a/rein/cli.py +++ b/rein/cli.py @@ -1436,7 +1436,7 @@ def start(multi, identity, setup): from .lib.bitcoinaddress import generate_sin host = '127.0.0.1' - port = 5002 + port = 5001 tmpl_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'html') @@ -1546,8 +1546,8 @@ def serve_static_file(path): return send_from_directory(tmpl_dir, path) if rein.has_no_account() or setup: - #webbrowser.open('http://'+host+':' + str(port) + '/setup') - print('open '+'http://'+host+':' + str(port) + '/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: @@ -2768,8 +2768,8 @@ def serve_template_file(): documents=documents, orders=relevant_orders) - #webbrowser.open('http://'+host+':' + str(port)) - print('open '+'http://'+host+':' + str(port)) + webbrowser.open('http://'+host+':' + str(port)) + #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 From 4110c1c059e2187c72d636c158e3811c952d71fc Mon Sep 17 00:00:00 2001 From: Andrew K Date: Tue, 11 Apr 2017 19:02:14 +0000 Subject: [PATCH 41/42] enable multi user wallet and pubkeys --- rein/cli.py | 2 +- rein/lib/pubkeys.py | 10 +++++++--- rein/lib/wallet.py | 12 +++++++++--- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/rein/cli.py b/rein/cli.py index ad8ffd6..5f6fcd8 100644 --- a/rein/cli.py +++ b/rein/cli.py @@ -1687,7 +1687,7 @@ def settings(): @app.route("/wallet", methods=['GET']) def wallet(): fee = float(PersistConfig.get(rein, 'fee', 0.001)) - wallet_entries = rein.session.query(Wallet) + wallet_entries = rein.session.query(Wallet).filter(Wallet.userid == rein.user.id).all() balance = 0. txs = [] for we in wallet_entries: diff --git a/rein/lib/pubkeys.py b/rein/lib/pubkeys.py index f639ed1..53be647 100644 --- a/rein/lib/pubkeys.py +++ b/rein/lib/pubkeys.py @@ -9,10 +9,12 @@ class Pubkeys(Base): pubkey = Column(String(34), primary_key=True) privkey = Column(String(52)) + userid = Column(Integer) - def __init__(self, session, pubkey, privkey): + def __init__(self, session, pubkey, privkey, userid=None): self.pubkey = pubkey self.privkey = privkey + self.userid = userid session.add(self) session.commit() @@ -25,10 +27,12 @@ def get(self, rein, pubkey, default=False): return default @classmethod - def set(self, rein, pubkey, privkey=''): + def set(self, rein, pubkey, privkey='', userid=None): res = rein.session.query(Pubkeys).filter(Pubkeys.pubkey == pubkey).first() if res: res.privkey = privkey + if userid is not None: + res.userid = userid rein.session.commit() else: - p = Pubkeys(rein.session, pubkey, privkey) + p = Pubkeys(rein.session, pubkey, privkey=privkey, userid=userid) diff --git a/rein/lib/wallet.py b/rein/lib/wallet.py index 7f59851..466ffeb 100644 --- a/rein/lib/wallet.py +++ b/rein/lib/wallet.py @@ -10,11 +10,13 @@ class Wallet(Base): address = Column(String(34), primary_key=True) privkey = Column(String(52)) ref = Column(String(64)) + userid = Column(Integer) - def __init__(self, session, address, privkey, ref=None): + def __init__(self, session, address, privkey, ref=None, userid=None): self.address = address self.privkey = privkey self.ref = ref + self.userid = userid session.add(self) session.commit() @@ -27,10 +29,14 @@ def get(self, rein, address, default=False): return default @classmethod - def set(self, rein, address, privkey='', ref=None): + def set(self, rein, address, privkey='', ref=None, userid=None): res = rein.session.query(Wallet).filter(Wallet.address == address).first() if res: res.privkey = privkey + if ref is not None: + res.ref = ref + if userid is not None: + res.userid = userid rein.session.commit() else: - p = Wallet(rein.session, address, privkey, ref=ref) + p = Wallet(rein.session, address, privkey, ref=ref, userid=userid) From ab710fdf134c669ea664140df2be23052cbe56a0 Mon Sep 17 00:00:00 2001 From: Andrew K Date: Tue, 11 Apr 2017 20:32:15 +0000 Subject: [PATCH 42/42] fix multi user wallet feature --- rein/cli.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/rein/cli.py b/rein/cli.py index 5f6fcd8..daa0dd3 100644 --- a/rein/cli.py +++ b/rein/cli.py @@ -298,8 +298,8 @@ def bid(multi, identity, defaults, dry_run): (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) - Wallet.set(rein,primary_payment_address,primary_payment_privkey,ref=job['Job ID']) + 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}, @@ -385,8 +385,8 @@ def offer(multi, identity, defaults, dry_run): (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) - Wallet.set(rein,payment_address,payment_privkey,ref=bid['Job ID']) + 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]) @@ -1953,8 +1953,8 @@ def job_offer(): (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) - Wallet.set(rein,payment_address,payment_privkey,ref=bid['Job ID']) + 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]) @@ -2596,8 +2596,8 @@ def job_bid(): (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) - Wallet.set(rein,primary_payment_address,primary_payment_privkey,ref=job['Job ID']) + 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},
    txid