Ejemplo n.º 1
0
def verify_using_cross_ref(domain_id, transaction_id, transaction_base_digest, cross_ref_data, sigdata):
    """Confirm the existence of the transaction using cross_ref

    Args:
        domain_id (bytes): target domain_id
        transaction_id (bytes): target transaction_id of which existence you want to confirm
        transaction_base_digest (bytes): digest obtained from the outer domain
        cross_ref_data (bytes): packed BBcCrossRef object
        sigdata (bytes): packed signature
    Returns:
        bool: True if valid
    """
    cross = BBcCrossRef(unpack=cross_ref_data)
    if cross.domain_id != domain_id or cross.transaction_id != transaction_id:
        return False
    dat = bytearray(transaction_base_digest)
    dat.extend(bbclib_binary.to_2byte(1))
    dat.extend(bbclib_binary.to_4byte(len(cross_ref_data)))
    dat.extend(cross.pack())
    digest = hashlib.sha256(bytes(dat)).digest()
    sig = BBcSignature(unpack=sigdata)
    return sig.verify(digest) == 1
Ejemplo n.º 2
0
class BBcTransaction:
    """Transaction object"""
    WITH_WIRE = False  # for backward compatibility

    def __init__(self, version=1, unpack=None, id_length=None):
        self.idlen_conf = id_length_conf.copy()
        if id_length is not None:
            if isinstance(id_length, int):
                for k in self.idlen_conf.keys():
                    self.idlen_conf[k] = id_length
            elif isinstance(id_length, dict):
                for k in id_length.keys():
                    self.idlen_conf[k] = id_length[k]
        self.version = version
        self.timestamp = int(time.time() * 1000)  # milliseconds
        self.events = []
        self.references = []
        self.relations = []
        self.witness = None
        self.cross_ref = None
        self.signatures = []
        self.userid_sigidx_mapping = dict()
        self.transaction_id = None
        self.transaction_base_digest = None
        self.transaction_data = None
        self.asset_group_ids = dict()
        if unpack is not None:
            self.unpack(unpack)

    def __str__(self):
        ret = "------- Dump of the transaction data ------\n"
        ret += "* transaction_id: %s\n" % bbclib_binary.str_binary(
            self.transaction_id)
        ret += "version: %d\n" % self.version
        ret += "timestamp: %d\n" % self.timestamp
        if self.version != 0:
            ret += "id_length of transaction_id: %d\n" % self.idlen_conf[
                "transaction_id"]
        ret += "Event[]: %d\n" % len(self.events)
        for i, evt in enumerate(self.events):
            ret += " [%d]\n" % i
            ret += str(evt)
        ret += "Reference[]: %d\n" % len(self.references)
        for i, refe in enumerate(self.references):
            ret += " [%d]\n" % i
            ret += str(refe)
        ret += "Relation[]: %d\n" % len(self.relations)
        for i, rtn in enumerate(self.relations):
            ret += " [%d]\n" % i
            ret += str(rtn)
        if self.witness is None:
            ret += "Witness: None\n"
        else:
            ret += str(self.witness)
        if self.cross_ref is None:
            ret += "Cross_Ref: None\n"
        else:
            ret += str(self.cross_ref)
        ret += "Signature[]: %d\n" % len(self.signatures)
        for i, sig in enumerate(self.signatures):
            ret += " [%d]\n" % i
            ret += str(sig)
        return ret

    def add(self,
            event=None,
            reference=None,
            relation=None,
            witness=None,
            cross_ref=None):
        """Add parts"""
        if event is not None:
            if isinstance(event, list):
                self.events.extend(event)
            else:
                self.events.append(event)
        if reference is not None:
            if isinstance(reference, list):
                self.references.extend(reference)
            else:
                self.references.append(reference)
        if relation is not None:
            if isinstance(relation, list):
                self.relations.extend(relation)
            else:
                self.relations.append(relation)
            for rtn in self.relations:
                rtn.version = self.version
        if witness is not None:
            witness.transaction = self
            self.witness = witness
        if cross_ref is not None:
            self.cross_ref = cross_ref
        return True

    def add_event(self,
                  asset_group_id,
                  reference_indices=None,
                  mandatory_approvers=None,
                  option_approvers=None):
        """Add BBcEvent object in this transaction (for allowing method chain style coding)"""
        event = BBcEvent(asset_group_id=asset_group_id,
                         id_length=self.idlen_conf,
                         version=self.version)
        event.add(reference_index=reference_indices,
                  mandatory_approver=mandatory_approvers,
                  option_approver=option_approvers)
        self.add(event=event)
        return event

    def add_relation(self, asset_group_id):
        """Add BBcRelation object in this transaction (for allowing method chain style coding)"""
        relation = BBcRelation(asset_group_id=asset_group_id,
                               id_length=self.idlen_conf,
                               version=self.version)
        self.add(relation=relation)
        return relation

    def create_reference(self,
                         asset_group_id,
                         ref_transaction=None,
                         event_index_in_ref=0):
        """Add BBcReference object in this transaction (for allowing method chain style coding)"""
        ref = BBcReference(asset_group_id=asset_group_id,
                           transaction=self,
                           ref_transaction=ref_transaction,
                           event_index_in_ref=event_index_in_ref,
                           id_length=self.idlen_conf)
        self.add(reference=ref)
        return self

    def create_cross_ref(self, transaction_id, domain_id=None):
        """Add BBcCrossRef object in this transaction (for allowing method chain style coding)"""
        cr = BBcCrossRef(domain_id=domain_id, transaction_id=transaction_id)
        self.add(cross_ref=cr)
        return self

    def add_witness(self, user_id):
        """Add BBcWitness object in this transaction"""
        if self.witness is None:
            self.witness = BBcWitness(id_length=self.idlen_conf,
                                      version=self.version)
            self.witness.transaction = self
        self.witness.add_witness(user_id)
        return self

    def add_signature(self,
                      user_id,
                      key_type=None,
                      private_key=None,
                      public_key=None,
                      keypair=None,
                      no_pubkey=False):
        """Add BBcWitness and BBcSignature objects in this transaction (for allowing method chain style coding)"""
        user_id = user_id[:self.idlen_conf["user_id"]]
        sig = self.sign(key_type=key_type,
                        private_key=private_key,
                        public_key=public_key,
                        keypair=keypair,
                        no_pubkey=no_pubkey)
        if not self.add_signature_object(user_id=user_id, signature=sig):
            if self.references is not None:
                for ref in self.references:
                    if ref.add_signature(user_id=user_id, signature=sig):
                        return self
        return self

    def get_sig_index(self, user_id):
        """Reserve a space for signature for the specified user_id

        Args:
            user_id (bytes): user_id whose signature will be added to the signature part
        Returns:
            int: position (index) in the signature part
        """
        if user_id not in self.userid_sigidx_mapping:
            self.userid_sigidx_mapping[user_id] = len(
                self.userid_sigidx_mapping)
            self.signatures.append(BBcSignature())
        return self.userid_sigidx_mapping[user_id]

    def set_sig_index(self, user_id, idx):
        """Map a user_id with the index of signature list

        Args:
            user_id (bytes): user_id whose signature will be added to the signature part
            idx (int): index number
        """
        if user_id in self.userid_sigidx_mapping:
            return
        self.userid_sigidx_mapping[user_id] = idx

    def add_signature_object(self, user_id=None, signature=None):
        """Add signature in the reserved space

        Args:
            user_id (bytes): user_id of the signature owner
            signature (BBcSignature): signature
        Returns:
            bool: True if successful
        """
        if user_id not in self.userid_sigidx_mapping:
            return False
        idx = self.userid_sigidx_mapping[user_id]
        signature.not_initialized = False
        self.signatures[idx] = signature
        return True

    def digest(self):
        """Calculate the digest

        The digest corresponds to the transaction_id of this object

        Returns:
            bytes: transaction_id (or digest)
        """
        target = self.pack(for_id=True)
        d = hashlib.sha256(target).digest()
        self.transaction_id = d[:self.idlen_conf["transaction_id"]]
        return d

    def pack(self, for_id=False):
        """Pack the whole parts"""
        dat = bytearray(bbclib_binary.to_4byte(self.version))
        dat.extend(bbclib_binary.to_8byte(self.timestamp))
        if self.version != 0:
            dat.extend(
                bbclib_binary.to_2byte(self.idlen_conf["transaction_id"]))
        dat.extend(bbclib_binary.to_2byte(len(self.events)))
        for i in range(len(self.events)):
            evt = self.events[i].pack()
            dat.extend(bbclib_binary.to_4byte(len(evt)))
            dat.extend(evt)
        dat.extend(bbclib_binary.to_2byte(len(self.references)))
        for i in range(len(self.references)):
            refe = self.references[i].pack()
            dat.extend(bbclib_binary.to_4byte(len(refe)))
            dat.extend(refe)
        dat.extend(bbclib_binary.to_2byte(len(self.relations)))
        for i in range(len(self.relations)):
            rtn = self.relations[i].pack()
            dat.extend(bbclib_binary.to_4byte(len(rtn)))
            dat.extend(rtn)
        if self.witness is not None:
            dat.extend(bbclib_binary.to_2byte(1))
            witness = self.witness.pack()
            dat.extend(bbclib_binary.to_4byte(len(witness)))
            dat.extend(witness)
        else:
            dat.extend(bbclib_binary.to_2byte(0))
        self.transaction_base_digest = hashlib.sha256(dat).digest()

        dat_cross = bytearray()
        if self.cross_ref is not None:
            cross = self.cross_ref.pack()
            dat_cross.extend(bbclib_binary.to_2byte(1))
            dat_cross.extend(bbclib_binary.to_4byte(len(cross)))
            dat_cross.extend(cross)
        else:
            dat_cross.extend(bbclib_binary.to_2byte(0))

        if for_id:
            dat_for_id = bytearray(self.transaction_base_digest)
            dat_for_id.extend(dat_cross)
            return bytes(dat_for_id)

        dat.extend(dat_cross)

        dat.extend(bbclib_binary.to_2byte(len(self.signatures)))
        for signature in self.signatures:
            sig = signature.pack()
            dat.extend(bbclib_binary.to_4byte(len(sig)))
            dat.extend(sig)
        self.transaction_data = bytes(dat)
        return self.transaction_data

    def unpack(self, data):
        """Unpack into this object

        Args:
            data (bytes): packed binary data
        Returns:
            bool: True if successful
        """
        self.transaction_data = data[:]
        ptr = 0
        data_size = len(data)
        try:
            ptr, self.version = bbclib_binary.get_n_byte_int(ptr, 4, data)
            ptr, self.timestamp = bbclib_binary.get_n_byte_int(ptr, 8, data)
            if self.version != 0:
                ptr, id_length = bbclib_binary.get_n_byte_int(ptr, 2, data)
                self.idlen_conf["transaction_id"] = id_length
            ptr, evt_num = bbclib_binary.get_n_byte_int(ptr, 2, data)
            self.events = []
            for i in range(evt_num):
                ptr, size = bbclib_binary.get_n_byte_int(ptr, 4, data)
                ptr, evtdata = bbclib_binary.get_n_bytes(ptr, size, data)
                evt = BBcEvent()
                if not evt.unpack(evtdata):
                    return False
                self.events.append(evt)
                if ptr >= data_size:
                    return False
                self.asset_group_ids[evt.asset.asset_id] = evt.asset_group_id

            ptr, ref_num = bbclib_binary.get_n_byte_int(ptr, 2, data)
            self.references = []
            for i in range(ref_num):
                ptr, size = bbclib_binary.get_n_byte_int(ptr, 4, data)
                ptr, refdata = bbclib_binary.get_n_bytes(ptr, size, data)
                refe = BBcReference(None, self)
                if not refe.unpack(refdata):
                    return False
                self.references.append(refe)
                if ptr >= data_size:
                    return False

            ptr, rtn_num = bbclib_binary.get_n_byte_int(ptr, 2, data)
            self.relations = []
            for i in range(rtn_num):
                ptr, size = bbclib_binary.get_n_byte_int(ptr, 4, data)
                ptr, rtndata = bbclib_binary.get_n_bytes(ptr, size, data)
                rtn = BBcRelation()
                if not rtn.unpack(rtndata, self.version):
                    return False
                self.relations.append(rtn)
                if ptr >= data_size:
                    return False
                if rtn.asset is not None:
                    self.asset_group_ids[
                        rtn.asset.asset_id] = rtn.asset_group_id
                if rtn.asset_raw is not None:
                    self.asset_group_ids[
                        rtn.asset_raw.asset_id] = rtn.asset_group_id
                if rtn.asset_hash is not None:
                    for h in rtn.asset_hash.asset_ids:
                        self.asset_group_ids[h] = rtn.asset_group_id

            ptr, witness_num = bbclib_binary.get_n_byte_int(ptr, 2, data)
            if witness_num == 0:
                self.witness = None
            else:
                ptr, size = bbclib_binary.get_n_byte_int(ptr, 4, data)
                ptr, witnessdata = bbclib_binary.get_n_bytes(ptr, size, data)
                self.witness = BBcWitness()
                self.witness.transaction = self
                if not self.witness.unpack(witnessdata):
                    return False

            ptr, cross_num = bbclib_binary.get_n_byte_int(ptr, 2, data)
            if cross_num == 0:
                self.cross_ref = None
            else:
                ptr, size = bbclib_binary.get_n_byte_int(ptr, 4, data)
                ptr, crossdata = bbclib_binary.get_n_bytes(ptr, size, data)
                self.cross_ref = BBcCrossRef()
                if not self.cross_ref.unpack(crossdata):
                    return False

            ptr, sig_num = bbclib_binary.get_n_byte_int(ptr, 2, data)
            self.signatures = []
            for i in range(sig_num):
                ptr, size = bbclib_binary.get_n_byte_int(ptr, 4, data)
                sig = BBcSignature()
                if size > 4:
                    ptr, sigdata = bbclib_binary.get_n_bytes(ptr, size, data)
                    if not sig.unpack(sigdata):
                        return False
                self.signatures.append(sig)
                if ptr > data_size:
                    return False
            self.digest()
        except Exception as e:
            print("Transaction data unpack: %s" % e)
            print(traceback.format_exc())
            return False
        return True

    def sign(self,
             key_type=DEFAULT_CURVETYPE,
             private_key=None,
             public_key=None,
             keypair=None,
             no_pubkey=False):
        """Sign the transaction

        Args:
            key_type (int): Type of encryption key's curve
            private_key (bytes):
            public_key (bytes):
            keypair (KeyPair): keypair or set of private_key and public_key needs to be given
            no_pubkey (bool): If True, public key is not contained in the BBcSignature object (needs to be given externally when verification)
        Returns:
            BBcSignature:
        """
        bbclib._reset_error()
        if keypair is None:
            if len(private_key) != 32 or len(public_key) <= 32:
                bbclib._set_error(
                    code=bbclib_error.EBADKEYPAIR,
                    txt="Bad private_key/public_key (must be in bytes format)")
                return None
            keypair = KeyPair(curvetype=key_type,
                              privkey=private_key,
                              pubkey=public_key)
            if keypair is None:
                bbclib._set_error(code=bbclib_error.EBADKEYPAIR,
                                  txt="Bad private_key/public_key")
                return None

        sig = BBcSignature(key_type=keypair.curvetype)
        s = keypair.sign(self.digest())
        if s is None:
            bbclib._set_error(code=bbclib_error.EOTHER,
                              txt="sig_type %d is not supported" %
                              keypair.curvetype)
            return None
        if no_pubkey:
            sig.add(signature=s)
        else:
            sig.add(signature=s, pubkey=keypair.public_key)
        return sig
Ejemplo n.º 3
0
    def unpack(self, data):
        """Unpack into this object

        Args:
            data (bytes): packed binary data
        Returns:
            bool: True if successful
        """
        self.transaction_data = data[:]
        ptr = 0
        data_size = len(data)
        try:
            ptr, self.version = bbclib_binary.get_n_byte_int(ptr, 4, data)
            ptr, self.timestamp = bbclib_binary.get_n_byte_int(ptr, 8, data)
            if self.version != 0:
                ptr, id_length = bbclib_binary.get_n_byte_int(ptr, 2, data)
                self.idlen_conf["transaction_id"] = id_length
            ptr, evt_num = bbclib_binary.get_n_byte_int(ptr, 2, data)
            self.events = []
            for i in range(evt_num):
                ptr, size = bbclib_binary.get_n_byte_int(ptr, 4, data)
                ptr, evtdata = bbclib_binary.get_n_bytes(ptr, size, data)
                evt = BBcEvent()
                if not evt.unpack(evtdata):
                    return False
                self.events.append(evt)
                if ptr >= data_size:
                    return False
                self.asset_group_ids[evt.asset.asset_id] = evt.asset_group_id

            ptr, ref_num = bbclib_binary.get_n_byte_int(ptr, 2, data)
            self.references = []
            for i in range(ref_num):
                ptr, size = bbclib_binary.get_n_byte_int(ptr, 4, data)
                ptr, refdata = bbclib_binary.get_n_bytes(ptr, size, data)
                refe = BBcReference(None, self)
                if not refe.unpack(refdata):
                    return False
                self.references.append(refe)
                if ptr >= data_size:
                    return False

            ptr, rtn_num = bbclib_binary.get_n_byte_int(ptr, 2, data)
            self.relations = []
            for i in range(rtn_num):
                ptr, size = bbclib_binary.get_n_byte_int(ptr, 4, data)
                ptr, rtndata = bbclib_binary.get_n_bytes(ptr, size, data)
                rtn = BBcRelation()
                if not rtn.unpack(rtndata, self.version):
                    return False
                self.relations.append(rtn)
                if ptr >= data_size:
                    return False
                if rtn.asset is not None:
                    self.asset_group_ids[
                        rtn.asset.asset_id] = rtn.asset_group_id
                if rtn.asset_raw is not None:
                    self.asset_group_ids[
                        rtn.asset_raw.asset_id] = rtn.asset_group_id
                if rtn.asset_hash is not None:
                    for h in rtn.asset_hash.asset_ids:
                        self.asset_group_ids[h] = rtn.asset_group_id

            ptr, witness_num = bbclib_binary.get_n_byte_int(ptr, 2, data)
            if witness_num == 0:
                self.witness = None
            else:
                ptr, size = bbclib_binary.get_n_byte_int(ptr, 4, data)
                ptr, witnessdata = bbclib_binary.get_n_bytes(ptr, size, data)
                self.witness = BBcWitness()
                self.witness.transaction = self
                if not self.witness.unpack(witnessdata):
                    return False

            ptr, cross_num = bbclib_binary.get_n_byte_int(ptr, 2, data)
            if cross_num == 0:
                self.cross_ref = None
            else:
                ptr, size = bbclib_binary.get_n_byte_int(ptr, 4, data)
                ptr, crossdata = bbclib_binary.get_n_bytes(ptr, size, data)
                self.cross_ref = BBcCrossRef()
                if not self.cross_ref.unpack(crossdata):
                    return False

            ptr, sig_num = bbclib_binary.get_n_byte_int(ptr, 2, data)
            self.signatures = []
            for i in range(sig_num):
                ptr, size = bbclib_binary.get_n_byte_int(ptr, 4, data)
                sig = BBcSignature()
                if size > 4:
                    ptr, sigdata = bbclib_binary.get_n_bytes(ptr, size, data)
                    if not sig.unpack(sigdata):
                        return False
                self.signatures.append(sig)
                if ptr > data_size:
                    return False
            self.digest()
        except Exception as e:
            print("Transaction data unpack: %s" % e)
            print(traceback.format_exc())
            return False
        return True
Ejemplo n.º 4
0
 def create_cross_ref(self, transaction_id, domain_id=None):
     """Add BBcCrossRef object in this transaction (for allowing method chain style coding)"""
     cr = BBcCrossRef(domain_id=domain_id, transaction_id=transaction_id)
     self.add(cross_ref=cr)
     return self