Onionr/docs/html/onionr/onionrblocks/onionrblockapi.html

1910 lines
72 KiB
HTML
Raw Normal View History

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" />
<meta name="generator" content="pdoc 0.6.3" />
<title>onionr.onionrblocks.onionrblockapi API documentation</title>
<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">
<style>.flex{display:flex !important}body{line-height:1.5em}#content{padding:20px}#sidebar{padding:30px;overflow:hidden}.http-server-breadcrumbs{font-size:130%;margin:0 0 15px 0}#footer{font-size:.75em;padding:5px 30px;border-top:1px solid #ddd;text-align:right}#footer p{margin:0 0 0 1em;display:inline-block}#footer p:last-child{margin-right:30px}h1,h2,h3,h4,h5{font-weight:300}h1{font-size:2.5em;line-height:1.1em}h2{font-size:1.75em;margin:1em 0 .50em 0}h3{font-size:1.4em;margin:25px 0 10px 0}h4{margin:0;font-size:105%}a{color:#058;text-decoration:none;transition:color .3s ease-in-out}a:hover{color:#e82}.title code{font-weight:bold}h2[id^="header-"]{margin-top:2em}.ident{color:#900}pre code{background:#f8f8f8;font-size:.8em;line-height:1.4em}code{background:#f2f2f1;padding:1px 4px;overflow-wrap:break-word}h1 code{background:transparent}pre{background:#f8f8f8;border:0;border-top:1px solid #ccc;border-bottom:1px solid #ccc;margin:1em 0;padding:1ex}#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:1px solid #ddd}#index ul{padding:0}#index h4{font-weight:bold}#index h4 + ul{margin-bottom:.6em}@media (min-width:200ex){#index .two-column{column-count:2}}@media (min-width:300ex){#index .two-column{column-count:3}}dl{margin-bottom:2em}dl dl:last-child{margin-bottom:4em}dd{margin:0 0 1em 3em}#header-classes + dl > dd{margin-bottom:3em}dd dd{margin-left:2em}dd p{margin:10px 0}.name{background:#eee;font-weight:bold;font-size:.85em;padding:5px 10px;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:.4em}.inherited{color:#999;border-left:5px solid #eee;padding-left:1em}.inheritance em{font-style:normal;font-weight:bold}.desc h2{font-weight:400;font-size:1.25em}.desc h3{font-size:1em}.desc dt code{background:inherit}.source summary{color:#666;text-align:right;font-weight:400;font-size:.8em;text-transform:uppercase;cursor:pointer}.source pre{max-height:500px;overflow:auto;margin:0}.source pre code{font-size:12px;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:1em}img{max-width:100%}.admonition{padding:.1em .5em;margin-bottom:1em}.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>
<style media="screen and (min-width: 700px)">@media screen and (min-width:700px){#sidebar{width:30%}#content{width:70%;max-width:100ch;padding:3em 4em;border-left:1px solid #ddd}pre code{font-size:1em}.item .name{font-size:1em}main{display:flex;flex-direction:row-reverse;justify-content:flex-end}.toc ul ul,#index ul{padding-left:1.5em}.toc > ul > li{margin-top:.5em}}</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:1px 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>
<h1 class="title">Module <code>onionr.onionrblocks.onionrblockapi</code></h1>
</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">
<summary>Source code</summary>
<pre><code class="python">&#39;&#39;&#39;
Onionr - P2P Anonymous Storage Network
This file contains the OnionrBlocks class which is a class for working with Onionr blocks
&#39;&#39;&#39;
&#39;&#39;&#39;
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 &lt;https://www.gnu.org/licenses/&gt;.
&#39;&#39;&#39;
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 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):
&#39;&#39;&#39;
Decrypt a block, loading decrypted data into their vars
&#39;&#39;&#39;
if self.decrypted:
return True
retData = False
# decrypt data
if self.getHeader(&#39;encryptType&#39;) == &#39;asym&#39;:
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[&#39;signer&#39;] = 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(&#34;Block has invalid signature&#34;)
# Check for replay attacks
try:
if epoch.get_epoch() - blockmetadb.get_block_date(self.hash) &gt; 60:
if not cryptoutils.replay_validator(self.bmetadata[&#39;rply&#39;]): 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 = &#39;&#39;
self.bheader[&#39;signer&#39;] = &#39;&#39;
self.signedData = &#39;&#39;
self.signature = &#39;&#39;
raise onionrexceptions.ReplayAttack(&#39;Signature is too old. possible replay attack&#39;)
try:
if not self.bmetadata[&#39;forwardEnc&#39;]: 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(&#39;Could not decrypt block. Either invalid key or corrupted data&#39;)
except onionrexceptions.ReplayAttack:
logger.warn(&#39;%s is possibly a replay attack&#39; % (self.hash,))
else:
retData = True
self.decrypted = True
return retData
def verifySig(self):
&#39;&#39;&#39;
Verify if a block&#39;s signature is signed by its claimed signer
&#39;&#39;&#39;
if self.signer is None or 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):
&#39;&#39;&#39;
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
&#39;&#39;&#39;
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(&#39;Block does not exist&#39;)
else:
self.blockFile = None
# parse block
self.raw = blockdata
self.bheader = json.loads(self.getRaw()[:self.getRaw().index(b&#39;\n&#39;)])
self.bcontent = self.getRaw()[self.getRaw().index(b&#39;\n&#39;) + 1:]
if (&#39;encryptType&#39; in self.bheader) and (self.bheader[&#39;encryptType&#39;] in (&#39;asym&#39;, &#39;sym&#39;)):
self.bmetadata = self.getHeader(&#39;meta&#39;, None)
self.isEncrypted = True
else:
self.bmetadata = json.loads(self.getHeader(&#39;meta&#39;, None))
self.btype = self.getMetadata(&#39;type&#39;, None)
self.signed = (&#39;sig&#39; in self.getHeader() and self.getHeader(&#39;sig&#39;) != &#39;&#39;)
# TODO: detect if signer is hash of pubkey or not
self.signer = self.getHeader(&#39;signer&#39;, None)
self.signature = self.getHeader(&#39;sig&#39;, None)
# signed data is jsonMeta + block content (no linebreak)
self.signedData = (None if not self.isSigned() else self.getHeader(&#39;meta&#39;) + self.getContent())
self.date = blockmetadb.get_block_date(self.getHash())
self.claimedTime = self.getHeader(&#39;time&#39;, 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(&#39;Failed to parse block %s&#39; % self.getHash(), error = e, timestamp = False)
# if block can&#39;t be parsed, it&#39;s a waste of precious space. Throw it away.
if not self.delete():
logger.warn(&#39;Failed to delete invalid block %s.&#39; % self.getHash(), error = e)
else:
logger.debug(&#39;Deleted invalid block %s.&#39; % self.getHash(), timestamp = False)
self.valid = False
return False
def delete(self):
&#39;&#39;&#39;
Deletes the block&#39;s file and records, if they exist
Outputs:
- (bool): whether or not the operation was successful
&#39;&#39;&#39;
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):
&#39;&#39;&#39;
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
&#39;&#39;&#39;
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(&#39;Not writing block; it is invalid.&#39;)
except Exception as e:
logger.error(&#39;Failed to save block.&#39;, error = e, timestamp = False)
return False
# getters
def getExpire(self):
&#39;&#39;&#39;
Returns the expire time for a block
Outputs:
- (int): the expire time for a block, or None
&#39;&#39;&#39;
return self.expire
def getHash(self):
&#39;&#39;&#39;
Returns the hash of the block if saved to file
Outputs:
- (str): the hash of the block, or None
&#39;&#39;&#39;
return self.hash
def getType(self):
&#39;&#39;&#39;
Returns the type of the block
Outputs:
- (str): the type of the block
&#39;&#39;&#39;
return self.btype
def getRaw(self):
&#39;&#39;&#39;
Returns the raw contents of the block, if saved to file
Outputs:
- (bytes): the raw contents of the block, or None
&#39;&#39;&#39;
return self.raw
def getHeader(self, key = None, default = None):
&#39;&#39;&#39;
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
&#39;&#39;&#39;
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):
&#39;&#39;&#39;
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
&#39;&#39;&#39;
if not key is None:
if key in self.getMetadata():
return self.getMetadata()[key]
return default
return self.bmetadata
def getContent(self):
&#39;&#39;&#39;
Returns the contents of the block
Outputs:
- (str): the contents of the block
&#39;&#39;&#39;
return str(self.bcontent)
def getDate(self):
&#39;&#39;&#39;
Returns the date that the block was received, if loaded from file
Outputs:
- (datetime): the date that the block was received
&#39;&#39;&#39;
return self.date
def getBlockFile(self):
&#39;&#39;&#39;
Returns the location of the block file if it is saved
Outputs:
- (str): the location of the block file, or None
&#39;&#39;&#39;
return self.blockFile
def isValid(self):
&#39;&#39;&#39;
Checks if the block is valid
Outputs:
- (bool): whether or not the block is valid
&#39;&#39;&#39;
return self.valid
def isSigned(self):
&#39;&#39;&#39;
Checks if the block was signed
Outputs:
- (bool): whether or not the block is signed
&#39;&#39;&#39;
return self.signed
def getSignature(self):
&#39;&#39;&#39;
Returns the base64-encoded signature
Outputs:
- (str): the signature, or None
&#39;&#39;&#39;
return self.signature
def getSignedData(self):
&#39;&#39;&#39;
Returns the data that was signed
Outputs:
- (str): the data that was signed, or None
&#39;&#39;&#39;
return self.signedData
def isSigner(self, signer, encodedData = True):
&#39;&#39;&#39;
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
&#39;&#39;&#39;
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):
&#39;&#39;&#39;
Sets the type of the block
Inputs:
- btype (str): the type of block to be set to
Outputs:
- (Block): the Block instance
&#39;&#39;&#39;
self.btype = btype
return self
def setMetadata(self, key, val):
&#39;&#39;&#39;
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
&#39;&#39;&#39;
self.bmetadata[key] = val
return self
def setContent(self, bcontent):
&#39;&#39;&#39;
Sets the contents of the block
Inputs:
- bcontent (str): the contents to be set to
Outputs:
- (Block): the Block instance
&#39;&#39;&#39;
self.bcontent = str(bcontent)
return self
# static functions
def getBlocks(type = None, signer = None, signed = None, reverse = False, limit = None):
&#39;&#39;&#39;
Returns a list of Block objects based on supplied filters
Inputs:
- type (str): filters by block type
- signer (str/list): filters by signer (one in the list has to be a signer)
- signed (bool): filters out by whether or not the block is signed
- reverse (bool): reverses the list if True
Outputs:
- (list): a list of Block objects that match the input
&#39;&#39;&#39;
try:
relevant_blocks = list()
blocks = (blockmetadb.get_block_list() if type is None else blockmetadb.get_blocks_by_type(type))
for block in blocks:
if Block.exists(block):
block = Block(block)
relevant = True
if (not signed is None) and (block.isSigned() != bool(signed)):
relevant = False
if not signer is None:
if isinstance(signer, (str,)):
signer = [signer]
if isinstance(signer, (bytes,)):
signer = [signer.decode()]
isSigner = False
for key in signer:
if block.isSigner(key):
isSigner = True
break
if not isSigner:
relevant = False
if relevant and (limit is None or len(relevant_Blocks) &lt;= int(limit)):
relevant_blocks.append(block)
if bool(reverse):
relevant_blocks.reverse()
return relevant_blocks
except Exception as e:
logger.debug(&#39;Failed to get blocks.&#39;, error = e)
return list()
def exists(bHash):
&#39;&#39;&#39;
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
&#39;&#39;&#39;
# 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</code></pre>
</details>
</section>
<section>
</section>
<section>
</section>
<section>
</section>
<section>
<h2 class="section-title" id="header-classes">Classes</h2>
<dl>
<dt id="onionr.onionrblocks.onionrblockapi.Block"><code class="flex name class">
<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">
<summary>Source code</summary>
<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):
&#39;&#39;&#39;
Decrypt a block, loading decrypted data into their vars
&#39;&#39;&#39;
if self.decrypted:
return True
retData = False
# decrypt data
if self.getHeader(&#39;encryptType&#39;) == &#39;asym&#39;:
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[&#39;signer&#39;] = 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(&#34;Block has invalid signature&#34;)
# Check for replay attacks
try:
if epoch.get_epoch() - blockmetadb.get_block_date(self.hash) &gt; 60:
if not cryptoutils.replay_validator(self.bmetadata[&#39;rply&#39;]): 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 = &#39;&#39;
self.bheader[&#39;signer&#39;] = &#39;&#39;
self.signedData = &#39;&#39;
self.signature = &#39;&#39;
raise onionrexceptions.ReplayAttack(&#39;Signature is too old. possible replay attack&#39;)
try:
if not self.bmetadata[&#39;forwardEnc&#39;]: 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(&#39;Could not decrypt block. Either invalid key or corrupted data&#39;)
except onionrexceptions.ReplayAttack:
logger.warn(&#39;%s is possibly a replay attack&#39; % (self.hash,))
else:
retData = True
self.decrypted = True
return retData
def verifySig(self):
&#39;&#39;&#39;
Verify if a block&#39;s signature is signed by its claimed signer
&#39;&#39;&#39;
if self.signer is None or 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):
&#39;&#39;&#39;
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
&#39;&#39;&#39;
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(&#39;Block does not exist&#39;)
else:
self.blockFile = None
# parse block
self.raw = blockdata
self.bheader = json.loads(self.getRaw()[:self.getRaw().index(b&#39;\n&#39;)])
self.bcontent = self.getRaw()[self.getRaw().index(b&#39;\n&#39;) + 1:]
if (&#39;encryptType&#39; in self.bheader) and (self.bheader[&#39;encryptType&#39;] in (&#39;asym&#39;, &#39;sym&#39;)):
self.bmetadata = self.getHeader(&#39;meta&#39;, None)
self.isEncrypted = True
else:
self.bmetadata = json.loads(self.getHeader(&#39;meta&#39;, None))
self.btype = self.getMetadata(&#39;type&#39;, None)
self.signed = (&#39;sig&#39; in self.getHeader() and self.getHeader(&#39;sig&#39;) != &#39;&#39;)
# TODO: detect if signer is hash of pubkey or not
self.signer = self.getHeader(&#39;signer&#39;, None)
self.signature = self.getHeader(&#39;sig&#39;, None)
# signed data is jsonMeta + block content (no linebreak)
self.signedData = (None if not self.isSigned() else self.getHeader(&#39;meta&#39;) + self.getContent())
self.date = blockmetadb.get_block_date(self.getHash())
self.claimedTime = self.getHeader(&#39;time&#39;, 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(&#39;Failed to parse block %s&#39; % self.getHash(), error = e, timestamp = False)
# if block can&#39;t be parsed, it&#39;s a waste of precious space. Throw it away.
if not self.delete():
logger.warn(&#39;Failed to delete invalid block %s.&#39; % self.getHash(), error = e)
else:
logger.debug(&#39;Deleted invalid block %s.&#39; % self.getHash(), timestamp = False)
self.valid = False
return False
def delete(self):
&#39;&#39;&#39;
Deletes the block&#39;s file and records, if they exist
Outputs:
- (bool): whether or not the operation was successful
&#39;&#39;&#39;
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):
&#39;&#39;&#39;
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
&#39;&#39;&#39;
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(&#39;Not writing block; it is invalid.&#39;)
except Exception as e:
logger.error(&#39;Failed to save block.&#39;, error = e, timestamp = False)
return False
# getters
def getExpire(self):
&#39;&#39;&#39;
Returns the expire time for a block
Outputs:
- (int): the expire time for a block, or None
&#39;&#39;&#39;
return self.expire
def getHash(self):
&#39;&#39;&#39;
Returns the hash of the block if saved to file
Outputs:
- (str): the hash of the block, or None
&#39;&#39;&#39;
return self.hash
def getType(self):
&#39;&#39;&#39;
Returns the type of the block
Outputs:
- (str): the type of the block
&#39;&#39;&#39;
return self.btype
def getRaw(self):
&#39;&#39;&#39;
Returns the raw contents of the block, if saved to file
Outputs:
- (bytes): the raw contents of the block, or None
&#39;&#39;&#39;
return self.raw
def getHeader(self, key = None, default = None):
&#39;&#39;&#39;
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
&#39;&#39;&#39;
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):
&#39;&#39;&#39;
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
&#39;&#39;&#39;
if not key is None:
if key in self.getMetadata():
return self.getMetadata()[key]
return default
return self.bmetadata
def getContent(self):
&#39;&#39;&#39;
Returns the contents of the block
Outputs:
- (str): the contents of the block
&#39;&#39;&#39;
return str(self.bcontent)
def getDate(self):
&#39;&#39;&#39;
Returns the date that the block was received, if loaded from file
Outputs:
- (datetime): the date that the block was received
&#39;&#39;&#39;
return self.date
def getBlockFile(self):
&#39;&#39;&#39;
Returns the location of the block file if it is saved
Outputs:
- (str): the location of the block file, or None
&#39;&#39;&#39;
return self.blockFile
def isValid(self):
&#39;&#39;&#39;
Checks if the block is valid
Outputs:
- (bool): whether or not the block is valid
&#39;&#39;&#39;
return self.valid
def isSigned(self):
&#39;&#39;&#39;
Checks if the block was signed
Outputs:
- (bool): whether or not the block is signed
&#39;&#39;&#39;
return self.signed
def getSignature(self):
&#39;&#39;&#39;
Returns the base64-encoded signature
Outputs:
- (str): the signature, or None
&#39;&#39;&#39;
return self.signature
def getSignedData(self):
&#39;&#39;&#39;
Returns the data that was signed
Outputs:
- (str): the data that was signed, or None
&#39;&#39;&#39;
return self.signedData
def isSigner(self, signer, encodedData = True):
&#39;&#39;&#39;
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
&#39;&#39;&#39;
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):
&#39;&#39;&#39;
Sets the type of the block
Inputs:
- btype (str): the type of block to be set to
Outputs:
- (Block): the Block instance
&#39;&#39;&#39;
self.btype = btype
return self
def setMetadata(self, key, val):
&#39;&#39;&#39;
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
&#39;&#39;&#39;
self.bmetadata[key] = val
return self
def setContent(self, bcontent):
&#39;&#39;&#39;
Sets the contents of the block
Inputs:
- bcontent (str): the contents to be set to
Outputs:
- (Block): the Block instance
&#39;&#39;&#39;
self.bcontent = str(bcontent)
return self
# static functions
def getBlocks(type = None, signer = None, signed = None, reverse = False, limit = None):
&#39;&#39;&#39;
Returns a list of Block objects based on supplied filters
Inputs:
- type (str): filters by block type
- signer (str/list): filters by signer (one in the list has to be a signer)
- signed (bool): filters out by whether or not the block is signed
- reverse (bool): reverses the list if True
Outputs:
- (list): a list of Block objects that match the input
&#39;&#39;&#39;
try:
relevant_blocks = list()
blocks = (blockmetadb.get_block_list() if type is None else blockmetadb.get_blocks_by_type(type))
for block in blocks:
if Block.exists(block):
block = Block(block)
relevant = True
if (not signed is None) and (block.isSigned() != bool(signed)):
relevant = False
if not signer is None:
if isinstance(signer, (str,)):
signer = [signer]
if isinstance(signer, (bytes,)):
signer = [signer.decode()]
isSigner = False
for key in signer:
if block.isSigner(key):
isSigner = True
break
if not isSigner:
relevant = False
if relevant and (limit is None or len(relevant_Blocks) &lt;= int(limit)):
relevant_blocks.append(block)
if bool(reverse):
relevant_blocks.reverse()
return relevant_blocks
except Exception as e:
logger.debug(&#39;Failed to get blocks.&#39;, error = e)
return list()
def exists(bHash):
&#39;&#39;&#39;
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
&#39;&#39;&#39;
# 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</code></pre>
</details>
<h3>Class variables</h3>
<dl>
<dt id="onionr.onionrblocks.onionrblockapi.Block.blockCache"><code class="name">var <span class="ident">blockCache</span></code></dt>
<dd>
<section class="desc"></section>
</dd>
<dt id="onionr.onionrblocks.onionrblockapi.Block.blockCacheOrder"><code class="name">var <span class="ident">blockCacheOrder</span></code></dt>
<dd>
<section class="desc"></section>
</dd>
</dl>
<h3>Methods</h3>
<dl>
<dt id="onionr.onionrblocks.onionrblockapi.Block.decrypt"><code class="name flex">
<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">
<summary>Source code</summary>
<pre><code class="python">def decrypt(self, encodedData = True):
&#39;&#39;&#39;
Decrypt a block, loading decrypted data into their vars
&#39;&#39;&#39;
if self.decrypted:
return True
retData = False
# decrypt data
if self.getHeader(&#39;encryptType&#39;) == &#39;asym&#39;:
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[&#39;signer&#39;] = 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(&#34;Block has invalid signature&#34;)
# Check for replay attacks
try:
if epoch.get_epoch() - blockmetadb.get_block_date(self.hash) &gt; 60:
if not cryptoutils.replay_validator(self.bmetadata[&#39;rply&#39;]): 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 = &#39;&#39;
self.bheader[&#39;signer&#39;] = &#39;&#39;
self.signedData = &#39;&#39;
self.signature = &#39;&#39;
raise onionrexceptions.ReplayAttack(&#39;Signature is too old. possible replay attack&#39;)
try:
if not self.bmetadata[&#39;forwardEnc&#39;]: 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(&#39;Could not decrypt block. Either invalid key or corrupted data&#39;)
except onionrexceptions.ReplayAttack:
logger.warn(&#39;%s is possibly a replay attack&#39; % (self.hash,))
else:
retData = True
self.decrypted = True
return retData</code></pre>
</details>
</dd>
<dt id="onionr.onionrblocks.onionrblockapi.Block.delete"><code class="name flex">
<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">
<summary>Source code</summary>
<pre><code class="python">def delete(self):
&#39;&#39;&#39;
Deletes the block&#39;s file and records, if they exist
Outputs:
- (bool): whether or not the operation was successful
&#39;&#39;&#39;
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>
<dt id="onionr.onionrblocks.onionrblockapi.Block.exists"><code class="name flex">
<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">
<summary>Source code</summary>
<pre><code class="python">def exists(bHash):
&#39;&#39;&#39;
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
&#39;&#39;&#39;
# 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</code></pre>
</details>
</dd>
<dt id="onionr.onionrblocks.onionrblockapi.Block.getBlockFile"><code class="name flex">
<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">
<summary>Source code</summary>
<pre><code class="python">def getBlockFile(self):
&#39;&#39;&#39;
Returns the location of the block file if it is saved
Outputs:
- (str): the location of the block file, or None
&#39;&#39;&#39;
return self.blockFile</code></pre>
</details>
</dd>
<dt id="onionr.onionrblocks.onionrblockapi.Block.getBlocks"><code class="name flex">
<span>def <span class="ident">getBlocks</span></span>(<span>type=None, signer=None, signed=None, reverse=False, limit=None)</span>
</code></dt>
<dd>
<section class="desc"><p>Returns a list of Block objects based on supplied filters</p>
<p>Inputs:
- type (str): filters by block type
- signer (str/list): filters by signer (one in the list has to be a signer)
- signed (bool): filters out by whether or not the block is signed
- reverse (bool): reverses the list if True</p>
<p>Outputs:
- (list): a list of Block objects that match the input</p></section>
<details class="source">
<summary>Source code</summary>
<pre><code class="python">def getBlocks(type = None, signer = None, signed = None, reverse = False, limit = None):
&#39;&#39;&#39;
Returns a list of Block objects based on supplied filters
Inputs:
- type (str): filters by block type
- signer (str/list): filters by signer (one in the list has to be a signer)
- signed (bool): filters out by whether or not the block is signed
- reverse (bool): reverses the list if True
Outputs:
- (list): a list of Block objects that match the input
&#39;&#39;&#39;
try:
relevant_blocks = list()
blocks = (blockmetadb.get_block_list() if type is None else blockmetadb.get_blocks_by_type(type))
for block in blocks:
if Block.exists(block):
block = Block(block)
relevant = True
if (not signed is None) and (block.isSigned() != bool(signed)):
relevant = False
if not signer is None:
if isinstance(signer, (str,)):
signer = [signer]
if isinstance(signer, (bytes,)):
signer = [signer.decode()]
isSigner = False
for key in signer:
if block.isSigner(key):
isSigner = True
break
if not isSigner:
relevant = False
if relevant and (limit is None or len(relevant_Blocks) &lt;= int(limit)):
relevant_blocks.append(block)
if bool(reverse):
relevant_blocks.reverse()
return relevant_blocks
except Exception as e:
logger.debug(&#39;Failed to get blocks.&#39;, error = e)
return list()</code></pre>
</details>
</dd>
<dt id="onionr.onionrblocks.onionrblockapi.Block.getContent"><code class="name flex">
<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">
<summary>Source code</summary>
<pre><code class="python">def getContent(self):
&#39;&#39;&#39;
Returns the contents of the block
Outputs:
- (str): the contents of the block
&#39;&#39;&#39;
return str(self.bcontent)</code></pre>
</details>
</dd>
<dt id="onionr.onionrblocks.onionrblockapi.Block.getDate"><code class="name flex">
<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">
<summary>Source code</summary>
<pre><code class="python">def getDate(self):
&#39;&#39;&#39;
Returns the date that the block was received, if loaded from file
Outputs:
- (datetime): the date that the block was received
&#39;&#39;&#39;
return self.date</code></pre>
</details>
</dd>
<dt id="onionr.onionrblocks.onionrblockapi.Block.getExpire"><code class="name flex">
<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">
<summary>Source code</summary>
<pre><code class="python">def getExpire(self):
&#39;&#39;&#39;
Returns the expire time for a block
Outputs:
- (int): the expire time for a block, or None
&#39;&#39;&#39;
return self.expire</code></pre>
</details>
</dd>
<dt id="onionr.onionrblocks.onionrblockapi.Block.getHash"><code class="name flex">
<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">
<summary>Source code</summary>
<pre><code class="python">def getHash(self):
&#39;&#39;&#39;
Returns the hash of the block if saved to file
Outputs:
- (str): the hash of the block, or None
&#39;&#39;&#39;
return self.hash</code></pre>
</details>
</dd>
<dt id="onionr.onionrblocks.onionrblockapi.Block.getHeader"><code class="name flex">
<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">
<summary>Source code</summary>
<pre><code class="python">def getHeader(self, key = None, default = None):
&#39;&#39;&#39;
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
&#39;&#39;&#39;
if not key is None:
if key in self.getHeader():
return self.getHeader()[key]
return default
return self.bheader</code></pre>
</details>
</dd>
<dt id="onionr.onionrblocks.onionrblockapi.Block.getMetadata"><code class="name flex">
<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">
<summary>Source code</summary>
<pre><code class="python">def getMetadata(self, key = None, default = None):
&#39;&#39;&#39;
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
&#39;&#39;&#39;
if not key is None:
if key in self.getMetadata():
return self.getMetadata()[key]
return default
return self.bmetadata</code></pre>
</details>
</dd>
<dt id="onionr.onionrblocks.onionrblockapi.Block.getRaw"><code class="name flex">
<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">
<summary>Source code</summary>
<pre><code class="python">def getRaw(self):
&#39;&#39;&#39;
Returns the raw contents of the block, if saved to file
Outputs:
- (bytes): the raw contents of the block, or None
&#39;&#39;&#39;
return self.raw</code></pre>
</details>
</dd>
<dt id="onionr.onionrblocks.onionrblockapi.Block.getSignature"><code class="name flex">
<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">
<summary>Source code</summary>
<pre><code class="python">def getSignature(self):
&#39;&#39;&#39;
Returns the base64-encoded signature
Outputs:
- (str): the signature, or None
&#39;&#39;&#39;
return self.signature</code></pre>
</details>
</dd>
<dt id="onionr.onionrblocks.onionrblockapi.Block.getSignedData"><code class="name flex">
<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">
<summary>Source code</summary>
<pre><code class="python">def getSignedData(self):
&#39;&#39;&#39;
Returns the data that was signed
Outputs:
- (str): the data that was signed, or None
&#39;&#39;&#39;
return self.signedData</code></pre>
</details>
</dd>
<dt id="onionr.onionrblocks.onionrblockapi.Block.getType"><code class="name flex">
<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">
<summary>Source code</summary>
<pre><code class="python">def getType(self):
&#39;&#39;&#39;
Returns the type of the block
Outputs:
- (str): the type of the block
&#39;&#39;&#39;
return self.btype</code></pre>
</details>
</dd>
<dt id="onionr.onionrblocks.onionrblockapi.Block.isSigned"><code class="name flex">
<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">
<summary>Source code</summary>
<pre><code class="python">def isSigned(self):
&#39;&#39;&#39;
Checks if the block was signed
Outputs:
- (bool): whether or not the block is signed
&#39;&#39;&#39;
return self.signed</code></pre>
</details>
</dd>
<dt id="onionr.onionrblocks.onionrblockapi.Block.isSigner"><code class="name flex">
<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">
<summary>Source code</summary>
<pre><code class="python">def isSigner(self, signer, encodedData = True):
&#39;&#39;&#39;
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
&#39;&#39;&#39;
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>
<dt id="onionr.onionrblocks.onionrblockapi.Block.isValid"><code class="name flex">
<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">
<summary>Source code</summary>
<pre><code class="python">def isValid(self):
&#39;&#39;&#39;
Checks if the block is valid
Outputs:
- (bool): whether or not the block is valid
&#39;&#39;&#39;
return self.valid</code></pre>
</details>
</dd>
<dt id="onionr.onionrblocks.onionrblockapi.Block.save"><code class="name flex">
<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">
<summary>Source code</summary>
<pre><code class="python">def save(self, sign = False, recreate = True):
&#39;&#39;&#39;
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
&#39;&#39;&#39;
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(&#39;Not writing block; it is invalid.&#39;)
except Exception as e:
logger.error(&#39;Failed to save block.&#39;, error = e, timestamp = False)
return False</code></pre>
</details>
</dd>
<dt id="onionr.onionrblocks.onionrblockapi.Block.setContent"><code class="name flex">
<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">
<summary>Source code</summary>
<pre><code class="python">def setContent(self, bcontent):
&#39;&#39;&#39;
Sets the contents of the block
Inputs:
- bcontent (str): the contents to be set to
Outputs:
- (Block): the Block instance
&#39;&#39;&#39;
self.bcontent = str(bcontent)
return self</code></pre>
</details>
</dd>
<dt id="onionr.onionrblocks.onionrblockapi.Block.setMetadata"><code class="name flex">
<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">
<summary>Source code</summary>
<pre><code class="python">def setMetadata(self, key, val):
&#39;&#39;&#39;
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
&#39;&#39;&#39;
self.bmetadata[key] = val
return self</code></pre>
</details>
</dd>
<dt id="onionr.onionrblocks.onionrblockapi.Block.setType"><code class="name flex">
<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">
<summary>Source code</summary>
<pre><code class="python">def setType(self, btype):
&#39;&#39;&#39;
Sets the type of the block
Inputs:
- btype (str): the type of block to be set to
Outputs:
- (Block): the Block instance
&#39;&#39;&#39;
self.btype = btype
return self</code></pre>
</details>
</dd>
<dt id="onionr.onionrblocks.onionrblockapi.Block.update"><code class="name flex">
<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">
<summary>Source code</summary>
<pre><code class="python">def update(self, data = None, file = None):
&#39;&#39;&#39;
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
&#39;&#39;&#39;
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(&#39;Block does not exist&#39;)
else:
self.blockFile = None
# parse block
self.raw = blockdata
self.bheader = json.loads(self.getRaw()[:self.getRaw().index(b&#39;\n&#39;)])
self.bcontent = self.getRaw()[self.getRaw().index(b&#39;\n&#39;) + 1:]
if (&#39;encryptType&#39; in self.bheader) and (self.bheader[&#39;encryptType&#39;] in (&#39;asym&#39;, &#39;sym&#39;)):
self.bmetadata = self.getHeader(&#39;meta&#39;, None)
self.isEncrypted = True
else:
self.bmetadata = json.loads(self.getHeader(&#39;meta&#39;, None))
self.btype = self.getMetadata(&#39;type&#39;, None)
self.signed = (&#39;sig&#39; in self.getHeader() and self.getHeader(&#39;sig&#39;) != &#39;&#39;)
# TODO: detect if signer is hash of pubkey or not
self.signer = self.getHeader(&#39;signer&#39;, None)
self.signature = self.getHeader(&#39;sig&#39;, None)
# signed data is jsonMeta + block content (no linebreak)
self.signedData = (None if not self.isSigned() else self.getHeader(&#39;meta&#39;) + self.getContent())
self.date = blockmetadb.get_block_date(self.getHash())
self.claimedTime = self.getHeader(&#39;time&#39;, 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(&#39;Failed to parse block %s&#39; % self.getHash(), error = e, timestamp = False)
# if block can&#39;t be parsed, it&#39;s a waste of precious space. Throw it away.
if not self.delete():
logger.warn(&#39;Failed to delete invalid block %s.&#39; % self.getHash(), error = e)
else:
logger.debug(&#39;Deleted invalid block %s.&#39; % self.getHash(), timestamp = False)
self.valid = False
return False</code></pre>
</details>
</dd>
<dt id="onionr.onionrblocks.onionrblockapi.Block.verifySig"><code class="name flex">
<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">
<summary>Source code</summary>
<pre><code class="python">def verifySig(self):
&#39;&#39;&#39;
Verify if a block&#39;s signature is signed by its claimed signer
&#39;&#39;&#39;
if self.signer is None or signing.ed_verify(data=self.signedData, key=self.signer, sig=self.signature, encodedData=True):
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>
<li><code><a title="onionr.onionrblocks" href="index.html">onionr.onionrblocks</a></code></li>
</ul>
</li>
<li><h3><a href="#header-classes">Classes</a></h3>
<ul>
<li>
<h4><code><a title="onionr.onionrblocks.onionrblockapi.Block" href="#onionr.onionrblocks.onionrblockapi.Block">Block</a></code></h4>
<ul class="two-column">
<li><code><a title="onionr.onionrblocks.onionrblockapi.Block.blockCache" href="#onionr.onionrblocks.onionrblockapi.Block.blockCache">blockCache</a></code></li>
<li><code><a title="onionr.onionrblocks.onionrblockapi.Block.blockCacheOrder" href="#onionr.onionrblocks.onionrblockapi.Block.blockCacheOrder">blockCacheOrder</a></code></li>
<li><code><a title="onionr.onionrblocks.onionrblockapi.Block.decrypt" href="#onionr.onionrblocks.onionrblockapi.Block.decrypt">decrypt</a></code></li>
<li><code><a title="onionr.onionrblocks.onionrblockapi.Block.delete" href="#onionr.onionrblocks.onionrblockapi.Block.delete">delete</a></code></li>
<li><code><a title="onionr.onionrblocks.onionrblockapi.Block.exists" href="#onionr.onionrblocks.onionrblockapi.Block.exists">exists</a></code></li>
<li><code><a title="onionr.onionrblocks.onionrblockapi.Block.getBlockFile" href="#onionr.onionrblocks.onionrblockapi.Block.getBlockFile">getBlockFile</a></code></li>
<li><code><a title="onionr.onionrblocks.onionrblockapi.Block.getBlocks" href="#onionr.onionrblocks.onionrblockapi.Block.getBlocks">getBlocks</a></code></li>
<li><code><a title="onionr.onionrblocks.onionrblockapi.Block.getContent" href="#onionr.onionrblocks.onionrblockapi.Block.getContent">getContent</a></code></li>
<li><code><a title="onionr.onionrblocks.onionrblockapi.Block.getDate" href="#onionr.onionrblocks.onionrblockapi.Block.getDate">getDate</a></code></li>
<li><code><a title="onionr.onionrblocks.onionrblockapi.Block.getExpire" href="#onionr.onionrblocks.onionrblockapi.Block.getExpire">getExpire</a></code></li>
<li><code><a title="onionr.onionrblocks.onionrblockapi.Block.getHash" href="#onionr.onionrblocks.onionrblockapi.Block.getHash">getHash</a></code></li>
<li><code><a title="onionr.onionrblocks.onionrblockapi.Block.getHeader" href="#onionr.onionrblocks.onionrblockapi.Block.getHeader">getHeader</a></code></li>
<li><code><a title="onionr.onionrblocks.onionrblockapi.Block.getMetadata" href="#onionr.onionrblocks.onionrblockapi.Block.getMetadata">getMetadata</a></code></li>
<li><code><a title="onionr.onionrblocks.onionrblockapi.Block.getRaw" href="#onionr.onionrblocks.onionrblockapi.Block.getRaw">getRaw</a></code></li>
<li><code><a title="onionr.onionrblocks.onionrblockapi.Block.getSignature" href="#onionr.onionrblocks.onionrblockapi.Block.getSignature">getSignature</a></code></li>
<li><code><a title="onionr.onionrblocks.onionrblockapi.Block.getSignedData" href="#onionr.onionrblocks.onionrblockapi.Block.getSignedData">getSignedData</a></code></li>
<li><code><a title="onionr.onionrblocks.onionrblockapi.Block.getType" href="#onionr.onionrblocks.onionrblockapi.Block.getType">getType</a></code></li>
<li><code><a title="onionr.onionrblocks.onionrblockapi.Block.isSigned" href="#onionr.onionrblocks.onionrblockapi.Block.isSigned">isSigned</a></code></li>
<li><code><a title="onionr.onionrblocks.onionrblockapi.Block.isSigner" href="#onionr.onionrblocks.onionrblockapi.Block.isSigner">isSigner</a></code></li>
<li><code><a title="onionr.onionrblocks.onionrblockapi.Block.isValid" href="#onionr.onionrblocks.onionrblockapi.Block.isValid">isValid</a></code></li>
<li><code><a title="onionr.onionrblocks.onionrblockapi.Block.save" href="#onionr.onionrblocks.onionrblockapi.Block.save">save</a></code></li>
<li><code><a title="onionr.onionrblocks.onionrblockapi.Block.setContent" href="#onionr.onionrblocks.onionrblockapi.Block.setContent">setContent</a></code></li>
<li><code><a title="onionr.onionrblocks.onionrblockapi.Block.setMetadata" href="#onionr.onionrblocks.onionrblockapi.Block.setMetadata">setMetadata</a></code></li>
<li><code><a title="onionr.onionrblocks.onionrblockapi.Block.setType" href="#onionr.onionrblocks.onionrblockapi.Block.setType">setType</a></code></li>
<li><code><a title="onionr.onionrblocks.onionrblockapi.Block.update" href="#onionr.onionrblocks.onionrblockapi.Block.update">update</a></code></li>
<li><code><a title="onionr.onionrblocks.onionrblockapi.Block.verifySig" href="#onionr.onionrblocks.onionrblockapi.Block.verifySig">verifySig</a></code></li>
</ul>
</li>
</ul>
</li>
</ul>
</nav>
</main>
<footer id="footer">
<p>Generated by <a href="https://pdoc3.github.io/pdoc"><cite>pdoc</cite> 0.6.3</a>.</p>
</footer>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script>
<script>hljs.initHighlightingOnLoad()</script>
</body>
</html>