diff --git a/README.md b/README.md new file mode 100644 index 0000000..d63e74d --- /dev/null +++ b/README.md @@ -0,0 +1,40 @@ +# SuchWow! + +TBD + +## Setup + +``` +# initialize new wallet and retain seed +docker run --rm -it --name suchwow-wallet-init \ + -v $(pwd)/data:/root \ + lalanza808/wownero \ + wownero-wallet-cli \ + --daemon-address https://node.suchwow.xyz:443 \ + --generate-new-wallet /root/wow \ + --password zzzzzz \ + +# setup rpc process +docker run --rm -d --name suchwow-wallet \ + -v $(pwd)/data:/root \ + -p 8888:8888 \ + lalanza808/wownero \ + wownero-wallet-rpc \ + --daemon-address https://node.suchwow.xyz:443 \ + --wallet-file /root/wow \ + --password zzzzzz \ + --rpc-bind-port 8888 \ + --rpc-bind-ip 0.0.0.0 \ + --confirm-external-bind \ + --rpc-login xxxx:yyyy \ + --log-file /root/rpc.log + +# install python dependencies +python3 -m venv .venv +source .venv/bin/activate +pip install -r requirements.txt + +# setup secrets in config file outside of git +cp suchwow/config.example.py suchwow/config.py +vim !$ +``` diff --git a/suchwow/config.example.py b/suchwow/config.example.py index 7829d6f..95931a0 100644 --- a/suchwow/config.example.py +++ b/suchwow/config.example.py @@ -4,6 +4,11 @@ OIDC_CLIENT_SECRET = 'xxxxxxxxxx', OIDC_REDIRECT_URL = 'http://localhost:5000/auth' SECRET_KEY = 'yyyyyyyyyyyyy', SESSION_TYPE = 'filesystem' -UPLOAD_FOLDER = '/path/to/the/uploads' +DATA_FOLDER = '/path/to/the/uploads' ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'} -MAX_CONTENT_LENGTH = 16 * 1024 * 1024 \ No newline at end of file +MAX_CONTENT_LENGTH = 16 * 1024 * 1024 +WALLET_HOST = 'localhost' +WALLET_PORT = 8888 +WALLET_PROTO = 'http' +WALLET_USER = 'suchwow' +WALLET_PASS = 'zzzzzzzzzzzzzzz' diff --git a/suchwow/models.py b/suchwow/models.py index bf86cd6..721e03c 100644 --- a/suchwow/models.py +++ b/suchwow/models.py @@ -1,8 +1,9 @@ from peewee import * from datetime import datetime +from suchwow import config -db = SqliteDatabase('data/sqlite.db') +db = SqliteDatabase(f'{config.DATA_FOLDER}/db/sqlite.db') class Post(Model): id = AutoField() diff --git a/suchwow/routes/post.py b/suchwow/routes/post.py index ec9d85d..150e1cb 100644 --- a/suchwow/routes/post.py +++ b/suchwow/routes/post.py @@ -2,6 +2,7 @@ from os import path from flask import render_template, Blueprint, request, session, flash from flask import send_from_directory, redirect, url_for, current_app from werkzeug.utils import secure_filename +from suchwow import wownero from suchwow.models import Post from suchwow.utils.decorators import login_required from suchwow.utils.helpers import allowed_file @@ -12,8 +13,13 @@ bp = Blueprint("post", "post") @bp.route("/post/") def read(id): if Post.filter(id=id): - post = Post.get(Post.id == id) - return render_template("post/read.html", post=post) + wallet = wownero.Wallet() + post = Post.get(id=id) + if wallet.connected: + address = wallet.addresses(account=post.account_index)[0] + else: + address = "?" + return render_template("post/read.html", post=post, address=address) else: return "no meme there brah" @@ -37,15 +43,21 @@ def create(): return redirect(request.url) if file and allowed_file(file.filename): filename = secure_filename(file.filename) - save_path = path.join(current_app.config["UPLOAD_FOLDER"], filename) + save_path_base = path.join(current_app.config["DATA_FOLDER"], "uploads") + save_path = path.join(save_path_base, filename) file.save(save_path) # gen wallet + try: + wallet = wownero.Wallet() + account_index = wallet.new_account() + except: + account_index = 0 post = Post( title=post_title, text=request.form.get("text", ""), submitter=session["auth"]["preferred_username"], image_name=filename, - account_index=0, + account_index=account_index, address_index=0 ) post.save() @@ -54,4 +66,5 @@ def create(): @bp.route("/uploads/") def uploaded_file(filename): - return send_from_directory(current_app.config["UPLOAD_FOLDER"], filename) + file_path = path.join(current_app.config["DATA_FOLDER"], "uploads") + return send_from_directory(file_path, filename) diff --git a/suchwow/templates/about.html b/suchwow/templates/about.html index 4819974..302bfe2 100644 --- a/suchwow/templates/about.html +++ b/suchwow/templates/about.html @@ -2,7 +2,7 @@ {% block content %} -
+

About

Post memes! Have fun! Comment and talk shit! Earn WOW! (soon :tm:)

diff --git a/suchwow/templates/base.html b/suchwow/templates/base.html index 3ea1b8c..3817ae7 100644 --- a/suchwow/templates/base.html +++ b/suchwow/templates/base.html @@ -40,6 +40,7 @@ + SuchWow! diff --git a/suchwow/templates/post/read.html b/suchwow/templates/post/read.html index 8ba3cdc..3ff30be 100644 --- a/suchwow/templates/post/read.html +++ b/suchwow/templates/post/read.html @@ -2,8 +2,7 @@ {% block content %} -
- +
< Go Back {% if post.hidden %} @@ -11,8 +10,20 @@ {% else %}

{{ post.title }}

{{ post.text }}

+

Submitted by {{ post.submitter }} at {{ post.timestamp }}

+
-

Submitted by {{ post.submitter }} at {{ post.timestamp }}

+
+

Vote for this post by sending WOW to the following address:
{{ address }}

+
+

Comments

+ {% if comments %} + {% for comment in comments %} + {{ comment.text }} + {% endfor %} + {% else %} +

No comments yet.

+ {% endif %} {% endif %}
diff --git a/suchwow/wownero.py b/suchwow/wownero.py new file mode 100644 index 0000000..ce5fc5a --- /dev/null +++ b/suchwow/wownero.py @@ -0,0 +1,117 @@ +import requests +import six +import json +import operator +from suchwow import config +from decimal import Decimal + + +PICOWOW = Decimal('0.00000000001') + +class Wallet(object): + def __init__(self): + self.host = config.WALLET_HOST + self.port = config.WALLET_PORT + self.proto = config.WALLET_PROTO + self.username = config.WALLET_USER + self.password = config.WALLET_PASS + self.endpoint = '{}://{}:{}/json_rpc'.format( + self.proto, self.host, self.port + ) + self.auth = requests.auth.HTTPDigestAuth( + self.username, self.password + ) + + try: + r = self.height() + height = r['height'] + self.connected = True + except: + self.connected = False + + def make_wallet_rpc(self, method, params={}): + r = requests.get( + self.endpoint, + data=json.dumps({'method': method, 'params': params}), + auth=self.auth + ) + # print(r.status_code) + if 'error' in r.json(): + return r.json()['error'] + else: + return r.json()['result'] + + def height(self): + return self.make_wallet_rpc('get_height', {}) + + def spend_key(self): + return self.make_wallet_rpc('query_key', {'key_type': 'spend_key'})['key'] + + def view_key(self): + return self.make_wallet_rpc('query_key', {'key_type': 'view_key'})['key'] + + def seed(self): + return self.make_wallet_rpc('query_key', {'key_type': 'mnemonic'})['key'] + + def accounts(self): + accounts = [] + _accounts = self.make_wallet_rpc('get_accounts') + idx = 0 + self.master_address = _accounts['subaddress_accounts'][0]['base_address'] + for _acc in _accounts['subaddress_accounts']: + assert idx == _acc['account_index'] + accounts.append(_acc['account_index']) + idx += 1 + return accounts + + def new_account(self, label=None): + _account = self.make_wallet_rpc('create_account', {'label': label}) + return _account['account_index'] + + def addresses(self, account, addr_indices=None): + qdata = {'account_index': account} + if addr_indices: + qdata['address_index'] = addr_indices + _addresses = self.make_wallet_rpc('get_address', qdata) + addresses = [None] * (max(map(operator.itemgetter('address_index'), _addresses['addresses'])) + 1) + for _addr in _addresses['addresses']: + addresses[_addr['address_index']] = _addr['address'] + return addresses + + def new_address(self, account, label=None): + data = {'account_index': account, 'label': label} + _address = self.make_wallet_rpc('create_address', data) + return (_address['address_index'], _address['address']) + + def balances(self, account): + data = {'account_index': account} + _balance = self.make_wallet_rpc('get_balance', data) + return (from_atomic(_balance['balance']), from_atomic(_balance['unlocked_balance'])) + + def transfer(self, dest_address, amount, priority, account): + data = { + 'account_index': account, + 'destinations': [{'address': dest_address, 'amount': to_atomic(amount)}], + 'priority': priority, + 'unlock_time': 0, + 'get_tx_key': True, + 'get_tx_hex': True, + 'new_algorithm': True, + 'do_not_relay': False, + 'ring_size': 22 + } + transfer = self.make_wallet_rpc('transfer', data) + return transfer + + +def to_atomic(amount): + if not isinstance(amount, (Decimal, float) + six.integer_types): + raise ValueError("Amount '{}' doesn't have numeric type. Only Decimal, int, long and " + "float (not recommended) are accepted as amounts.") + return int(amount * 10**11) + +def from_atomic(amount): + return (Decimal(amount) * PICOWOW).quantize(PICOWOW) + +def as_wownero(amount): + return Decimal(amount).quantize(PICOWOW)