示例#1
0
class TransactionFactory(object):
    def __init__(self,
                 config,
                 mongo,
                 block_height,
                 bulletin_secret='',
                 username='',
                 value=0,
                 fee=0.0,
                 requester_rid='',
                 requested_rid='',
                 public_key='',
                 dh_public_key='',
                 private_key='',
                 dh_private_key='',
                 to='',
                 inputs='',
                 outputs='',
                 coinbase=False,
                 chattext=None,
                 signin=None):
        self.config = config
        self.mongo = mongo
        self.block_height = block_height
        self.bulletin_secret = bulletin_secret
        self.username = username
        self.requester_rid = requester_rid
        self.requested_rid = requested_rid
        self.public_key = public_key
        self.dh_public_key = dh_public_key
        self.private_key = private_key
        self.value = value
        self.fee = float(fee)
        self.dh_private_key = dh_private_key
        self.to = to
        self.time = str(int(time.time()))
        self.outputs = []
        for x in outputs:
            self.outputs.append(Output.from_dict(x))
        self.inputs = []
        for x in inputs:
            if 'signature' in x:
                self.inputs.append(
                    ExternalInput.from_dict(self.config, self.mongo, x))
            else:
                self.inputs.append(Input.from_dict(x))
        self.coinbase = coinbase
        self.chattext = chattext
        self.signin = signin
        self.do_money()
        inputs_concat = self.get_input_hashes()
        outputs_concat = self.get_output_hashes()
        if bulletin_secret:
            self.rid = self.generate_rid()
            if self.chattext:
                self.relationship = json.dumps({"chatText": self.chattext})
                self.cipher = Crypt(self.config.wif)
                self.encrypted_relationship = self.cipher.encrypt(
                    self.relationship)
            elif self.signin:
                for shared_secret in TU.get_shared_secrets_by_rid(
                        self.config, self.mongo, self.rid):
                    self.relationship = SignIn(self.signin)
                    self.cipher = Crypt(shared_secret.encode('hex'),
                                        shared=True)
                    self.encrypted_relationship = self.cipher.shared_encrypt(
                        self.relationship.to_json())
            else:
                if not self.dh_public_key or not self.dh_private_key:
                    a = os.urandom(32)
                    self.dh_public_key = scalarmult_base(a).encode('hex')
                    self.dh_private_key = a.encode('hex')
                self.relationship = self.generate_relationship()
                if not private_key:
                    raise BaseException('missing private key')
                self.cipher = Crypt(self.config.wif)
                self.encrypted_relationship = self.cipher.encrypt(
                    self.relationship.to_json())
        else:
            self.rid = ''
            self.encrypted_relationship = ''

        self.header = (self.public_key + self.time + self.dh_public_key +
                       self.rid + self.encrypted_relationship +
                       "{0:.8f}".format(self.fee) + self.requester_rid +
                       self.requested_rid + inputs_concat + outputs_concat)
        self.hash = hashlib.sha256(self.header).digest().encode('hex')
        if self.private_key:
            self.transaction_signature = TU.generate_signature_with_private_key(
                private_key, self.hash)
        else:
            self.transaction_signature = ''
        self.transaction = self.generate_transaction()

    def do_money(self):
        my_address = str(
            P2PKHBitcoinAddress.from_pubkey(self.public_key.decode('hex')))
        input_txns = BU.get_wallet_unspent_transactions(
            self.config, self.mongo, my_address)
        miner_transactions = self.mongo.db.miner_transactions.find()
        mtxn_ids = []
        for mtxn in miner_transactions:
            for mtxninput in mtxn['inputs']:
                mtxn_ids.append(mtxninput['id'])

        if self.inputs:
            inputs = self.inputs
        else:
            inputs = []
            for input_txn in input_txns:
                if input_txn['id'] not in mtxn_ids:
                    if 'signature' in input_txn:
                        inputs.append(
                            ExternalInput.from_dict(self.config, self.mongo,
                                                    input_txn))
                    else:
                        inputs.append(Input.from_dict(input_txn))

        input_sum = 0
        if self.coinbase:
            self.inputs = []
        else:
            if inputs:
                needed_inputs = []
                done = False
                for y in inputs:
                    print y.id
                    txn = BU.get_transaction_by_id(self.config,
                                                   self.mongo,
                                                   y.id,
                                                   instance=True)
                    if isinstance(y, ExternalInput):
                        y.verify()
                        address = str(
                            P2PKHBitcoinAddress.from_pubkey(
                                txn.public_key.decode('hex')))
                    else:
                        address = my_address
                    for txn_output in txn.outputs:
                        if txn_output.to == address:
                            input_sum += txn_output.value
                            needed_inputs.append(y)
                            if input_sum >= (
                                    sum([x.value
                                         for x in self.outputs]) + self.fee):
                                done = True
                                break
                    if done == True:
                        break

                if not done:
                    raise NotEnoughMoneyException('not enough money')
                self.inputs = needed_inputs
            else:
                self.inputs = []

            remainder = input_sum - (sum([x.value
                                          for x in self.outputs]) + self.fee)

            found = False
            for x in self.outputs:
                if my_address == x.to:
                    found = True
                    x.value += remainder
            if not found:
                return_change_output = Output(to=my_address, value=remainder)
                self.outputs.append(return_change_output)

    def get_input_hashes(self):
        from fastgraph import FastGraph
        input_hashes = []
        for x in self.inputs:
            txn = BU.get_transaction_by_id(self.config,
                                           self.mongo,
                                           x.id,
                                           instance=True,
                                           include_fastgraph=isinstance(
                                               self, FastGraph))
            input_hashes.append(str(txn.transaction_signature))

        return ''.join(sorted(input_hashes, key=str.lower))

    def get_output_hashes(self):
        outputs_sorted = sorted([x.to_dict() for x in self.outputs],
                                key=lambda x: x['to'].lower())
        return ''.join(
            [x['to'] + "{0:.8f}".format(x['value']) for x in outputs_sorted])

    def generate_rid(self):
        my_bulletin_secret = self.config.get_bulletin_secret()
        if my_bulletin_secret == self.bulletin_secret:
            raise BaseException(
                'bulletin secrets are identical. do you love yourself so much that you want a relationship on the blockchain?'
            )
        bulletin_secrets = sorted(
            [str(my_bulletin_secret),
             str(self.bulletin_secret)],
            key=str.lower)
        return hashlib.sha256(
            str(bulletin_secrets[0]) +
            str(bulletin_secrets[1])).digest().encode('hex')

    def generate_relationship(self):
        return Relationship(
            dh_private_key=self.dh_private_key,
            their_bulletin_secret=self.bulletin_secret,
            their_username=self.username,
            my_bulletin_secret=self.config.get_bulletin_secret(),
            my_username=self.config.username)

    def generate_transaction(self):
        return Transaction(self.config,
                           self.mongo,
                           self.block_height,
                           self.time,
                           self.rid,
                           self.transaction_signature,
                           self.encrypted_relationship,
                           self.public_key,
                           self.dh_public_key,
                           float(self.fee),
                           self.requester_rid,
                           self.requested_rid,
                           self.hash,
                           inputs=[x.to_dict() for x in self.inputs],
                           outputs=[x.to_dict() for x in self.outputs],
                           coinbase=self.coinbase)

    def generate_transaction_signature(self):
        return TU.generate_signature(self.hash, self.private_key)
class TransactionFactory(object):
    def __init__(self,
                 bulletin_secret='',
                 username='',
                 value=0,
                 fee=0.0,
                 requester_rid='',
                 requested_rid='',
                 public_key='',
                 dh_public_key='',
                 private_key='',
                 dh_private_key='',
                 to='',
                 inputs='',
                 outputs='',
                 coinbase=False,
                 chattext=None,
                 signin=None):
        self.bulletin_secret = bulletin_secret
        self.username = username
        self.requester_rid = requester_rid
        self.requested_rid = requested_rid
        self.public_key = public_key
        self.dh_public_key = dh_public_key
        self.private_key = private_key
        self.value = value
        self.fee = float(fee)
        self.dh_private_key = dh_private_key
        self.to = to
        self.outputs = outputs or []
        self.inputs = inputs
        self.coinbase = coinbase
        self.chattext = chattext
        self.signin = signin
        self.do_money()
        inputs_concat = self.get_input_hashes()
        outputs_concat = self.get_output_hashes()
        if bulletin_secret:
            self.rid = self.generate_rid()
            if self.chattext:
                self.relationship = json.dumps({"chatText": self.chattext})
            elif self.signin:
                for shared_secret in TU.get_shared_secrets_by_rid(self.rid):
                    self.relationship = SignIn(self.signin)
                    self.cipher = Crypt(shared_secret.encode('hex'),
                                        shared=True)
                    self.encrypted_relationship = self.cipher.shared_encrypt(
                        self.relationship.to_json())
            else:
                self.relationship = self.generate_relationship()
                if not private_key:
                    raise BaseException('missing private key')
                self.cipher = Crypt(Config.wif)
                self.encrypted_relationship = self.cipher.encrypt(
                    self.relationship.to_json())
        else:
            self.rid = ''
            self.encrypted_relationship = ''
        self.hash = hashlib.sha256(self.dh_public_key + self.rid +
                                   self.encrypted_relationship +
                                   "{0:.8f}".format(self.fee) +
                                   self.requester_rid + self.requested_rid +
                                   inputs_concat +
                                   outputs_concat).digest().encode('hex')

        self.transaction_signature = self.generate_transaction_signature()
        self.transaction = self.generate_transaction()

    def do_money(self):
        Mongo.init()
        my_address = str(
            P2PKHBitcoinAddress.from_pubkey(self.public_key.decode('hex')))
        input_txns = BU.get_wallet_unspent_transactions(my_address)
        miner_transactions = Mongo.db.miner_transactions.find()
        mtxn_ids = []
        for mtxn in miner_transactions:
            for mtxninput in mtxn['inputs']:
                mtxn_ids.append(mtxninput['id'])

        inputs = [
            Input.from_dict(input_txn) for input_txn in input_txns
            if input_txn['id'] not in mtxn_ids
        ]

        input_sum = 0
        if self.coinbase:
            self.inputs = []
        else:
            needed_inputs = []
            done = False
            for y in inputs:
                print y.id
                txn = BU.get_transaction_by_id(y.id, instance=True)
                for txn_output in txn.outputs:
                    if txn_output.to == my_address:
                        input_sum += txn_output.value
                        needed_inputs.append(y)
                        if input_sum >= (sum([x.value for x in self.outputs]) +
                                         self.fee):
                            done = True
                            break
                if done == True:
                    break

            if not done:
                raise NotEnoughMoneyException('not enough money')
            self.inputs = needed_inputs

            return_change_output = Output(
                to=my_address,
                value=input_sum - (sum([x.value
                                        for x in self.outputs]) + self.fee))
            self.outputs.append(return_change_output)

    def get_input_hashes(self):
        input_hashes = []
        for x in self.inputs:
            txn = BU.get_transaction_by_id(x.id, instance=True)
            input_hashes.append(str(txn.transaction_signature))

        return ''.join(sorted(input_hashes, key=str.lower))

    def get_output_hashes(self):
        outputs_sorted = sorted([x.to_dict() for x in self.outputs],
                                key=lambda x: x['to'].lower())
        return ''.join(
            [x['to'] + "{0:.8f}".format(x['value']) for x in outputs_sorted])

    def generate_rid(self):
        my_bulletin_secret = Config.get_bulletin_secret()
        if my_bulletin_secret == self.bulletin_secret:
            raise BaseException(
                'bulletin secrets are identical. do you love yourself so much that you want a relationship on the blockchain?'
            )
        bulletin_secrets = sorted(
            [str(my_bulletin_secret),
             str(self.bulletin_secret)],
            key=str.lower)
        return hashlib.sha256(
            str(bulletin_secrets[0]) +
            str(bulletin_secrets[1])).digest().encode('hex')

    def generate_relationship(self):
        return Relationship(dh_private_key=self.dh_private_key,
                            their_bulletin_secret=self.bulletin_secret,
                            their_username=self.username,
                            my_bulletin_secret=Config.get_bulletin_secret(),
                            my_username=Config.username)

    def generate_transaction(self):
        return Transaction(self.rid,
                           self.transaction_signature,
                           self.encrypted_relationship,
                           self.public_key,
                           self.dh_public_key,
                           float(self.fee),
                           self.requester_rid,
                           self.requested_rid,
                           self.hash,
                           inputs=self.inputs,
                           outputs=self.outputs,
                           coinbase=self.coinbase)

    def generate_transaction_signature(self):
        return TU.generate_signature(self.hash)