setting up transfers

This commit is contained in:
lza_menace 2020-09-11 13:59:48 -07:00
parent 215b9410c7
commit 5d15852607
11 changed files with 200 additions and 24 deletions

View file

@ -1,7 +1,7 @@
#!/bin/bash
source .venv/bin/activate
export FLASK_APP=wowstash/run.py
export FLASK_APP=wowstash/app.py
export FLASK_SECRETS=config.py
export FLASK_DEBUG=1
flask eviscerate
flask $1

View file

@ -1,38 +1,107 @@
from io import BytesIO
from base64 import b64encode
from qrcode import make as qrcode_make
from flask import request, render_template, session, redirect, url_for, current_app
from decimal import Decimal
from flask import request, render_template, session, redirect, url_for, current_app, flash
from flask_login import login_required, current_user
from wowstash.blueprints.wallet import wallet_bp
from wowstash.library.jsonrpc import wallet, daemon
from wowstash.factory import login_manager
from wowstash.models import User
from wowstash.forms import Send
from wowstash.factory import login_manager, db
from wowstash.models import User, Transaction
@wallet_bp.route("/wallet/dashboard")
@login_required
def dashboard():
all_transfers = list()
send_form = Send()
_address_qr = BytesIO()
user = User.query.get(current_user.id)
wallet_height = wallet.height()['height']
daemon_height = daemon.height()['height']
subaddress = wallet.get_address(0, user.subaddress_index)
balances = wallet.get_balance(0, user.subaddress_index)
transfers = wallet.get_transfers(0, user.subaddress_index)
txs_queued = Transaction.query.filter_by(from_user=user.id)
for type in transfers:
for tx in transfers[type]:
all_transfers.append(tx)
# data = {'account_index': account_index, 'address_indices': [subaddress_index]}
# _balance = self.make_rpc('get_balance', data)
# locked = from_atomic(_balance['per_subaddress'][0]['balance'])
# unlocked = from_atomic(_balance['per_subaddress'][0]['unlocked_balance'])
# return (float(locked), float(unlocked))
from wowstash.library.jsonrpc import from_atomic
from pprint import pprint
bal = wallet.make_rpc('get_balance', {'account_index': 0, 'address_indices': [user.subaddress_index]})
# print(from_atomic(bal))
pprint(bal)
# pprint(transfers)
# pprint(balances)
qr_uri = f'wownero:{subaddress}?tx_description="{current_user.email}"'
address_qr = qrcode_make(qr_uri).save(_address_qr)
qrcode = b64encode(_address_qr.getvalue()).decode()
return render_template(
"wallet/dashboard.html",
wallet_height=wallet_height,
daemon_height=daemon_height,
subaddress=subaddress,
balances=balances,
all_transfers=all_transfers,
qrcode=qrcode
qrcode=qrcode,
send_form=send_form,
txs_queued=txs_queued
)
@wallet_bp.route("/wallet/send", methods=["GET", "POST"])
@login_required
def send():
send_form = Send()
redirect_url = url_for('wallet.dashboard') + "#send"
if send_form.validate_on_submit():
address = str(send_form.address.data)
# Check if Wownero wallet is available
if wallet.connected is False:
flash('Wallet RPC interface is unavailable at this time. Try again later.')
return redirect(redirect_url)
# Check if user funds flag is locked
if current_user.funds_locked:
flash('You currently have transactions pending and transfers are locked. Try again later.')
return redirect(redirect_url)
# Quick n dirty check to see if address is WOW
if len(address) not in [97, 108]:
flash('Invalid Wownero address provided.')
return redirect(redirect_url)
# Make sure the amount provided is a number
try:
amount = Decimal(send_form.amount.data)
except:
flash('Invalid Wownero amount specified.')
return redirect(redirect_url)
# Lock user funds
user = User.query.get(current_user.id)
user.funds_locked = True
db.session.commit()
# Queue the transaction
tx = Transaction(
from_user=user.id,
address=address,
amount=amount,
)
db.session.add(tx)
db.session.commit()
# Redirect back
flash('Successfully queued transfer.')
return redirect(redirect_url)
else:
for field, errors in send_form.errors.items():
flash(f'{send_form[field].label}: {", ".join(errors)}')
return redirect(redirect_url)

View file

@ -1,12 +1,23 @@
from wowstash.library.jsonrpc import wallet
from wowstash.models import Transaction
from wowstash.factory import db
@app.errorhandler(404)
# @app.errorhandler(404)
def not_found(error):
return make_response(jsonify({
'error': 'Page not found'
}), 404)
@app.cli.command('initdb')
def initdb():
from wowstash.models import *
# @app.cli.command('initdb')
def init_db():
db.create_all()
# @app.cli.command('send_transfers')
def send_transfers():
txes = Transaction.query.all()
for i in txes:
print(i)
# tx = wallet.transfer(
# 0, current_user.subaddress_index, address, amount
# )

View file

@ -20,7 +20,7 @@ PASSWORD_SALT = 'salt here' # database salts
SECRET_KEY = 'secret session key here' # encrypts the session token
# Session
PERMANENT_SESSION_LIFETIME = 1800 # 30 minute session expiry
PERMANENT_SESSION_LIFETIME = 1800 # 60 minute session expiry
SESSION_TYPE = 'redis'
SESSION_COOKIE_NAME = 'wowstash'
SESSION_COOKIE_SECURE = False

View file

@ -1,5 +1,6 @@
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_wtf.csrf import CSRFProtect
from flask_session import Session
from flask_bcrypt import Bcrypt
from flask_login import LoginManager
@ -14,10 +15,11 @@ bcrypt = None
def _setup_db(app: Flask):
global db
uri = 'postgresql+psycopg2://{user}:{pw}@{url}/{db}'.format(
uri = 'postgresql+psycopg2://{user}:{pw}@{host}:{port}/{db}'.format(
user=config.DB_USER,
pw=config.DB_PASS,
url=config.DB_HOST,
host=config.DB_HOST,
port=config.DB_PORT,
db=config.DB_NAME
)
app.config['SQLALCHEMY_DATABASE_URI'] = uri
@ -27,8 +29,6 @@ def _setup_db(app: Flask):
db.create_all()
def _setup_session(app: Flask):
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_COOKIE_NAME'] = app.config.get('SESSION_COOKIE_NAME', 'wowstash')
app.config['SESSION_REDIS'] = Redis(
host=app.config['REDIS_HOST'],
port=app.config['REDIS_PORT']
@ -52,6 +52,7 @@ def create_app():
_setup_db(app)
_setup_session(app)
_setup_bcrypt(app)
CSRFProtect(app)
login_manager = LoginManager()
login_manager.init_app(app)
@ -60,7 +61,8 @@ def create_app():
@login_manager.user_loader
def load_user(user_id):
from wowstash.models import User
return User.query.get(user_id)
user = User.query.get(user_id)
return user
# template filters
@app.template_filter('datestamp')
@ -68,6 +70,27 @@ def create_app():
d = datetime.fromtimestamp(s)
return d.strftime('%Y-%m-%d %H:%M:%S')
# commands
@app.cli.command('send_transfers')
def send_transfers():
from wowstash.models import Transaction, User
from wowstash.library.jsonrpc import wallet
from decimal import Decimal
txes = Transaction.query.filter_by(sent=False)
for tx in txes:
user = User.query.get(tx.from_user)
new_tx = wallet.transfer(
0, user.subaddress_index, tx.address, Decimal(tx.amount)
)
if 'message' in new_tx:
print(f'Failed to send tx {tx.id}. Reason: {new_tx["message"]}')
else:
tx.sent = True
user.funds_locked = False
db.session.commit()
print(f'Successfully sent tx! {new_tx}')
# Routes
from wowstash.blueprints.auth import auth_bp
from wowstash.blueprints.wallet import wallet_bp

View file

@ -13,3 +13,7 @@ class Register(FlaskForm):
class Login(FlaskForm):
email = StringField('Email Address:', validators=[DataRequired()], render_kw={"placeholder": "Email", "class": "form-control", "type": "email"})
password = StringField('Password:', validators=[DataRequired()], render_kw={"placeholder": "Password", "class": "form-control", "type": "password"})
class Send(FlaskForm):
address = StringField('Destination Address:', validators=[DataRequired()], render_kw={"placeholder": "Wownero address", "class": "form-control"})
amount = StringField('Amount:', validators=[DataRequired()], render_kw={"placeholder": "Amount to send", "class": "form-control"})

View file

@ -78,6 +78,22 @@ class Wallet(JSONRPC):
}
return self.make_rpc('get_transfers', data)
def transfer(self, account_index, subaddress_index, dest_address, amount):
data = {
'account_index': account_index,
'subaddr_indices': [subaddress_index],
'destinations': [{'address': dest_address, 'amount': to_atomic(amount)}],
'priority': 0,
'unlock_time': 0,
'get_tx_key': True,
'get_tx_hex': True,
'new_algorithm': True,
'do_not_relay': False,
'ring_size': 22
}
transfer = self.make_rpc('transfer', data)
return transfer
class Daemon(JSONRPC):
def __init__(self, **kwargs):

View file

@ -1,4 +1,4 @@
from sqlalchemy import Column, Integer, DateTime, String
from sqlalchemy import Column, Integer, DateTime, String, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.sql import func
from wowstash.factory import db
@ -14,6 +14,7 @@ class User(db.Model):
email = db.Column(db.String(50), unique=True, index=True)
subaddress_index = db.Column(db.Integer)
registered_on = db.Column(db.DateTime, server_default=func.now())
funds_locked = db.Column(db.Boolean, default=False)
@property
def is_authenticated(self):
@ -36,3 +37,17 @@ class User(db.Model):
def __repr__(self):
return self.username
class Transaction(db.Model):
__tablename__ = 'transactions'
id = db.Column('tx_id', db.Integer, primary_key=True)
from_user = db.Column(db.Integer, ForeignKey(User.id))
sent = db.Column(db.Boolean, default=False)
address = db.Column(db.String(120))
amount = db.Column(db.String(120))
date = db.Column(db.DateTime, server_default=func.now())
def __repr__(self):
return self.id

View file

@ -525,10 +525,15 @@ header.masthead .header-content-lg {
.dashboard-buttons {
width: 100%;
margin: 2em auto;
margin: 3em auto;
display: block;
}
.dashboard-button {
display: inline;
}
.send-form {
width: 60%;
margin: 0 auto;
}

View file

@ -31,7 +31,6 @@
</form>
<hr>
<p class="small">Click <a href="{{ url_for('auth.register') }}" class="">here</a> if you need to register.</p>
</div>
</div>
</div>

View file

@ -21,10 +21,10 @@
<p class="inline small">({{ balances.0 }} locked)</p>
<span class="dashboard-buttons">
<div class="col-sm-6 dashboard-button">
<a class="btn btn-lg btn-link btn-outline btn-xl js-scroll-trigger" href="#transfers">List Tx</a>
<a class="btn btn-lg btn-link btn-outline btn-xl js-scroll-trigger" href="#transfers">List</a>
</div>
<div class="col-sm-6 dashboard-button">
<a class="btn btn-lg btn-link btn-outline btn-xl" href="#">Send Tx</a>
<a class="btn btn-lg btn-link btn-outline btn-xl js-scroll-trigger" href="#send">Send</a>
</div>
</span>
</div>
@ -50,7 +50,7 @@
<td>{{ tx.timestamp | datestamp }}</td>
<td>{{ tx.type }}</td>
<td><a href="https://wownero.club/transaction/{{ tx.txid }}" target="_blank">{{ tx.txid | truncate(12) }}</a></td>
<td>{{ tx.amount / 100000000000 }}</td>
<td>{% if tx.type == 'out' %}{{ tx.destinations.0.amount / 100000000000 }}{% else %}{{ tx.amount / 100000000000 }}{% endif %} WOW</td>
<td>{{ tx.confirmations }}</td>
<td>{{ tx.height }}</td>
<td>{{ tx.fee / 100000000000 }} WOW</td>
@ -61,6 +61,40 @@
</div>
</section>
<section class="section2" id="send">
<div class="container-slim">
<div class="section-heading text-center">
<h2>Send</h2>
{% if current_user.funds_locked %}
<p>Sending funds is currently locked due to a transfer already in progress. Please try again in a few minutes. Pending transfers:</p>
<ul>
{% for tx in txs_queued %}
<li class="slim small">{{ tx.amount }} - {{ tx.address }}</li>
{% endfor %}
</ul>
{% else %}
<form method="POST" action="{{ url_for('wallet.send') }}" class="send-form">
{{ send_form.csrf_token }}
{% for f in send_form %}
{% if f.name != 'csrf_token' %}
<div class="form-group">
{{ f.label }}
{{ f }}
</div>
{% endif %}
{% endfor %}
<ul>
{% for field, errors in send_form.errors.items() %}
<li>{{ send_form[field].label }}: {{ ', '.join(errors) }}</li>
{% endfor %}
</ul>
<input type="submit" value="Send" class="btn btn-link btn-outline btn-xl">
</form>
{% endif %}
</div>
</div>
</section>
{% include 'footer.html' %}
{% include 'scripts.html' %}