Module src.onionrblocks.onionrblockapi
Onionr - P2P Anonymous Storage Network
This file contains the OnionrBlocks class which is a class for working with Onionr blocks
Expand source code
'''
    Onionr - P2P Anonymous Storage Network
    This file contains the OnionrBlocks class which is a class for working with Onionr blocks
'''
'''
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
'''
import unpaddedbase32
import binascii
import logger, config, onionrexceptions, nacl.exceptions
import json, os, sys, datetime, base64, onionrstorage
from onionrusers import onionrusers
from onionrutils import stringvalidators, epoch
from coredb import blockmetadb
from onionrutils import bytesconverter
from onionrstorage import removeblock
import onionrblocks
from onionrcrypto import encryption, cryptoutils as cryptoutils, signing
class Block:
    blockCacheOrder = list() # NEVER write your own code that writes to this!
    blockCache = dict() # should never be accessed directly, look at Block.getCache()
    def __init__(self, hash = None, type = None, content = None, expire=None, decrypt=False, bypassReplayCheck=False):
        # take from arguments
        # sometimes people input a bytes object instead of str in `hash`
        if (not hash is None) and isinstance(hash, bytes):
            hash = hash.decode()
        self.hash = hash
        self.btype = type
        self.bcontent = content
        self.expire = expire
        self.bypassReplayCheck = bypassReplayCheck
        # initialize variables
        self.valid = True
        self.raw = None
        self.signed = False
        self.signature = None
        self.signedData = None
        self.blockFile = None
        self.bheader = {}
        self.bmetadata = {}
        self.isEncrypted = False
        self.decrypted = False
        self.signer = None
        self.validSig = False
        self.autoDecrypt = decrypt
        self.update()
    def decrypt(self, encodedData = True):
        '''
            Decrypt a block, loading decrypted data into their vars
        '''
        if self.decrypted:
            return True
        retData = False
        # decrypt data
        if self.getHeader('encryptType') == 'asym':
            try:
                try:
                    self.bcontent = encryption.pub_key_decrypt(self.bcontent, encodedData=encodedData)
                except (binascii.Error, ValueError) as e:
                    self.bcontent = encryption.pub_key_decrypt(self.bcontent, encodedData=False)
                bmeta = encryption.pub_key_decrypt(self.bmetadata, encodedData=encodedData)
                try:
                    bmeta = bmeta.decode()
                except AttributeError:
                    # yet another bytes fix
                    pass
                self.bmetadata = json.loads(bmeta)
                self.signature = encryption.pub_key_decrypt(self.signature, encodedData=encodedData)
                self.signer = encryption.pub_key_decrypt(self.signer, encodedData=encodedData)
                self.bheader['signer'] = self.signer.decode()
                self.signedData =  json.dumps(self.bmetadata).encode() + self.bcontent
                if not self.signer is None:
                    if not self.verifySig():
                        raise onionrexceptions.SignatureError("Block has invalid signature")
                # Check for replay attacks
                try:
                    if epoch.get_epoch() - blockmetadb.get_block_date(self.hash) > 60:
                        if not cryptoutils.replay_validator(self.bmetadata['rply']): raise onionrexceptions.ReplayAttack
                except (AssertionError, KeyError, TypeError, onionrexceptions.ReplayAttack) as e:
                    if not self.bypassReplayCheck:
                        # Zero out variables to prevent reading of replays
                        self.bmetadata = {}
                        self.signer = ''
                        self.bheader['signer'] = ''
                        self.signedData = ''
                        self.signature = ''
                        raise onionrexceptions.ReplayAttack('Signature is too old. possible replay attack')
                try:
                    if not self.bmetadata['forwardEnc']: raise KeyError
                except (AssertionError, KeyError) as e:
                    pass
                else:
                    try:
                        self.bcontent = onionrusers.OnionrUser(self.signer).forwardDecrypt(self.bcontent)
                    except (onionrexceptions.DecryptionError, nacl.exceptions.CryptoError) as e:
                        #logger.error(str(e))
                        pass
            except nacl.exceptions.CryptoError:
                pass
                #logger.debug('Could not decrypt block. Either invalid key or corrupted data')
            except onionrexceptions.ReplayAttack:
                logger.warn('%s is possibly a replay attack' % (self.hash,))
            else:
                retData = True
                self.decrypted = True
        return retData
    def verifySig(self):
        '''
            Verify if a block's signature is signed by its claimed signer
        '''
        if self.signer is None:
            return False
        if signing.ed_verify(data=self.signedData, key=self.signer, sig=self.signature, encodedData=True):
            self.validSig = True
        else:
            self.validSig = False
        return self.validSig
    def update(self, data = None, file = None):
        '''
            Loads data from a block in to the current object.
            Inputs:
            - data (str):
              - if None: will load from file by hash
              - else: will load from `data` string
            - file (str):
              - if None: will load from file specified in this parameter
              - else: will load from wherever block is stored by hash
            Outputs:
            - (bool): indicates whether or not the operation was successful
        '''
        try:
            # import from string
            blockdata = data
            # import from file
            if blockdata is None:
                try:
                    blockdata = onionrstorage.getData(self.getHash())#.decode()
                except AttributeError:
                    raise onionrexceptions.NoDataAvailable('Block does not exist')
            else:
                self.blockFile = None
            # parse block
            self.raw = blockdata
            self.bheader = json.loads(self.getRaw()[:self.getRaw().index(b'\n')])
            self.bcontent = self.getRaw()[self.getRaw().index(b'\n') + 1:]
            if ('encryptType' in self.bheader) and (self.bheader['encryptType'] in ('asym', 'sym')):
                self.bmetadata = self.getHeader('meta', None)
                self.isEncrypted = True
            else:
                self.bmetadata = json.loads(self.getHeader('meta', None))
            self.btype = self.getMetadata('type', None)
            self.signed = ('sig' in self.getHeader() and self.getHeader('sig') != '')
            # TODO: detect if signer is hash of pubkey or not
            self.signer = self.getHeader('signer', None)
            self.signature = self.getHeader('sig', None)
            # signed data is jsonMeta + block content (no linebreak)
            self.signedData = (None if not self.isSigned() else self.getHeader('meta').encode() + self.getContent())
            self.date = blockmetadb.get_block_date(self.getHash())
            self.claimedTime = self.getHeader('time', None)
            if not self.getDate() is None:
                self.date = datetime.datetime.fromtimestamp(self.getDate())
            self.valid = True
            if self.autoDecrypt:
                self.decrypt()
            return True
        except Exception as e:
            logger.warn('Failed to parse block %s' % self.getHash(), error = e, timestamp = False)
            # if block can't be parsed, it's a waste of precious space. Throw it away.
            if not self.delete():
                logger.warn('Failed to delete invalid block %s.' % self.getHash(), error = e)
            else:
                logger.debug('Deleted invalid block %s.' % self.getHash(), timestamp = False)
        self.valid = False
        return False
    def delete(self):
        '''
            Deletes the block's file and records, if they exist
            Outputs:
            - (bool): whether or not the operation was successful
        '''
        if self.exists():
            try:
                os.remove(self.getBlockFile())
            except TypeError:
                pass
            b_hash = self.getHash()
            onionrstorage.deleteBlock(b_hash)
            removeblock.remove_block(b_hash)
            return True
        return False
    def save(self, sign = False, recreate = True):
        '''
            Saves a block to file and imports it into Onionr
            Inputs:
            - sign (bool): whether or not to sign the block before saving
            - recreate (bool): if the block already exists, whether or not to recreate the block and save under a new hash
            Outputs:
            - (bool): whether or not the operation was successful
        '''
        try:
            if self.isValid() is True:
                self.hash = onionrblocks.insert(self.getRaw(), header = self.getType(), sign = sign, meta = self.getMetadata(), expire = self.getExpire())
                if self.hash != False:
                    self.update()
                return self.getHash()
            else:
                logger.warn('Not writing block; it is invalid.')
        except Exception as e:
            logger.error('Failed to save block.', error = e, timestamp = False)
        return False
    # getters
    def getExpire(self):
        '''
            Returns the expire time for a block
            Outputs:
            - (int): the expire time for a block, or None
        '''
        return self.expire
    def getHash(self):
        '''
            Returns the hash of the block if saved to file
            Outputs:
            - (str): the hash of the block, or None
        '''
        return self.hash
    def getType(self):
        '''
            Returns the type of the block
            Outputs:
            - (str): the type of the block
        '''
        return self.btype
    def getRaw(self):
        '''
            Returns the raw contents of the block, if saved to file
            Outputs:
            - (bytes): the raw contents of the block, or None
        '''
        return self.raw
    def getHeader(self, key = None, default = None):
        '''
            Returns the header information
            Inputs:
            - key (str): only returns the value of the key in the header
            Outputs:
            - (dict/str): either the whole header as a dict, or one value
        '''
        if not key is None:
            if key in self.getHeader():
                return self.getHeader()[key]
            return default
        return self.bheader
    def getMetadata(self, key = None, default = None):
        '''
            Returns the metadata information
            Inputs:
            - key (str): only returns the value of the key in the metadata
            Outputs:
            - (dict/str): either the whole metadata as a dict, or one value
        '''
        if not key is None:
            if key in self.getMetadata():
                return self.getMetadata()[key]
            return default
        return self.bmetadata
    def getContent(self):
        '''
            Returns the contents of the block
            Outputs:
            - (str): the contents of the block
        '''
        return self.bcontent
    def getDate(self):
        '''
            Returns the date that the block was received, if loaded from file
            Outputs:
            - (datetime): the date that the block was received
        '''
        return self.date
    def getBlockFile(self):
        '''
            Returns the location of the block file if it is saved
            Outputs:
            - (str): the location of the block file, or None
        '''
        return self.blockFile
    def isValid(self):
        '''
            Checks if the block is valid
            Outputs:
            - (bool): whether or not the block is valid
        '''
        return self.valid
    def isSigned(self):
        '''
            Checks if the block was signed
            Outputs:
            - (bool): whether or not the block is signed
        '''
        return self.signed
    def getSignature(self):
        '''
            Returns the base64-encoded signature
            Outputs:
            - (str): the signature, or None
        '''
        return self.signature
    def getSignedData(self):
        '''
            Returns the data that was signed
            Outputs:
            - (str): the data that was signed, or None
        '''
        return self.signedData
    def isSigner(self, signer, encodedData = True):
        '''
            Checks if the block was signed by the signer inputted
            Inputs:
            - signer (str): the public key of the signer to check against
            - encodedData (bool): whether or not the `signer` argument is base64 encoded
            Outputs:
            - (bool): whether or not the signer of the block is the signer inputted
        '''
        signer = unpaddedbase32.repad(bytesconverter.str_to_bytes(signer))
        try:
            if (not self.isSigned()) or (not stringvalidators.validate_pub_key(signer)):
                return False
            return bool(signing.ed_verify(self.getSignedData(), signer, self.getSignature(), encodedData = encodedData))
        except:
            return False
    # setters
    def setType(self, btype):
        '''
            Sets the type of the block
            Inputs:
            - btype (str): the type of block to be set to
            Outputs:
            - (Block): the Block instance
        '''
        self.btype = btype
        return self
    def setMetadata(self, key, val):
        '''
            Sets a custom metadata value
            Metadata should not store block-specific data structures.
            Inputs:
            - key (str): the key
            - val: the value (type is irrelevant)
            Outputs:
            - (Block): the Block instance
        '''
        self.bmetadata[key] = val
        return self
    def setContent(self, bcontent):
        '''
            Sets the contents of the block
            Inputs:
            - bcontent (str): the contents to be set to
            Outputs:
            - (Block): the Block instance
        '''
        self.bcontent = str(bcontent)
        return self
    # static functions
    def exists(bHash):
        '''
            Checks if a block is saved to file or not
            Inputs:
            - hash (str/Block):
              - if (Block): check if this block is saved to file
              - if (str): check if a block by this hash is in file
            Outputs:
            - (bool): whether or not the block file exists
        '''
        # no input data? scrap it.
        if bHash is None:
            return False
        if isinstance(bHash, Block):
            bHash = bHash.getHash()
        ret = isinstance(onionrstorage.getData(bHash), type(None))
        return not ret
Classes
class Block (hash=None, type=None, content=None, expire=None, decrypt=False, bypassReplayCheck=False)- 
Expand source code
class Block: blockCacheOrder = list() # NEVER write your own code that writes to this! blockCache = dict() # should never be accessed directly, look at Block.getCache() def __init__(self, hash = None, type = None, content = None, expire=None, decrypt=False, bypassReplayCheck=False): # take from arguments # sometimes people input a bytes object instead of str in `hash` if (not hash is None) and isinstance(hash, bytes): hash = hash.decode() self.hash = hash self.btype = type self.bcontent = content self.expire = expire self.bypassReplayCheck = bypassReplayCheck # initialize variables self.valid = True self.raw = None self.signed = False self.signature = None self.signedData = None self.blockFile = None self.bheader = {} self.bmetadata = {} self.isEncrypted = False self.decrypted = False self.signer = None self.validSig = False self.autoDecrypt = decrypt self.update() def decrypt(self, encodedData = True): ''' Decrypt a block, loading decrypted data into their vars ''' if self.decrypted: return True retData = False # decrypt data if self.getHeader('encryptType') == 'asym': try: try: self.bcontent = encryption.pub_key_decrypt(self.bcontent, encodedData=encodedData) except (binascii.Error, ValueError) as e: self.bcontent = encryption.pub_key_decrypt(self.bcontent, encodedData=False) bmeta = encryption.pub_key_decrypt(self.bmetadata, encodedData=encodedData) try: bmeta = bmeta.decode() except AttributeError: # yet another bytes fix pass self.bmetadata = json.loads(bmeta) self.signature = encryption.pub_key_decrypt(self.signature, encodedData=encodedData) self.signer = encryption.pub_key_decrypt(self.signer, encodedData=encodedData) self.bheader['signer'] = self.signer.decode() self.signedData = json.dumps(self.bmetadata).encode() + self.bcontent if not self.signer is None: if not self.verifySig(): raise onionrexceptions.SignatureError("Block has invalid signature") # Check for replay attacks try: if epoch.get_epoch() - blockmetadb.get_block_date(self.hash) > 60: if not cryptoutils.replay_validator(self.bmetadata['rply']): raise onionrexceptions.ReplayAttack except (AssertionError, KeyError, TypeError, onionrexceptions.ReplayAttack) as e: if not self.bypassReplayCheck: # Zero out variables to prevent reading of replays self.bmetadata = {} self.signer = '' self.bheader['signer'] = '' self.signedData = '' self.signature = '' raise onionrexceptions.ReplayAttack('Signature is too old. possible replay attack') try: if not self.bmetadata['forwardEnc']: raise KeyError except (AssertionError, KeyError) as e: pass else: try: self.bcontent = onionrusers.OnionrUser(self.signer).forwardDecrypt(self.bcontent) except (onionrexceptions.DecryptionError, nacl.exceptions.CryptoError) as e: #logger.error(str(e)) pass except nacl.exceptions.CryptoError: pass #logger.debug('Could not decrypt block. Either invalid key or corrupted data') except onionrexceptions.ReplayAttack: logger.warn('%s is possibly a replay attack' % (self.hash,)) else: retData = True self.decrypted = True return retData def verifySig(self): ''' Verify if a block's signature is signed by its claimed signer ''' if self.signer is None: return False if signing.ed_verify(data=self.signedData, key=self.signer, sig=self.signature, encodedData=True): self.validSig = True else: self.validSig = False return self.validSig def update(self, data = None, file = None): ''' Loads data from a block in to the current object. Inputs: - data (str): - if None: will load from file by hash - else: will load from `data` string - file (str): - if None: will load from file specified in this parameter - else: will load from wherever block is stored by hash Outputs: - (bool): indicates whether or not the operation was successful ''' try: # import from string blockdata = data # import from file if blockdata is None: try: blockdata = onionrstorage.getData(self.getHash())#.decode() except AttributeError: raise onionrexceptions.NoDataAvailable('Block does not exist') else: self.blockFile = None # parse block self.raw = blockdata self.bheader = json.loads(self.getRaw()[:self.getRaw().index(b'\n')]) self.bcontent = self.getRaw()[self.getRaw().index(b'\n') + 1:] if ('encryptType' in self.bheader) and (self.bheader['encryptType'] in ('asym', 'sym')): self.bmetadata = self.getHeader('meta', None) self.isEncrypted = True else: self.bmetadata = json.loads(self.getHeader('meta', None)) self.btype = self.getMetadata('type', None) self.signed = ('sig' in self.getHeader() and self.getHeader('sig') != '') # TODO: detect if signer is hash of pubkey or not self.signer = self.getHeader('signer', None) self.signature = self.getHeader('sig', None) # signed data is jsonMeta + block content (no linebreak) self.signedData = (None if not self.isSigned() else self.getHeader('meta').encode() + self.getContent()) self.date = blockmetadb.get_block_date(self.getHash()) self.claimedTime = self.getHeader('time', None) if not self.getDate() is None: self.date = datetime.datetime.fromtimestamp(self.getDate()) self.valid = True if self.autoDecrypt: self.decrypt() return True except Exception as e: logger.warn('Failed to parse block %s' % self.getHash(), error = e, timestamp = False) # if block can't be parsed, it's a waste of precious space. Throw it away. if not self.delete(): logger.warn('Failed to delete invalid block %s.' % self.getHash(), error = e) else: logger.debug('Deleted invalid block %s.' % self.getHash(), timestamp = False) self.valid = False return False def delete(self): ''' Deletes the block's file and records, if they exist Outputs: - (bool): whether or not the operation was successful ''' if self.exists(): try: os.remove(self.getBlockFile()) except TypeError: pass b_hash = self.getHash() onionrstorage.deleteBlock(b_hash) removeblock.remove_block(b_hash) return True return False def save(self, sign = False, recreate = True): ''' Saves a block to file and imports it into Onionr Inputs: - sign (bool): whether or not to sign the block before saving - recreate (bool): if the block already exists, whether or not to recreate the block and save under a new hash Outputs: - (bool): whether or not the operation was successful ''' try: if self.isValid() is True: self.hash = onionrblocks.insert(self.getRaw(), header = self.getType(), sign = sign, meta = self.getMetadata(), expire = self.getExpire()) if self.hash != False: self.update() return self.getHash() else: logger.warn('Not writing block; it is invalid.') except Exception as e: logger.error('Failed to save block.', error = e, timestamp = False) return False # getters def getExpire(self): ''' Returns the expire time for a block Outputs: - (int): the expire time for a block, or None ''' return self.expire def getHash(self): ''' Returns the hash of the block if saved to file Outputs: - (str): the hash of the block, or None ''' return self.hash def getType(self): ''' Returns the type of the block Outputs: - (str): the type of the block ''' return self.btype def getRaw(self): ''' Returns the raw contents of the block, if saved to file Outputs: - (bytes): the raw contents of the block, or None ''' return self.raw def getHeader(self, key = None, default = None): ''' Returns the header information Inputs: - key (str): only returns the value of the key in the header Outputs: - (dict/str): either the whole header as a dict, or one value ''' if not key is None: if key in self.getHeader(): return self.getHeader()[key] return default return self.bheader def getMetadata(self, key = None, default = None): ''' Returns the metadata information Inputs: - key (str): only returns the value of the key in the metadata Outputs: - (dict/str): either the whole metadata as a dict, or one value ''' if not key is None: if key in self.getMetadata(): return self.getMetadata()[key] return default return self.bmetadata def getContent(self): ''' Returns the contents of the block Outputs: - (str): the contents of the block ''' return self.bcontent def getDate(self): ''' Returns the date that the block was received, if loaded from file Outputs: - (datetime): the date that the block was received ''' return self.date def getBlockFile(self): ''' Returns the location of the block file if it is saved Outputs: - (str): the location of the block file, or None ''' return self.blockFile def isValid(self): ''' Checks if the block is valid Outputs: - (bool): whether or not the block is valid ''' return self.valid def isSigned(self): ''' Checks if the block was signed Outputs: - (bool): whether or not the block is signed ''' return self.signed def getSignature(self): ''' Returns the base64-encoded signature Outputs: - (str): the signature, or None ''' return self.signature def getSignedData(self): ''' Returns the data that was signed Outputs: - (str): the data that was signed, or None ''' return self.signedData def isSigner(self, signer, encodedData = True): ''' Checks if the block was signed by the signer inputted Inputs: - signer (str): the public key of the signer to check against - encodedData (bool): whether or not the `signer` argument is base64 encoded Outputs: - (bool): whether or not the signer of the block is the signer inputted ''' signer = unpaddedbase32.repad(bytesconverter.str_to_bytes(signer)) try: if (not self.isSigned()) or (not stringvalidators.validate_pub_key(signer)): return False return bool(signing.ed_verify(self.getSignedData(), signer, self.getSignature(), encodedData = encodedData)) except: return False # setters def setType(self, btype): ''' Sets the type of the block Inputs: - btype (str): the type of block to be set to Outputs: - (Block): the Block instance ''' self.btype = btype return self def setMetadata(self, key, val): ''' Sets a custom metadata value Metadata should not store block-specific data structures. Inputs: - key (str): the key - val: the value (type is irrelevant) Outputs: - (Block): the Block instance ''' self.bmetadata[key] = val return self def setContent(self, bcontent): ''' Sets the contents of the block Inputs: - bcontent (str): the contents to be set to Outputs: - (Block): the Block instance ''' self.bcontent = str(bcontent) return self # static functions def exists(bHash): ''' Checks if a block is saved to file or not Inputs: - hash (str/Block): - if (Block): check if this block is saved to file - if (str): check if a block by this hash is in file Outputs: - (bool): whether or not the block file exists ''' # no input data? scrap it. if bHash is None: return False if isinstance(bHash, Block): bHash = bHash.getHash() ret = isinstance(onionrstorage.getData(bHash), type(None)) return not retClass variables
var blockCache- 
dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)
 var blockCacheOrder- 
Built-in mutable sequence.
If no argument is given, the constructor creates a new empty list. The argument must be an iterable if specified.
 
Methods
def decrypt(self, encodedData=True)- 
Decrypt a block, loading decrypted data into their vars
Expand source code
def decrypt(self, encodedData = True): ''' Decrypt a block, loading decrypted data into their vars ''' if self.decrypted: return True retData = False # decrypt data if self.getHeader('encryptType') == 'asym': try: try: self.bcontent = encryption.pub_key_decrypt(self.bcontent, encodedData=encodedData) except (binascii.Error, ValueError) as e: self.bcontent = encryption.pub_key_decrypt(self.bcontent, encodedData=False) bmeta = encryption.pub_key_decrypt(self.bmetadata, encodedData=encodedData) try: bmeta = bmeta.decode() except AttributeError: # yet another bytes fix pass self.bmetadata = json.loads(bmeta) self.signature = encryption.pub_key_decrypt(self.signature, encodedData=encodedData) self.signer = encryption.pub_key_decrypt(self.signer, encodedData=encodedData) self.bheader['signer'] = self.signer.decode() self.signedData = json.dumps(self.bmetadata).encode() + self.bcontent if not self.signer is None: if not self.verifySig(): raise onionrexceptions.SignatureError("Block has invalid signature") # Check for replay attacks try: if epoch.get_epoch() - blockmetadb.get_block_date(self.hash) > 60: if not cryptoutils.replay_validator(self.bmetadata['rply']): raise onionrexceptions.ReplayAttack except (AssertionError, KeyError, TypeError, onionrexceptions.ReplayAttack) as e: if not self.bypassReplayCheck: # Zero out variables to prevent reading of replays self.bmetadata = {} self.signer = '' self.bheader['signer'] = '' self.signedData = '' self.signature = '' raise onionrexceptions.ReplayAttack('Signature is too old. possible replay attack') try: if not self.bmetadata['forwardEnc']: raise KeyError except (AssertionError, KeyError) as e: pass else: try: self.bcontent = onionrusers.OnionrUser(self.signer).forwardDecrypt(self.bcontent) except (onionrexceptions.DecryptionError, nacl.exceptions.CryptoError) as e: #logger.error(str(e)) pass except nacl.exceptions.CryptoError: pass #logger.debug('Could not decrypt block. Either invalid key or corrupted data') except onionrexceptions.ReplayAttack: logger.warn('%s is possibly a replay attack' % (self.hash,)) else: retData = True self.decrypted = True return retData def delete(self)- 
Deletes the block's file and records, if they exist
Outputs: - (bool): whether or not the operation was successful
Expand source code
def delete(self): ''' Deletes the block's file and records, if they exist Outputs: - (bool): whether or not the operation was successful ''' if self.exists(): try: os.remove(self.getBlockFile()) except TypeError: pass b_hash = self.getHash() onionrstorage.deleteBlock(b_hash) removeblock.remove_block(b_hash) return True return False def exists(bHash)- 
Checks if a block is saved to file or not
Inputs: - hash (str/Block): - if (Block): check if this block is saved to file - if (str): check if a block by this hash is in file
Outputs: - (bool): whether or not the block file exists
Expand source code
def exists(bHash): ''' Checks if a block is saved to file or not Inputs: - hash (str/Block): - if (Block): check if this block is saved to file - if (str): check if a block by this hash is in file Outputs: - (bool): whether or not the block file exists ''' # no input data? scrap it. if bHash is None: return False if isinstance(bHash, Block): bHash = bHash.getHash() ret = isinstance(onionrstorage.getData(bHash), type(None)) return not ret def getBlockFile(self)- 
Returns the location of the block file if it is saved
Outputs: - (str): the location of the block file, or None
Expand source code
def getBlockFile(self): ''' Returns the location of the block file if it is saved Outputs: - (str): the location of the block file, or None ''' return self.blockFile def getContent(self)- 
Returns the contents of the block
Outputs: - (str): the contents of the block
Expand source code
def getContent(self): ''' Returns the contents of the block Outputs: - (str): the contents of the block ''' return self.bcontent def getDate(self)- 
Returns the date that the block was received, if loaded from file
Outputs: - (datetime): the date that the block was received
Expand source code
def getDate(self): ''' Returns the date that the block was received, if loaded from file Outputs: - (datetime): the date that the block was received ''' return self.date def getExpire(self)- 
Returns the expire time for a block
Outputs: - (int): the expire time for a block, or None
Expand source code
def getExpire(self): ''' Returns the expire time for a block Outputs: - (int): the expire time for a block, or None ''' return self.expire def getHash(self)- 
Returns the hash of the block if saved to file
Outputs: - (str): the hash of the block, or None
Expand source code
def getHash(self): ''' Returns the hash of the block if saved to file Outputs: - (str): the hash of the block, or None ''' return self.hash def getHeader(self, key=None, default=None)- 
Returns the header information
Inputs: - key (str): only returns the value of the key in the header
Outputs: - (dict/str): either the whole header as a dict, or one value
Expand source code
def getHeader(self, key = None, default = None): ''' Returns the header information Inputs: - key (str): only returns the value of the key in the header Outputs: - (dict/str): either the whole header as a dict, or one value ''' if not key is None: if key in self.getHeader(): return self.getHeader()[key] return default return self.bheader def getMetadata(self, key=None, default=None)- 
Returns the metadata information
Inputs: - key (str): only returns the value of the key in the metadata
Outputs: - (dict/str): either the whole metadata as a dict, or one value
Expand source code
def getMetadata(self, key = None, default = None): ''' Returns the metadata information Inputs: - key (str): only returns the value of the key in the metadata Outputs: - (dict/str): either the whole metadata as a dict, or one value ''' if not key is None: if key in self.getMetadata(): return self.getMetadata()[key] return default return self.bmetadata def getRaw(self)- 
Returns the raw contents of the block, if saved to file
Outputs: - (bytes): the raw contents of the block, or None
Expand source code
def getRaw(self): ''' Returns the raw contents of the block, if saved to file Outputs: - (bytes): the raw contents of the block, or None ''' return self.raw def getSignature(self)- 
Returns the base64-encoded signature
Outputs: - (str): the signature, or None
Expand source code
def getSignature(self): ''' Returns the base64-encoded signature Outputs: - (str): the signature, or None ''' return self.signature def getSignedData(self)- 
Returns the data that was signed
Outputs: - (str): the data that was signed, or None
Expand source code
def getSignedData(self): ''' Returns the data that was signed Outputs: - (str): the data that was signed, or None ''' return self.signedData def getType(self)- 
Returns the type of the block
Outputs: - (str): the type of the block
Expand source code
def getType(self): ''' Returns the type of the block Outputs: - (str): the type of the block ''' return self.btype def isSigned(self)- 
Checks if the block was signed
Outputs: - (bool): whether or not the block is signed
Expand source code
def isSigned(self): ''' Checks if the block was signed Outputs: - (bool): whether or not the block is signed ''' return self.signed def isSigner(self, signer, encodedData=True)- 
Checks if the block was signed by the signer inputted
Inputs: - signer (str): the public key of the signer to check against - encodedData (bool): whether or not the
signerargument is base64 encodedOutputs: - (bool): whether or not the signer of the block is the signer inputted
Expand source code
def isSigner(self, signer, encodedData = True): ''' Checks if the block was signed by the signer inputted Inputs: - signer (str): the public key of the signer to check against - encodedData (bool): whether or not the `signer` argument is base64 encoded Outputs: - (bool): whether or not the signer of the block is the signer inputted ''' signer = unpaddedbase32.repad(bytesconverter.str_to_bytes(signer)) try: if (not self.isSigned()) or (not stringvalidators.validate_pub_key(signer)): return False return bool(signing.ed_verify(self.getSignedData(), signer, self.getSignature(), encodedData = encodedData)) except: return False def isValid(self)- 
Checks if the block is valid
Outputs: - (bool): whether or not the block is valid
Expand source code
def isValid(self): ''' Checks if the block is valid Outputs: - (bool): whether or not the block is valid ''' return self.valid def save(self, sign=False, recreate=True)- 
Saves a block to file and imports it into Onionr
Inputs: - sign (bool): whether or not to sign the block before saving - recreate (bool): if the block already exists, whether or not to recreate the block and save under a new hash
Outputs: - (bool): whether or not the operation was successful
Expand source code
def save(self, sign = False, recreate = True): ''' Saves a block to file and imports it into Onionr Inputs: - sign (bool): whether or not to sign the block before saving - recreate (bool): if the block already exists, whether or not to recreate the block and save under a new hash Outputs: - (bool): whether or not the operation was successful ''' try: if self.isValid() is True: self.hash = onionrblocks.insert(self.getRaw(), header = self.getType(), sign = sign, meta = self.getMetadata(), expire = self.getExpire()) if self.hash != False: self.update() return self.getHash() else: logger.warn('Not writing block; it is invalid.') except Exception as e: logger.error('Failed to save block.', error = e, timestamp = False) return False def setContent(self, bcontent)- 
Sets the contents of the block
Inputs: - bcontent (str): the contents to be set to
Outputs: - (Block): the Block instance
Expand source code
def setContent(self, bcontent): ''' Sets the contents of the block Inputs: - bcontent (str): the contents to be set to Outputs: - (Block): the Block instance ''' self.bcontent = str(bcontent) return self def setMetadata(self, key, val)- 
Sets a custom metadata value
Metadata should not store block-specific data structures.
Inputs: - key (str): the key - val: the value (type is irrelevant)
Outputs: - (Block): the Block instance
Expand source code
def setMetadata(self, key, val): ''' Sets a custom metadata value Metadata should not store block-specific data structures. Inputs: - key (str): the key - val: the value (type is irrelevant) Outputs: - (Block): the Block instance ''' self.bmetadata[key] = val return self def setType(self, btype)- 
Sets the type of the block
Inputs: - btype (str): the type of block to be set to
Outputs: - (Block): the Block instance
Expand source code
def setType(self, btype): ''' Sets the type of the block Inputs: - btype (str): the type of block to be set to Outputs: - (Block): the Block instance ''' self.btype = btype return self def update(self, data=None, file=None)- 
Loads data from a block in to the current object.
Inputs: - data (str): - if None: will load from file by hash - else: will load from
datastring - file (str): - if None: will load from file specified in this parameter - else: will load from wherever block is stored by hashOutputs: - (bool): indicates whether or not the operation was successful
Expand source code
def update(self, data = None, file = None): ''' Loads data from a block in to the current object. Inputs: - data (str): - if None: will load from file by hash - else: will load from `data` string - file (str): - if None: will load from file specified in this parameter - else: will load from wherever block is stored by hash Outputs: - (bool): indicates whether or not the operation was successful ''' try: # import from string blockdata = data # import from file if blockdata is None: try: blockdata = onionrstorage.getData(self.getHash())#.decode() except AttributeError: raise onionrexceptions.NoDataAvailable('Block does not exist') else: self.blockFile = None # parse block self.raw = blockdata self.bheader = json.loads(self.getRaw()[:self.getRaw().index(b'\n')]) self.bcontent = self.getRaw()[self.getRaw().index(b'\n') + 1:] if ('encryptType' in self.bheader) and (self.bheader['encryptType'] in ('asym', 'sym')): self.bmetadata = self.getHeader('meta', None) self.isEncrypted = True else: self.bmetadata = json.loads(self.getHeader('meta', None)) self.btype = self.getMetadata('type', None) self.signed = ('sig' in self.getHeader() and self.getHeader('sig') != '') # TODO: detect if signer is hash of pubkey or not self.signer = self.getHeader('signer', None) self.signature = self.getHeader('sig', None) # signed data is jsonMeta + block content (no linebreak) self.signedData = (None if not self.isSigned() else self.getHeader('meta').encode() + self.getContent()) self.date = blockmetadb.get_block_date(self.getHash()) self.claimedTime = self.getHeader('time', None) if not self.getDate() is None: self.date = datetime.datetime.fromtimestamp(self.getDate()) self.valid = True if self.autoDecrypt: self.decrypt() return True except Exception as e: logger.warn('Failed to parse block %s' % self.getHash(), error = e, timestamp = False) # if block can't be parsed, it's a waste of precious space. Throw it away. if not self.delete(): logger.warn('Failed to delete invalid block %s.' % self.getHash(), error = e) else: logger.debug('Deleted invalid block %s.' % self.getHash(), timestamp = False) self.valid = False return False def verifySig(self)- 
Verify if a block's signature is signed by its claimed signer
Expand source code
def verifySig(self): ''' Verify if a block's signature is signed by its claimed signer ''' if self.signer is None: return False if signing.ed_verify(data=self.signedData, key=self.signer, sig=self.signature, encodedData=True): self.validSig = True else: self.validSig = False return self.validSig