From 97c9aaf06660530ed7c5e8120b7fb1c7ab594511 Mon Sep 17 00:00:00 2001 From: Caner Candan <candan@info.univ-angers.fr> Date: Fri, 17 Jan 2014 18:30:49 +0100 Subject: [PATCH] * ucoin.py: forge-am added --- ucoin.py | 170 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 164 insertions(+), 6 deletions(-) diff --git a/ucoin.py b/ucoin.py index 99a579d9..fb89298e 100755 --- a/ucoin.py +++ b/ucoin.py @@ -18,8 +18,9 @@ # from pprint import pprint -import ucoin, json, logging, argparse, sys, gnupg, hashlib, re +import ucoin, json, logging, argparse, sys, gnupg, hashlib, re, datetime as dt from collections import OrderedDict +from merkle import Merkle logger = logging.getLogger("cli") @@ -134,7 +135,7 @@ def issue(): __dict.update(ucoin.settings) __dict['version'] = 1 __dict['number'] = 0 if not last_tx else last_tx['transaction']['number']+1 - __dict['previousHash'] = hashlib.sha1(("%(raw)s%(signature)s" % last_tx).encode('ascii')).hexdigest().upper() + __dict['previousHash'] = hashlib.sha1(("%(raw)s%(signature)s" % last_tx).encode('ascii')).hexdigest().upper() if last_tx else None __dict['type'] = 'ISSUANCE' # pprint(__dict) @@ -359,6 +360,161 @@ def pub_tht(): def forge_am(): logger.debug('forge_am') + def format_changes(s): + changes_str = (s or '')\ + .replace('"', '')\ + .replace('+', ';+')\ + .replace('-', ';-') + changes = [] + + for item in changes_str.split(';'): + if not item: continue + changes.append(item) + + return changes + + filter_plus = lambda x: x[0] == '+' + filter_minus = lambda x: x[0] == '-' + remove_sign = lambda x: x[1:] + + if not ucoin.settings['timestamp']: + ucoin.settings['timestamp'] = int(dt.datetime.timestamp(dt.datetime.today())) + + if not ucoin.settings['changes'] and ucoin.settings['stdin']: + ucoin.settings['changes'] = input() + + try: + current = ucoin.hdc.amendments.Current().get() + except ValueError: + current = None + + __dict = {} + __dict.update(ucoin.settings) + __dict['version'] = 1 + __dict['previousNumber'] = -1 if not current else current['number'] + __dict['number'] = __dict['previousNumber']+1 + __dict['previousHash'] = hashlib.sha1(("%(raw)s" % current).encode('ascii')).hexdigest().upper() if current else None + + am = """\ +Version: %(version)s +Currency: %(currency)s +Number: %(number)d +GeneratedOn: %(timestamp)d +""" % __dict + + if __dict['dividend']: am += "UniversalDividend: %(dividend)s\n" % __dict + if __dict['power10']: am += "CoinMinimalPower: %(power10)d\n" % __dict + + am += """\ +NextRequiredVotes: %(votes)d +""" % __dict + + if current: am += "PreviousHash: %(previousHash)s\n" % __dict + + if ucoin.settings['changes']: + ucoin.settings['changes'] = ucoin.settings['changes'].split(';') + + members_changes = format_changes(ucoin.settings['changes'][0]) if ucoin.settings['changes'] else [] + voters_changes = format_changes(ucoin.settings['changes'][1] if len(ucoin.settings['changes']) > 1 else '') if ucoin.settings['changes'] else [] + members_to_add = list(map(remove_sign, filter(filter_plus, members_changes))) + members_to_remove = list(map(remove_sign, filter(filter_minus, members_changes))) + voters_to_add = list(map(remove_sign, filter(filter_plus, voters_changes))) + voters_to_remove = list(map(remove_sign, filter(filter_minus, voters_changes))) + members_to_add = list(set(members_to_add) - set(members_to_remove)) + + if __dict['number']: + members_view = ucoin.hdc.amendments.view.Members('%(previousNumber)d-%(previousHash)s' % __dict).get() + else: + members_view = None + + members = [] + previous_members = [] + really_added_members = [] + really_removed_members = [] + + for fingerprint in members_view: + members.append(fingerprint['hash']) + previous_members.append(fingerprint['hash']) + + for fingerprint in members_to_add: + members.append(fingerprint) + + members = list(set(members)) + members = list(set(members) - set(members_to_remove)) + + members.sort() + + really_added_members = list(set(members) - set(previous_members)) + really_removed_members = list(set(members_to_remove) - set(members)) + really_removed_members = list(set(really_removed_members) & set(previous_members)) + + members_merkle = Merkle(members).process() + # pprint(members_merkle.__dict__) + + members_changes = [] + for fpr in really_added_members: + members_changes.append('+' + fpr) + for fpr in really_removed_members: + members_changes.append('-' + fpr) + members_changes.sort() + __dict['members_count'] = len(members_merkle.leaves) + __dict['members_root'] = members_merkle.root() + + if __dict['number']: + voters_view = ucoin.hdc.amendments.view.Voters('%(previousNumber)d-%(previousHash)s' % __dict).get() + else: + voters_view = None + + voters = [] + previous_voters = [] + really_added_voters = [] + really_removed_voters = [] + + for fingerprint in voters_view: + voters.append(fingerprint['hash']) + previous_voters.append(fingerprint['hash']) + + for fingerprint in voters_to_add: + voters.append(fingerprint) + + voters = list(set(voters)) + voters = list(set(voters) - set(voters_to_remove)) + + voters.sort() + + really_added_voters = list(set(voters) - set(previous_voters)) + really_removed_voters = list(set(voters_to_remove) - set(voters)) + + voters_merkle = Merkle(voters).process() + # pprint(voters_merkle.__dict__) + + voters_changes = [] + for fpr in really_added_voters: + voters_changes.append('+' + fpr) + for fpr in really_removed_voters: + voters_changes.append('-' + fpr) + voters_changes.sort() + __dict['voters_count'] = len(voters_merkle.leaves) + __dict['voters_root'] = voters_merkle.root() + + am += """\ +MembersRoot: %(members_root)s +MembersCount: %(members_count)d +MembersChanges: +""" % __dict + + for m in members_changes: am += "%s\n" % m + + am += """\ +VotersRoot: %(voters_root)s +VotersCount: %(voters_count)d +VotersChanges: +""" % __dict + + for m in voters_changes: am += "%s\n" % m + + print(am) + def clist(): logger.debug('clist') @@ -512,10 +668,12 @@ if __name__ == '__main__': subparsers.add_parser('pub-tht', help='Publish THT entry according to data returned by \'trust-list\' and \'host-list\'').set_defaults(func=pub_tht) sp = subparsers.add_parser('forge-am', help='Forge an amendment, following currently promoted of given node.') - sp.add_argument('--dividend', '-d', help='Universal Dividend value') - sp.add_argument('--power10', '-m', help='Minimal coin 10 power') - sp.add_argument('--votes', '-n', help='Number of required votes', required=True) - sp.add_argument('--timestamp', '-t', help='Generation timestamp') + sp.add_argument('changes', nargs='?', help='[members;voters] changes') + sp.add_argument('--dividend', '-d', type=int, help='Universal Dividend value') + sp.add_argument('--power10', '-m', type=int, help='Minimal coin 10 power') + sp.add_argument('--votes', '-n', type=int, help='Number of required votes', required=True) + sp.add_argument('--timestamp', '-t', type=int, help='Generation timestamp') + sp.add_argument('--stdin', '-C', action='store_true', default=False, help='forge-am will read community changes from STDIN') sp.set_defaults(func=forge_am) sp = subparsers.add_parser('clist', help='List coins of given user. May be limited by upper amount.') -- GitLab