def get_bytes(self, skip_signature=False, skip_second_signature=False): """ Serializes the given transaction prior to AIP11 (legacy). """ # TODO: rename to to_bytes which makes more sense than get_bytes if self.version and self.version != 1: raise Exception( "Invalid transaction version") # TODO: better exception bytes_data = bytes() bytes_data += write_bit8(self.type) bytes_data += write_bit32(self.timestamp) bytes_data += write_high(self.sender_public_key) # Apply a fix for broken type 1 (second signature) and 4 (multi signature) # transactions, which were erroneously calculated with a recipient id, # also apply a fix for all other broken transactions is_broken_type = self.type in [ TRANSACTION_TYPE_SECOND_SIGNATURE, TRANSACTION_TYPE_MULTI_SIGNATURE, ] if not self.recipient_id or (is_transaction_exception(self.id) or is_broken_type): bytes_data += pack("21x") else: bytes_data += b58decode_check(self.recipient_id) if self.vendor_field: encoded_vendor_field = self.vendor_field.encode("utf-8") bytes_data += encoded_vendor_field num_of_zeroes = 64 - len(encoded_vendor_field) if num_of_zeroes > 0: bytes_data += pack("{}x".format(num_of_zeroes)) else: bytes_data += pack("64x") bytes_data += write_bit64(self.amount) bytes_data += write_bit64(self.fee) if self.type == TRANSACTION_TYPE_SECOND_SIGNATURE: public_key = self.asset["signature"]["publicKey"] bytes_data += unhexlify(public_key) elif self.type == TRANSACTION_TYPE_DELEGATE_REGISTRATION: bytes_data += self.asset["delegate"]["username"].encode() elif self.type == TRANSACTION_TYPE_VOTE: bytes_data += "".join(self.asset["votes"]).encode() elif self.type == TRANSACTION_TYPE_MULTI_SIGNATURE: bytes_data += write_bit8(self.asset["multisignature"]["min"]) bytes_data += write_bit8(self.asset["multisignature"]["lifetime"]) bytes_data += "".join( self.asset["multisignature"]["keysgroup"]).encode() if not skip_signature and self.signature: bytes_data += write_high(self.signature) if not skip_second_signature and self.sign_signature: bytes_data += write_high(self.sign_signature) return bytes_data
def _serialize_vendor_field(self): """Serialize vendor field of the transaction """ bytes_data = bytes() if BaseTransaction.can_have_vendor_field( self.type) and self.vendor_field: data = self.vendor_field.encode("utf-8") bytes_data += write_bit8(len(data)) bytes_data += data return bytes_data else: bytes_data += write_bit8(0x00) return bytes_data
def _serialize_vendor_field(self): """Serialize vendor field of the transaction """ bytes_data = bytes() if Transaction.can_have_vendor_field(self.type): if self.vendor_field: bytes_data += write_bit8(len(self.vendor_field)) bytes_data += self.vendor_field.encode('utf-8') return bytes_data elif self.vendor_field_hex: bytes_data += write_bit8(len(self.vendor_field_hex) / 2) bytes_data += self.vendor_field_hex.encode('utf-8') return bytes_data bytes_data += write_bit8(0x00) return bytes_data
def address_from_public_key(public_key, network_version=None): """Get an address from a public key Args: public_key (str): network_version (int, optional): Returns: bytes: """ # TODO: also check if public key matches the regex, same as in core # ``` # const pubKeyRegex = /^[0-9A-Fa-f]{66}$/; # if (!pubKeyRegex.test(publicKey)) { # throw new Error(`publicKey '${publicKey}' is invalid`); # } # ``` # TODO: resolve the network vesion to get it from the config somehow # if not network_version: # network = get_network() # network_version = network['version'] ripemd160 = hashlib.new('ripemd160', unhexlify(public_key.encode())) seed = write_bit8(network_version) + ripemd160.digest() return b58encode_check(seed).decode()
def serialize(self): """Serialize Transaction """ bytes_data = bytes() # bytes() or bytes(512)? bytes_data += write_bit8( 0xFF) # fill, to distinguish between v1 and v2 bytes_data += write_bit8(self.version or 0x01) bytes_data += write_bit8(self.network or config.network["pubKeyHash"]) bytes_data += write_bit8(self.type) bytes_data += write_bit32(self.timestamp) bytes_data += write_high(self.sender_public_key.encode("utf-8")) bytes_data += write_bit64(self.fee) # TODO: test this thorougly as it might be completely wrong bytes_data += self._serialize_vendor_field() bytes_data += self._serialize_type() bytes_data += self._serialize_signatures() return hexlify(bytes_data)
def _serialize_signatures(self): """Serialize signature data of the transaction """ bytes_data = bytes() if self.signature: bytes_data += unhexlify(self.signature) if self.second_signature: bytes_data += unhexlify(self.second_signature) elif self.sign_signature: bytes_data += unhexlify(self.sign_signature) if self.signatures: # add 0xff separator to signal start of multi-signature transactions bytes_data += write_bit8(0xff) bytes_data += unhexlify(''.join(self.signatures)) return bytes_data
def address_from_public_key(public_key, network_version=None): """Get an address from a public key Args: public_key (str): network_version (int, optional): Returns: bytes: """ match = re.fullmatch("^[0-9A-Fa-f]{66}$", public_key) if not match: raise Exception("Invalid public key") # TODO: better exception if not network_version: network_version = config.network["pubKeyHash"] ripemd160 = hashlib.new("ripemd160", unhexlify(public_key.encode())) payload = write_bit8(network_version) + ripemd160.digest() return b58encode_check(payload).decode()
def _serialize_type(self): """Serialize transaction specific data (eg. delegate registration) """ bytes_data = bytes() if self.type == TRANSACTION_TYPE_TRANSFER: bytes_data += write_bit64(self.amount) bytes_data += write_bit32(self.expiration or 0) bytes_data += write_high( hexlify(b58decode_check(self.recipient_id))) elif self.type == TRANSACTION_TYPE_SECOND_SIGNATURE: bytes_data += unhexlify( self.asset['signature']['publicKey'].encode('utf-8')) elif self.type == TRANSACTION_TYPE_DELEGATE_REGISTRATION: delegate_bytes = hexlify( self.asset['delegate']['username'].encode('utf-8')) bytes_data += write_bit8(len(delegate_bytes)) bytes_data += unhexlify(delegate_bytes) elif self.type == TRANSACTION_TYPE_VOTE: vote_bytes = [] for vote in self.asset['votes']: if vote.startswith('+'): vote_bytes.append('01{}'.format(vote[1:])) else: vote_bytes.append('00{}'.format(vote[1:])) bytes_data += write_bit8(len(self.asset['votes'])) bytes_data += unhexlify(''.join(vote_bytes)) elif self.type == TRANSACTION_TYPE_MULTI_SIGNATURE: keysgroup = [] if self.version is None or self.version == 1: for key in self.asset['multisignature']['keysgroup']: keysgroup.append(key[1:] if key.startswith('+') else key) else: keysgroup = self.asset['multisignature']['keysgroup'] bytes_data += write_bit8(self.asset['multisignature']['min']) bytes_data += write_bit8( len(self.asset['multisignature']['keysgroup'])) bytes_data += write_bit8(self.asset['multisignature']['lifetime']) bytes_data += unhexlify(''.join(keysgroup)) elif self.type == TRANSACTION_TYPE_IPFS: bytes_data += write_bit8(len(self.asset['ipfs']['dag']) // 2) bytes_data += unhexlify(self.asset['ipfs']['dag']) elif self.type == TRANSACTION_TYPE_TIMELOCK_TRANSFER: bytes_data += write_bit64(self.amount) bytes_data += write_bit8(self.timelock_type) bytes_data += write_bit64(self.timelock) bytes_data += hexlify(b58decode_check(self.recipientId)) elif self.type == TRANSACTION_TYPE_MULTI_PAYMENT: bytes_data += write_bit32(len(self.asset['payments'])) for payment in self.asset['payments']: bytes_data += write_bit64(payment['amount']) bytes_data += hexlify(b58decode_check(payment['recipientId'])) elif self.type == TRANSACTION_TYPE_DELEGATE_RESIGNATION: pass else: raise Exception( 'Transaction type is invalid') # TODO: better exception return bytes_data
def _serialize_type(self): """Serialize transaction specific data (eg. delegate registration) """ bytes_data = bytes() if self.type == TRANSACTION_TYPE_TRANSFER: bytes_data += write_bit64(self.amount) bytes_data += write_bit32(self.expiration or 0) bytes_data += write_high( hexlify(b58decode_check(self.recipient_id))) elif self.type == TRANSACTION_TYPE_SECOND_SIGNATURE: bytes_data += unhexlify( self.asset["signature"]["publicKey"].encode("utf-8")) elif self.type == TRANSACTION_TYPE_DELEGATE_REGISTRATION: delegate_bytes = hexlify( self.asset["delegate"]["username"].encode("utf-8")) bytes_data += write_bit8(len(delegate_bytes)) bytes_data += unhexlify(delegate_bytes) elif self.type == TRANSACTION_TYPE_VOTE: vote_bytes = [] for vote in self.asset["votes"]: if vote.startswith("+"): vote_bytes.append("01{}".format(vote[1:])) else: vote_bytes.append("00{}".format(vote[1:])) bytes_data += write_bit8(len(self.asset["votes"])) bytes_data += unhexlify("".join(vote_bytes)) elif self.type == TRANSACTION_TYPE_MULTI_SIGNATURE: keysgroup = [] if self.version is None or self.version == 1: for key in self.asset["multisignature"]["keysgroup"]: keysgroup.append(key[1:] if key.startswith("+") else key) else: keysgroup = self.asset["multisignature"]["keysgroup"] bytes_data += write_bit8(self.asset["multisignature"]["min"]) bytes_data += write_bit8( len(self.asset["multisignature"]["keysgroup"])) bytes_data += write_bit8(self.asset["multisignature"]["lifetime"]) bytes_data += unhexlify("".join(keysgroup).encode("utf-8")) elif self.type == TRANSACTION_TYPE_IPFS: bytes_data += write_bit8(len(self.asset["ipfs"]["dag"]) // 2) bytes_data += unhexlify(self.asset["ipfs"]["dag"]) elif self.type == TRANSACTION_TYPE_TIMELOCK_TRANSFER: bytes_data += write_bit64(self.amount) bytes_data += write_bit8(self.timelock_type) bytes_data += write_bit64(self.timelock) bytes_data += b58decode_check(self.recipient_id) elif self.type == TRANSACTION_TYPE_MULTI_PAYMENT: bytes_data += write_bit32(len(self.asset["payments"])) for payment in self.asset["payments"]: bytes_data += write_bit64(payment["amount"]) bytes_data += b58decode_check(payment["recipientId"]) elif self.type == TRANSACTION_TYPE_DELEGATE_RESIGNATION: pass else: raise Exception( "Transaction type is invalid") # TODO: better exception return bytes_data