Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V1.1 updates #4

Open
wants to merge 9 commits into
base: v1.1-updates
Choose a base branch
from
60 changes: 60 additions & 0 deletions .idea/workspace.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

61 changes: 52 additions & 9 deletions api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
import re
import falcon
import pickle as pickle

import sys
import magic
import psutil
import time
from lib.ipfs import IPFSTools
from lib.db import MemeChainDB
from lib.memechain import MemeTx, Validate
Expand All @@ -23,6 +26,18 @@
# Memechain allowed content types
ALLOWED_IMAGE_TYPES = ('image/gif', 'image/jpeg', 'image/png')


def check_running():
counter = 0
for q in psutil.process_iter():
if q.name().find('sync'):
if len(q.cmdline())>1 and 'sync.py' in q.cmdline()[1]:
counter = counter + 1
if counter > 1:
return True
else:
return False

def validate_image_type(req, resp, resource, params):
if req.content_type not in ALLOWED_IMAGE_TYPES:
logger.error('COMMAND %s Failed %s: %s'
Expand All @@ -40,6 +55,14 @@ def validate_ip_address(req, resp, resource, params):
raise falcon.HTTPError(falcon.HTTP_401, 'Memechain Error',
"IP address not allowed.")

def validate_address(req, resp, resource, params):
if req.params['addr'] == '':
logger.error('COMMAND %s Failed %s: %s'
% ('validate_address', 'Memechain Error',
"Address param must be not null."))
raise falcon.HTTPError(falcon.HTTP_400, 'Memechain Error',
"Address param must be not null.")

class get_info(object):
def on_get(self, req, resp):
logger.info('COMMAND %s Received' % self.__class__.__name__)
Expand Down Expand Up @@ -227,20 +250,31 @@ class add_meme(object):

@falcon.before(validate_ip_address)
@falcon.before(validate_image_type)
@falcon.before(validate_address)

def on_post(self, req, resp):
logger.info('COMMAND %s Received' % self.__class__.__name__)
db = MemeChainDB(os.path.join(config['DATA_DIR'], 'memechain.json'))



while check_running():
time.sleep(3)
logger.info('Waiting for the synchronization process to complete add_meme')


# Generate random placeholder img name
img_placeholder_name = str(random.random()).split(".")[1]

ext = mimetypes.guess_extension(req.content_type)
if ext == '.jpe':
ext = '.jpg'


name = '{img_name}{ext}'.format(img_name=img_placeholder_name, ext=ext)
image_path = os.path.join(config['DATA_DIR'], name)


# Write image to local storage
with io.open(image_path, 'wb') as image_file:
while True:
Expand All @@ -250,6 +284,12 @@ def on_post(self, req, resp):

image_file.write(chunk)


if magic.from_file(image_path).lower().find(mimetypes.guess_extension(req.content_type)[1:]) < 0:
raise falcon.HTTPBadRequest( "Memechain error",
"Meme has not passed validation, file extension is wrong.")


# Check file size
meme_filesize = os.path.getsize(image_path) * 0.000001 # in MB

Expand All @@ -271,18 +311,16 @@ def on_post(self, req, resp):
"Meme was not able to be added on IPFS."))
raise falcon.HTTPError(falcon.HTTP_400, "Memechain error",
"Meme was not able to be added on IPFS.")

# Rename local img file to ipfs_id for easy reference
new_name = '{img_name}{ext}'.format(img_name=ipfs_id, ext=ext)
os.rename(image_path, os.path.join(config['DATA_DIR'], new_name))

# Add to Kekcoin chain
memetx = MemeTx(ipfs_id)
memetx = MemeTx(ipfs_id, req.params['addr'])
prev_block_memes = db.get_prev_block_memes()

if prev_block_memes:
memetx.generate_hashlink(prev_block_memes)

try:
Validate(memetx, db=db, ipfs_dir=config['DATA_DIR'],
prev_block_memes=prev_block_memes)
Expand All @@ -293,13 +331,18 @@ def on_post(self, req, resp):

logger.error('COMMAND %s Failed %s: %s'
% (self.__class__.__name__, 'Memechain Error',
"Meme has not passed memechain validation, file extension not supported."))
"Meme has not passed memechain validation, file extension not supported.%s") )
raise falcon.HTTPError(falcon.HTTP_400, "Memechain error",
"Meme has not passed validation, file extension not supported.")
"Meme has not passed validation, file extension not supported.%s" % e )

if memetx.is_meme_valid():


memetx.blockchain_write()

db.add_meme(**{"ipfs_id": ipfs_id, "hashlink": memetx.get_hashlink(),
"txid": memetx.get_txid(), "author": memetx.get_author(), "block": 0, "imgformat": ext[1:],
"status": "unconfirm"})
resp.status = falcon.HTTP_201
resp.set_header('Powered-By', 'Memechain')

Expand All @@ -319,7 +362,7 @@ def on_post(self, req, resp):
"Meme has not passed validation: ")
else:
# Genesis block logic
memetx = MemeTx(ipfs_id)
memetx = MemeTx(ipfs_id, req.params['addr'])
memetx.generate_genesis_hashlink()

memetx.blockchain_write()
Expand Down Expand Up @@ -358,4 +401,4 @@ def on_post(self, req, resp):
app.add_route('/api/getmemeimgbyhash/{ipfs_id}', get_meme_img_by_hash())

# Add meme command
app.add_route('/api/addmeme', add_meme())
app.add_route('/api/addmeme', add_meme())
11 changes: 8 additions & 3 deletions config.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
{
"DATA_DIR" : "/root/memechain-api/data",
"DATA_DIR" : "/home/parallels/memechain-api/data",
"RPC_USER" : "user",
"RPC_PASS" : "pass",
"RPC_IP" : "127.0.0.1",
"RPC_PORT" : "13377",
"ALLOWED_IP_ADDRESSES" : []
}
"ALLOWED_IP_ADDRESSES" : [],
"ALLOWED_IMAGE_EXTENSIONS" : ["jpg","png","gif"],
"ENABLE_LOG_MEMTX_NOT_FOUND" : false,
"MULTIPLE_SYNC_RUNNING" : false,
"CHECK_FILES_ON_RUNNING" : true
}
39 changes: 15 additions & 24 deletions lib/blockchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ def get_blockchain_info():
Returns:
getinfo output (dict)
"""
rpc = AuthServiceProxy(("http://%s:%s@127.0.0.1:%s/") %
(config['RPC_USER'], config['RPC_PASS'], config['RPC_PORT']))
rpc = AuthServiceProxy(("http://%s:%s@%s:%s/") % (config['RPC_USER'], config['RPC_PASS'], config['RPC_IP'], config['RPC_PORT']))

return rpc.getinfo()

Expand All @@ -42,8 +41,7 @@ def get_block_height():
Returns:
Block height (int)
"""
rpc = AuthServiceProxy(("http://%s:%s@127.0.0.1:%s/") %
(config['RPC_USER'], config['RPC_PASS'], config['RPC_PORT']))
rpc = AuthServiceProxy(("http://%s:%s@%s:%s/") % (config['RPC_USER'], config['RPC_PASS'], config['RPC_IP'], config['RPC_PORT']))

return rpc.getblockcount()

Expand All @@ -58,36 +56,34 @@ def get_block_txs(height):
Returns:
List of transaction ids (array)
"""
rpc = AuthServiceProxy(("http://%s:%s@127.0.0.1:%s/") %
(config['RPC_USER'], config['RPC_PASS'], config['RPC_PORT']))
rpc = AuthServiceProxy(("http://%s:%s@%s:%s/") % (config['RPC_USER'], config['RPC_PASS'], config['RPC_IP'], config['RPC_PORT']))

block_hash = rpc.getblockhash(height)
block = rpc.getblock(block_hash)

return block['tx']


def get_input():
def get_input(addr):
"""
Method used to get unspent inputs

Returns:
Transaction object (dict)
"""
rpc = AuthServiceProxy(("http://%s:%s@127.0.0.1:%s/") %
(config['RPC_USER'], config['RPC_PASS'], config['RPC_PORT']))
rpc = AuthServiceProxy(("http://%s:%s@%s:%s/") % (config['RPC_USER'], config['RPC_PASS'], config['RPC_IP'], config['RPC_PORT']))

unspent = rpc.listunspent()

for tx in unspent:
if float(tx["amount"]) > 0.01:
if float(tx["amount"]) > 0.01 and addr == tx['address']:
return tx
else:
raise Exception(
"No valid inputs, inputs must be greater than 0.001 KEK")


def create_raw_op_return_transaction(metadata):
def create_raw_op_return_transaction(metadata,addr):
"""
Method used to create a transaction with embedded data through OP_RETURN

Expand All @@ -98,8 +94,7 @@ def create_raw_op_return_transaction(metadata):
Raw transaction (hex)
Author address (str)
"""
rpc = AuthServiceProxy(("http://%s:%s@127.0.0.1:%s/") %
(config['RPC_USER'], config['RPC_PASS'], config['RPC_PORT']))
rpc = AuthServiceProxy(("http://%s:%s@%s:%s/") % (config['RPC_USER'], config['RPC_PASS'], config['RPC_IP'], config['RPC_PORT']))

if sys.getsizeof(metadata) > MAX_OP_RETURN_BYTES:
raise Exception("Metadata size is over MAX_OP_RETURN_BYTES")
Expand All @@ -108,11 +103,11 @@ def create_raw_op_return_transaction(metadata):
raise Exception(
"This tool set does not currently support reading op_return data with less than 4 chars")

input_tx = get_input()
input_tx = get_input(addr)

init_raw_tx = rpc.createrawtransaction([{"txid": input_tx["txid"], "vout": input_tx["vout"]}], {
rpc.getnewaddress(): TX_BURN_AMOUNT, input_tx["address"]: round(float(input_tx["amount"]) - TX_BURN_AMOUNT - TX_FEE_RATE, 8)})

for vout in rpc.decoderawtransaction(init_raw_tx)["vout"]:
if float(vout["value"]) == TX_BURN_AMOUNT:
oldScriptPubKey = "19" + vout["scriptPubKey"]["hex"]
Expand All @@ -138,8 +133,7 @@ def sign_raw_transaction(tx):
Returns:
Signed raw transaction (hex)
"""
rpc = AuthServiceProxy(("http://%s:%s@127.0.0.1:%s/") %
(config['RPC_USER'], config['RPC_PASS'], config['RPC_PORT']))
rpc = AuthServiceProxy(("http://%s:%s@%s:%s/") % (config['RPC_USER'], config['RPC_PASS'], config['RPC_IP'], config['RPC_PORT']))

return rpc.signrawtransaction(tx)["hex"]

Expand All @@ -154,8 +148,7 @@ def send_raw_transaction(tx_hex):
Returns:
Transaction id (str)
"""
rpc = AuthServiceProxy(("http://%s:%s@127.0.0.1:%s/") %
(config['RPC_USER'], config['RPC_PASS'], config['RPC_PORT']))
rpc = AuthServiceProxy(("http://%s:%s@%s:%s/") % (config['RPC_USER'], config['RPC_PASS'], config['RPC_IP'], config['RPC_PORT']))

return rpc.sendrawtransaction(tx_hex)

Expand All @@ -171,8 +164,7 @@ def get_op_return_data(txid):
Embedded metadata (str)
Author address (str)
"""
rpc = AuthServiceProxy(("http://%s:%s@127.0.0.1:%s/") %
(config['RPC_USER'], config['RPC_PASS'], config['RPC_PORT']))
rpc = AuthServiceProxy(("http://%s:%s@%s:%s/") % (config['RPC_USER'], config['RPC_PASS'], config['RPC_IP'], config['RPC_PORT']))

raw_tx = rpc.getrawtransaction(txid)
tx_data = rpc.decoderawtransaction(raw_tx)
Expand All @@ -191,7 +183,7 @@ def get_op_return_data(txid):
author = None
else:
op_return_data = None
author = None
author = None

return op_return_data, author

Expand All @@ -205,8 +197,7 @@ def get_tx_burn_amount(txid):
Returns:
Sum of input values, i.e. burn amount (float)
"""
rpc = AuthServiceProxy(("http://%s:%s@127.0.0.1:%s/") %
(config['RPC_USER'], config['RPC_PASS'], config['RPC_PORT']))
rpc = AuthServiceProxy(("http://%s:%s@%s:%s/") % (config['RPC_USER'], config['RPC_PASS'], config['RPC_IP'], config['RPC_PORT']))

raw_tx = rpc.getrawtransaction(txid)
tx_data = rpc.decoderawtransaction(raw_tx)
Expand Down
15 changes: 13 additions & 2 deletions lib/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,24 @@ class MemeChainDB(object):
def __init__(self, db_path):
self._db = TinyDB(db_path)

def add_meme(self, ipfs_id, hashlink, txid, block, imgformat, author):
def add_meme(self, ipfs_id, hashlink, txid, block, imgformat, author, status):
self._db.insert({"ipfs_id": ipfs_id, "hashlink": hashlink,
"txid": txid, "block": block, "imgformat" : imgformat, "author" : author})
"txid": txid, "block": block, "imgformat" : imgformat, "author" : author, "status": status})

def remove_meme(self, ipfs_id):
self._db.remove(Query().ipfs_id == ipfs_id)

def get_memechain_height(self):
return len(self._db)

def update_meme(self, ipfs_id, block):
memes = self._db.search(Query().ipfs_id == ipfs_id)
print(memes)
for meme in memes:
meme["block"] = block
meme["status"] = "confirm"
self._db.write_back(memes)

def search_by_block(self, block):
"""
Get a meme entry using block number as the search parameter
Expand Down Expand Up @@ -119,5 +127,8 @@ def get_prev_block_memes(self):
def get_last_meme(self):
return Index(self._db)[-1]

def get_all_memes(self):
return self._db.search(Query().ipfs_id.exists())

def get_meme_height_by_ipfs_id(self, ipfs_id):
return Index(self._db).return_index(self._db.get(Query().ipfs_id == ipfs_id)) + 1
Loading