2019-09-23 23:20:14 +00:00
<!doctype html>
< html lang = "en" >
< head >
< meta charset = "utf-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1, minimum-scale=1" / >
2020-02-04 20:46:17 +00:00
< meta name = "generator" content = "pdoc 0.7.4" / >
< title > src.onionrblocks.onionrblockapi API documentation< / title >
2019-09-23 23:20:14 +00:00
< meta name = "description" content = "Onionr - P2P Anonymous Storage Network …" / >
< link href = 'https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.0/normalize.min.css' rel = 'stylesheet' >
< link href = 'https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/8.0.0/sanitize.min.css' rel = 'stylesheet' >
< link href = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/github.min.css" rel = "stylesheet" >
2020-02-04 20:46:17 +00:00
< style > . flex { display : flex !important } body { line-height : 1.5 em } # content { padding : 20 px } # sidebar { padding : 30 px ; overflow : hidden } . http-server-breadcrumbs { font-size : 130 % ; margin : 0 0 15 px 0 } # footer { font-size : .75 em ; padding : 5 px 30 px ; border-top : 1 px solid #ddd ; text-align : right } # footer p { margin : 0 0 0 1 em ; display : inline-block } # footer p : last-child { margin-right : 30 px } h1 , h2 , h3 , h4 , h5 { font-weight : 300 } h1 { font-size : 2.5 em ; line-height : 1.1 em } h2 { font-size : 1.75 em ; margin : 1 em 0 .50 em 0 } h3 { font-size : 1.4 em ; margin : 25 px 0 10 px 0 } h4 { margin : 0 ; font-size : 105 % } a { color : #058 ; text-decoration : none ; transition : color .3 s ease-in-out } a : hover { color : #e82 } . title code { font-weight : bold } h2 [ id ^ = "header-" ] { margin-top : 2 em } . ident { color : #900 } pre code { background : #f8f8f8 ; font-size : .8 em ; line-height : 1.4 em } code { background : #f2f2f1 ; padding : 1 px 4 px ; overflow-wrap : break-word } h1 code { background : transparent } pre { background : #f8f8f8 ; border : 0 ; border-top : 1 px solid #ccc ; border-bottom : 1 px solid #ccc ; margin : 1 em 0 ; padding : 1 ex } # http-server-module-list { display : flex ; flex-flow : column } # http-server-module-list div { display : flex } # http-server-module-list dt { min-width : 10 % } # http-server-module-list p { margin-top : 0 } . toc ul , # index { list-style-type : none ; margin : 0 ; padding : 0 } # index code { background : transparent } # index h3 { border-bottom : 1 px solid #ddd } # index ul { padding : 0 } # index h4 { font-weight : bold } # index h4 + ul { margin-bottom : .6 em } @ media ( min-width : 200ex ) { # index . two-column { column-count : 2 } } @ media ( min-width : 300ex ) { # index . two-column { column-count : 3 } } dl { margin-bottom : 2 em } dl dl : last-child { margin-bottom : 4 em } dd { margin : 0 0 1 em 3 em } # header-classes + dl > dd { margin-bottom : 3 em } dd dd { margin-left : 2 em } dd p { margin : 10 px 0 } . name { background : #eee ; font-weight : bold ; font-size : .85 em ; padding : 5 px 10 px ; display : inline-block ; min-width : 40 % } . name : hover { background : #e0e0e0 } . name > span : first-child { white-space : nowrap } . name . class > span : nth-child ( 2 ) { margin-left : .4 em } . inherited { color : #999 ; border-left : 5 px solid #eee ; padding-left : 1 em } . inheritance em { font-style : normal ; font-weight : bold } . desc h2 { font-weight : 400 ; font-size : 1.25 em } . desc h3 { font-size : 1 em } . desc dt code { background : inherit } . source summary , . git-link-div { color : #666 ; text-align : right ; font-weight : 400 ; font-size : .8 em ; text-transform : uppercase } . source summary > * { white-space : nowrap ; cursor : pointer } . git-link { color : inherit ; margin-left : 1 em } . source pre { max-height : 500 px ; overflow : auto ; margin : 0 } . source pre code { font-size : 12 px ; overflow : visible } . hlist { list-style : none } . hlist li { display : inline } . hlist li : after { content : ',\2002' } . hlist li : last-child : after { content : none } . hlist . hlist { display : inline ; padding-left : 1 em } img { max-width : 100 % } . admonition { padding : .1 em .5 em ; margin-bottom : 1 em } . admonition-title { font-weight : bold } . admonition . note , . admonition . info , . admonition . important { background : #aef } . admonition . todo , . admonition . versionadded , . admonition . tip , . admonition . hint { background : #dfd } . admonition . warning , . admonition . versionchanged , . admonition . deprecated { background : #fd4 } . admonition . error , . admonition . danger , . admonition . caution { background : lightpink } < / style >
2019-09-23 23:20:14 +00:00
< style media = "screen and (min-width: 700px)" > @ media screen and ( min-width : 700px ) { # sidebar { width : 30 % } # content { width : 70 % ; max-width : 100 ch ; padding : 3 em 4 em ; border-left : 1 px solid #ddd } pre code { font-size : 1 em } . item . name { font-size : 1 em } main { display : flex ; flex-direction : row-reverse ; justify-content : flex-end } . toc ul ul , # index ul { padding-left : 1.5 em } . toc > ul > li { margin-top : .5 em } } < / style >
< style media = "print" > @ media print { # sidebar h1 { page-break-before : always } . source { display : none } } @ media print { * { background : transparent !important ; color : #000 !important ; box-shadow : none !important ; text-shadow : none !important } a [ href ] : after { content : " (" attr ( href ) ")" ; font-size : 90 % } a [ href ] [ title ] : after { content : none } abbr [ title ] : after { content : " (" attr ( title ) ")" } . ir a : after , a [ href ^ = "javascript:" ] : after , a [ href ^ = "#" ] : after { content : "" } pre , blockquote { border : 1 px solid #999 ; page-break-inside : avoid } thead { display : table-header-group } tr , img { page-break-inside : avoid } img { max-width : 100 % !important } @ page { margin : 0 . 5cm } p , h2 , h3 { orphans : 3 ; widows : 3 } h1 , h2 , h3 , h4 , h5 , h6 { page-break-after : avoid } } < / style >
< / head >
< body >
< main >
< article id = "content" >
< header >
2020-02-04 20:46:17 +00:00
< h1 class = "title" > Module < code > src.onionrblocks.onionrblockapi< / code > < / h1 >
2019-09-23 23:20:14 +00:00
< / header >
< section id = "section-intro" >
< p > Onionr - P2P Anonymous Storage Network< / p >
< p > This file contains the OnionrBlocks class which is a class for working with Onionr blocks< / p >
< details class = "source" >
2020-02-04 20:46:17 +00:00
< summary >
< span > Expand source code< / span >
< / summary >
2019-09-23 23:20:14 +00:00
< pre > < code class = "python" > ' ' '
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/> .
' ' '
2020-02-04 20:46:17 +00:00
import unpaddedbase32
2019-09-23 23:20:14 +00:00
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
2020-02-04 20:46:17 +00:00
from onionrutils import bytesconverter
2019-09-23 23:20:14 +00:00
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:
2020-02-04 20:46:17 +00:00
if not cryptoutils.replay_validator(self.bmetadata[' rply' ]): raise onionrexceptions.ReplayAttack
2019-09-23 23:20:14 +00:00
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
' ' '
2020-02-04 20:46:17 +00:00
if self.signer is None:
return False
if signing.ed_verify(data=self.signedData, key=self.signer, sig=self.signature, encodedData=True):
2019-09-23 23:20:14 +00:00
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)
2020-02-04 20:46:17 +00:00
self.signedData = (None if not self.isSigned() else self.getHeader(' meta' ).encode() + self.getContent())
2019-09-23 23:20:14 +00:00
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
2020-02-04 20:46:17 +00:00
2019-09-23 23:20:14 +00:00
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
' ' '
2020-02-04 20:46:17 +00:00
return self.bcontent
2019-09-23 23:20:14 +00:00
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
' ' '
2020-02-04 20:46:17 +00:00
signer = unpaddedbase32.repad(bytesconverter.str_to_bytes(signer))
2019-09-23 23:20:14 +00:00
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()
2020-02-04 20:46:17 +00:00
2019-09-23 23:20:14 +00:00
ret = isinstance(onionrstorage.getData(bHash), type(None))
return not ret< / code > < / pre >
< / details >
< / section >
< section >
< / section >
< section >
< / section >
< section >
< / section >
< section >
< h2 class = "section-title" id = "header-classes" > Classes< / h2 >
< dl >
2020-02-04 20:46:17 +00:00
< dt id = "src.onionrblocks.onionrblockapi.Block" > < code class = "flex name class" >
2019-09-23 23:20:14 +00:00
< span > class < span class = "ident" > Block< / span > < / span >
< span > (< / span > < span > hash=None, type=None, content=None, expire=None, decrypt=False, bypassReplayCheck=False)< / span >
< / code > < / dt >
< dd >
< section class = "desc" > < / section >
< details class = "source" >
2020-02-04 20:46:17 +00:00
< summary >
< span > Expand source code< / span >
< / summary >
2019-09-23 23:20:14 +00:00
< pre > < code class = "python" > 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:
2020-02-04 20:46:17 +00:00
if not cryptoutils.replay_validator(self.bmetadata[' rply' ]): raise onionrexceptions.ReplayAttack
2019-09-23 23:20:14 +00:00
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
' ' '
2020-02-04 20:46:17 +00:00
if self.signer is None:
return False
if signing.ed_verify(data=self.signedData, key=self.signer, sig=self.signature, encodedData=True):
2019-09-23 23:20:14 +00:00
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)
2020-02-04 20:46:17 +00:00
self.signedData = (None if not self.isSigned() else self.getHeader(' meta' ).encode() + self.getContent())
2019-09-23 23:20:14 +00:00
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
2020-02-04 20:46:17 +00:00
2019-09-23 23:20:14 +00:00
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
' ' '
2020-02-04 20:46:17 +00:00
return self.bcontent
2019-09-23 23:20:14 +00:00
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
' ' '
2020-02-04 20:46:17 +00:00
signer = unpaddedbase32.repad(bytesconverter.str_to_bytes(signer))
2019-09-23 23:20:14 +00:00
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()
2020-02-04 20:46:17 +00:00
2019-09-23 23:20:14 +00:00
ret = isinstance(onionrstorage.getData(bHash), type(None))
return not ret< / code > < / pre >
< / details >
< h3 > Class variables< / h3 >
< dl >
2020-02-04 20:46:17 +00:00
< dt id = "src.onionrblocks.onionrblockapi.Block.blockCache" > < code class = "name" > var < span class = "ident" > blockCache< / span > < / code > < / dt >
2019-09-23 23:20:14 +00:00
< dd >
2020-02-04 20:46:17 +00:00
< section class = "desc" > < p > 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)< / p > < / section >
2019-09-23 23:20:14 +00:00
< / dd >
2020-02-04 20:46:17 +00:00
< dt id = "src.onionrblocks.onionrblockapi.Block.blockCacheOrder" > < code class = "name" > var < span class = "ident" > blockCacheOrder< / span > < / code > < / dt >
2019-09-23 23:20:14 +00:00
< dd >
2020-02-04 20:46:17 +00:00
< section class = "desc" > < p > Built-in mutable sequence.< / p >
< p > If no argument is given, the constructor creates a new empty list.
The argument must be an iterable if specified.< / p > < / section >
2019-09-23 23:20:14 +00:00
< / dd >
< / dl >
< h3 > Methods< / h3 >
< dl >
2020-02-04 20:46:17 +00:00
< dt id = "src.onionrblocks.onionrblockapi.Block.decrypt" > < code class = "name flex" >
2019-09-23 23:20:14 +00:00
< span > def < span class = "ident" > decrypt< / span > < / span > (< span > self, encodedData=True)< / span >
< / code > < / dt >
< dd >
< section class = "desc" > < p > Decrypt a block, loading decrypted data into their vars< / p > < / section >
< details class = "source" >
2020-02-04 20:46:17 +00:00
< summary >
< span > Expand source code< / span >
< / summary >
2019-09-23 23:20:14 +00:00
< pre > < code class = "python" > 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:
2020-02-04 20:46:17 +00:00
if not cryptoutils.replay_validator(self.bmetadata[' rply' ]): raise onionrexceptions.ReplayAttack
2019-09-23 23:20:14 +00:00
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< / code > < / pre >
< / details >
< / dd >
2020-02-04 20:46:17 +00:00
< dt id = "src.onionrblocks.onionrblockapi.Block.delete" > < code class = "name flex" >
2019-09-23 23:20:14 +00:00
< span > def < span class = "ident" > delete< / span > < / span > (< span > self)< / span >
< / code > < / dt >
< dd >
< section class = "desc" > < p > Deletes the block's file and records, if they exist< / p >
< p > Outputs:
- (bool): whether or not the operation was successful< / p > < / section >
< details class = "source" >
2020-02-04 20:46:17 +00:00
< summary >
< span > Expand source code< / span >
< / summary >
2019-09-23 23:20:14 +00:00
< pre > < code class = "python" > 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< / code > < / pre >
< / details >
< / dd >
2020-02-04 20:46:17 +00:00
< dt id = "src.onionrblocks.onionrblockapi.Block.exists" > < code class = "name flex" >
2019-09-23 23:20:14 +00:00
< span > def < span class = "ident" > exists< / span > < / span > (< span > bHash)< / span >
< / code > < / dt >
< dd >
< section class = "desc" > < p > Checks if a block is saved to file or not< / p >
< p > 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< / p >
< p > Outputs:
- (bool): whether or not the block file exists< / p > < / section >
< details class = "source" >
2020-02-04 20:46:17 +00:00
< summary >
< span > Expand source code< / span >
< / summary >
2019-09-23 23:20:14 +00:00
< pre > < code class = "python" > 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()
2020-02-04 20:46:17 +00:00
2019-09-23 23:20:14 +00:00
ret = isinstance(onionrstorage.getData(bHash), type(None))
return not ret< / code > < / pre >
< / details >
< / dd >
2020-02-04 20:46:17 +00:00
< dt id = "src.onionrblocks.onionrblockapi.Block.getBlockFile" > < code class = "name flex" >
2019-09-23 23:20:14 +00:00
< span > def < span class = "ident" > getBlockFile< / span > < / span > (< span > self)< / span >
< / code > < / dt >
< dd >
< section class = "desc" > < p > Returns the location of the block file if it is saved< / p >
< p > Outputs:
- (str): the location of the block file, or None< / p > < / section >
< details class = "source" >
2020-02-04 20:46:17 +00:00
< summary >
< span > Expand source code< / span >
< / summary >
2019-09-23 23:20:14 +00:00
< pre > < code class = "python" > 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< / code > < / pre >
< / details >
< / dd >
2020-02-04 20:46:17 +00:00
< dt id = "src.onionrblocks.onionrblockapi.Block.getContent" > < code class = "name flex" >
2019-09-23 23:20:14 +00:00
< span > def < span class = "ident" > getContent< / span > < / span > (< span > self)< / span >
< / code > < / dt >
< dd >
< section class = "desc" > < p > Returns the contents of the block< / p >
< p > Outputs:
- (str): the contents of the block< / p > < / section >
< details class = "source" >
2020-02-04 20:46:17 +00:00
< summary >
< span > Expand source code< / span >
< / summary >
2019-09-23 23:20:14 +00:00
< pre > < code class = "python" > def getContent(self):
' ' '
Returns the contents of the block
Outputs:
- (str): the contents of the block
' ' '
2020-02-04 20:46:17 +00:00
return self.bcontent< / code > < / pre >
2019-09-23 23:20:14 +00:00
< / details >
< / dd >
2020-02-04 20:46:17 +00:00
< dt id = "src.onionrblocks.onionrblockapi.Block.getDate" > < code class = "name flex" >
2019-09-23 23:20:14 +00:00
< span > def < span class = "ident" > getDate< / span > < / span > (< span > self)< / span >
< / code > < / dt >
< dd >
< section class = "desc" > < p > Returns the date that the block was received, if loaded from file< / p >
< p > Outputs:
- (datetime): the date that the block was received< / p > < / section >
< details class = "source" >
2020-02-04 20:46:17 +00:00
< summary >
< span > Expand source code< / span >
< / summary >
2019-09-23 23:20:14 +00:00
< pre > < code class = "python" > 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< / code > < / pre >
< / details >
< / dd >
2020-02-04 20:46:17 +00:00
< dt id = "src.onionrblocks.onionrblockapi.Block.getExpire" > < code class = "name flex" >
2019-09-23 23:20:14 +00:00
< span > def < span class = "ident" > getExpire< / span > < / span > (< span > self)< / span >
< / code > < / dt >
< dd >
< section class = "desc" > < p > Returns the expire time for a block< / p >
< p > Outputs:
- (int): the expire time for a block, or None< / p > < / section >
< details class = "source" >
2020-02-04 20:46:17 +00:00
< summary >
< span > Expand source code< / span >
< / summary >
2019-09-23 23:20:14 +00:00
< pre > < code class = "python" > def getExpire(self):
' ' '
Returns the expire time for a block
Outputs:
- (int): the expire time for a block, or None
' ' '
return self.expire< / code > < / pre >
< / details >
< / dd >
2020-02-04 20:46:17 +00:00
< dt id = "src.onionrblocks.onionrblockapi.Block.getHash" > < code class = "name flex" >
2019-09-23 23:20:14 +00:00
< span > def < span class = "ident" > getHash< / span > < / span > (< span > self)< / span >
< / code > < / dt >
< dd >
< section class = "desc" > < p > Returns the hash of the block if saved to file< / p >
< p > Outputs:
- (str): the hash of the block, or None< / p > < / section >
< details class = "source" >
2020-02-04 20:46:17 +00:00
< summary >
< span > Expand source code< / span >
< / summary >
2019-09-23 23:20:14 +00:00
< pre > < code class = "python" > 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< / code > < / pre >
< / details >
< / dd >
2020-02-04 20:46:17 +00:00
< dt id = "src.onionrblocks.onionrblockapi.Block.getHeader" > < code class = "name flex" >
2019-09-23 23:20:14 +00:00
< span > def < span class = "ident" > getHeader< / span > < / span > (< span > self, key=None, default=None)< / span >
< / code > < / dt >
< dd >
< section class = "desc" > < p > Returns the header information< / p >
< p > Inputs:
- key (str): only returns the value of the key in the header< / p >
< p > Outputs:
- (dict/str): either the whole header as a dict, or one value< / p > < / section >
< details class = "source" >
2020-02-04 20:46:17 +00:00
< summary >
< span > Expand source code< / span >
< / summary >
2019-09-23 23:20:14 +00:00
< pre > < code class = "python" > 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< / code > < / pre >
< / details >
< / dd >
2020-02-04 20:46:17 +00:00
< dt id = "src.onionrblocks.onionrblockapi.Block.getMetadata" > < code class = "name flex" >
2019-09-23 23:20:14 +00:00
< span > def < span class = "ident" > getMetadata< / span > < / span > (< span > self, key=None, default=None)< / span >
< / code > < / dt >
< dd >
< section class = "desc" > < p > Returns the metadata information< / p >
< p > Inputs:
- key (str): only returns the value of the key in the metadata< / p >
< p > Outputs:
- (dict/str): either the whole metadata as a dict, or one value< / p > < / section >
< details class = "source" >
2020-02-04 20:46:17 +00:00
< summary >
< span > Expand source code< / span >
< / summary >
2019-09-23 23:20:14 +00:00
< pre > < code class = "python" > 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< / code > < / pre >
< / details >
< / dd >
2020-02-04 20:46:17 +00:00
< dt id = "src.onionrblocks.onionrblockapi.Block.getRaw" > < code class = "name flex" >
2019-09-23 23:20:14 +00:00
< span > def < span class = "ident" > getRaw< / span > < / span > (< span > self)< / span >
< / code > < / dt >
< dd >
< section class = "desc" > < p > Returns the raw contents of the block, if saved to file< / p >
< p > Outputs:
- (bytes): the raw contents of the block, or None< / p > < / section >
< details class = "source" >
2020-02-04 20:46:17 +00:00
< summary >
< span > Expand source code< / span >
< / summary >
2019-09-23 23:20:14 +00:00
< pre > < code class = "python" > 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< / code > < / pre >
< / details >
< / dd >
2020-02-04 20:46:17 +00:00
< dt id = "src.onionrblocks.onionrblockapi.Block.getSignature" > < code class = "name flex" >
2019-09-23 23:20:14 +00:00
< span > def < span class = "ident" > getSignature< / span > < / span > (< span > self)< / span >
< / code > < / dt >
< dd >
< section class = "desc" > < p > Returns the base64-encoded signature< / p >
< p > Outputs:
- (str): the signature, or None< / p > < / section >
< details class = "source" >
2020-02-04 20:46:17 +00:00
< summary >
< span > Expand source code< / span >
< / summary >
2019-09-23 23:20:14 +00:00
< pre > < code class = "python" > def getSignature(self):
' ' '
Returns the base64-encoded signature
Outputs:
- (str): the signature, or None
' ' '
return self.signature< / code > < / pre >
< / details >
< / dd >
2020-02-04 20:46:17 +00:00
< dt id = "src.onionrblocks.onionrblockapi.Block.getSignedData" > < code class = "name flex" >
2019-09-23 23:20:14 +00:00
< span > def < span class = "ident" > getSignedData< / span > < / span > (< span > self)< / span >
< / code > < / dt >
< dd >
< section class = "desc" > < p > Returns the data that was signed< / p >
< p > Outputs:
- (str): the data that was signed, or None< / p > < / section >
< details class = "source" >
2020-02-04 20:46:17 +00:00
< summary >
< span > Expand source code< / span >
< / summary >
2019-09-23 23:20:14 +00:00
< pre > < code class = "python" > def getSignedData(self):
' ' '
Returns the data that was signed
Outputs:
- (str): the data that was signed, or None
' ' '
return self.signedData< / code > < / pre >
< / details >
< / dd >
2020-02-04 20:46:17 +00:00
< dt id = "src.onionrblocks.onionrblockapi.Block.getType" > < code class = "name flex" >
2019-09-23 23:20:14 +00:00
< span > def < span class = "ident" > getType< / span > < / span > (< span > self)< / span >
< / code > < / dt >
< dd >
< section class = "desc" > < p > Returns the type of the block< / p >
< p > Outputs:
- (str): the type of the block< / p > < / section >
< details class = "source" >
2020-02-04 20:46:17 +00:00
< summary >
< span > Expand source code< / span >
< / summary >
2019-09-23 23:20:14 +00:00
< pre > < code class = "python" > def getType(self):
' ' '
Returns the type of the block
Outputs:
- (str): the type of the block
' ' '
return self.btype< / code > < / pre >
< / details >
< / dd >
2020-02-04 20:46:17 +00:00
< dt id = "src.onionrblocks.onionrblockapi.Block.isSigned" > < code class = "name flex" >
2019-09-23 23:20:14 +00:00
< span > def < span class = "ident" > isSigned< / span > < / span > (< span > self)< / span >
< / code > < / dt >
< dd >
< section class = "desc" > < p > Checks if the block was signed< / p >
< p > Outputs:
- (bool): whether or not the block is signed< / p > < / section >
< details class = "source" >
2020-02-04 20:46:17 +00:00
< summary >
< span > Expand source code< / span >
< / summary >
2019-09-23 23:20:14 +00:00
< pre > < code class = "python" > def isSigned(self):
' ' '
Checks if the block was signed
Outputs:
- (bool): whether or not the block is signed
' ' '
return self.signed< / code > < / pre >
< / details >
< / dd >
2020-02-04 20:46:17 +00:00
< dt id = "src.onionrblocks.onionrblockapi.Block.isSigner" > < code class = "name flex" >
2019-09-23 23:20:14 +00:00
< span > def < span class = "ident" > isSigner< / span > < / span > (< span > self, signer, encodedData=True)< / span >
< / code > < / dt >
< dd >
< section class = "desc" > < p > Checks if the block was signed by the signer inputted< / p >
< p > Inputs:
- signer (str): the public key of the signer to check against
- encodedData (bool): whether or not the < code > signer< / code > argument is base64 encoded< / p >
< p > Outputs:
- (bool): whether or not the signer of the block is the signer inputted< / p > < / section >
< details class = "source" >
2020-02-04 20:46:17 +00:00
< summary >
< span > Expand source code< / span >
< / summary >
2019-09-23 23:20:14 +00:00
< pre > < code class = "python" > 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
' ' '
2020-02-04 20:46:17 +00:00
signer = unpaddedbase32.repad(bytesconverter.str_to_bytes(signer))
2019-09-23 23:20:14 +00:00
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< / code > < / pre >
< / details >
< / dd >
2020-02-04 20:46:17 +00:00
< dt id = "src.onionrblocks.onionrblockapi.Block.isValid" > < code class = "name flex" >
2019-09-23 23:20:14 +00:00
< span > def < span class = "ident" > isValid< / span > < / span > (< span > self)< / span >
< / code > < / dt >
< dd >
< section class = "desc" > < p > Checks if the block is valid< / p >
< p > Outputs:
- (bool): whether or not the block is valid< / p > < / section >
< details class = "source" >
2020-02-04 20:46:17 +00:00
< summary >
< span > Expand source code< / span >
< / summary >
2019-09-23 23:20:14 +00:00
< pre > < code class = "python" > def isValid(self):
' ' '
Checks if the block is valid
Outputs:
- (bool): whether or not the block is valid
' ' '
return self.valid< / code > < / pre >
< / details >
< / dd >
2020-02-04 20:46:17 +00:00
< dt id = "src.onionrblocks.onionrblockapi.Block.save" > < code class = "name flex" >
2019-09-23 23:20:14 +00:00
< span > def < span class = "ident" > save< / span > < / span > (< span > self, sign=False, recreate=True)< / span >
< / code > < / dt >
< dd >
< section class = "desc" > < p > Saves a block to file and imports it into Onionr< / p >
< p > 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< / p >
< p > Outputs:
- (bool): whether or not the operation was successful< / p > < / section >
< details class = "source" >
2020-02-04 20:46:17 +00:00
< summary >
< span > Expand source code< / span >
< / summary >
2019-09-23 23:20:14 +00:00
< pre > < code class = "python" > 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< / code > < / pre >
< / details >
< / dd >
2020-02-04 20:46:17 +00:00
< dt id = "src.onionrblocks.onionrblockapi.Block.setContent" > < code class = "name flex" >
2019-09-23 23:20:14 +00:00
< span > def < span class = "ident" > setContent< / span > < / span > (< span > self, bcontent)< / span >
< / code > < / dt >
< dd >
< section class = "desc" > < p > Sets the contents of the block< / p >
< p > Inputs:
- bcontent (str): the contents to be set to< / p >
< p > Outputs:
- (Block): the Block instance< / p > < / section >
< details class = "source" >
2020-02-04 20:46:17 +00:00
< summary >
< span > Expand source code< / span >
< / summary >
2019-09-23 23:20:14 +00:00
< pre > < code class = "python" > 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< / code > < / pre >
< / details >
< / dd >
2020-02-04 20:46:17 +00:00
< dt id = "src.onionrblocks.onionrblockapi.Block.setMetadata" > < code class = "name flex" >
2019-09-23 23:20:14 +00:00
< span > def < span class = "ident" > setMetadata< / span > < / span > (< span > self, key, val)< / span >
< / code > < / dt >
< dd >
< section class = "desc" > < p > Sets a custom metadata value< / p >
< p > Metadata should not store block-specific data structures.< / p >
< p > Inputs:
- key (str): the key
- val: the value (type is irrelevant)< / p >
< p > Outputs:
- (Block): the Block instance< / p > < / section >
< details class = "source" >
2020-02-04 20:46:17 +00:00
< summary >
< span > Expand source code< / span >
< / summary >
2019-09-23 23:20:14 +00:00
< pre > < code class = "python" > 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< / code > < / pre >
< / details >
< / dd >
2020-02-04 20:46:17 +00:00
< dt id = "src.onionrblocks.onionrblockapi.Block.setType" > < code class = "name flex" >
2019-09-23 23:20:14 +00:00
< span > def < span class = "ident" > setType< / span > < / span > (< span > self, btype)< / span >
< / code > < / dt >
< dd >
< section class = "desc" > < p > Sets the type of the block< / p >
< p > Inputs:
- btype (str): the type of block to be set to< / p >
< p > Outputs:
- (Block): the Block instance< / p > < / section >
< details class = "source" >
2020-02-04 20:46:17 +00:00
< summary >
< span > Expand source code< / span >
< / summary >
2019-09-23 23:20:14 +00:00
< pre > < code class = "python" > 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< / code > < / pre >
< / details >
< / dd >
2020-02-04 20:46:17 +00:00
< dt id = "src.onionrblocks.onionrblockapi.Block.update" > < code class = "name flex" >
2019-09-23 23:20:14 +00:00
< span > def < span class = "ident" > update< / span > < / span > (< span > self, data=None, file=None)< / span >
< / code > < / dt >
< dd >
< section class = "desc" > < p > Loads data from a block in to the current object.< / p >
< p > Inputs:
- data (str):
- if None: will load from file by hash
- else: will load from < code > data< / code > string
- file (str):
- if None: will load from file specified in this parameter
- else: will load from wherever block is stored by hash< / p >
< p > Outputs:
- (bool): indicates whether or not the operation was successful< / p > < / section >
< details class = "source" >
2020-02-04 20:46:17 +00:00
< summary >
< span > Expand source code< / span >
< / summary >
2019-09-23 23:20:14 +00:00
< pre > < code class = "python" > 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)
2020-02-04 20:46:17 +00:00
self.signedData = (None if not self.isSigned() else self.getHeader(' meta' ).encode() + self.getContent())
2019-09-23 23:20:14 +00:00
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
2020-02-04 20:46:17 +00:00
2019-09-23 23:20:14 +00:00
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< / code > < / pre >
< / details >
< / dd >
2020-02-04 20:46:17 +00:00
< dt id = "src.onionrblocks.onionrblockapi.Block.verifySig" > < code class = "name flex" >
2019-09-23 23:20:14 +00:00
< span > def < span class = "ident" > verifySig< / span > < / span > (< span > self)< / span >
< / code > < / dt >
< dd >
< section class = "desc" > < p > Verify if a block's signature is signed by its claimed signer< / p > < / section >
< details class = "source" >
2020-02-04 20:46:17 +00:00
< summary >
< span > Expand source code< / span >
< / summary >
2019-09-23 23:20:14 +00:00
< pre > < code class = "python" > def verifySig(self):
' ' '
Verify if a block' s signature is signed by its claimed signer
' ' '
2020-02-04 20:46:17 +00:00
if self.signer is None:
return False
if signing.ed_verify(data=self.signedData, key=self.signer, sig=self.signature, encodedData=True):
2019-09-23 23:20:14 +00:00
self.validSig = True
else:
self.validSig = False
return self.validSig< / code > < / pre >
< / details >
< / dd >
< / dl >
< / dd >
< / dl >
< / section >
< / article >
< nav id = "sidebar" >
< h1 > Index< / h1 >
< div class = "toc" >
< ul > < / ul >
< / div >
< ul id = "index" >
< li > < h3 > Super-module< / h3 >
< ul >
2020-02-04 20:46:17 +00:00
< li > < code > < a title = "src.onionrblocks" href = "index.html" > src.onionrblocks< / a > < / code > < / li >
2019-09-23 23:20:14 +00:00
< / ul >
< / li >
< li > < h3 > < a href = "#header-classes" > Classes< / a > < / h3 >
< ul >
< li >
2020-02-04 20:46:17 +00:00
< h4 > < code > < a title = "src.onionrblocks.onionrblockapi.Block" href = "#src.onionrblocks.onionrblockapi.Block" > Block< / a > < / code > < / h4 >
2019-09-23 23:20:14 +00:00
< ul class = "two-column" >
2020-02-04 20:46:17 +00:00
< li > < code > < a title = "src.onionrblocks.onionrblockapi.Block.blockCache" href = "#src.onionrblocks.onionrblockapi.Block.blockCache" > blockCache< / a > < / code > < / li >
< li > < code > < a title = "src.onionrblocks.onionrblockapi.Block.blockCacheOrder" href = "#src.onionrblocks.onionrblockapi.Block.blockCacheOrder" > blockCacheOrder< / a > < / code > < / li >
< li > < code > < a title = "src.onionrblocks.onionrblockapi.Block.decrypt" href = "#src.onionrblocks.onionrblockapi.Block.decrypt" > decrypt< / a > < / code > < / li >
< li > < code > < a title = "src.onionrblocks.onionrblockapi.Block.delete" href = "#src.onionrblocks.onionrblockapi.Block.delete" > delete< / a > < / code > < / li >
< li > < code > < a title = "src.onionrblocks.onionrblockapi.Block.exists" href = "#src.onionrblocks.onionrblockapi.Block.exists" > exists< / a > < / code > < / li >
< li > < code > < a title = "src.onionrblocks.onionrblockapi.Block.getBlockFile" href = "#src.onionrblocks.onionrblockapi.Block.getBlockFile" > getBlockFile< / a > < / code > < / li >
< li > < code > < a title = "src.onionrblocks.onionrblockapi.Block.getContent" href = "#src.onionrblocks.onionrblockapi.Block.getContent" > getContent< / a > < / code > < / li >
< li > < code > < a title = "src.onionrblocks.onionrblockapi.Block.getDate" href = "#src.onionrblocks.onionrblockapi.Block.getDate" > getDate< / a > < / code > < / li >
< li > < code > < a title = "src.onionrblocks.onionrblockapi.Block.getExpire" href = "#src.onionrblocks.onionrblockapi.Block.getExpire" > getExpire< / a > < / code > < / li >
< li > < code > < a title = "src.onionrblocks.onionrblockapi.Block.getHash" href = "#src.onionrblocks.onionrblockapi.Block.getHash" > getHash< / a > < / code > < / li >
< li > < code > < a title = "src.onionrblocks.onionrblockapi.Block.getHeader" href = "#src.onionrblocks.onionrblockapi.Block.getHeader" > getHeader< / a > < / code > < / li >
< li > < code > < a title = "src.onionrblocks.onionrblockapi.Block.getMetadata" href = "#src.onionrblocks.onionrblockapi.Block.getMetadata" > getMetadata< / a > < / code > < / li >
< li > < code > < a title = "src.onionrblocks.onionrblockapi.Block.getRaw" href = "#src.onionrblocks.onionrblockapi.Block.getRaw" > getRaw< / a > < / code > < / li >
< li > < code > < a title = "src.onionrblocks.onionrblockapi.Block.getSignature" href = "#src.onionrblocks.onionrblockapi.Block.getSignature" > getSignature< / a > < / code > < / li >
< li > < code > < a title = "src.onionrblocks.onionrblockapi.Block.getSignedData" href = "#src.onionrblocks.onionrblockapi.Block.getSignedData" > getSignedData< / a > < / code > < / li >
< li > < code > < a title = "src.onionrblocks.onionrblockapi.Block.getType" href = "#src.onionrblocks.onionrblockapi.Block.getType" > getType< / a > < / code > < / li >
< li > < code > < a title = "src.onionrblocks.onionrblockapi.Block.isSigned" href = "#src.onionrblocks.onionrblockapi.Block.isSigned" > isSigned< / a > < / code > < / li >
< li > < code > < a title = "src.onionrblocks.onionrblockapi.Block.isSigner" href = "#src.onionrblocks.onionrblockapi.Block.isSigner" > isSigner< / a > < / code > < / li >
< li > < code > < a title = "src.onionrblocks.onionrblockapi.Block.isValid" href = "#src.onionrblocks.onionrblockapi.Block.isValid" > isValid< / a > < / code > < / li >
< li > < code > < a title = "src.onionrblocks.onionrblockapi.Block.save" href = "#src.onionrblocks.onionrblockapi.Block.save" > save< / a > < / code > < / li >
< li > < code > < a title = "src.onionrblocks.onionrblockapi.Block.setContent" href = "#src.onionrblocks.onionrblockapi.Block.setContent" > setContent< / a > < / code > < / li >
< li > < code > < a title = "src.onionrblocks.onionrblockapi.Block.setMetadata" href = "#src.onionrblocks.onionrblockapi.Block.setMetadata" > setMetadata< / a > < / code > < / li >
< li > < code > < a title = "src.onionrblocks.onionrblockapi.Block.setType" href = "#src.onionrblocks.onionrblockapi.Block.setType" > setType< / a > < / code > < / li >
< li > < code > < a title = "src.onionrblocks.onionrblockapi.Block.update" href = "#src.onionrblocks.onionrblockapi.Block.update" > update< / a > < / code > < / li >
< li > < code > < a title = "src.onionrblocks.onionrblockapi.Block.verifySig" href = "#src.onionrblocks.onionrblockapi.Block.verifySig" > verifySig< / a > < / code > < / li >
2019-09-23 23:20:14 +00:00
< / ul >
< / li >
< / ul >
< / li >
< / ul >
< / nav >
< / main >
< footer id = "footer" >
2020-02-04 20:46:17 +00:00
< p > Generated by < a href = "https://pdoc3.github.io/pdoc" > < cite > pdoc< / cite > 0.7.4< / a > .< / p >
2019-09-23 23:20:14 +00:00
< / footer >
< script src = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js" > < / script >
< script > hljs . initHighlightingOnLoad ( ) < / script >
< / body >
< / html >