Skip to content
Snippets Groups Projects
Select Git revision
  • 94ed2f47cbaaef483fcbc16491f947c9e11b3c41
  • master default protected
  • json-output
  • nostr
  • 48-error-base-58-requirement-is-violated
  • no-rename
  • hugo/tx-comments
  • poka/dev
  • hugo/dev
  • tuxmain/mail
  • 0.4.3-RC2
  • 0.4.3-RC1
  • 0.4.2
  • 0.4.1
  • 0.4.0
  • 0.3.0
  • 0.2.17
  • 0.2.16
  • 0.2.15
  • 0.2.14
  • 0.2.13
  • 0.2.12
  • 0.2.10
  • 0.2.9
  • 0.2.8
  • 0.2.7
  • 0.2.6
  • 0.2.5
  • 0.2.4
  • 0.2.3
30 results

transfer.rs

Blame
  • transaction.py 8.69 KiB
    from .document import Document, MalformedDocumentError
    import re
    
    
    class Transaction(Document):
        """
    .. note:: A transaction document is specified by the following format :
    
        | Document format :
        | Version: VERSION
        | Type: Transaction
        | Currency: CURRENCY_NAME
        | Issuers:
        | PUBLIC_KEY
        | ...
        | Inputs:
        | INDEX:SOURCE:NUMBER:FINGERPRINT:AMOUNT
        | ...
        | Outputs:
        | PUBLIC_KEY:AMOUNT
        | ...
        | Comment: COMMENT
        | ...
        |
        |
        | Compact format :
        | TX:VERSION:NB_ISSUERS:NB_INPUTS:NB_OUTPUTS:HAS_COMMENT
        | PUBLIC_KEY:INDEX
        | ...
        | INDEX:SOURCE:FINGERPRINT:AMOUNT
        | ...
        | PUBLIC_KEY:AMOUNT
        | ...
        | COMMENT
        | SIGNATURE
        | ...
    
        """
    
        re_type = re.compile("Type: (Transaction)\n")
        re_header = re.compile("TX:([0-9]+):([0-9]+):([0-9]+):([0-9]+):(0|1)\n")
        re_issuers = re.compile("Issuers:\n")
        re_inputs = re.compile("Inputs:\n")
        re_outputs = re.compile("Outputs:\n")
        re_compact_comment = re.compile("([^\n]+)\n")
        re_comment = re.compile("Comment: ([^\n]*)\n")
        re_pubkey = re.compile("([1-9A-Za-z][^OIl]{42,45})\n")
    
        fields_parsers = {**Document.fields_parsers, **{
                "Type": re_type,
                "TX": re_header,
                "Issuers": re_issuers,
                "Inputs": re_inputs,
                "Outputs": re_outputs,
                "Comment": re_comment,
                "Compact comment": re_compact_comment,
                "Pubkey": re_pubkey
            }
        }
    
        def __init__(self, version, currency, issuers, inputs, outputs,
                     comment, signatures):
            """
            Constructor
            """
            super().__init__(version, currency, signatures)
    
            self.issuers = issuers
            self.inputs = inputs
            self.outputs = outputs
            self.comment = comment
    
        @classmethod
        def from_compact(cls, currency, compact):
            lines = compact.splitlines(True)
            n = 0
    
            header_data = Transaction.re_header.match(lines[n])
            if header_data is None:
                raise MalformedDocumentError("Compact TX header")
            version = int(header_data.group(1))
            issuers_num = int(header_data.group(2))
            inputs_num = int(header_data.group(3))
            outputs_num = int(header_data.group(4))
            has_comment = int(header_data.group(5))
            n += 1
    
            issuers = []
            inputs = []
            outputs = []
            signatures = []
            for i in range(0, issuers_num):
                issuer = Transaction.parse_field("Pubkey", lines[n])
                issuers.append(issuer)
                n += 1
    
            for i in range(0, inputs_num):
                input_source = InputSource.from_inline(lines[n])
                inputs.append(input_source)
                n += 1
    
            for i in range(0, outputs_num):
                output_source = OutputSource.from_inline(lines[n])
                outputs.append(output_source)
                n += 1
    
            comment = ""
            if has_comment == 1:
                comment = Transaction.re_compact_comment.match(lines[n]).group(1)
                n += 1
    
            while n < len(lines):
                signatures.append(Transaction.re_signature.match(lines[n]).group(1))
                n += 1
    
            return cls(version, currency, issuers, inputs, outputs, comment, signatures)
    
        @classmethod
        def from_signed_raw(cls, raw):
            lines = raw.splitlines(True)
            n = 0
    
            version = int(Transaction.parse_field("Version", lines[n]))
            n += 1
    
            Transaction.parse_field("Type", lines[n])
            n += 1
    
            currency = Transaction.parse_field("Currency", lines[n])
            n += 1
    
            issuers = []
            inputs = []
            outputs = []
            signatures = []
    
            if Transaction.re_issuers.match(lines[n]):
                n += 1
                while Transaction.re_inputs.match(lines[n]) is None:
                    issuer = Transaction.parse_field("Pubkey", lines[n])
                    issuers.append(issuer)
                    n += 1
    
            if Transaction.re_inputs.match(lines[n]):
                n += 1
                while Transaction.re_outputs.match(lines[n]) is None:
                    input_source = InputSource.from_inline(lines[n])
                    inputs.append(input_source)
                    n += 1
    
            if Transaction.re_outputs.match(lines[n]) is not None:
                n += 1
                while not Transaction.re_comment.match(lines[n]):
                    output = OutputSource.from_inline(lines[n])
                    outputs.append(output)
                    n += 1
    
            comment = Transaction.parse_field("Comment", lines[n])
            n += 1
    
            if Transaction.re_signature.match(lines[n]) is not None:
                while n < len(lines):
                    sign = Transaction.parse_field("Signature", lines[n])
                    signatures.append(sign)
                    n += 1
    
            return cls(version, currency, issuers, inputs, outputs,
                       comment, signatures)
    
        def raw(self):
            doc = """Version: {0}
    Type: Transaction
    Currency: {1}
    Issuers:
    """.format(self.version,
                       self.currency)
    
            for p in self.issuers:
                doc += "{0}\n".format(p)
    
            doc += "Inputs:\n"
            for i in self.inputs:
                doc += "{0}\n".format(i.inline())
    
            doc += "Outputs:\n"
            for o in self.outputs:
                doc += "{0}\n".format(o.inline())
    
            doc += "Comment: "
            doc += "{0}\n".format(self.comment)
    
            return doc
    
        def compact(self):
            """
            Return a transaction in its compact format.
            """
            """TX:VERSION:NB_ISSUERS:NB_INPUTS:NB_OUTPUTS:HAS_COMMENT
    PUBLIC_KEY:INDEX
    ...
    INDEX:SOURCE:FINGERPRINT:AMOUNT
    ...
    PUBLIC_KEY:AMOUNT
    ...
    COMMENT
    """
            doc = "TX:{0}:{1}:{2}:{3}:{4}\n".format(self.version,
                                                  len(self.issuers),
                                                  len(self.inputs),
                                                  len(self.outputs),
                                                  '1' if self.comment != "" else '0')
            for pubkey in self.issuers:
                doc += "{0}\n".format(pubkey)
            for i in self.inputs:
                doc += "{0}\n".format(i.inline())
            for o in self.outputs:
                doc += "{0}\n".format(o.inline())
            if self.comment != "":
                doc += "{0}\n".format(self.comment)
            for s in self.signatures:
                doc += "{0}\n".format(s)
    
            return doc
    
    
    class SimpleTransaction(Transaction):
        """
    As transaction class, but for only one issuer.
    ...
        """
        def __init__(self, version, currency, issuer,
                     single_input, outputs, comment, signature):
            """
            Constructor
            """
            super().__init__(version, currency, [issuer], [single_input],
                  outputs, comment, [signature])
    
    
    class InputSource:
        """
        A Transaction INPUT
    
    .. note:: Compact :
        INDEX:SOURCE:FINGERPRINT:AMOUNT
    
        """
        re_inline = re.compile("([0-9]+):(D|T):([0-9]+):([0-9a-fA-F]{5,40}):([0-9]+)\n")
        re_compact = re.compile("([0-9]+):(D|T):([0-9a-fA-F]{5,40}):([0-9]+)\n")
    
        def __init__(self, index, source, number, txhash, amount):
            self.index = index
            self.source = source
            self.number = number
            self.txhash = txhash
            self.amount = amount
    
        @classmethod
        def from_inline(cls, inline):
            data = InputSource.re_inline.match(inline)
            if data is None:
                raise MalformedDocumentError("Inline input")
            index = int(data.group(1))
            source = data.group(2)
            number = int(data.group(3))
            txhash = data.group(4)
            amount = int(data.group(5))
            return cls(index, source, number, txhash, amount)
    
        @classmethod
        def from_bma(cls, bma_data):
            index = None
            source = bma_data['type']
            number = bma_data['number']
            txhash = bma_data['fingerprint']
            amount = bma_data['amount']
            return cls(index, source, number, txhash, amount)
    
        def inline(self):
            return "{0}:{1}:{2}:{3}:{4}".format(self.index,
                                                self.source,
                                                self.number,
                                                self.txhash,
                                                self.amount)
    
    
    class OutputSource():
        """
        A Transaction OUTPUT
        """
        re_inline = re.compile("([1-9A-Za-z][^OIl]{42,45}):([0-9]+)")
    
        def __init__(self, pubkey, amount):
            self.pubkey = pubkey
            self.amount = amount
    
        @classmethod
        def from_inline(cls, inline):
            data = OutputSource.re_inline.match(inline)
            if data is None:
                raise MalformedDocumentError("Inline output")
            pubkey = data.group(1)
            amount = int(data.group(2))
            return cls(pubkey, amount)
    
        def inline(self):
            return "{0}:{1}".format(self.pubkey, self.amount)