2019-09-08 09:48:16 +00:00
from typing import Union
2019-07-18 23:07:18 +00:00
import json
from onionrutils import bytesconverter , epoch
2019-09-21 23:49:24 +00:00
import filepaths , onionrstorage
from . import storagecounter
2019-09-21 22:45:46 +00:00
from onionrplugins import onionrevents as events
2019-07-19 19:49:56 +00:00
from etc import powchoice , onionrvalues
2019-09-23 23:00:50 +00:00
import config , onionrcrypto as crypto , onionrexceptions
2019-07-20 15:52:03 +00:00
from onionrusers import onionrusers
2019-07-21 16:15:20 +00:00
from onionrutils import localcommand , blockmetadata , stringvalidators
2019-07-20 15:52:03 +00:00
import coredb
2019-08-28 02:46:33 +00:00
import onionrproofs
2019-09-23 23:00:50 +00:00
from onionrproofs import subprocesspow
2019-09-08 09:48:16 +00:00
import logger
def insert_block ( data : Union [ str , bytes ] , header : str = ' txt ' ,
sign : bool = False , encryptType : str = ' ' , symKey : str = ' ' ,
asymPeer : str = ' ' , meta : dict = { } ,
expire : Union [ int , None ] = None , disableForward : bool = False ) - > Union [ str , bool ] :
"""
2019-07-18 23:07:18 +00:00
Inserts a block into the network
encryptType must be specified to encrypt a block
2019-09-08 09:48:16 +00:00
"""
2019-07-20 15:52:03 +00:00
use_subprocess = powchoice . use_subprocess ( config )
2019-07-18 23:07:18 +00:00
storage_counter = storagecounter . StorageCounter ( )
allocationReachedMessage = ' Cannot insert block, disk allocation reached. '
2019-09-08 09:48:16 +00:00
if storage_counter . is_full ( ) :
2019-07-18 23:07:18 +00:00
logger . error ( allocationReachedMessage )
2019-09-08 09:48:16 +00:00
raise onionrexceptions . DiskAllocationReached
2019-07-18 23:07:18 +00:00
retData = False
if type ( data ) is None :
raise ValueError ( ' Data cannot be none ' )
createTime = epoch . get_epoch ( )
2019-07-20 15:52:03 +00:00
dataNonce = bytesconverter . bytes_to_str ( crypto . hashers . sha3_hash ( data ) )
2019-07-18 23:07:18 +00:00
try :
with open ( filepaths . data_nonce_file , ' r ' ) as nonces :
if dataNonce in nonces :
return retData
except FileNotFoundError :
pass
# record nonce
with open ( filepaths . data_nonce_file , ' a ' ) as nonceFile :
nonceFile . write ( dataNonce + ' \n ' )
if type ( data ) is bytes :
data = data . decode ( )
data = str ( data )
plaintext = data
plaintextMeta = { }
plaintextPeer = asymPeer
retData = ' '
signature = ' '
signer = ' '
metadata = { }
# metadata is full block metadata, meta is internal, user specified metadata
# only use header if not set in provided meta
meta [ ' type ' ] = str ( header )
2019-08-27 08:47:22 +00:00
if encryptType in ( ' asym ' , ' sym ' ) :
2019-07-18 23:07:18 +00:00
metadata [ ' encryptType ' ] = encryptType
else :
2019-09-28 01:38:47 +00:00
if not config . get ( ' general.store_plaintext_blocks ' , True ) : raise onionrexceptions . InvalidMetadata ( " Plaintext blocks are disabled, yet a plaintext block was being inserted " )
2019-08-27 08:47:22 +00:00
if not encryptType in ( ' ' , None ) :
raise onionrexceptions . InvalidMetadata ( ' encryptType must be asym or sym, or blank ' )
2019-07-18 23:07:18 +00:00
try :
data = data . encode ( )
except AttributeError :
pass
if encryptType == ' asym ' :
meta [ ' rply ' ] = createTime # Duplicate the time in encrypted messages to prevent replays
2019-07-21 16:15:20 +00:00
if not disableForward and sign and asymPeer != crypto . pub_key :
2019-07-18 23:07:18 +00:00
try :
forwardEncrypted = onionrusers . OnionrUser ( asymPeer ) . forwardEncrypt ( data )
data = forwardEncrypted [ 0 ]
meta [ ' forwardEnc ' ] = True
expire = forwardEncrypted [ 2 ] # Expire time of key. no sense keeping block after that
except onionrexceptions . InvalidPubkey :
pass
#onionrusers.OnionrUser(self, asymPeer).generateForwardKey()
fsKey = onionrusers . OnionrUser ( asymPeer ) . generateForwardKey ( )
#fsKey = onionrusers.OnionrUser(self, asymPeer).getGeneratedForwardKeys().reverse()
meta [ ' newFSKey ' ] = fsKey
jsonMeta = json . dumps ( meta )
plaintextMeta = jsonMeta
if sign :
2019-07-20 15:52:03 +00:00
signature = crypto . signing . ed_sign ( jsonMeta . encode ( ) + data , key = crypto . priv_key , encodeResult = True )
2019-07-21 16:15:20 +00:00
signer = crypto . pub_key
2019-07-18 23:07:18 +00:00
if len ( jsonMeta ) > 1000 :
raise onionrexceptions . InvalidMetadata ( ' meta in json encoded form must not exceed 1000 bytes ' )
# encrypt block metadata/sig/content
if encryptType == ' sym ' :
2019-07-20 15:52:03 +00:00
raise NotImplementedError ( " not yet implemented " )
2019-07-18 23:07:18 +00:00
elif encryptType == ' asym ' :
if stringvalidators . validate_pub_key ( asymPeer ) :
# Encrypt block data with forward secrecy key first, but not meta
jsonMeta = json . dumps ( meta )
2019-07-20 15:52:03 +00:00
jsonMeta = crypto . encryption . pub_key_encrypt ( jsonMeta , asymPeer , encodedData = True ) . decode ( )
2019-09-10 20:25:50 +00:00
data = crypto . encryption . pub_key_encrypt ( data , asymPeer , encodedData = False ) #.decode()
2019-07-21 16:15:20 +00:00
signature = crypto . encryption . pub_key_encrypt ( signature , asymPeer , encodedData = True ) . decode ( )
signer = crypto . encryption . pub_key_encrypt ( signer , asymPeer , encodedData = True ) . decode ( )
2019-07-18 23:07:18 +00:00
try :
onionrusers . OnionrUser ( asymPeer , saveUser = True )
except ValueError :
# if peer is already known
pass
else :
raise onionrexceptions . InvalidPubkey ( asymPeer + ' is not a valid base32 encoded ed25519 key ' )
# compile metadata
metadata [ ' meta ' ] = jsonMeta
2019-09-10 06:05:59 +00:00
if len ( signature ) > 0 : # I don't like not pattern
metadata [ ' sig ' ] = signature
metadata [ ' signer ' ] = signer
2019-07-18 23:07:18 +00:00
metadata [ ' time ' ] = createTime
# ensure expire is integer and of sane length
if type ( expire ) is not type ( None ) :
2019-09-09 00:21:36 +00:00
if not len ( str ( int ( expire ) ) ) < 20 : raise ValueError ( ' expire must be valid int less than 20 digits in length ' )
2019-07-18 23:07:18 +00:00
metadata [ ' expire ' ] = expire
# send block data (and metadata) to POW module to get tokenized block data
if use_subprocess :
payload = subprocesspow . SubprocessPOW ( data , metadata ) . start ( )
else :
payload = onionrproofs . POW ( metadata , data ) . waitForResult ( )
if payload != False :
try :
2019-07-24 17:22:19 +00:00
retData = onionrstorage . set_data ( payload )
2019-07-18 23:07:18 +00:00
except onionrexceptions . DiskAllocationReached :
logger . error ( allocationReachedMessage )
retData = False
else :
# Tell the api server through localCommand to wait for the daemon to upload this block to make statistical analysis more difficult
if localcommand . local_command ( ' /ping ' , maxWait = 10 ) == ' pong! ' :
if config . get ( ' general.security_level ' , 1 ) == 0 :
localcommand . local_command ( ' /waitforshare/ ' + retData , post = True , maxWait = 5 )
coredb . daemonqueue . daemon_queue_add ( ' uploadBlock ' , retData )
else :
pass
2019-07-20 15:52:03 +00:00
coredb . blockmetadb . add . add_to_block_DB ( retData , selfInsert = True , dataSaved = True )
2019-08-18 05:27:33 +00:00
if expire is None :
coredb . blockmetadb . update_block_info ( retData , ' expire ' , createTime + onionrvalues . DEFAULT_EXPIRE )
else :
coredb . blockmetadb . update_block_info ( retData , ' expire ' , expire )
2019-07-20 15:52:03 +00:00
blockmetadata . process_block_metadata ( retData )
2019-07-24 18:23:31 +00:00
2019-07-18 23:07:18 +00:00
if retData != False :
if plaintextPeer == onionrvalues . DENIABLE_PEER_ADDRESS :
2019-07-24 18:23:31 +00:00
events . event ( ' insertdeniable ' , { ' content ' : plaintext , ' meta ' : plaintextMeta , ' hash ' : retData , ' peer ' : bytesconverter . bytes_to_str ( asymPeer ) } , threaded = True )
2019-07-18 23:07:18 +00:00
else :
2019-07-24 18:23:31 +00:00
events . event ( ' insertblock ' , { ' content ' : plaintext , ' meta ' : plaintextMeta , ' hash ' : retData , ' peer ' : bytesconverter . bytes_to_str ( asymPeer ) } , threaded = True )
2019-08-13 22:28:53 +00:00
coredb . daemonqueue . daemon_queue_add ( ' remove_from_insert_list ' , data = dataNonce )
2019-07-18 23:07:18 +00:00
return retData